Fix the S3 publish summary, and add a changelog and ADR - #3
Closed
t4k wants to merge 3 commits into
Closed
Conversation
Uploads built assets to an S3 bucket and invalidates the matching CloudFront paths, authenticating with OIDC so no long-lived AWS keys exist anywhere. Replaces the per-repository publish_to_s3.bash and invalidate_cdn.bash scripts, which needed an uncommitted media.env to exist on someone's machine. The invalidation stays in this action rather than becoming a separate one. It is cleanup after an upload, not a step anyone would run on its own. Two guards, both covering ways the old scripts could do damage quietly: - It refuses to publish an empty or missing source directory. A build that "succeeds" into nothing would otherwise leave the CDN serving stale objects with no signal that anything went wrong. - The invalidation is scoped to the project's own prefix. The previous script invalidated /*, which charged for and discarded every other project's cached objects in the same distribution. Content types are set explicitly per extension rather than left to the AWS CLI's guess, which omits the charset and has changed between CLI versions. publish_to_s3.bash set text/javascript and text/css with charset=utf-8, and the live objects carry those values; replacing it should not quietly change them. It uses `cp`, not `sync --delete`. The CL-web-components prefix holds a fonts/ directory that exists in no repository -- three woff files that page.tmpl and the footer component load. A delete-enabled sync would remove them and nothing would put them back. --acl public-read is kept and the reason recorded: the media bucket is in legacy ObjectWriter mode with no bucket policy, so public read comes entirely from per-object ACLs. Removing the need for it means adding a bucket policy first, verifying the CDN still serves, then setting BucketOwnerEnforced -- in that order, or every project on the distribution goes down. The calling job must declare `permissions: id-token: write`; a composite action cannot. The role's trust policy must name the calling repository's subject claim, which is repo-specific. Both are documented, because both cost time to diagnose from the error message alone. CI tests the argument handling and the guards but holds no AWS credentials. This repository should not have any. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add an S3 publish action
Five things, all surfacing from the first dry run of publish-to-s3 against a real consumer. Fixed: the summary step failed after a successful publish whenever 40 or fewer files were uploaded -- every realistic upload. It ended on a test that returns non-zero when false, and composite action steps run with `bash -eo pipefail`, so the step failed after the work had already succeeded. CI did not catch it because the guards job exercises the script and this was in the action; the consumer's dry run is what found it. `prefix` is now optional. Empty means the bucket root, which is the shape a project gets when it has a bucket to itself. Paths no longer collapse into a double slash, stray leading and trailing slashes are normalised, and the resulting whole-distribution invalidation is reported rather than silent. The `acl` default is now `none`, following AWS's recommendation to disable ACLs and grant access by bucket policy, which is also what new buckets get. That is a breaking change: a bucket in legacy ObjectWriter mode with no policy must now ask for public-read. It is safe to make now only because nothing consumes this action yet. The risk in that flip is a silent failure -- an object uploaded without an ACL the bucket needs uploads fine and is unreadable -- so setting public-base-url now also fetches one published file afterwards and fails on a non-200. Both mistakes are loud in either direction: a missing ACL fails the fetch, and setting one on a bucket with BucketOwnerEnforced already failed with AccessControlListNotSupported. CHANGELOG.md, following Keep a Changelog 1.1.0. Consumers reference a moving major tag, so when @v1 advances they have had no way to learn what moved short of reading commits. CONTRIBUTING.md asks for an entry with any change that reaches a consumer, and documents the release procedure: entries accumulate under [Unreleased], releasing renames that heading and tags the commit that does so, one commit and one tag per release rather than per change. ADR-0007 records why publishing touches only what it published -- copy rather than sync with delete, no ACL by default, explicit content types, invalidation scoped to the prefix. Each looks like an oversight to a reader assuming a dedicated bucket, and each is destructive if "corrected". It is scoped to the action rather than to any deployment: operational facts about a particular bucket belong in the repository that publishes to it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
t4k
force-pushed
the
ci/fix-summary-exit
branch
from
September 2, 2026 01:18
5aaad71 to
112a60a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three things, all arising from the first dry run against the released
@v1.1. The summary step failed after a successful publish
Returns 1 when the count is below the threshold, and it was the last command in the block. Composite action steps run with
bash -eo pipefail, so the step failed. Replaced with anif.It triggers on uploads of 40 files or fewer — every realistic upload — so
v1is currently broken for any consumer.CI missed it because
publish-to-s3-guardsexercisesbin/publish-to-s3.sh, while this lived in the action's summary step, reachable only by running the action end to end. That needs AWS credentials this repository deliberately does not hold, so a consuming repository's dry run is the test — working as intended, just later than ideal.2.
CHANGELOG.mdFollowing Keep a Changelog 1.1.0, with
v1.0.0andv1.1.0reconstructed.Consumers reference
@v1, which moves. Until now there was no way to learn what moved short of reading commits — the one real cost of a moving major tag.CONTRIBUTING.mdnow asks for an entry with any change that reaches a consumer.3. ADR-0007 — constraints on publishing to the media bucket
Four choices in
publish-to-s3that each look like an oversight and each cause a visible outage if "corrected":cp, neversync --deletefonts/directory, which exists in no repository — every site usingfooter-globalloses its typeface--acl public-read/$PREFIX/*, not/*All four were defended only by comments in the script. A comment does not survive someone rewriting the file it lives in, and the failure modes are not guessable from the code.
The ADR also records the order for the bucket migration that would relax two of them — policy first, verify, then
BucketOwnerEnforced— since doing it in any other order takes the distribution down.After merging
v1needs to move again, since the released one still has the bug. Then I'll re-run the consumer dry run before opening the CL-web-components caller.🤖 Generated with Claude Code