feat: add cdn-deploy action for single-step site deployments#17
feat: add cdn-deploy action for single-step site deployments#17LautaroPetaccio wants to merge 9 commits into
Conversation
new composite action that replaces the three-stage site deploy relay
(oddish-action npm publish -> static-sites-pipeline gitlab upload ->
set-rollout-action -> webhooks-receiver) with a single in-job step:
- uploads the pre-built folder straight to S3 (auth via github OIDC,
no static aws keys) using @dcl/cdn-uploader with the same
{ immutable, concurrency: 10 } config and <packageName>/<version> key
- patches the cloudflare KV rollout record inline via patchRollouts from
@well-known-components/rollouts-lib, keyed by path/domain with the
environment selecting the namespace
- keeps slack and github deployment/commit status for observability
no npm publish, no npm re-download, no gitlab hop, no webhooks-receiver
call. cloudflare KV still uses a scoped api token (no OIDC federation
available there); everything else uses OIDC or the ephemeral GITHUB_TOKEN.
adds a no-rebuild "promote" path alongside the existing deploy: - `source-version` triggers redeploy: server-side copy of every object under <packageName>/<sourceVersion>/ to <packageName>/<version>/ in the same bucket (MetadataDirective COPY + public-read ACL, paginated, so the uncompressed/.gzip/.br variant objects and their metadata are reproduced exactly), then the KV is pointed at the target version - `version` alone (no folder, no source-version) is a pure repoint: KV pointer only, no S3 op and no aws creds needed - mode is resolved from the inputs (deploy | redeploy | repoint) and exposed as a `mode` output `folder` and `aws-role-to-assume` become optional; the aws credentials step is gated so a repoint needs no OIDC. use case: cutting a release over a commit already on the cdn copies the bytes to the release tag and points prod at it, no second build.
update: added redeploy / promote mode (no rebuild)the action now picks one of three modes from its inputs:
redeploy is the "promote without rebuilding" path: cutting a release over a commit already on the cdn copies those exact bytes to the release-tagged prefix and points prod at it — no second build. it copies every object under the source prefix with
63 unit tests pass (added |
- aws-actions/configure-aws-credentials -> e7f100c # v6.2.0 - actions/checkout -> df4cb1c # v6.0.3 (readme example) - actions/setup-node -> 48b55a0 # v6.4.0 (readme example) - readme examples set up node 24
a0e128b to
433af6e
Compare
- deploy mode fails fast when the folder has no index.html at its root (new `require-index` input, default true) so an empty/broken build is caught before it reaches the cdn and the kv pointer - write a github job summary table (mode, version, env, cdn url, ...) - pin dev node via .nvmrc (24) and declare engines (node >=20)
- reusable workflow .github/workflows/cdn-deploy.yml wraps checkout + node 24 + build + the cdn-deploy action so a site's deploy is a few lines of `with:` inputs - release workflow moves a floating major tag (cdn-deploy-v1) on each cdn-deploy-v*.*.* tag so consumers can pin @cdn-deploy-v1 - dependabot keeps the npm deps and the sha-pinned actions current (chore: prefix so its PR titles pass validate-pr-title)
infer the S3 work from state instead of an explicit mode: - HEAD <package>/<version>/index.html; already there -> skip and just repoint; absent -> upload the folder, or copy the commit's build for a release; `force` redoes it - version is now commit-deterministic (<base>-commit-<sha>, no run id) so a release run can locate and copy the build the last master commit uploaded - `deployment-environments` repoints several envs from one upload; an empty list stages bytes in S3 with no KV change (release staging) cut what each site configures: - default the bucket + region in the action; the KV namespace ids come from org secrets CF_NS_ZONE/TODAY/ORG (they live in a private repo — never hardcoded here) - reusable workflow defaults account-id/role to org vars and reads the org CF_KV_API_TOKEN / ROLLOUTS_SLACK_WEBHOOK / CF_NS_* via `secrets: inherit` - derive deployment-path from the package name; default envs to zone+today - reusable workflow serializes one deploy at a time per repo (concurrency)
e1a3834 to
2d7194d
Compare
add a `commit` input: the action computes the version from it (overriding the workflow sha) so you can promote a specific build by commit — e.g. deploy to stg what you set on dev. The reusable workflow checks the repo out at that commit so the base version is read from its package.json, and reads package.json from the repo root when there's no upload folder. - `commit` participates in the S3 "already deployed?" check (errors clearly if that commit's build isn't on the CDN) - version still takes precedence when both are given
P0 — the release/promote flow was broken: stage-release didn't check out (build-command empty, no commit), so the action read no package.json and baseVersion silently became "0.0.0". The copy source `<pkg>/0.0.0-commit-<sha>` never matched the dev deploy's `<pkg>/<realbase>-commit-<sha>`, so the copy found no objects and failed. Fixes: - read packageName + baseVersion from the repo-root package.json (not the upload folder, which for a Vite build has no package.json), so every flow computes the same version for a commit - the reusable workflow now always checks out (ref: commit || triggering ref) so the base version is available for release/repoint, not just builds Also: - multi-env KV writes attempt every environment and, on failure, throw an aggregate naming which envs were already updated vs failed (was: abort on first failure, hiding the partial write); helper renamed to patchRolloutInEnvironments - parsePercentage rejects non-integers (KV stored a truncated value while logs/Slack reported the fractional one); dropped the `| 0` truncation - warn when no GitHub deployment id is returned (e.g. 202) instead of silently leaving the deployment without status - removed dead rolloutHasVersion; typed the `mode` output as S3Action Security hardening: - cloudflare.ts uses encodeURIComponent for the KV key (single path segment) - reusable workflow runs build-command via `bash -ec "$BUILD_COMMAND"` from env instead of interpolating it into the run line
…e workflow the build belonged to the site, not a shared workflow. the action already took a pre-built folder, so: - rename the `folder` input to `dist-path` (the caller builds its own artifact and hands over the path); build + deploy run in the same job, no artifact round-trip - remove the reusable workflow whose only extra job was running the build (the thing we're moving out). sites call the composite action directly, setting their own concurrency group and wiring the org secrets/vars - README rewritten for the direct-action, one-job model the release/manual flows are unchanged (version/commit/source-version still resolve against the repo-root package.json, which the deploy job checks out).
what
A new GitHub Action (
cdn-deploy) + reusable workflow that deploy a Decentraland static site to the CDN in one step: upload the built folder straight to S3 (auth via GitHub OIDC) and patch the Cloudflare KV the worker reads — replacing the three-stage relay (oddish-actionnpm publish →static-sites-pipelinegitlab S3 upload →set-rollout-action→webhooks-receiver). No npm publish, no npm re-download, no gitlab hop, no webhooks-receiver call.Lives in this monorepo: a composite action under
cdn-deploy/, a reusable workflow.github/workflows/cdn-deploy.yml, a release workflow, and Dependabot.state-aware (no explicit modes)
The action infers the S3 work from state — it HEADs
<package>/<version>/index.htmland:folder→ upload the buildversion≠ commit version) → copy the commit's already-uploaded build (no rebuild)force→ redo;deployment-environments: '[]'→ stage (S3 only, no KV)The version is commit-deterministic (
<base>-commit-<sha>, no run id), so a later run can reconstruct and reuse a build.deployment-environmentsrepoints several envs from one upload. A manual run can target a build byversionorcommit(promote dev→stg).OIDC everywhere possible
aws-actions/configure-aws-credentials, SHA-pinned) — no static keysGITHUB_TOKENruntime contract preserved
The CF worker still serves
https://cdn.decentraland.org/<prefix>/<version>/…and selects the version from{ records: { <name>: RolloutRecord[] } }. S3 key stays<packageName>/<version>/…,prefix === packageName, and the KV value is merged withpatchRolloutsfrom@well-known-components/rollouts-lib— the same functionwebhooks-receiverused.slim per-site config
A site's deploy is a few
with:lines +secrets: inherit. Shared config is org-level, set once:CF_KV_API_TOKEN,ROLLOUTS_SLACK_WEBHOOK, and the KV namespace idsCF_NS_ZONE/CF_NS_TODAY/CF_NS_ORG(these live in the privatesites-deployerrepo, so they're not hardcoded here)CF_ACCOUNT_ID,CDN_DEPLOY_ROLE_ARNpackage-name+deployment-pathderived frompackage.jsonalso in here
cdn-deploy.yml) — checkout + Node 24 + build + deploy; one deploy at a time per repo (concurrency).cdn-deploy-v*.*.*moves the floatingcdn-deploy-v1major tag for pinning.chore:prefix); SHA-pinned third-party actions on Node 24.index.html; acore.summaryrun table; GitHub deployment +cdn-rollout/uploadcommit status.dist/index.js(ncc bundle).not in this PR
Infra prereqs (one-time): an IAM role with GitHub-OIDC trust +
s3:PutObjecton the CDN bucket, a scoped Cloudflare Workers-KV-Edit token, and the org secrets/variables above. A worked, inert adoption example: decentraland/sites#565.