Skip to content

feat: Slack channel canvas shows deployment status - #33

Merged
mikhail-dcl merged 11 commits into
mainfrom
feat/slack-canvas-deploy-status
Jul 20, 2026
Merged

feat: Slack channel canvas shows deployment status#33
mikhail-dcl merged 11 commits into
mainfrom
feat/slack-canvas-deploy-status

Conversation

@mikhail-dcl

Copy link
Copy Markdown
Collaborator

Keeps the Pulse Slack channel's canvas in sync with deployments (dev and prd): which branch/tag and commit is running, the outcome of the latest deploy attempt, since when — updated automatically on every deployment, with no Slack app (delivery is a member-created Workflow Builder webhook).

How it works

  • A new workflow (slack-canvas.yml) triggers on deployment_status — every deploy flow already ends in dcl-deploy-action, which creates a GitHub Deployment whose status decentraland-bot drives through the real container rollout. Filtered to task == "dcl/container-deployment" in states in_progress|success|failure|error; no existing deploy workflow is modified.
  • The composite action (.github/actions/slack-canvas-status) renders both environments statelessly from the GitHub Deployments API (newest deployment → "last deploy" line; newest success → "running" line; failure streaks render ⚠️ no recent success) and POSTs nine plain-text variables to a Slack Workflow Builder webhook whose Update a canvas step replaces the channel canvas wholesale.
  • The canvas is therefore CI-owned; render is idempotent, so any run fully repairs it.

Setup required after merge

  1. Create the Slack workflow (webhook trigger + Update a canvas in Replace mode) — click path and canvas template in docs/slack-canvas.md.
  2. Add repo secret SLACK_CANVAS_WEBHOOK_URL.
  3. Run the Slack Canvas Deploy Status dispatch once to validate end-to-end.

Testing

  • test-render.sh + test-flow.sh (mock GitHub API + webhook; 5 scenarios incl. failed-newest/succeeded-older, all-failures page, API error, webhook rejection) — wired into CI as the slack-canvas-tests job.
  • actionlint clean on all workflows.
  • Reviewed twice (feature review + rework review); all findings addressed.

🤖 Generated with Claude Code

mikhail-dcl and others added 10 commits July 17, 2026 18:22
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No Slack app needed: CI renders both environments statelessly from the
GitHub Deployments API and POSTs to a member-created Slack workflow whose
Update-a-canvas step replaces the channel canvas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #33 — feat: Slack channel canvas shows deployment status

Well-crafted PR. The stateless, idempotent design is the right call — every run reconstructs full state from the Deployments API, so any event self-heals the canvas without needing persistent storage. Secrets are properly scoped, permissions are least-privilege, the BASH_SOURCE guard correctly supports both sourcing (for tests) and direct execution, and jq --arg handles all untrusted API data safely. CI is green (all 5 checks pass). No P0 or P1 issues found.

Findings

[P2] echo-embedded fmt_time failure silently swallowed in render_last_deploy_line (update-canvas.sh:73)
render_last_deploy_line wraps fmt_time in echo "... $(fmt_time ...) ..."set -e does not catch failures of command substitutions embedded in a simple command's arguments (the echo itself succeeds). In contrast, render_running_line uses line+="... $(fmt_time ...)", where the assignment does propagate the failure. In practice this only matters if the GitHub API returns a malformed timestamp (extremely unlikely on ubuntu-latest with GNU date), but the asymmetry is worth noting. A local ts; ts="$(fmt_time ...)" before the echo would make both functions consistent.

[P2] Superseded v1 plan doc ships in the PR (docs/superpowers/plans/)
The plan (774 lines) describes the abandoned Slack-app design and references secrets that won't exist (SLACK_BOT_TOKEN, SLACK_PULSE_CHANNEL_ID). The "SUPERSEDED" banner is clear, but shipping stale planning docs that name non-existent secrets is a potential confusion vector for future readers or automated agents. Consider whether these should live in git history only (omit from this PR) rather than as shipped documentation. The spec (docs/superpowers/specs/) is fine — it has a v2 banner that accurately describes the merged implementation.

[P2] No test coverage for statuses-endpoint failure path
The gh_error scenario only exercises the deployments endpoint returning HTTP 500. A separate scenario where a specific deployment's statuses endpoint fails would confirm the latest_statusexit 1set -e propagation chain works end-to-end (it does — I verified manually — but a regression test would be valuable).

