mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-10-30 19:51:59 +01:00
ci: publish main Docker images only from stable release tags
This commit is contained in:
1 parent
d0a34d16e2
commit
582ef4d8e7
3 files changed
+132
-4
No files matched your search
@@ -3,8 +3,10 @@ name: Publish Docker Image
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
tags:
|
||||
- "[0-9]*.[0-9]*.[0-9]*"
|
||||
- "v[0-9]*.[0-9]*.[0-9]*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_repository:
|
||||
@@ -50,6 +52,12 @@ jobs:
|
||||
version="$(python -m setuptools_scm)"
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate release channel
|
||||
id: release_channel
|
||||
env:
|
||||
PACKAGE_VERSION: ${{ steps.package_version.outputs.version }}
|
||||
run: bash scripts/docker-release-channel.sh
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -77,7 +85,7 @@ jobs:
|
||||
with:
|
||||
images: ${{ steps.image_repo.outputs.image_repository }}
|
||||
tags: |
|
||||
type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=raw,value=main,enable=${{ steps.release_channel.outputs.channel == 'main' }}
|
||||
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
|
||||
type=raw,value=v${{ steps.package_version.outputs.version }}
|
||||
type=sha,format=short
|
||||
@@ -99,10 +107,10 @@ jobs:
|
||||
- name: Notify Home Assistant add-on repository
|
||||
if: >-
|
||||
github.repository == 'openhop-dev/openhop_repeater' &&
|
||||
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')
|
||||
(steps.release_channel.outputs.channel == 'main' || steps.release_channel.outputs.channel == 'dev')
|
||||
env:
|
||||
DISPATCH_TOKEN: ${{ secrets.HA_ADDON_REPO_DISPATCH_TOKEN }}
|
||||
CHANNEL: ${{ github.ref_name }}
|
||||
CHANNEL: ${{ steps.release_channel.outputs.channel }}
|
||||
REVISION: ${{ github.sha }}
|
||||
run: |
|
||||
if [ -z "${DISPATCH_TOKEN}" ]; then
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validate stable publication before Docker login/build. Dev remains push-driven.
|
||||
set -euo pipefail
|
||||
|
||||
channel=""
|
||||
case "${GITHUB_REF:?}" in
|
||||
refs/heads/dev) channel=dev ;;
|
||||
refs/heads/main|refs/tags/*)
|
||||
git fetch origin main
|
||||
git merge-base --is-ancestor HEAD FETCH_HEAD || {
|
||||
printf '%s\n' 'Release commit must belong to main.' >&2
|
||||
exit 1
|
||||
}
|
||||
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||
tag="${GITHUB_REF#refs/tags/}"
|
||||
else
|
||||
tag="$(git describe --tags --exact-match HEAD)"
|
||||
fi
|
||||
[[ "$tag" =~ ^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] || {
|
||||
printf '%s\n' 'Main publication requires an exact stable X.Y.Z or vX.Y.Z tag.' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ "$(git rev-parse "refs/tags/${tag}^{commit}")" == "$(git rev-parse HEAD)" ]] || exit 1
|
||||
[[ "${PACKAGE_VERSION:?}" == "${tag#v}" ]] || {
|
||||
printf '%s\n' 'Computed package version does not match the release tag.' >&2
|
||||
exit 1
|
||||
}
|
||||
channel=main
|
||||
;;
|
||||
esac
|
||||
printf 'channel=%s\n' "$channel" >> "${GITHUB_OUTPUT:?}"
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Exercise publication guards with real temporary Git history; no registry writes."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[1] / "scripts/docker-release-channel.sh"
|
||||
|
||||
|
||||
class ReleaseChannelTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temp.cleanup)
|
||||
self.root = Path(self.temp.name)
|
||||
self.repo = self.root / "repo"
|
||||
self.repo.mkdir()
|
||||
self.git("init", "-b", "main")
|
||||
self.git("config", "user.name", "Test")
|
||||
self.git("config", "user.email", "test@example.invalid")
|
||||
self.git("commit", "--allow-empty", "-m", "base")
|
||||
self.git("remote", "add", "origin", str(self.repo))
|
||||
self.git("tag", "1.2.3")
|
||||
|
||||
def git(self, *args):
|
||||
return subprocess.run(
|
||||
["git", *args], cwd=self.repo, check=True, capture_output=True, text=True
|
||||
).stdout.strip()
|
||||
|
||||
def run_guard(self, ref, version, channel=None):
|
||||
output = self.root / "output"
|
||||
output.write_text("")
|
||||
result = subprocess.run(
|
||||
["bash", str(SCRIPT)],
|
||||
cwd=self.repo,
|
||||
env={**os.environ, "GITHUB_REF": ref, "PACKAGE_VERSION": version,
|
||||
"GITHUB_OUTPUT": str(output)},
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if channel is None:
|
||||
self.assertNotEqual(result.returncode, 0, result.stdout)
|
||||
self.assertEqual(output.read_text(), "")
|
||||
else:
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(output.read_text(), f"channel={channel}\n")
|
||||
|
||||
def test_release_tag(self):
|
||||
self.run_guard("refs/tags/1.2.3", "1.2.3", "main")
|
||||
|
||||
def test_v_release_tag(self):
|
||||
self.git("tag", "v1.2.3")
|
||||
self.run_guard("refs/tags/v1.2.3", "1.2.3", "main")
|
||||
|
||||
def test_manual_tagged_main(self):
|
||||
self.run_guard("refs/heads/main", "1.2.3", "main")
|
||||
|
||||
def test_untagged_main(self):
|
||||
self.git("commit", "--allow-empty", "-m", "untagged")
|
||||
self.run_guard("refs/heads/main", "1.2.4.dev1")
|
||||
|
||||
def test_prerelease_rejected(self):
|
||||
self.git("tag", "1.2.4.dev1")
|
||||
self.run_guard("refs/tags/1.2.4.dev1", "1.2.4.dev1")
|
||||
|
||||
def test_version_mismatch_rejected(self):
|
||||
self.run_guard("refs/tags/1.2.3", "1.2.3.dev348")
|
||||
|
||||
def test_tag_on_non_main_commit_rejected(self):
|
||||
self.git("checkout", "-b", "dev")
|
||||
self.git("commit", "--allow-empty", "-m", "dev only")
|
||||
self.git("tag", "1.2.4")
|
||||
self.run_guard("refs/tags/1.2.4", "1.2.4")
|
||||
|
||||
def test_tag_points_elsewhere_rejected(self):
|
||||
self.git("commit", "--allow-empty", "-m", "new main")
|
||||
self.run_guard("refs/tags/1.2.3", "1.2.3")
|
||||
|
||||
def test_dev_unchanged(self):
|
||||
self.run_guard("refs/heads/dev", "1.2.4.dev7", "dev")
|
||||
|
||||
def test_feature_manual_does_not_publish_main(self):
|
||||
self.run_guard("refs/heads/fix/example", "1.2.4.dev7", "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user