Two main merges built minutes apart both computed the same GitVersion version. The later pipeline overwrote the image tag in GHCR, github-release tagged whatever main pointed at (--target main), and the GitOps bump was a no-op, so production ran an image without the later merge (Website-Backend 0.1.24 and 0.1.32). - allocate_version: on main, reserve vX.Y.Z by creating the git tag through the GitHub refs API (create-if-absent). On conflict with another commit, take the next patch; reruns of the same commit reuse their tag. Existing tags are never moved. Runs in mvn/gradle/node build-and-test before the version is baked into artifacts. - guard_image_tags: refuse to push a main or supplemental version tag that already exists for a different commit (org.opencontainers.image.revision label, now set on every build); fail closed on registry errors. - update_cd_repo: verify vX.Y.Z belongs to this commit before bumping the GitOps repo; record the source commit in the GitOps commit message. - github_release: target CIRCLE_SHA1 instead of main and refuse when the tag points elsewhere. - Generator: render cd_path only when cd.path is set, so manifests pinned to orb versions predating the parameter (website develop branches, Concilium, makers4) stop failing with "Unexpected argument(s): cd_path". Tests: tests/test_allocate_version.sh (incl. 8 concurrent allocators against an atomic fake refs API) and tests/test_release_guards.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.1 KiB
Bash
Executable File
88 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Source shared helpers
|
|
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
|
|
# shellcheck source=munitor_helpers.sh
|
|
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
|
|
elif ! type munitor_header &>/dev/null; then
|
|
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
|
|
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
|
|
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
|
|
fi
|
|
|
|
munitor_header "github_release"
|
|
|
|
# Validate required environment
|
|
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
|
|
echo "ERROR: GITHUB_TOKEN environment variable is not set."
|
|
echo "Add a 'github' context with GITHUB_TOKEN to your CircleCI project."
|
|
exit 1
|
|
fi
|
|
|
|
# PROJECT_VERSION is set by GitVersion in the build job and restored via .munitor-version
|
|
VERSION="${PROJECT_VERSION:?PROJECT_VERSION is not set. Ensure the build job ran and version metadata was persisted.}"
|
|
TAG="v${VERSION}"
|
|
|
|
echo "Version: ${VERSION}"
|
|
echo "Tag: ${TAG}"
|
|
|
|
munitor_check_tool gh --version
|
|
|
|
# Detect pre-release versions (RC, alpha, beta, SNAPSHOT)
|
|
if [[ "${VERSION}" =~ -(RC|alpha|beta|SNAPSHOT) ]]; then
|
|
PRERELEASE_FLAG="--prerelease"
|
|
TARGET="${CIRCLE_BRANCH:-main}"
|
|
else
|
|
PRERELEASE_FLAG=""
|
|
# Target the commit this pipeline built, never the moving branch head: a
|
|
# later merge must not receive this version's tag.
|
|
TARGET="${CIRCLE_SHA1:-main}"
|
|
fi
|
|
|
|
echo "Target: ${TARGET}"
|
|
echo "Pre-release: ${PRERELEASE_FLAG:-no}"
|
|
|
|
# Create tag + GitHub Release via API (skip if already exists)
|
|
# Uses gh release create which handles both tag and release in one call,
|
|
# bypassing CircleCI's read-only SSH deploy key entirely.
|
|
# The release tag is normally reserved by allocate_version. If it exists it
|
|
# must point at this commit; an existing tag is never moved.
|
|
if [[ -n "${CIRCLE_SHA1:-}" ]] && TAG_SHA=$(gh api "repos/{owner}/{repo}/commits/${TAG}" --jq '.sha' 2>/dev/null); then
|
|
if [[ "${TAG_SHA}" != "${CIRCLE_SHA1}" ]]; then
|
|
echo "ERROR: Tag ${TAG} already points at ${TAG_SHA}, not ${CIRCLE_SHA1}. Refusing to release."
|
|
exit 1
|
|
fi
|
|
echo "Tag ${TAG} points at ${CIRCLE_SHA1}."
|
|
fi
|
|
|
|
if gh release view "${TAG}" >/dev/null 2>&1; then
|
|
echo "Release ${TAG} already exists, skipping."
|
|
else
|
|
echo "Creating GitHub Release ${TAG}..."
|
|
gh release create "${TAG}" \
|
|
--generate-notes \
|
|
--target "${TARGET}" \
|
|
--title "Release ${VERSION}" \
|
|
${PRERELEASE_FLAG}
|
|
echo "GitHub Release ${TAG} created."
|
|
fi
|
|
|
|
# Upload release artifacts if specified
|
|
if [[ -n "${MUNITOR_RELEASE_ARTIFACTS:-}" ]]; then
|
|
echo "Uploading release artifacts: ${MUNITOR_RELEASE_ARTIFACTS}"
|
|
for artifact in ${MUNITOR_RELEASE_ARTIFACTS}; do
|
|
if [[ ! -f "${artifact}" ]]; then
|
|
echo "ERROR: Release artifact '${artifact}' not found."
|
|
echo " Working directory: $(pwd)"
|
|
echo " Available files: $(ls -la)"
|
|
exit 1
|
|
fi
|
|
done
|
|
# shellcheck disable=SC2086
|
|
gh release upload "${TAG}" ${MUNITOR_RELEASE_ARTIFACTS} --clobber
|
|
echo "Artifacts uploaded to ${TAG}."
|
|
fi
|
|
|
|
echo "github_release completed"
|