[P2] actions/checkout@v4 pinned by tag, not SHA (slack-canvas.yml:30)
Standard supply-chain hardening pins third-party actions to a commit SHA. Low risk given this is the official actions/checkout, but worth aligning with best practices.

[P2] No --max-time on curl calls
A hung connection to the GitHub API or Slack webhook could stall the job until the 5-minute timeout-minutes kills the whole workflow run. Adding --max-time 30 (or similar) to gh_get and the webhook POST would fail faster and produce a clearer error.

[P2] Minor: all-deployments-without-status edge case
If every deployment on the page has zero statuses (all skipped via continue), the canvas shows RUNNING_LINE="⚠️ no recent success" alongside LAST_LINE="—", which is slightly contradictory. Extremely unlikely in practice (statuses arrive almost immediately), but a comment in collect_env would clarify the intent.

Security

No security issues found. Secrets are step-scoped env vars (never written to disk or echoed). Webhook URL is a GitHub Actions secret with automatic log redaction. All API data flows through jq --arg for safe JSON escaping — no shell injection vectors. Workflow permissions are minimal (contents: read, deployments: read). The ref: main checkout prevents a malicious deploy branch from smuggling modified action code.

Architecture

Sound design. The stateless full-render approach with cancel-in-progress: true is correct for this use case — any run is self-consistent and the newest one always wins. The MAX_DEPLOYMENTS=30 ceiling is reasonable (the loop breaks early on the first success, so the common case is 1–2 API calls per environment). Test coverage is thorough: 5 scenarios covering normal, empty, API error, webhook rejection, and degraded states.

Git conventions (ADR-6) ✅

  • PR title feat: Slack channel canvas shows deployment status — correct semantic commit format
  • Branch feat/slack-canvas-deploy-status — correct <type>/<summary> pattern

Reviewed by Jarvis 🤖 · Requested by Mikhail Agapov (<@U04A7TYN13L>) via Slack

Comment thread .github/actions/slack-canvas-status/update-canvas.sh
Comment thread .github/workflows/slack-canvas.yml
Comment thread .github/actions/slack-canvas-status/update-canvas.sh Outdated
…tusless edge

- --max-time 30 on GitHub API and webhook curls
- shopt -s inherit_errexit so failures inside $(render/jq) assignments propagate
- all-statusless page keeps both placeholder lines instead of contradicting itself
- new flow scenarios: statuses-endpoint failure, statusless; superseded v1 plan doc untracked

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikhail-dcl

Copy link
Copy Markdown
Collaborator Author

Review addressed in 13ae07e — all six P2s:

  • fmt_time swallowed failures: fixed at the root with shopt -s inherit_errexit (the suggested assignment split alone was inert — both render functions run inside command substitutions, which don't inherit errexit; detail in the inline thread). Verified: malformed timestamp now aborts the run.
  • curl timeouts: --max-time 30 on both API and webhook calls.
  • statuses-endpoint failure coverage: new gh_status_error flow scenario (non-zero exit, error body on stderr, webhook not called).
  • all-statusless contradiction: went one step past the suggested comment — the ⚠️ no recent success fallback is now gated on a last-deploy line existing, so an all-statusless page keeps both placeholders; pinned by the new statusless scenario.
  • superseded v1 plan doc: removed from the tree (repo convention keeps plan docs untracked; it survives in git history).
  • checkout SHA pinning: kept the tag pin for consistency with every other workflow in this repo — repo-wide SHA pinning would be a separate chore (inline thread).

Flow suite is now 7 scenarios; render + flow green locally and in the slack-canvas-tests CI job.

🤖 Generated with Claude Code

@mikhail-dcl
mikhail-dcl merged commit 495adcd into main Jul 20, 2026
5 checks passed
@mikhail-dcl
mikhail-dcl deleted the feat/slack-canvas-deploy-status branch July 20, 2026 16:08
mikhail-dcl added a commit that referenced this pull request Aug 4, 2026
WebTransport (#25) + Slack canvas (#33). Resolutions: metrics files keep
both worlds (per-transport counters + latency histograms); drain-cycle and
RTT histograms stay top-level on TransportSnapshot (ENet-side measurements);
ENetHostedService keeps main's class shape (no ITransport) plus the
continent resolver; five hand-built test snapshots gained ByTransport.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants