You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ab5358a
Browse filesBrowse the repository at this point in the historyBrowse files
fix(hub): pin and verify multiarchitecture production images (#19)
## Why
The production Ruby and Node image tags could resolve to different
contents without a source change. Pinning their official
multiarchitecture indexes addresses code-scanning alerts 3 and 4 while
retaining the current runtime tracks.
## What changes
Hub CI builds AMD64 and ARM64 images in parallel from one job definition
on native runners, avoiding QEMU overhead. Each image must pass the
existing image checks and start successfully. The unchanged `Production
image` check requires both architectures to pass and permits skipped
builds only when suite selection explicitly excludes Hub.
Releases also build each architecture on its native runner, using one
validated version and immutable source revision. Build jobs publish by
digest without updating release tags. One final job combines both
digests and applies the existing stable or prerelease tags, then uploads
the deployment bundle once. A failed architecture blocks that job.
Release publication has not been exercised against GHCR; no release was
triggered.
The maintenance guide explains refreshing image tags and digests
together. Existing Dependabot configuration does not update Docker
images; no new update process is added.
Alert 5 remains unchanged. Bundler deployment mode already freezes
dependencies; the image's Bundler rejected inconsistent and missing
lockfiles without rewriting them. The scanner rule should recognize
deployment mode rather than require redundant configuration.
---------
Co-authored-by: Copilot <[email protected]>
Copy file name to clipboardExpand all lines: docs/testing-and-review.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,7 +56,7 @@ The [shared selection workflow](../.github/workflows/ci-changes.yml) calls [the
56
56
| Root contributor documents, shared and Hub guides, skills, and documentation assets | Neither |
57
57
| Workflow changes, selector changes, and unclassified paths | Both |
58
58
59
-
The workflows preserve the existing required check names; skipping an entire workflow with a path filter could leave those checks pending. Static jobs use job-level conditions. The four required Vitest shard jobs always expand their matrix and validate selection, but skip checkout, setup, and tests when Hub is unaffected. The required `Frontend tests`and `Replica action tests` result jobs accept omitted work only after successful selection explicitly excluded it. Selection failures, invalid outputs, and failures in selected suites fail those result checks. Keep required-check configuration unchanged.
59
+
The workflows preserve the existing required check names; skipping an entire workflow with a path filter could leave those checks pending. Static jobs use job-level conditions. The four required Vitest shard jobs always expand their matrix and validate selection, but skip checkout, setup, and tests when Hub is unaffected. The required `Frontend tests`, `Production image`, and `Replica action tests` result jobs accept omitted work only after successful selection explicitly excluded it. The production-image matrix checks AMD64 and ARM64 independently without cancelling the other architecture on failure. Selection failures, invalid outputs, and failures in selected suites fail those result checks. Keep required-check configuration unchanged.
60
60
61
61
When adding a shared dependency or a new project, update the selector and its behavioral tests together. Unclassified paths deliberately run both suites until their dependencies are known. Run `node --test scripts/select-ci-suites.test.mjs` and `ruby scripts/check-ci-workflows.rb` from the repository root. The latter reads the actual workflow gates and checks their success, failure, cancellation, and missing-output behavior; both commands run before CI selects suites. Check changed workflows with `actionlint` when available. Actual job skipping and check reporting still need verification on GitHub after publishing.
Copy file name to clipboardExpand all lines: hub/docs/testing-and-operations.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,6 +48,14 @@ The standard-button follow-up was checked in a fresh isolated browser on that sa
48
48
49
49
Follow the [shared dependency policy](../../docs/testing-and-review.md#update-dependencies), including the seven-day publication delay and security checks. Run `bin/devcontainer exec bin/check-dependency-age` for Hub lockfiles.
50
50
51
+
The production [Dockerfile](../Dockerfile) pins the official Ruby and Node images to SHA-256 image index digests. Each index includes Linux AMD64 and ARM64 images. When refreshing a pin, resolve its tag from Docker Hub's official `library/ruby` or `library/node` repository, verify both architectures with `docker buildx imagetools inspect`, and update the tag and its matching index digest together. Do not use a single-architecture manifest digest. Keep `RUBY_VERSION` and `.ruby-version` aligned with the Ruby image; overriding the version alone does not change the pinned image.
52
+
53
+
The existing [Dependabot configuration](../../.github/dependabot.yml) updates Bundler, npm, and GitHub Actions dependencies, but not Docker images. Rebuilding with a pinned digest does not pick up upstream base-image security fixes. Maintainers must refresh those pins to receive them. Adding Docker updates to Dependabot requires approval; do not introduce another updater. Before shipping refreshed pins, build both release architectures with the learning build context, run the production-image CI checks, and verify that the app starts.
54
+
55
+
Hub CI runs the same production-image checks in parallel on native AMD64 and ARM64 runners. Each job builds and loads its image without publishing, checks secret cleanup and deployment configuration, and requires the production app's `/up` health endpoint to succeed. It removes the test container and generated configuration afterward. The required `Production image` result passes only when both architectures succeed, or when suite selection explicitly excludes Hub and the image jobs are skipped.
56
+
57
+
`BUNDLE_DEPLOYMENT=1` makes Bundler installs frozen unless explicitly overridden by `BUNDLE_FROZEN=false`. The production build inherits deployment mode and copies `Gemfile.lock` before installing gems, so missing or inconsistent lockfiles fail rather than being rewritten. Scanner rules checking for frozen installs should recognize deployment mode, not require a redundant `BUNDLE_FROZEN` setting.
58
+
51
59
Hub uses the native TypeScript compiler through `@typescript/native` for the `tsc` command and maps the `typescript` package name to a compiler-API compatibility package for tools such as typescript-eslint. Keep both aliases in [package.json](../package.json) until those tools support the native compiler's API.
52
60
53
61
## Keep local and GitHub CI aligned
@@ -133,6 +141,8 @@ It also recovers failed GitHub webhook deliveries every minute and cleans bounde
133
141
134
142
Create a GitHub release from a Hub semantic version tag in the form `hub-v<major>.<minor>.<patch>`. Publishing the release builds AMD64 and ARM64 images, pushes the applicable tags to `ghcr.io/github/replica-hub`, and uploads the deployment bundle and its checksum.
135
143
144
+
The workflow builds each architecture in parallel on its native runner, using the same source revision, version, and image labels. Build jobs push immutable image digests without updating release tags. Only after both builds succeed does the publication job combine their digests into one multiarchitecture image and apply the release tags. If either build fails, no release tags or deployment bundle are published.
145
+
136
146
A stable release updates the exact, minor, major, and `latest` image tags. A prerelease updates only its exact version tag.
137
147
138
148
After the first publication, a repository administrator must confirm that the package is linked to this repository and set its visibility to **Public**. Confirm that a signed-out user can pull the image before announcing the release.
0 commit comments