Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions .github/workflows/helm-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,39 @@ jobs:
[ -n "$name" ] || continue
tag="${name}-v${version}"
asset="${name}-${version}.tgz"

# git ls-remote --exit-code exits 2 for "no such tag" and non-zero
# for anything else (e.g. an unreachable remote); only 2 may be
# read as "no tag" or a network blip would silently fall through
# to hand-creating a release with no tag verification at all.
git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null </dev/null && rc=0 || rc=$?
case "$rc" in
0) has_tag=true ;;
2) has_tag=false ;;
*) echo "::error::git ls-remote for tag ${tag} exited ${rc}; origin may be unreachable, refusing to assume no tag exists"; exit 1 ;;
esac

if [ "$has_tag" = "true" ]; then
# The tag pre-dates this run (normally created by an earlier
# release run for this same version), whether or not it already
# has a release, or a release with no asset yet. Never
# hand-create <chart>-v* tags: refuse to trust it unless its
# commit is on main and its tree for this chart (plus any
# manifest this chart's own README points at) is identical to
# $target's, so the release always documents the commit it
# actually came from.
git fetch -q --no-tags origin "+refs/tags/${tag}:refs/tags/${tag}" </dev/null
if ! git merge-base --is-ancestor "refs/tags/${tag}^{commit}" HEAD </dev/null; then
echo "::error::tag ${tag} exists but its commit is not on main; refusing to trust it. Hand-made <chart>-v* tags are never created outside this job; delete ${tag} and let this job recreate it."
exit 1
fi
manifests="$(git show "refs/tags/${tag}:helm-charts/${name}/README.md" 2>/dev/null | grep -oE '\bmanifests/[A-Za-z0-9_.-]+\.ya?ml\b' | sort -u || true)"
if ! git diff --quiet "refs/tags/${tag}" "${target}" -- "helm-charts/${name}" ${manifests} </dev/null; then
echo "::error::tag ${tag} exists but its tree for helm-charts/${name} ${manifests} differs from ${target}; refusing to trust it. Hand-made <chart>-v* tags are never created outside this job; delete ${tag} and let this job recreate it."
exit 1
fi
fi

if gh release view "$tag" >/dev/null 2>&1 </dev/null; then
if ! gh release view "$tag" --json assets --jq '.assets[].name' </dev/null | grep -qxF "$asset"; then
gh release upload "$tag" "_site/${asset}" </dev/null
Expand All @@ -179,6 +212,7 @@ jobs:
echo "release ${tag} already has ${asset} (${want})"
continue
fi

