Repository navigation
Deploy staging API from GHCR by digest - #995
Merged
Merged
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the staging API image-release path, dependent database-reseed deployment, Terraform source declaration, Docker commit metadata, and rollout documentation.
- Immutable staging releases: Builds the verified selected commit in GitHub Actions, publishes to GHCR, records the digest, and deploys that exact image through Render while leaving production release logic unchanged.
- Deployment verification: Requires an identified HTTP 201 response with matching image reference, follows that deploy to
live, and checks public health and the final live image before recording success. - Reseed compatibility: Redeploys the currently live immutable image after database replacement without depending on Git-backed deploy metadata.
- Infrastructure and rollout: Declares the staging API image source and initial digest, preserves existing service configuration, and documents the coordinator-owned source transition and public-registry setup.
- Validation: All 232 release/reseed regression tests and 52 infrastructure tests passed locally. Render request/response fields and Docker action outputs were checked against their upstream definitions. Live registry pulls, runtime commit reporting, and the real-state Terraform plan remain rollout verification; no live infrastructure was changed during review.
gpt-6.1-sol | 𝕏
SonarCloud S7637 requires a full commit SHA for each action dependency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed commit 033db5c7 since the previous Pullfrog review, covering the three Docker action pins in .github/workflows/release.yml.
- Pinned staging image actions: Replaced moving major-version references with full commit SHAs for Docker setup-buildx, login, and build-push without changing workflow inputs or deployment logic.
- Verified upstream identities: Confirmed each SHA matches its annotated upstream release tag (
v4.4.1,v4.7.0, andv7.4.0) and checked the pinned action definitions for the configured inputs and image digest output. - Validated release behavior: Reran all 137 release-selection, Render-deployment, and public-health regression tests successfully. No live image build or Render deployment was performed.
gpt-6.1-sol | 𝕏
|
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.




Tracks thomasluizon/orbit-tickets#1874.
Staging releases build the selected API commit in GitHub Actions and deploy its immutable GHCR digest, avoiding Render Docker builds when pipeline minutes are exhausted. Production's release job is byte-for-byte unchanged.
Approach
.github/workflows/release.ymlretains the workflow checkout for release helpers, checks out the prepared SHA separately, verifies identity and branch ancestry, then buildsDockerfilewith the Docker driver and pushesghcr.io/thomasluizon/orbit-api:<sha>withGITHUB_TOKENandpackages: write. The digest reaches the summary before deployment. Render must return HTTP 201 with a deploy ID and matchingimage.ref; that deploy must becomelive, public health must reportHealthywith the planned commit, and the final live image must match. The first Git-backed service rejection explains the required coordinator source switch.DockerfilebakesAPI_COMMIT_SHAintoRENDER_GIT_COMMIT, the existing/healthcommit source. No API contract changes. The build regenerated OpenAPI without a content change.infra/staging.tf,infra/variables.tfandinfra/example.tfvarsdeclare the public API image, seedstaging_api_digestbeside the web digest and ignore later digest changes. Identity, free plan, domains, health check and environment links remain declared as before..github/workflows/staging-reseed.ymlmust change with the service source: its commit-only redeploy cannot run on an image-backed service. It now redeploys the currently live immutable image, verifies the HTTP 201 image identity and follows the returned deploy..github/scripts/staging-reseed-image.test.mjscovers this required dependency.README.mdandinfra/README.mddocument the release flow and coordinator rollout. Existing release behavior tests undertools/__tests__were adapted for image deployments; no harness implementation changed.External interface evidence
Read the current Render OpenAPI specification, linked from Render's API spec page. Re-derive with
curl -fsSL https://api-docs.render.com/openapi/render-public-api-1.json:paths["/services/{serviceId}/deploys"].post.requestBodydeclaresimageUrlfor an image-backed service, mutually exclusive withcommitId. Responses declare HTTP 201 ascomponents.schemas.deployand HTTP 202 as queued without a response body. See also Trigger deploy.components.schemas.deploy.propertiesdeclares stringid,image.refandstatus;statusreferencesdeployStatus, which declares every waiting, success and failure status used here. The deploy detail GET returns that same schema. See Retrieve deploy.deployList, whosedeployWithCursoritems wrap adeploy. This confirms.[0].deploy.image.reffor live-image verification and reseed. Its existingstatusandlimitquery parameters are retained.outputs.digestis the image digest consumed assteps.image.outputs.digest. The web workflow was read fromorbit-ui-mobileorigin/mainas ordered; the second repository was not modified.Assumptions
release-sourcerather than replacing the trusted workflow checkout, preserving the existing health verifier and Render read helpers at the dispatch commit.Test evidence
node --test tools/__tests__/release-selection.test.mjs tools/__tests__/release-render.test.mjs tools/__tests__/release-health.test.mjspassed all 164 existing tests while the commit-backed staging path was present.node --test --test-name-pattern='staging: sends only' tools/__tests__/release-render.test.mjsfailed the newstaging: sends only the immutable image and follows its returned deployassertion because the actual POST containedcommitIdinstead ofimageUrl. After the fix the same command passed. The complete adapted release suite passed all 137 tests.node --test .github/scripts/staging-reseed.test.mjspassed all 91 unchanged tests.node --test --test-name-pattern='reseed redeploys' .github/scripts/staging-reseed-image.test.mjsthen failed with exit 4 from the old jq commit lookup on a schema-derived image deploy without a Git commit. After the fix,node --test .github/scripts/staging-reseed-image.test.mjs .github/scripts/staging-reseed.test.mjspassed all 95 tests.dotnet build Orbit.slnxand finaldotnet build Orbit.slnx --no-restore: exit 0, zero errors.dotnet test tests/Orbit.Domain.Tests: exit 0, 1,215 passed.dotnet test tests/Orbit.Application.Tests: exit 0, 9,130 passed.dotnet test tests/Orbit.Infrastructure.Tests: exit 0, 6,598 passed and 14 skipped.dotnet test tests/Orbit.Analyzers.Tests: exit 0, 40 passed.concurrency.queuecompatibility exclusion.terraform fmt -check infra/staging.tf infra/variables.tf infra/example.tfvarspasses.terraform validatewas not run because providers are not initialized in this worktree. No real-state plan or apply was run.git diff --check, dash checks and commit hooks passed. Live build avoidance, Render pull access and public health rollout are coordinator verification after merge.Manual steps
mainwithenvironment=staging,branch=mainand the planned fullsha. The first run pushes the image before the source switch; copy its digest from the summary even if deployment fails.ghcr.io/thomasluizon/orbit-api@<published digest>proves access; no new secret or registry credential is required.orbit-api-staging(srv-dasotg8473hc739a5gjg) to that public image through the Render API, preserving ID, custom domains, environment groups, free plan and/health. This uses the authorized workaround for provider issue #80; do not apply a Terraform source update through the affected free-service provider.staging_api_digestin ignoredinfra/local.tfvarsto the publishedsha256:..., refresh Terraform state, then review a plan targeted only torender_web_service.staging_api. It must show no change and no replacement. The worker has not run Terraform against real state.livewith no image build andhttps://api-staging.useorbit.org/healthreportsstatus: Healthyand that exact commit. Existing staging secretRENDER_API_KEYand repository variableRENDER_STAGING_SERVICE_IDremain the release inputs. Production rollout is outside this ticket.