Development
Repository layout, test tiers, CI and deploy workflows, and the pre-push suite.
Layout
app/ Worker API + CLI (Worker: zudo-slack-notify-app)
src/ index.ts (handler), notification.ts (validation and rendering)
cli/notify.ts CLI sender
tests/ unit, CLI, and workerd tests
examples/ sample payloads
slack-app-manifest.json
wrangler.toml
doc/ zudo-doc site (Worker: zudo-slack-notify)
skills/notify-slack/ Claude Code skill
scripts/ push-worker-secrets.mjs, smoke.sh, run-b4push.shThe repository is a pnpm workspace (pnpm 11, Node 24) with two members, app and doc.
Local Worker
Copy app/ to app/, replace the placeholders, and run:
pnpm --filter zudo-slack-notify-app devPoint a sender at it with ZUDO_. Never commit .dev.vars.
Test tiers
| Tier | Command | What it covers |
|---|---|---|
| Ops scripts | pnpm test:ops | Vitest for the root scripts/: push-worker-secrets.mjs and smoke.sh, with fakes. No real network or wrangler. |
| App unit and CLI | pnpm --filter zudo-slack-notify-app test | Validation, rendering, handler and CLI behavior against an injected fake Slack. |
| workerd | pnpm --filter zudo-slack-notify-app test:workerd | The Worker in the real Workers runtime, including native fetch handling. Not part of root pnpm test; run it on its own. |
| Post-deploy smoke | bash scripts/ or doc | The deployed origins, run by the deploy workflows after each deploy. |
Root pnpm test is pnpm test:ops followed by every member's test script, so it covers the first two rows. The suites supply their own fakes and never need real credentials or channel IDs.
Smoke tests
scripts/ takes one mode, app or doc, and needs curl and jq.
app:GET /healthzmust return the Worker's{ ok: true, service: "zudo-slack-notify" }. An unauthenticatedPOST /v1/notifymust return401; before Slack is wired a503 server_misconfiguredis also tolerated. Any2xxfails. See The SLACK_WIRED smoke variable.doc:GET /must return200with thezudo-slack-notifysite marker, and an unknown path must return404.
Right after a first deploy a custom domain can take a moment to answer, so the script retries until the first real answer (the Worker's own JSON envelope for app, a 200 for doc), then tolerates nothing. Optional environment overrides:
| Variable | Default | Meaning |
|---|---|---|
APP_BASE_URL | https: | API origin, for local runs and tests |
DOC_BASE_URL | https: | Docs origin |
SLACK_WIRED | empty | true once Slack is wired: only 401 passes on POST /v1/notify |
SMOKE_RETRY_WINDOW_SECONDS | 120 | How long to wait for the first real answer |
SMOKE_RETRY_INTERVAL_SECONDS | 5 | Pause between attempts |
Pre-push suite
pnpm b4pushRuns scripts/ in eight steps: frozen install, Prettier check, markdown format check, typecheck, pnpm test, the app workerd suite, build, and the doc link check. The heavy steps go through the machine-wide queue when it is present. It collects failures so one run reports every broken step.
Formatting
pnpm formatandpnpm format:checkrun Prettier.pnpm format:mdandpnpm format:md:checkrun@takazudo/mdx-formatterover every.mdand.mdxfile, including these pages.
CI and deploy workflows
All workflows share the composite action . (pnpm from the packageManager field, Node from .node-version, and pnpm install --frozen-lockfile).
| Workflow | Trigger | Jobs |
|---|---|---|
ci.yml | pull requests, and pushes to any branch except main | format (Prettier and markdown), typecheck, test-app (unit then workerd), test-ops, build-app, build-doc (build and check links) |
actionlint.yml | pull requests and pushes that touch .github/workflows/** or .github/actions/** | actionlint (pinned, checksum-verified release) |
deploy-app.yml | push to main touching app/**, root package or workspace files, or the setup action; manual dispatch | gate, test (typecheck, unit, workerd), deploy (wrangler deploy in app/), smoke (scripts/ with SLACK_WIRED from the repo variable) |
deploy-doc.yml | push to main touching doc/**, root package or workspace files, or the setup action; manual dispatch | gate, deploy (doc build, check:links, wrangler deploy in doc/), smoke (scripts/) |
Each deploy workflow starts with a gate job that skips the rest, cleanly and green, when the ref is not main or when the CLOUDFLARE_API_TOKEN or CLOUDFLARE_ACCOUNT_ID repository secret is absent, so forks and unconfigured clones stay green. Cloudflare credentials are scoped to the single deploy step. Deploy runs never overlap (a concurrency group per workflow, without cancellation).
Editing these docs
pnpm --filter zudo-slack-notify-doc dev # dev server
pnpm --filter zudo-slack-notify-doc build # static export
pnpm --filter zudo-slack-notify-doc check:linkscheck:links fails on any broken internal link. Sidebar order comes from sidebar_position in each page's frontmatter, and each top-level folder's index.mdx names its category.