notes="$(printf '%s\n' \
"Chart \`${name}\` version \`${version}\`, published to ${HELM_REPO_URL}" \
"" \
Expand All @@ -189,18 +223,7 @@ jobs:
'```' \
"" \
"Chart README: https://github.com/${GITHUB_REPOSITORY}/blob/${tag}/helm-charts/${name}/README.md")"
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null </dev/null; then
# The tag pre-dates this run (normally created by an earlier
# release run for this same version). Never hand-create
# <chart>-v* tags: refuse to attach the release unless the tag's
# tree for this chart and manifests/ is identical to $target's,
# so the release always documents the commit it actually came
# from.
git fetch -q --no-tags origin "+refs/tags/${tag}:refs/tags/${tag}" </dev/null
if ! git diff --quiet "refs/tags/${tag}" "${target}" -- "helm-charts/${name}" manifests/ </dev/null; then
echo "::error::tag ${tag} already exists but its tree for helm-charts/${name} and manifests/ differs from ${target}; refusing to attach ${asset} there. Hand-made <chart>-v* tags are never created outside this job; delete ${tag} and let this job recreate it."
exit 1
fi
if [ "$has_tag" = "true" ]; then
gh release create "$tag" "_site/${asset}" --verify-tag \
--title "${name} ${version}" --notes "${notes}" </dev/null
else
Expand Down
35 changes: 21 additions & 14 deletions scripts/publish-helm-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ verify_tag_chart_version() {
"+refs/heads/$MAIN_BRANCH:refs/remotes/$TAG_REMOTE/$MAIN_BRANCH" \
"+refs/tags/$tag:refs/tags/$tag" </dev/null ||
die "could not fetch $MAIN_BRANCH and tag $tag from $TAG_REMOTE to verify $name $version"
git merge-base --is-ancestor "refs/tags/$tag^{commit}" "refs/remotes/$TAG_REMOTE/$MAIN_BRANCH" || return 1
git merge-base --is-ancestor "refs/tags/$tag^{commit}" "refs/remotes/$TAG_REMOTE/$MAIN_BRANCH" || return 2
chart="$(chart_dir_at "refs/tags/$tag^{commit}" "$name")" || return 1
[ "$(git show "refs/tags/$tag^{commit}:$chart/Chart.yaml" | yq -r '.version')" = "$version" ] || return 1
echo "$chart"
Expand All @@ -250,9 +250,13 @@ verify_tag_chart_version() {
# contents as <asset>.
verify_against_tag_tree() {
local tag="$1" name="$2" version="$3" asset="$4"
local chart src="$work/recover-src/$tag"
chart="$(verify_tag_chart_version "$tag" "$name" "$version")" ||
die "tag $tag is not on $MAIN_BRANCH, or has no $CHARTS_DIR/*/Chart.yaml named $name at version $version; refusing to recover $name $version from it"
local chart src="$work/recover-src/$tag" rc
chart="$(verify_tag_chart_version "$tag" "$name" "$version")" && rc=0 || rc=$?
if [ "$rc" = 2 ]; then
die "tag $tag is not on $MAIN_BRANCH; refusing to recover $name $version from it"
elif [ "$rc" != 0 ]; then
die "tag $tag has no $CHARTS_DIR/*/Chart.yaml named $name at version $version; refusing to recover $name $version from it"
fi
mkdir -p "$src/pkg"
git archive "refs/tags/$tag^{commit}" "$chart" | tar -x -C "$src" ||
die "could not extract $chart from tag $tag"
Expand All @@ -275,24 +279,27 @@ recover_from_release() {
# reports every other failure (a 5xx, a rate limit, a reset) the same way a
# published release's absence is reported, which would silently drop a live
# version instead of failing the build.
local tag_rc
if ! gh api "repos/$REPOSITORY/releases/tags/$tag" </dev/null >"$listing" 2>"$errors"; then
grep -qE 'HTTP 404' "$errors" ||
die "looking up release $tag of $REPOSITORY failed: $(tr '\n' ' ' <"$errors")"
if verify_tag_chart_version "$tag" "$name" "$version" >/dev/null; then
echo "::warning::$name $version is tagged ($tag) but has no GitHub release, so nothing was ever published for it; it stays unpublished until $MAIN_BRANCH carries that version"
else
echo "::warning::$tag is not on $MAIN_BRANCH, or its tree is not $name $version, and it has no GitHub release either; it is not a real chart release and stays unpublished"
fi
verify_tag_chart_version "$tag" "$name" "$version" >/dev/null && tag_rc=0 || tag_rc=$?
case "$tag_rc" in
0) echo "::warning::$name $version is tagged ($tag) but has no GitHub release, so nothing was ever published for it; it stays unpublished until $MAIN_BRANCH carries that version" ;;
2) echo "::warning::$tag is not on $MAIN_BRANCH, and it has no GitHub release either; it is not a real chart release and stays unpublished" ;;
*) echo "::warning::$tag's tree is not $name $version, and it has no GitHub release either; it is not a real chart release and stays unpublished" ;;
esac
return 1
fi
local recorded actual chart_yaml legacy
recorded="$(jq -r --arg a "$asset" '.assets[] | select(.name == $a) | .digest // "none"' "$listing")"
if [ -z "$recorded" ]; then
if verify_tag_chart_version "$tag" "$name" "$version" >/dev/null; then
echo "::warning::release $tag has no asset $asset, so $name $version cannot be restored from it; it stays unpublished until $MAIN_BRANCH carries that version"
else
echo "::warning::$tag is not on $MAIN_BRANCH, or its tree is not $name $version, and its release has no $asset either; it is not a real chart release and stays unpublished"
fi
verify_tag_chart_version "$tag" "$name" "$version" >/dev/null && tag_rc=0 || tag_rc=$?
case "$tag_rc" in
0) echo "::warning::release $tag has no asset $asset, so $name $version cannot be restored from it; it stays unpublished until $MAIN_BRANCH carries that version" ;;
2) echo "::warning::$tag is not on $MAIN_BRANCH, and its release has no $asset either; it is not a real chart release and stays unpublished" ;;
*) echo "::warning::$tag's tree is not $name $version, and its release has no $asset either; it is not a real chart release and stays unpublished" ;;
esac
return 1
fi
gh release download "$tag" -R "$REPOSITORY" -p "$asset" -D "$dir" </dev/null ||
Expand Down
Loading