mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-16 14:57:36 +08:00
ci: keep release tooling and cache keys stable across jobs
This commit is contained in:
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -4,9 +4,9 @@ The release workflow builds the frontend once, then runs each configured Go targ
|
||||
|
||||
Each build uses GoReleaser OSS in snapshot mode with the selected release version and one target. Archive naming, bundled files, Go flags and release templates remain in the existing GoReleaser configurations. Every archive is accompanied by its source commit, target, version and SHA256. The publishing job verifies the complete matrix before building images or publishing. It uses GoReleaser's `extra_files` support to publish existing archives and checksums, with builds disabled. No Pro license is needed.
|
||||
|
||||
Go caches are isolated by target and refreshed on each source commit, with fallback to the preceding target cache. Matrix jobs upload uniquely named artifacts. The publishing job extracts only the regular Linux binary from each verified archive and restores its executable permission before constructing Docker contexts. QEMU remains limited to runtime-image instructions. DockerHub images are omitted when its credentials are absent; GHCR is always retained. Simple mode still publishes only the amd64 GHCR image and the simple release description.
|
||||
Go caches are isolated by target and refreshed on each source commit, with fallback to the preceding target cache. Save uses the original restore key, even if a build hook changes `go.sum`. Matrix jobs upload uniquely named artifacts. The publishing job extracts only the regular Linux binary from each verified archive and restores its executable permission before constructing Docker contexts. QEMU remains limited to runtime-image instructions. DockerHub images are omitted when its credentials are absent; GHCR is always retained. Simple mode still publishes only the amd64 GHCR image and the simple release description.
|
||||
|
||||
All build jobs use the commit resolved by `prepare`, including a manual release's selected tag. The workflow serializes release runs to prevent simultaneous updates to moving image tags.
|
||||
All build jobs use the commit resolved by `prepare`, including a manual release's selected tag. Helper scripts come from the workflow revision and are passed as a run-local artifact, so older application tags do not need to contain the new scripts. The workflow serializes release runs to prevent simultaneous updates to moving image tags.
|
||||
|
||||
## Validate without publication
|
||||
|
||||
|
||||
@@ -159,5 +159,33 @@ class ReleaseMatrixTest(unittest.TestCase):
|
||||
self.assertIn('ghcr.io/exampleowner/sub2api', log)
|
||||
|
||||
|
||||
def test_published_full_and_simple_image_tags(self):
|
||||
fake_bin = Path('bin')
|
||||
fake_bin.mkdir()
|
||||
docker = fake_bin / 'docker'
|
||||
docker.write_text('#!/bin/sh\nprintf "%s\\n" "$*" >> "$DOCKER_LOG"\n')
|
||||
docker.chmod(0o755)
|
||||
for simple in (False, True):
|
||||
with self.subTest(simple=simple):
|
||||
log_path = Path(f'docker-{simple}.log').resolve()
|
||||
env = {**os.environ, 'PATH': str(fake_bin.resolve()) + os.pathsep + os.environ['PATH'],
|
||||
'DOCKER_LOG': str(log_path), 'RUNNER_TEMP': self.temp.name,
|
||||
'RELEASE_VERSION': '9.8.7', 'RELEASE_SHA': 'a' * 40, 'GITHUB_REPOSITORY': 'ExampleOwner/sub2api',
|
||||
'DRY_RUN': 'false', 'SIMPLE_RELEASE': str(simple).lower(), 'DOCKERHUB_USERNAME': 'fixturehub'}
|
||||
subprocess.run(['bash', str(ROOT / '.github/release-tools/release-images.sh')], env=env, check=True)
|
||||
log = log_path.read_text()
|
||||
self.assertIn('--push', log)
|
||||
self.assertEqual(log.count('buildx build'), 1 if simple else 2)
|
||||
if simple:
|
||||
self.assertNotIn('fixturehub', log)
|
||||
self.assertNotIn('imagetools', log)
|
||||
self.assertIn('ghcr.io/exampleowner/sub2api:latest', log)
|
||||
else:
|
||||
self.assertEqual(log.count('imagetools create'), 2)
|
||||
self.assertIn('fixturehub/sub2api:9.8', log)
|
||||
self.assertIn('ghcr.io/exampleowner/sub2api:9', log)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -45,14 +45,21 @@ jobs:
|
||||
simple: ${{ steps.plan.outputs.simple }}
|
||||
dry_run: ${{ steps.plan.outputs.dry_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Checkout workflow tooling
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
- name: Preserve tooling for releases of older tags
|
||||
run: cp -R .github/release-tools "$RUNNER_TEMP/release-tools"
|
||||
- name: Checkout selected application source
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.tag || github.ref }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- run: python -m pip install -r .github/release-tools/requirements-release.txt
|
||||
- run: python -m pip install -r "$RUNNER_TEMP/release-tools/requirements-release.txt"
|
||||
- name: Resolve one source commit and target matrix
|
||||
id: plan
|
||||
env:
|
||||
@@ -61,7 +68,12 @@ jobs:
|
||||
args=(--ref "$REQUESTED_REF")
|
||||
if [[ "$SIMPLE_RELEASE" == true ]]; then args+=(--simple); fi
|
||||
if [[ "$DRY_RUN" == true ]]; then args+=(--dry-run); fi
|
||||
python .github/release-tools/release_matrix.py plan "${args[@]}"
|
||||
python "$RUNNER_TEMP/release-tools/release_matrix.py" plan "${args[@]}"
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: release-tooling
|
||||
path: ${{ runner.temp }}/release-tools/
|
||||
retention-days: 1
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: version-file
|
||||
@@ -107,6 +119,10 @@ jobs:
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: release-tooling
|
||||
path: ${{ runner.temp }}/release-tools/
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: version-file
|
||||
@@ -136,17 +152,17 @@ jobs:
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- run: python -m pip install -r .github/release-tools/requirements-release.txt
|
||||
- run: python -m pip install -r "$RUNNER_TEMP/release-tools/requirements-release.txt"
|
||||
- name: Select one GoReleaser target
|
||||
env:
|
||||
TARGET_OS: ${{ matrix.goos }}
|
||||
TARGET_ARCH: ${{ matrix.goarch }}
|
||||
run: python .github/release-tools/release_matrix.py config build --goos "$TARGET_OS" --goarch "$TARGET_ARCH" --output .goreleaser.matrix.yaml
|
||||
run: python "$RUNNER_TEMP/release-tools/release_matrix.py" config build --goos "$TARGET_OS" --goarch "$TARGET_ARCH" --output "$RUNNER_TEMP/goreleaser.matrix.yaml"
|
||||
- name: Build and archive without publishing
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: '~> v2'
|
||||
args: release --snapshot --clean --skip=validate,publish,docker,announce --config=.goreleaser.matrix.yaml
|
||||
args: release --snapshot --clean --skip=validate,publish,docker,announce --config=${{ runner.temp }}/goreleaser.matrix.yaml
|
||||
env:
|
||||
GITHUB_REPO_OWNER: ${{ github.repository_owner }}
|
||||
GITHUB_REPO_OWNER_LOWER: ${{ needs.prepare.outputs.owner_lower }}
|
||||
@@ -158,7 +174,7 @@ jobs:
|
||||
TARGET_OS: ${{ matrix.goos }}
|
||||
TARGET_ARCH: ${{ matrix.goarch }}
|
||||
RELEASE_SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: python .github/release-tools/release_matrix.py collect --version "$RELEASE_VERSION" --sha "$RELEASE_SHA" --goos "$TARGET_OS" --goarch "$TARGET_ARCH" --output matrix-output
|
||||
run: python "$RUNNER_TEMP/release-tools/release_matrix.py" collect --version "$RELEASE_VERSION" --sha "$RELEASE_SHA" --goos "$TARGET_OS" --goarch "$TARGET_ARCH" --output matrix-output
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: release-binary-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
@@ -172,7 +188,7 @@ jobs:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: release-go-${{ runner.os }}-${{ runner.arch }}-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('backend/go.mod', 'backend/go.sum') }}-${{ needs.prepare.outputs.sha }}
|
||||
key: ${{ steps.go-cache.outputs.cache-primary-key }}
|
||||
|
||||
release:
|
||||
needs: [prepare, build-binaries]
|
||||
@@ -181,6 +197,8 @@ jobs:
|
||||
contents: write
|
||||
packages: write
|
||||
env:
|
||||
SIMPLE_RELEASE: ${{ needs.prepare.outputs.simple }}
|
||||
DRY_RUN: ${{ needs.prepare.outputs.dry_run }}
|
||||
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
|
||||
RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE_SHA: ${{ needs.prepare.outputs.sha }}
|
||||
@@ -189,6 +207,10 @@ jobs:
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: release-tooling
|
||||
path: ${{ runner.temp }}/release-tools/
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: release-binary-*
|
||||
@@ -197,12 +219,12 @@ jobs:
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- run: python -m pip install -r .github/release-tools/requirements-release.txt
|
||||
- run: python -m pip install -r "$RUNNER_TEMP/release-tools/requirements-release.txt"
|
||||
- name: Verify complete matrix and prepare Docker contexts
|
||||
run: |
|
||||
args=(--version "$RELEASE_VERSION" --sha "$RELEASE_SHA" --input release-input --output .release-context)
|
||||
if [[ "$SIMPLE_RELEASE" == true ]]; then args+=(--simple); fi
|
||||
python .github/release-tools/release_matrix.py contexts "${args[@]}"
|
||||
python "$RUNNER_TEMP/release-tools/release_matrix.py" contexts "${args[@]}"
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: backend/go.mod
|
||||
@@ -229,7 +251,7 @@ jobs:
|
||||
- name: Build images and publish manifests
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME || 'skip' }}
|
||||
run: bash .github/release-tools/release-images.sh
|
||||
run: bash "$RUNNER_TEMP/release-tools/release-images.sh"
|
||||
- name: Get annotated tag notes
|
||||
id: tag_message
|
||||
run: |
|
||||
@@ -242,14 +264,14 @@ jobs:
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Prepare publication-only GoReleaser config
|
||||
run: |
|
||||
args=(publish --output .goreleaser.publisher.yaml)
|
||||
args=(publish --output "$RUNNER_TEMP/goreleaser.publisher.yaml")
|
||||
if [[ "$SIMPLE_RELEASE" == true ]]; then args+=(--simple); fi
|
||||
python .github/release-tools/release_matrix.py config "${args[@]}"
|
||||
python "$RUNNER_TEMP/release-tools/release_matrix.py" config "${args[@]}"
|
||||
- name: Publish existing archives and release notes
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: '~> v2'
|
||||
args: release --clean --skip=validate,before,docker ${{ env.DRY_RUN == 'true' && '--snapshot --skip=publish,announce' || '' }} --config=.goreleaser.publisher.yaml
|
||||
args: release --clean --skip=validate,before,docker ${{ env.DRY_RUN == 'true' && '--snapshot --skip=publish,announce' || '' }} --config=${{ runner.temp }}/goreleaser.publisher.yaml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_CURRENT_TAG: ${{ needs.prepare.outputs.tag }}
|
||||
|
||||
Reference in New Issue
Block a user