ci: make release promotion tag-driven and keep audits informational
Some checks failed
CICD / Build and Push CICD Images (pull_request) Failing after 1m4s
CICD / Source Checks (pull_request) Has been skipped
CICD / Dependency Audits (Informational) (pull_request) Has been skipped
CICD / Build Release Images (pull_request) Has been skipped
CICD / Build Tester Images (pull_request) Has been skipped
CICD / Build CICD Image Failure Postmortem (pull_request) Successful in 11s
CICD / CICD Tests Complete (pull_request) Failing after 6s
CICD / Production Images Complete (pull_request) Failing after 7s
CICD / Runtime Black-Box Integration Tests (pull_request) Has been skipped
CICD / Production Image Failures Postmortem (pull_request) Has been skipped
CICD / End-to-End Tests (pull_request) Has been skipped
CICD / Promote Staging Images To Release (pull_request) Has been skipped
CICD / Source Lanes Failure Postmortem (pull_request) Has been skipped
CICD / Integration Tests Failure Postmortem (pull_request) Has been skipped
CICD / E2E Tests Failure Postmortem (pull_request) Has been skipped
Some checks failed
CICD / Build and Push CICD Images (pull_request) Failing after 1m4s
CICD / Source Checks (pull_request) Has been skipped
CICD / Dependency Audits (Informational) (pull_request) Has been skipped
CICD / Build Release Images (pull_request) Has been skipped
CICD / Build Tester Images (pull_request) Has been skipped
CICD / Build CICD Image Failure Postmortem (pull_request) Successful in 11s
CICD / CICD Tests Complete (pull_request) Failing after 6s
CICD / Production Images Complete (pull_request) Failing after 7s
CICD / Runtime Black-Box Integration Tests (pull_request) Has been skipped
CICD / Production Image Failures Postmortem (pull_request) Has been skipped
CICD / End-to-End Tests (pull_request) Has been skipped
CICD / Promote Staging Images To Release (pull_request) Has been skipped
CICD / Source Lanes Failure Postmortem (pull_request) Has been skipped
CICD / Integration Tests Failure Postmortem (pull_request) Has been skipped
CICD / E2E Tests Failure Postmortem (pull_request) Has been skipped
This commit is contained in:
@@ -788,6 +788,7 @@ jobs:
|
||||
dependency-audits:
|
||||
name: Dependency Audits (Informational)
|
||||
runs-on: ubuntu-act
|
||||
continue-on-error: true
|
||||
timeout-minutes: 15
|
||||
needs: build_cicd
|
||||
steps:
|
||||
@@ -2465,9 +2466,13 @@ jobs:
|
||||
outputs:
|
||||
release_version: ${{ steps.promote.outputs.release_version }}
|
||||
release_short_sha: ${{ steps.promote.outputs.release_short_sha }}
|
||||
release_tagged_version: ${{ steps.promote.outputs.release_tagged_version }}
|
||||
previous_release_version: ${{ steps.promote.outputs.previous_release_version }}
|
||||
backend_release_version_ref: ${{ steps.promote.outputs.backend_release_version_ref }}
|
||||
backend_release_tagged_ref: ${{ steps.promote.outputs.backend_release_tagged_ref }}
|
||||
backend_release_build_ref: ${{ steps.promote.outputs.backend_release_build_ref }}
|
||||
frontend_release_version_ref: ${{ steps.promote.outputs.frontend_release_version_ref }}
|
||||
frontend_release_tagged_ref: ${{ steps.promote.outputs.frontend_release_tagged_ref }}
|
||||
frontend_release_build_ref: ${{ steps.promote.outputs.frontend_release_build_ref }}
|
||||
steps:
|
||||
- *identify_runner_step
|
||||
@@ -2485,7 +2490,32 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
trap 'rm -f /tmp/release_ssh_key' EXIT
|
||||
TMP_REPO_DIR="$(mktemp -d)"
|
||||
trap 'rm -f /tmp/release_ssh_key; rm -rf "${TMP_REPO_DIR}"' EXIT
|
||||
|
||||
retry_cmd() {
|
||||
attempts="${1:-5}"
|
||||
backoff="${2:-2}"
|
||||
shift 2
|
||||
|
||||
attempt=1
|
||||
while [ "${attempt}" -le "${attempts}" ]; do
|
||||
if "$@"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "${attempt}" -lt "${attempts}" ]; then
|
||||
sleep_seconds=$((backoff * attempt))
|
||||
echo "Command failed (attempt ${attempt}/${attempts}): $*"
|
||||
echo "Retrying in ${sleep_seconds}s"
|
||||
sleep "${sleep_seconds}"
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
echo "Command failed after ${attempts} attempts: $*"
|
||||
return 1
|
||||
}
|
||||
|
||||
docker_login_with_retry() {
|
||||
attempts="${1:-5}"
|
||||
@@ -2555,50 +2585,88 @@ jobs:
|
||||
| sort -V \
|
||||
| tail -n 1)"
|
||||
|
||||
if [ -n "${HEAD_SEMVER_TAG}" ]; then
|
||||
RELEASE_VERSION="${HEAD_SEMVER_TAG}"
|
||||
else
|
||||
BASE_TAG="$({ GIT_SSH_COMMAND="${GIT_SSH_COMMAND}" git ls-remote --tags --refs "${GITEA_REPO_SSH_URL}" 2>/dev/null || true; } \
|
||||
| awk '{sub("refs/tags/", "", $2); print $2}' \
|
||||
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \
|
||||
| sort -V \
|
||||
| tail -n 1)"
|
||||
|
||||
if [ -z "${BASE_TAG}" ]; then
|
||||
BASE_TAG="v0.0.0"
|
||||
fi
|
||||
|
||||
BASE_MAJOR="$(printf '%s' "${BASE_TAG}" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/')"
|
||||
BASE_MINOR="$(printf '%s' "${BASE_TAG}" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/')"
|
||||
BASE_PATCH="$(printf '%s' "${BASE_TAG}" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\3/')"
|
||||
|
||||
EXISTING_TAGS_JSON="$({ curl -fsSL -u "${REGISTRY_USER}:${PACKAGE_ACCESS_TOKEN}" "http://${GITEA_REGISTRY}/v2/darkhelm.org/deployable-backend/tags/list" 2>/dev/null || true; })"
|
||||
|
||||
MAX_PATCH_FOR_LINE="${BASE_PATCH}"
|
||||
if [ -n "${EXISTING_TAGS_JSON}" ] && command -v python3 >/dev/null 2>&1; then
|
||||
REGISTRY_MAX_PATCH="$(printf '%s' "${EXISTING_TAGS_JSON}" | python3 -c "import json,re,sys; data=json.load(sys.stdin); tags=data.get('tags') or []; major=int(sys.argv[1]); minor=int(sys.argv[2]); patches=[int(m.group(3)) for tag in tags for m in [re.match(r'^v(\\d+)\\.(\\d+)\\.(\\d+)$', str(tag))] if m and int(m.group(1))==major and int(m.group(2))==minor]; print(max(patches) if patches else -1)" "${BASE_MAJOR}" "${BASE_MINOR}")"
|
||||
|
||||
if [ "${REGISTRY_MAX_PATCH}" -ge "${MAX_PATCH_FOR_LINE}" ]; then
|
||||
MAX_PATCH_FOR_LINE="${REGISTRY_MAX_PATCH}"
|
||||
fi
|
||||
fi
|
||||
|
||||
NEXT_PATCH="$((MAX_PATCH_FOR_LINE + 1))"
|
||||
RELEASE_VERSION="v${BASE_MAJOR}.${BASE_MINOR}.${NEXT_PATCH}"
|
||||
if [ -z "${HEAD_SEMVER_TAG}" ]; then
|
||||
echo "No semver tag found on HEAD (${HEAD_SHA}); skipping release promotion and release notes generation."
|
||||
echo "release_version=" >> "$GITHUB_OUTPUT"
|
||||
echo "release_short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tagged_version=" >> "$GITHUB_OUTPUT"
|
||||
echo "previous_release_version=" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_version_ref=" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_tagged_ref=" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_build_ref=" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_version_ref=" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_tagged_ref=" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_build_ref=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
RELEASE_BUILD_TAG="${RELEASE_VERSION}-${SHORT_SHA}"
|
||||
TAG_MAJOR="$(printf '%s' "${HEAD_SEMVER_TAG}" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\1/')"
|
||||
TAG_MINOR="$(printf '%s' "${HEAD_SEMVER_TAG}" | sed -E 's/^v([0-9]+)\.([0-9]+)\.([0-9]+)$/\2/')"
|
||||
RELEASE_VERSION="v${TAG_MAJOR}.${TAG_MINOR}.0"
|
||||
RELEASE_TAGGED_VERSION="${HEAD_SEMVER_TAG}"
|
||||
RELEASE_BUILD_TAG="${RELEASE_TAGGED_VERSION}-${SHORT_SHA}"
|
||||
|
||||
RELEASE_TAG_LIST="$({ GIT_SSH_COMMAND="${GIT_SSH_COMMAND}" git ls-remote --tags --refs "${GITEA_REPO_SSH_URL}" 2>/dev/null || true; } \
|
||||
| awk '{sub("refs/tags/", "", $2); print $2}' \
|
||||
| grep -E '^v[0-9]+\.[0-9]+\.0$' \
|
||||
| sort -V)"
|
||||
|
||||
PREVIOUS_RELEASE_VERSION="$(printf '%s\n' "${RELEASE_TAG_LIST}" | awk -v current="${RELEASE_VERSION}" 'NF {if ($0 == current) {print prev; found=1; exit} prev=$0} END {if (!found) print prev}')"
|
||||
|
||||
RELEASE_BACKEND_REPO="${GITEA_REGISTRY}/darkhelm.org/deployable-backend"
|
||||
RELEASE_FRONTEND_REPO="${GITEA_REGISTRY}/darkhelm.org/deployable-frontend"
|
||||
|
||||
BACKEND_RELEASE_VERSION_REF="${RELEASE_BACKEND_REPO}:${RELEASE_VERSION}"
|
||||
BACKEND_RELEASE_TAGGED_REF="${RELEASE_BACKEND_REPO}:${RELEASE_TAGGED_VERSION}"
|
||||
BACKEND_RELEASE_BUILD_REF="${RELEASE_BACKEND_REPO}:${RELEASE_BUILD_TAG}"
|
||||
BACKEND_RELEASE_LATEST_REF="${RELEASE_BACKEND_REPO}:latest"
|
||||
FRONTEND_RELEASE_VERSION_REF="${RELEASE_FRONTEND_REPO}:${RELEASE_VERSION}"
|
||||
FRONTEND_RELEASE_TAGGED_REF="${RELEASE_FRONTEND_REPO}:${RELEASE_TAGGED_VERSION}"
|
||||
FRONTEND_RELEASE_BUILD_REF="${RELEASE_FRONTEND_REPO}:${RELEASE_BUILD_TAG}"
|
||||
FRONTEND_RELEASE_LATEST_REF="${RELEASE_FRONTEND_REPO}:latest"
|
||||
|
||||
retry_cmd 5 2 git -C "${TMP_REPO_DIR}" init -q
|
||||
retry_cmd 5 3 env GIT_SSH_COMMAND="${GIT_SSH_COMMAND}" git -C "${TMP_REPO_DIR}" remote add origin "${GITEA_REPO_SSH_URL}"
|
||||
retry_cmd 5 3 env GIT_SSH_COMMAND="${GIT_SSH_COMMAND}" git -C "${TMP_REPO_DIR}" fetch --no-tags --depth 200 origin "${HEAD_SHA}"
|
||||
retry_cmd 5 3 env GIT_SSH_COMMAND="${GIT_SSH_COMMAND}" git -C "${TMP_REPO_DIR}" fetch --tags --force origin
|
||||
|
||||
RELEASE_NOTES_FILE="$(mktemp)"
|
||||
if [ -n "${PREVIOUS_RELEASE_VERSION}" ]; then
|
||||
LOG_RANGE="${PREVIOUS_RELEASE_VERSION}..${HEAD_SHA}"
|
||||
CHANGES_HEADER="Changes since previous release ${PREVIOUS_RELEASE_VERSION}:"
|
||||
else
|
||||
LOG_RANGE="${HEAD_SHA}"
|
||||
CHANGES_HEADER="Changes included in this initial tracked release:"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## Release ${RELEASE_VERSION}"
|
||||
echo
|
||||
echo "- Release series tag: ${RELEASE_VERSION}"
|
||||
echo "- Version tag on commit: ${RELEASE_TAGGED_VERSION}"
|
||||
echo "- Build tag: ${RELEASE_BUILD_TAG}"
|
||||
if [ -n "${PREVIOUS_RELEASE_VERSION}" ]; then
|
||||
echo "- Previous release: ${PREVIOUS_RELEASE_VERSION}"
|
||||
else
|
||||
echo "- Previous release: (none)"
|
||||
fi
|
||||
echo
|
||||
echo "${CHANGES_HEADER}"
|
||||
git -C "${TMP_REPO_DIR}" log --no-merges --pretty='- %s (%h)' "${LOG_RANGE}" || true
|
||||
} > "${RELEASE_NOTES_FILE}"
|
||||
|
||||
if ! grep -q '^- ' "${RELEASE_NOTES_FILE}"; then
|
||||
{
|
||||
echo
|
||||
echo "- No commit-summary entries were found in the selected range."
|
||||
} >> "${RELEASE_NOTES_FILE}"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "### Generated Release Notes"
|
||||
cat "${RELEASE_NOTES_FILE}"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
docker_login_with_retry 5 3
|
||||
retry_registry_op pull "${STAGING_BACKEND_DIGEST_REF}" 5 3
|
||||
retry_registry_op pull "${STAGING_FRONTEND_DIGEST_REF}" 5 3
|
||||
@@ -2607,24 +2675,32 @@ jobs:
|
||||
FRONTEND_IMAGE_ID="$(docker image inspect --format '{{.Id}}' "${STAGING_FRONTEND_DIGEST_REF}")"
|
||||
|
||||
docker tag "${BACKEND_IMAGE_ID}" "${BACKEND_RELEASE_VERSION_REF}"
|
||||
docker tag "${BACKEND_IMAGE_ID}" "${BACKEND_RELEASE_TAGGED_REF}"
|
||||
docker tag "${BACKEND_IMAGE_ID}" "${BACKEND_RELEASE_BUILD_REF}"
|
||||
docker tag "${BACKEND_IMAGE_ID}" "${BACKEND_RELEASE_LATEST_REF}"
|
||||
docker tag "${FRONTEND_IMAGE_ID}" "${FRONTEND_RELEASE_VERSION_REF}"
|
||||
docker tag "${FRONTEND_IMAGE_ID}" "${FRONTEND_RELEASE_TAGGED_REF}"
|
||||
docker tag "${FRONTEND_IMAGE_ID}" "${FRONTEND_RELEASE_BUILD_REF}"
|
||||
docker tag "${FRONTEND_IMAGE_ID}" "${FRONTEND_RELEASE_LATEST_REF}"
|
||||
|
||||
retry_registry_op push "${BACKEND_RELEASE_VERSION_REF}" 5 4
|
||||
retry_registry_op push "${BACKEND_RELEASE_TAGGED_REF}" 5 4
|
||||
retry_registry_op push "${BACKEND_RELEASE_BUILD_REF}" 5 4
|
||||
retry_registry_op push "${BACKEND_RELEASE_LATEST_REF}" 5 4
|
||||
retry_registry_op push "${FRONTEND_RELEASE_VERSION_REF}" 5 4
|
||||
retry_registry_op push "${FRONTEND_RELEASE_TAGGED_REF}" 5 4
|
||||
retry_registry_op push "${FRONTEND_RELEASE_BUILD_REF}" 5 4
|
||||
retry_registry_op push "${FRONTEND_RELEASE_LATEST_REF}" 5 4
|
||||
|
||||
echo "release_version=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tagged_version=${RELEASE_TAGGED_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "previous_release_version=${PREVIOUS_RELEASE_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_version_ref=${BACKEND_RELEASE_VERSION_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_tagged_ref=${BACKEND_RELEASE_TAGGED_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "backend_release_build_ref=${BACKEND_RELEASE_BUILD_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_version_ref=${FRONTEND_RELEASE_VERSION_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_tagged_ref=${FRONTEND_RELEASE_TAGGED_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "frontend_release_build_ref=${FRONTEND_RELEASE_BUILD_REF}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- *failure_diagnostics_step
|
||||
|
||||
@@ -171,16 +171,19 @@ jobs:
|
||||
promote-release-images:
|
||||
needs: [build_cicd, build-release-images, integration-tests, e2e-tests]
|
||||
# retags staging images to deployable-backend/deployable-frontend with:
|
||||
# - release version tag (semver)
|
||||
# - release build tag (<semver>-<7-char-short-sha>)
|
||||
# - release series tag (<major>.<minor>.0)
|
||||
# - exact git semver tag from HEAD (authoritative release tag)
|
||||
# - release build tag (<git-semver>-<7-char-short-sha>)
|
||||
# - latest
|
||||
```
|
||||
|
||||
### Release Tagging Model
|
||||
|
||||
- If the current commit has a semver git tag (`v<major>.<minor>.<patch>`), promotion uses that exact version.
|
||||
- Otherwise, promotion finds the latest semver baseline and auto-increments patch for the next successful release in that major/minor line.
|
||||
- Each promoted release also publishes a build-distinguishing tag: `<semver>-<short_sha>`, where `<short_sha>` is the 7-character commit shorthand.
|
||||
- Promotion is release-tag driven: if HEAD has no semver git tag (`v<major>.<minor>.<patch>`), promotion is skipped.
|
||||
- Release identity is derived from the git tag on HEAD.
|
||||
- Release series tag is normalized to `<major>.<minor>.0`.
|
||||
- Each promoted release also publishes a build-distinguishing tag: `<git-semver>-<short_sha>`, where `<short_sha>` is the 7-character commit shorthand.
|
||||
- Promotion emits generated release notes summarizing commit subjects since the previous `<major>.<minor>.0` release tag.
|
||||
|
||||
### Responsibility Split
|
||||
|
||||
|
||||
@@ -415,7 +415,7 @@ The pipeline is intentionally staged so expensive image jobs run only after sour
|
||||
4. Runtime validation lanes:
|
||||
`Runtime Black-Box Integration Tests` and `End-to-End Tests` validate staged runtime artifacts.
|
||||
5. Promotion lane:
|
||||
`Promote Release Images` retags validated staging artifacts to release repos (`deployable-backend`, `deployable-frontend`) with `latest`, `<semver>`, and `<semver>-<7-char-short-sha>` tags.
|
||||
`Promote Release Images` runs only when HEAD has a semver git tag and retags validated staging artifacts to release repos (`deployable-backend`, `deployable-frontend`) with `latest`, `<major>.<minor>.0`, `<tagged-semver>`, and `<tagged-semver>-<7-char-short-sha>` tags.
|
||||
6. Postmortem lanes:
|
||||
targeted postmortem jobs run when key lanes fail to capture diagnostics even when primary jobs fail early.
|
||||
|
||||
@@ -426,6 +426,7 @@ Current workflow behavior includes:
|
||||
- registry auth realm host pinning from `WWW-Authenticate` challenge when registry tokens are issued from a different host
|
||||
- bounded retry logic for docker login/pull/push operations in image lanes
|
||||
- digest/tag contract checks for deployable image references before runtime black-box tests
|
||||
- release-note summary generation in the promotion lane (commit bullets since previous `<major>.<minor>.0` release tag)
|
||||
- context hydration for image-build lanes by copying `/workspace` from the published CICD image
|
||||
- runner split between `ubuntu-act` and `ubuntu-act-8gb` based on lane resource requirements
|
||||
|
||||
|
||||
Reference in New Issue
Block a user