Repository navigation
ci(deploy): deploy pre-built images tagged by commit SHA - #2131
alanpeixinho wants to merge 7 commits into
Conversation
e9a1955 to
bfb9c73
Compare
felipebergamin
left a comment
There was a problem hiding this comment.
Two things that will bite us on the actual deploy path — left them inline.
| uses: ./.github/workflows/deploy-staging.yaml | ||
| secrets: inherit | ||
| with: | ||
| image_tag: ${{ github.sha }} |
There was a problem hiding this comment.
This is going to race with Publish GHCR Images. Both still start on their own when something lands on main, and CI never waits for the images to actually be in the registry.
That used to be fine because staging built on the box. Now it pulls this SHA with --no-build, so if tests finish before all three images are pushed, deploy just fails. A retry/wait on pull would keep build and deploy as separate workflows and still make this reliable.
There was a problem hiding this comment.
Good catch — addressed in 7c53d27.
Staging deploy now retries docker compose pull (up to ~10 min) so it waits for the SHA tags in GHCR instead of racing Publish. Build and Deploy stay separate.
| cp ~/.env-staging dashboard-staging/.env && | ||
| cd dashboard-staging && | ||
| git checkout ${GITHUB_SHA} && | ||
| git checkout ${IMAGE_TAG} && |
There was a problem hiding this comment.
This clone only has main, so git checkout will fail for a SHA that isn't on that branch. That's the hotfix path from #2087 — publish from a branch, then deploy that SHA. Actions checkout can fetch it; the server can't. Same thing in the production workflow.
Fetching the SHA first (git fetch origin "$IMAGE_TAG" then checkout) would cover that and also save us from cloning the whole history on every deploy.
There was a problem hiding this comment.
Also fixed in 7c53d27 (staging + production).
We now git fetch origin "$IMAGE_TAG" before checkout, and clone with --depth 1 so hotfix SHAs not on main still resolve without pulling full history.
| outputs: | ||
| digest: ${{ steps.build.outputs.digest }} | ||
| steps: | ||
| - uses: actions/checkout@v4 |
There was a problem hiding this comment.
since we are here I think we can update the action version.
| - uses: actions/checkout@v4 | |
| - uses: actions/checkout@v7 |
There was a problem hiding this comment.
Done in efdad26. Publish now uses actions/checkout@v7.
| resolve-ref: | ||
| if: github.repository == 'kernelci/dashboard' || github.event_name == 'workflow_dispatch' | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| sha: ${{ steps.git.outputs.sha }} | ||
| steps: | ||
| - name: Resolve commit SHA | ||
| id: git | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| REF: ${{ github.event.inputs.ref || github.sha }} | ||
| run: | | ||
| set -euo pipefail | ||
| sha=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${REF}" --jq .sha) | ||
| echo "sha=${sha}" >> "$GITHUB_OUTPUT" | ||
|
|
There was a problem hiding this comment.
do we need this? I'm not sure but I think we can pass the ref directly to the checkout action.
There was a problem hiding this comment.
Done in efdad26. We check out inputs.ref (or github.sha on push) and take the image tag from git rev-parse HEAD, so a branch or tag still becomes one commit SHA. The job stays so a bad ref fails once before the three image builds.
| inputs: | ||
| ref: | ||
| description: Git ref to build (branch, tag, or commit SHA) | ||
| required: true | ||
| type: string | ||
|
|
There was a problem hiding this comment.
Dont we loose the power to go to any commit with that? I believe it only brings tags and branches.
There was a problem hiding this comment.
Yes, but I see this as a good thing to enforce having a branch or a tag to be deployed.
Otherwise we can select any commit and after some time would be hard to tell why that commit is on prod.
There was a problem hiding this comment.
Done. The free-text ref is gone. A build comes from a branch or tag, and the image tag is the full github.sha.
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ needs.resolve-ref.outputs.sha }} |
There was a problem hiding this comment.
if I'm not mistaken we can use ${{ github.sha }}.
maybe we don't even need to provide this arg, it defaults to github.ref
There was a problem hiding this comment.
The image we want is inputs.ref, which can be a different SHA than the one from github.sha.
There was a problem hiding this comment.
not if we rely on "Use workflow from" selector as I said in my other comment
github.sha will carry the resolved sha for the selected ref, and for workflows dispatched by pushes it will carry the sha for the commit that triggered the workflow.
There was a problem hiding this comment.
Done. Checkout follows the selected branch or tag, and the image tag is the full github.sha.
| tags: | | ||
| ${{ steps.meta.outputs.prefix }}/dashboard-frontend:latest | ||
| ${{ steps.meta.outputs.prefix }}/dashboard-frontend:${{ github.sha }} | ||
| tags: ${{ steps.meta.outputs.prefix }}/dashboard-frontend:${{ needs.resolve-ref.outputs.sha }}${{ github.event_name == 'push' && format(',{0}/dashboard-frontend:latest', steps.meta.outputs.prefix) || '' }} |
There was a problem hiding this comment.
I think we can use a short sha to make easier to read the image tags.
- name: Get short SHA
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
- name: Show short SHA
run: echo "${{ env.SHORT_SHA }}"
There was a problem hiding this comment.
I might be too much conservative (especially because we are a small repo), but I still prefer long hashes due to reduced collision chance.
Do you consider too problematic to keep long hash here?
There was a problem hiding this comment.
I just think that a short sha would be easier to read. I don't know about the chances of a collision. But consider this an optional.
There was a problem hiding this comment.
Keeping the full SHA.
| image_tag: | ||
| description: GHCR image tag (Git commit SHA) to deploy | ||
| required: true | ||
| default: 'main' | ||
| type: string |
There was a problem hiding this comment.
I'd replace this by github.ref_name
There was a problem hiding this comment.
github.ref_name is the branch we are running the workflow from image_tag is the one we are using to build the image.
There was a problem hiding this comment.
My comments are considering a change to use the github ref selector.
If we select a tag there, github.ref_name will carry the tag name.
There was a problem hiding this comment.
Done. No open input. The selected branch or tag is deployed as its full github.sha.
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.image_tag }} |
There was a problem hiding this comment.
Same as above: checkout image_tag, not github.ref_name.
| SSH_HOST: ${{ secrets.STAGING_HOST }} | ||
| SSH_KEY: ${{ secrets.STAGING_KEY }} | ||
| DASHBOARD_VERSION: ${{ github.event.inputs.tag }} | ||
| IMAGE_TAG: ${{ github.event.inputs.image_tag }} |
There was a problem hiding this comment.
again, I'd use github.(ref | ref_name | sha) instead of relying in the input exactly as provided
There was a problem hiding this comment.
The host pulls image_tag. github.sha and github.ref_name are the workflow run, not that image.
There was a problem hiding this comment.
My comment consider that we would drop the open input and use the github ref selector.
If we select a tag there, github.ref_name will carry the tag name. It is basically the same that typing the tag name in the input.
There was a problem hiding this comment.
Done. The open input is gone. Deploy uses the full github.sha of the selected branch or tag.
| inputs: | ||
| image_tag: | ||
| description: GHCR image tag (Git commit SHA) to deploy | ||
| required: true | ||
| type: string |
There was a problem hiding this comment.
Same as production. Staging can deploy a SHA other than the branch that started the workflow.
* Publish GHCR Images: optional ref on manual dispatch; tag images with resolved commit SHA; push `latest` only on push to main * Staging: required `image_tag`; pull GHCR images with `--no-build`; manual `workflow_dispatch`; CI passes `github.sha` after tests * Production: required `image_tag` (replaces unused `tag` default); export `IMAGE_TAG` on deploy; checkout deploy SHA on server * Align staging `docker-compose.yml` image paths with `IMAGE_OWNER` / `IMAGE_REPOSITORY` from `.env.example` * Update DEPLOYMENT.md and README for SHA-based deploy flow Closes kernelci#2087
Retry staging pulls until SHA tags exist so CI does not race Publish. Fetch the image_tag on the host before checkout so hotfix commits work.
Checkout the requested ref and tag images with git rev-parse HEAD. Bump actions/checkout to v7. Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
* Drop free-text ref and image_tag inputs; use the GitHub branch/tag selector * Tag and deploy the full github.sha; keep latest only for pushes to main * Remove resolve-ref; checkout already matches github.sha * On the host, fetch the selected ref and check out that SHA * Document the selector in DEPLOYMENT.md and README
8146d18 to
dda2c1b
Compare
| NEW_MIGRATIONS=$(git diff --name-only HEAD~1 HEAD -- '**/migrations/*.py' \ | ||
| | grep -v '__init__.py' || true) |
There was a problem hiding this comment.
can we rely on this check?
if we dispatch this action in a branch, it will check only the last commit, migrations added in previous commits won't be notified.
I think HEAD~1 only works when we use squash and merge to main.
There was a problem hiding this comment.
this would be tricky to implement in a more generic way, but we can include a proper comment to ensure that this requires the squash and merge.
| git fetch --depth 1 origin "${GIT_REF}" | ||
| git checkout --detach "${IMAGE_TAG}" |
There was a problem hiding this comment.
nit: There is a small chance that the ref could be updated between the start of the action and the point when the KCI server tries to fetch it. This could cause github.sha to differ from the actual SHA of the ref in the repository.
I don't think this is very likely to happen, but it's something we should be aware of.
There was a problem hiding this comment.
Good catch. The host now fetches github.sha directly, so a push to the branch after the run starts cannot change the checkout.
git fetch --depth 1 origin "${IMAGE_TAG}"
git checkout --detach "${IMAGE_TAG}"IMAGE_TAG is still the SHA of the branch or tag selected in "Use workflow from".
The Discord notice diffs HEAD~1, so it only covers the whole pull request while main is updated by one squash commit. Assisted-by: Grok 4.7 <noreply@x.ai> Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
|
I think I would be more comfortable with this PR if |
|
Yeah, I agree. Staging on its own server would be a good change. I believe #1516 already has some comments on this. What we want from this PR is the rollback path from #2087. If a deploy goes bad, we redeploy an older commit SHA instead of being stuck on Do you see something we could still do in this PR to make the shared host less of a problem? Should we also hold this merge until #1516 is done? @nuclearcat might want to share his take on this too. |

