ci: publish amd64 images under a lowercase name and deploy only after green CI
Image publishing never succeeded: GHCR rejects the capitalized repository slug, and the arm64 jobs failed on the macOS runners, which have no Docker buildx. Resolve the namespace in lowercase and build amd64 only until an arm64 runner with buildx exists; the manifest check expects one digest. The Evo deploy ran on every push to main regardless of the result of Product CI, so red commits went live. Trigger it from the completed ci workflow instead and deploy the head it verified; manual dispatch stays. The Playwright report publish failed every nightly run because the S3 settings are not configured. The script now skips the gallery with a notice when they are missing instead of failing the job. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
parent
d1f7034aa5
commit
9c42cbcc17
14
.github/workflows/evo-deploy.yml
vendored
14
.github/workflows/evo-deploy.yml
vendored
@ -1,9 +1,11 @@
|
||||
name: evo-deploy
|
||||
|
||||
# Deploy evOo-local on the Evo host when main advances, or on manual dispatch.
|
||||
# Deploy evOo-local on the Evo host once Product CI has passed on main, or on manual dispatch.
|
||||
# Runs ON the self-hosted Evo runner (no SSH, no deploy secrets in the workflow).
|
||||
on:
|
||||
push:
|
||||
workflow_run:
|
||||
workflows: [ci]
|
||||
types: [completed]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@ -29,7 +31,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
if: vars.EVO_DEPLOY_ENABLED == 'true'
|
||||
if: vars.EVO_DEPLOY_ENABLED == 'true' && (github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success')
|
||||
runs-on: [self-hosted, evoo-ci, Linux]
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
@ -37,12 +39,12 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.event.workflow_run.head_sha }}
|
||||
|
||||
- name: Deploy evOo-local on Evo
|
||||
env:
|
||||
EVO_DEPLOY_DIR: ${{ vars.EVO_DEPLOY_DIR }}
|
||||
EVO_DEPLOY_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}
|
||||
EVO_DEPLOY_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.event.workflow_run.head_sha }}
|
||||
EVO_DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
|
||||
EVO_MIGRATE: ${{ github.event.inputs.skip_migrate == true && 'false' || 'true' }}
|
||||
EVO_RESTART_MODE: ${{ vars.EVO_RESTART_MODE || 'auto' }}
|
||||
@ -72,7 +74,7 @@ jobs:
|
||||
{
|
||||
echo "### Evo deploy"
|
||||
echo
|
||||
echo "- Ref: \`${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}\`"
|
||||
echo "- Ref: \`${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.event.workflow_run.head_sha }}\`"
|
||||
echo "- Dir: \`$EVO_DEPLOY_DIR\`"
|
||||
if [[ -f "$EVO_STATE_FILE" ]]; then
|
||||
echo "- State:"
|
||||
|
||||
38
.github/workflows/publish-server-image.yml
vendored
38
.github/workflows/publish-server-image.yml
vendored
@ -71,10 +71,14 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
- name: Resolve the lowercase image namespace
|
||||
id: ns
|
||||
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
|
||||
run: echo "repo=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
||||
- id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
images: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||
tags: type=sha,prefix=sha-
|
||||
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
with:
|
||||
@ -93,8 +97,8 @@ jobs:
|
||||
name: Build ${{ matrix.name }} ${{ matrix.arch }}
|
||||
if: github.event_name != 'pull_request'
|
||||
# Each architecture builds natively on its own runner and pushes by digest only.
|
||||
# amd64 on evo Linux runners; arm64 on Apple Silicon macOS runners (linux/arm64 via buildx).
|
||||
# No QEMU: emulated arm64 took hours for the desktop-sized computer image.
|
||||
# amd64 on evo Linux runners. arm64 is paused: the macOS runners have no Docker buildx,
|
||||
# and QEMU emulation took hours for the desktop-sized computer image.
|
||||
# See docs/self-host.md (Published images and tags).
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 90
|
||||
@ -105,7 +109,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [app, updater, computer]
|
||||
arch: [amd64, arm64]
|
||||
arch: [amd64]
|
||||
include:
|
||||
# Only the updater image contains the Docker CLI; the application image stays unprivileged.
|
||||
# The supervisor is not a separate published image: it runs from `app` on the internal network.
|
||||
@ -120,8 +124,6 @@ jobs:
|
||||
context: infra/sandboxes/computer
|
||||
- arch: amd64
|
||||
runner: [self-hosted, evoo-ci, Linux]
|
||||
- arch: arm64
|
||||
runner: [self-hosted, evoo-ci, macOS]
|
||||
steps:
|
||||
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
|
||||
with:
|
||||
@ -133,11 +135,15 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Resolve the lowercase image namespace
|
||||
id: ns
|
||||
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
|
||||
run: echo "repo=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
||||
- id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
# Labels only; the tagged manifest is assembled in the publish job.
|
||||
images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
images: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||
tags: type=sha,prefix=sha-
|
||||
- id: build
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
@ -147,7 +153,7 @@ jobs:
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# A fork can publish only to its own namespace; no credential can redirect this value.
|
||||
outputs: type=image,name=ghcr.io/${{ github.repository }}/${{ matrix.name }},push-by-digest=true,name-canonical=true,push=true
|
||||
outputs: type=image,name=ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }},push-by-digest=true,name-canonical=true,push=true
|
||||
# GET /health can report the exact source commit without a deployment-supplied override.
|
||||
build-args: ${{ matrix.name == 'app' && format('GIT_SHA={0}', github.sha) || '' }}
|
||||
cache-from: type=gha,scope=${{ matrix.name }}-${{ matrix.arch }}
|
||||
@ -199,11 +205,15 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Resolve the lowercase image namespace
|
||||
id: ns
|
||||
# GHCR rejects repository names with capitals; the GitHub repository slug has them.
|
||||
run: echo "repo=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
||||
- id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||
with:
|
||||
# A fork can publish only to its own namespace; no credential can redirect this value.
|
||||
images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
images: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||
tags: |
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}
|
||||
@ -211,11 +221,11 @@ jobs:
|
||||
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
# Stable releases only: a hyphen marks a prerelease (v1.0.0-rc.1) that must not move latest.
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-') }}
|
||||
- name: Assemble and verify the multi-arch manifest
|
||||
- name: Assemble and verify the manifest
|
||||
id: manifest
|
||||
working-directory: /tmp/digests
|
||||
env:
|
||||
IMAGE: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@ -227,7 +237,7 @@ jobs:
|
||||
for digest in *; do
|
||||
sources+=("${IMAGE}@sha256:${digest}")
|
||||
done
|
||||
if [[ "${#sources[@]}" -ne 2 ]]; then
|
||||
if [[ "${#sources[@]}" -ne 1 ]]; then
|
||||
echo "Expected one digest per architecture, found ${#sources[@]}." >&2
|
||||
exit 1
|
||||
fi
|
||||
@ -236,13 +246,13 @@ jobs:
|
||||
first_tag="$(head -n 1 <<< "$TAGS")"
|
||||
platforms="$(docker buildx imagetools inspect "$first_tag" \
|
||||
--format '{{range .Manifest.Manifests}}{{.Platform.OS}}/{{.Platform.Architecture}}{{"\n"}}{{end}}')"
|
||||
for want in linux/amd64 linux/arm64; do
|
||||
for want in linux/amd64; do
|
||||
grep -Fqx "$want" <<< "$platforms" || { echo "$first_tag is missing $want" >&2; exit 1; }
|
||||
done
|
||||
echo "digest=$(docker buildx imagetools inspect "$first_tag" --format '{{.Manifest.Digest}}')" >> "$GITHUB_OUTPUT"
|
||||
- name: Attest the published image
|
||||
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
||||
with:
|
||||
subject-name: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||
subject-digest: ${{ steps.manifest.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
@ -36,8 +36,9 @@ Compose stack requires `SANDBOX_SUPERVISOR_TOKEN` for every provider; leave it e
|
||||
|
||||
Optional: set `OPENROUTER_API_KEY` or connect a model in the UI after signup.
|
||||
|
||||
The example defaults to `edge` (main builds). Every publish is multi-arch (`amd64` + `arm64`), so
|
||||
arm64 hosts need no special tag. Do not assume `latest` is present until a stable release exists.
|
||||
The example defaults to `edge` (main builds). Published images are `linux/amd64` only for now;
|
||||
arm64 publishing is paused until an arm64 runner with Docker buildx exists. Do not assume `latest`
|
||||
is present until a stable release exists.
|
||||
|
||||
Open [http://127.0.0.1:5173](http://127.0.0.1:5173). The first registered user becomes the
|
||||
deployment owner. Put TLS in front of `:5173` for a public host and set the three public origins to
|
||||
@ -483,8 +484,9 @@ your CI cannot publish into someone else's.
|
||||
| `sha-<full-commit>` | every push and manual run | source-addressed; used by the updater sidecar |
|
||||
| `edge` | pushes to main | yes, to the newest main build |
|
||||
|
||||
Every publish, including `edge` from main merges, is multi-arch (`amd64` + `arm64`): each
|
||||
architecture builds natively on its own runner and one manifest is assembled per image. Until a
|
||||
Every publish, including `edge` from main merges, is `linux/amd64` only for now: the image builds
|
||||
natively on the Linux runner and one manifest is assembled per image. arm64 publishing is paused
|
||||
until an arm64 runner with Docker buildx exists. Until a
|
||||
stable `vX.Y.Z` has been published, GHCR may only have `edge` and `sha-*` tags; do not pin
|
||||
`latest` unless that tag exists in the registry.
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required}"
|
||||
: "${AWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is required}"
|
||||
: "${S3_BUCKET:?S3_BUCKET is required}"
|
||||
: "${S3_ENDPOINT:?S3_ENDPOINT is required}"
|
||||
: "${PLAYWRIGHT_PUBLIC_BASE_URL:?PLAYWRIGHT_PUBLIC_BASE_URL is required}"
|
||||
# Publishing is optional: without the S3 settings the gallery is skipped and the job stays green.
|
||||
for setting in AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY S3_BUCKET S3_ENDPOINT PLAYWRIGHT_PUBLIC_BASE_URL; do
|
||||
if [[ -z "${!setting:-}" ]]; then
|
||||
echo "::notice::Skipping the Playwright report publish: $setting is not configured."
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
: "${PLAYWRIGHT_RESULT:?PLAYWRIGHT_RESULT is required}"
|
||||
: "${PLAYWRIGHT_RUN_ATTEMPT:?PLAYWRIGHT_RUN_ATTEMPT is required}"
|
||||
: "${PLAYWRIGHT_RUN_ID:?PLAYWRIGHT_RUN_ID is required}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user