Replace the CircleCI pipeline with GitHub Actions - #952
Draft
ikuwow wants to merge 4 commits into
Draft
Conversation
Lint, build and deploy now run in .github/workflows/ci.yml. Deploy authenticates to AWS through OIDC instead of long-lived access keys, so the workflow needs no stored credentials beyond the role ARN. The oEmbed cache under .cache/ is not carried between runs, so every build refetches the embeds it needs from the oEmbed endpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nmxp9cDJY2sQjgssBLLgLe
The manual deploy example takes a placeholder bucket name, matching CLAUDE.md, so the workflow's S3_BUCKET secret is the only place the real name lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nmxp9cDJY2sQjgssBLLgLe
The deploy job syncs with --delete, so any file absent from the artifact is removed from the live bucket. upload-artifact excludes hidden files by default and only warns when it matches nothing, so both defaults are overridden to fail or include instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nmxp9cDJY2sQjgssBLLgLe
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.
Summary
Moves lint, build and deploy from CircleCI to GitHub Actions, and switches the deploy credentials from long-lived AWS access keys to OIDC. The pipeline had no CircleCI-specific dependencies, so this is a straight port; the Slack notification is dropped rather than migrated.
Changes
.github/workflows/ci.ymlreplaces.circleci/config.ymlwith three jobs:lintruns the same six commands as before, each guarded byif: ${{ !cancelled() }}so one failure does not hide the others (the CircleCIwhen: alwaysequivalent)buildrunsbundle exec middleman build, re-runs it with--verboseon failure, and uploadsbuild/as an artifactdeployruns only onpushtomaster, needs both other jobs, assumes an IAM role through OIDC, then syncs to S3 and invalidates CloudFrontRuby and Node versions come from
.tool-versions:ruby/setup-rubyreads it automatically, andactions/setup-nodeis pointed at it throughnode-version-file. Dependency installs arebundler-cache: true(deployment mode) andnpm ci, replacing the hand-written cache save/restore steps andnpm install.The artifact upload overrides two
upload-artifactdefaults, because deploy syncs with--deleteand anything absent from the artifact is deleted from the live bucket.include-hidden-files: truekeeps dotfiles, which the action's search stage otherwise drops without a warning and which CircleCI'spersist_to_workspacehad no reason to filter.if-no-files-found: errorfails the build on empty output instead of warning and leaving the failure to surface two jobs later.Every AWS value is a repository secret, so
README.mdandCLAUDE.mdno longer carry the bucket name in their manual-deploy examples. The value is recoverable from git history; this only stops adding new copies..github/dependabot.ymlgains agithub-actionsentry so the new action pins stay current, andpackage.json'sdescriptionsheds the CircleCI badge markdown that had been pasted into it along with a run of=characters.Setup required outside this PR
AWS
token.actions.githubusercontent.comwith audiencests.amazonaws.comtoken.actions.githubusercontent.com:aud=sts.amazonaws.comandtoken.actions.githubusercontent.com:sub=repo:ikuwow/query_ok:ref:refs/heads/masters3:ListBucketon the bucket,s3:GetObject/s3:PutObject/s3:DeleteObjecton its objects, andcloudfront:CreateInvalidationon the distributionThe
subclaim uses the plain form because this repository predates immutable subject claims and has not opted in.GitHub
AWS_ROLE_ARN,AWS_REGION,S3_BUCKET,CLOUDFRONT_DISTRIBUTION_ID.gh api repos/ikuwow/query_ok/actions/secretscurrently reportstotal_count: 0, so the first master deploy fails at the credential step until these existProtect defaultruleset fromci/circleci: build/ci/circleci: linttobuild/lint. The CircleCI config is deleted here, so its checks never report and this PR cannot merge before the changeBehavior not carried over
The oEmbed cache under
.cache/is not persisted between runs.extensions/oembed_converter.rbfetches from the oEmbed endpoint on a cache miss without rescuing, so every build refetches its embeds and a failed request fails the build. Adding anactions/cachestep for.cache/is the fix if this proves flaky.CircleCI ran lint and build on every push to every branch. The new triggers are
pull_requestand pushes tomaster, so a branch pushed without a PR gets no checks../article.shstops at pushing a branch, so a new article gets its first check when its PR opens.bundle exec erb_lint source/**/*.erbkeeps its existing shell-glob behavior. Withglobstaroff,**collapses to*, so the 10.erbfiles directly undersource/— includingindex.html.erband every shared partial — are not linted. That is pre-existing and left for a separate change, along with the copy of the same command inCLAUDE.md.Follow-ups this review surfaced, deliberately left out of scope
<script src>tags:Util#asset_pathfabricates a URL for an asset missing from the sitemap rather than raising. A webpack run that fails does fail the build, throughexternal_pipeline.rb'sexit(1)reaching the main thread in build modeaws cloudfront create-invalidationreturns once the request is accepted, so a green deploy does not mean the cache is cleared.aws cloudfront wait invalidation-completedwould close that gapOEmbed::Providers.get(url, query).htmlreturningnilon an HTTP 200 would silently drop the URL from the article and cache thenilVerification
Run 31311416877:
lintpassed with all six lint steps reportingsuccessbuildpassed and uploaded thesiteartifact; the--verbosefallback step was skippeddeploywas skipped, as expected on a pull requestactionlintexits 0 on the workflowThe cold oEmbed fetch works.
source/posts/holds 106 embeddable status URLs; 3 sit in apublished: falsedraft that is never built and 2 more point atyushakobo, which the converter's regex excludes, leaving 101 expected conversions. The artifact contains exactly 101twitter-tweetblockquotes across 26 pages.Enabling
include-hidden-fileschanged nothing in the output: a recursive diff of the artifacts from before and after that commit differs only insitemap.xml, whoselastmodcarries the build timestamp.Still to confirm after merge:
deployassumes the role,aws s3 syncreports the expected diff,create-invalidationreturns an ID, and https://queryok.ikuwow.com/ serves the new build.