What it is
Staging and production deploy pre-built GHCR images by commit SHA instead of
:latestor on-server builds.:latestonly on push tomain.up --no-build.DASHBOARD_VERSIONcomes from that commit..env.example; docs updated.Do not run Deploy production Dashboard for this PR.
How to test
The workflow file has to be on
kernelci/dashboard. A fork branch is not a valid--refthere. Push this commit to a branch on that repo, then pass that branch name.1. Publish (no staging, no prod)
There is no
refinput. The branch you pass is the commit that gets built.Ref: <branch>andCommit SHA: <full sha>.https://github.com/orgs/kernelci/packages):dashboard-backend,dashboard-frontend,dashboard-proxyeach have a tag equal to that full SHA.latesttag digest should be unchanged.2. Negative staging (unpublished SHA)
Use a real branch whose commit was not published. A fake SHA cannot be passed;
--refmust exist or the run never starts.Timed out waiting for GHCR images.dashboard-stagingbefore the pull, so that directory changes.3. Manual staging (live staging)
Same command as (2),
--ref= the branch published in (1). This updates staging.Deploying staging \` (``)`.ghcr.io/kernelci/dashboard/dashboard-backend:<full sha>, and the same fordashboard-frontendanddashboard-proxy.git describe, not the image tag.4. After merge to
main(live staging; existing CI behavior)Two runs, same merge commit. Actions at
https://github.com/kernelci/dashboard/actions.CI
pull_request). Title is the merge commit.github.sha).Deploying staging \main` (``)` equals the header SHA.…/dashboard-*:<same sha>, not:latest.Publish
Commit SHA: <sha>matches the SHA from the CI staging job.Discord only fires if staging fails; success is Actions Summary only.
Closes #2087.