ci: spread portable jobs across macOS runners; keep GHCR handoff tags
Drop Linux label from gh-only and Node gate jobs so air/mini can pick them up. Replace mapfile in autopilot for macOS bash 3.2. Remove unverified GHCR tag DELETE step; document that ci-* handoff aliases are harmless. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
28cdb33e57
commit
629fcf6b41
7
.github/workflows/autopilot-auto-merge.yml
vendored
7
.github/workflows/autopilot-auto-merge.yml
vendored
@ -21,7 +21,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
auto-merge:
|
auto-merge:
|
||||||
name: Autopilot auto-merge
|
name: Autopilot auto-merge
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
steps:
|
steps:
|
||||||
- name: Resolve pull request
|
- name: Resolve pull request
|
||||||
id: pr
|
id: pr
|
||||||
@ -71,7 +71,10 @@ jobs:
|
|||||||
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
|
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
mapfile -t conclusions < <(
|
conclusions=()
|
||||||
|
while IFS= read -r conclusion; do
|
||||||
|
[[ -n "$conclusion" ]] && conclusions+=("$conclusion")
|
||||||
|
done < <(
|
||||||
gh api "repos/${GITHUB_REPOSITORY}/commits/${HEAD_SHA}/check-runs" --paginate \
|
gh api "repos/${GITHUB_REPOSITORY}/commits/${HEAD_SHA}/check-runs" --paginate \
|
||||||
--jq '.check_runs[] | select(.name == "Product CI") | .conclusion'
|
--jq '.check_runs[] | select(.name == "Product CI") | .conclusion'
|
||||||
)
|
)
|
||||||
|
|||||||
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@ -29,10 +29,13 @@ concurrency:
|
|||||||
group: ci-${{ github.event.pull_request.number || github.ref }}
|
group: ci-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
# Jobs that only need Node/pnpm/bash use [self-hosted, evoo-ci] so macOS runners (air, mini)
|
||||||
|
# can share load. Linux-only work keeps the Linux label (Docker, xvfb, local Postgres :5433).
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changes:
|
changes:
|
||||||
name: Detect product changes
|
name: Detect product changes
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
outputs:
|
outputs:
|
||||||
product: ${{ steps.filter.outputs.product }}
|
product: ${{ steps.filter.outputs.product }}
|
||||||
steps:
|
steps:
|
||||||
@ -63,7 +66,7 @@ jobs:
|
|||||||
name: Lint
|
name: Lint
|
||||||
needs: changes
|
needs: changes
|
||||||
if: needs.changes.outputs.product == 'true'
|
if: needs.changes.outputs.product == 'true'
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
@ -99,7 +102,7 @@ jobs:
|
|||||||
name: Typecheck
|
name: Typecheck
|
||||||
needs: changes
|
needs: changes
|
||||||
if: needs.changes.outputs.product == 'true'
|
if: needs.changes.outputs.product == 'true'
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
@ -190,7 +193,7 @@ jobs:
|
|||||||
name: Unit tests
|
name: Unit tests
|
||||||
needs: changes
|
needs: changes
|
||||||
if: needs.changes.outputs.product == 'true'
|
if: needs.changes.outputs.product == 'true'
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
@ -274,7 +277,7 @@ jobs:
|
|||||||
name: Product CI
|
name: Product CI
|
||||||
if: always()
|
if: always()
|
||||||
needs: [changes, lint, check, build, test, test-integration, test-e2e]
|
needs: [changes, lint, check, build, test, test-integration, test-e2e]
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
steps:
|
steps:
|
||||||
- name: Verify product checks
|
- name: Verify product checks
|
||||||
env:
|
env:
|
||||||
|
|||||||
26
.github/workflows/publish-server-image.yml
vendored
26
.github/workflows/publish-server-image.yml
vendored
@ -5,8 +5,9 @@ name: publish-server-image
|
|||||||
# cache, so untrusted code can validate the images without receiving package-publish authority.
|
# cache, so untrusted code can validate the images without receiving package-publish authority.
|
||||||
# Self-hosted runners on one host often share ~/.docker; build/publish set DOCKER_CONFIG under
|
# Self-hosted runners on one host often share ~/.docker; build/publish set DOCKER_CONFIG under
|
||||||
# RUNNER_TEMP so docker/login-action post-logout cannot evict a concurrent job's GHCR session.
|
# RUNNER_TEMP so docker/login-action post-logout cannot evict a concurrent job's GHCR session.
|
||||||
# `ci-<run>-<image>-<arch>` handoff tags are registry labels on the per-arch digest; publish
|
# `ci-<run>-<image>-<arch>` handoff tags are extra aliases on the per-arch digest; GHCR does not
|
||||||
# removes them via the distribution API (tag only, not the digest edge/sha tags reference).
|
# expose a safe tag-only delete API (DELETE /v2/.../manifests/<tag> is unsupported or digest-scoped),
|
||||||
|
# so they are left in place and do not affect edge/sha-* manifests assembled in publish.
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@ -52,7 +53,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
changes:
|
changes:
|
||||||
name: Detect image publish inputs
|
name: Detect image publish inputs
|
||||||
runs-on: [self-hosted, evoo-ci, Linux]
|
runs-on: [self-hosted, evoo-ci]
|
||||||
outputs:
|
outputs:
|
||||||
publish: ${{ steps.decide.outputs.publish }}
|
publish: ${{ steps.decide.outputs.publish }}
|
||||||
steps:
|
steps:
|
||||||
@ -355,22 +356,3 @@ jobs:
|
|||||||
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||||
subject-digest: ${{ steps.manifest.outputs.digest }}
|
subject-digest: ${{ steps.manifest.outputs.digest }}
|
||||||
push-to-registry: true
|
push-to-registry: true
|
||||||
- name: Delete CI handoff tags
|
|
||||||
env:
|
|
||||||
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
|
||||||
IMAGE_NAME: ${{ matrix.name }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
repository="${IMAGE#ghcr.io/}"
|
|
||||||
token="$(curl -fsSL -u "${GITHUB_ACTOR}:${GHCR_TOKEN}" \
|
|
||||||
"https://ghcr.io/token?service=ghcr.io&scope=repository:${repository}:pull,push" \
|
|
||||||
| jq -er .token)"
|
|
||||||
for arch in amd64; do
|
|
||||||
tag="ci-${GITHUB_RUN_ID}-${IMAGE_NAME}-${arch}"
|
|
||||||
curl -fsSL -o /dev/null -X DELETE \
|
|
||||||
-H "Authorization: Bearer ${token}" \
|
|
||||||
-H "Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json" \
|
|
||||||
"https://ghcr.io/v2/${repository}/manifests/${tag}"
|
|
||||||
echo "Removed handoff tag ${tag} from ${repository}."
|
|
||||||
done
|
|
||||||
|
|||||||
@ -62,12 +62,12 @@ describe("server image publish workflow", () => {
|
|||||||
expect(workflowText).toContain("ci-${GITHUB_RUN_ID}-");
|
expect(workflowText).toContain("ci-${GITHUB_RUN_ID}-");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isolates Docker registry auth per job and deletes handoff tags without imagetools rm", () => {
|
it("isolates Docker registry auth per job and does not delete handoff tags via the registry API", () => {
|
||||||
expect(workflowText).toContain('echo "DOCKER_CONFIG=${cfg}" >> "$GITHUB_ENV"');
|
expect(workflowText).toContain('echo "DOCKER_CONFIG=${cfg}" >> "$GITHUB_ENV"');
|
||||||
expect(workflowText).toContain("Prepare isolated Docker config directory");
|
expect(workflowText).toContain("Prepare isolated Docker config directory");
|
||||||
expect(workflowText).not.toContain("imagetools rm");
|
expect(workflowText).not.toContain("imagetools rm");
|
||||||
expect(workflowText).toContain("Delete CI handoff tags");
|
expect(workflowText).not.toContain("Delete CI handoff tags");
|
||||||
expect(workflowText).toContain("/v2/${repository}/manifests/${tag}");
|
expect(workflowText).toContain("safe tag-only delete API");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("names every image under the lowercase repository namespace", () => {
|
it("names every image under the lowercase repository namespace", () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user