Schedule moves to Mondays 04:00 UTC. release.sh now deletes older releases for the same image/distro (and their assets and tags) after publishing, so only the newest is kept. Add prune-releases.sh for a one-off cleanup.
185 lines
6.2 KiB
Bash
Executable File
185 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generate a package changelog for a freshly built image and publish it as a
|
|
# Gitea release (instead of committing JSON files under changelogs/).
|
|
#
|
|
# Usage: release.sh <image> <distro> <buildid> <packages-file>
|
|
#
|
|
# The full package list is attached to each release as
|
|
# packages-<image>-<distro>.txt
|
|
# and the previous release's asset is used as the diff baseline.
|
|
set -euo pipefail
|
|
|
|
if [[ $# -lt 4 ]]; then
|
|
echo "Usage: $0 <image> <distro> <buildid> <packages-file>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
IMAGE="$1"
|
|
DISTRO="$2"
|
|
BUILDID="$3"
|
|
PACKAGES_FILE="$4"
|
|
|
|
GITEA_URL="${GITEA_URL:-https://git.plabble.org}"
|
|
GITEA_REPO="${GITEA_REPO:-Misthios/bootc-images}"
|
|
TOKEN="${RELEASE_TOKEN:-${GITHUB_TOKEN:-}}"
|
|
|
|
[[ -n "${TOKEN}" ]] || { echo "RELEASE_TOKEN (or GITHUB_TOKEN) is required" >&2; exit 1; }
|
|
[[ -f "${PACKAGES_FILE}" ]] || { echo "Missing package list: ${PACKAGES_FILE}" >&2; exit 1; }
|
|
|
|
API="${GITEA_URL%/}/api/v1/repos/${GITEA_REPO}"
|
|
RELEASE_TAG="${IMAGE}-${DISTRO}.${BUILDID}"
|
|
ASSET_NAME="packages-${IMAGE}-${DISTRO}.txt"
|
|
RELEASE_TITLE="${IMAGE} ${DISTRO} ${BUILDID}"
|
|
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "${WORK}"' EXIT
|
|
|
|
# Idempotency: never create the same release twice (e.g. a re-run).
|
|
if curl -fsSL -H "Authorization: token ${TOKEN}" \
|
|
"${API}/releases/tags/${RELEASE_TAG}" >/dev/null 2>&1; then
|
|
echo "Release ${RELEASE_TAG} already exists; nothing to do."
|
|
exit 0
|
|
fi
|
|
|
|
# --- package list from the build -------------------------------------------
|
|
echo "Using package list ${PACKAGES_FILE} ..."
|
|
cp "${PACKAGES_FILE}" "${WORK}/packages-current.txt"
|
|
|
|
# --- previous package list from the last release ---------------------------
|
|
# Walk the release list (newest first) until we find the newest release for
|
|
# this image/distro.
|
|
prev_release_id=""
|
|
page=1
|
|
while :; do
|
|
page_json="$(curl -fsSL -H "Authorization: token ${TOKEN}" \
|
|
"${API}/releases?limit=50&page=${page}")"
|
|
[[ "$(jq 'length' <<< "${page_json}")" -eq 0 ]] && break
|
|
prev_release_id="$(jq -r --arg prefix "${IMAGE}-${DISTRO}." \
|
|
'.[] | select(.tag_name | startswith($prefix)) | .id' <<< "${page_json}" \
|
|
| head -n1)"
|
|
[[ -n "${prev_release_id}" ]] && break
|
|
page=$((page + 1))
|
|
[[ "${page}" -gt 20 ]] && break
|
|
done
|
|
|
|
if [[ -n "${prev_release_id}" ]]; then
|
|
asset_url="$(curl -fsSL -H "Authorization: token ${TOKEN}" \
|
|
"${API}/releases/${prev_release_id}" \
|
|
| jq -r --arg name "${ASSET_NAME}" \
|
|
'.assets[] | select(.name == $name) | .browser_download_url' \
|
|
| head -n1)"
|
|
if [[ -n "${asset_url}" && "${asset_url}" != "null" ]]; then
|
|
curl -fsSL -H "Authorization: token ${TOKEN}" "${asset_url}" \
|
|
-o "${WORK}/packages-previous.txt" || true
|
|
fi
|
|
fi
|
|
# First ever build: baseline against itself so nothing shows up as removed.
|
|
[[ -f "${WORK}/packages-previous.txt" ]] \
|
|
|| cp "${WORK}/packages-current.txt" "${WORK}/packages-previous.txt"
|
|
|
|
# --- diff -------------------------------------------------------------------
|
|
CUR_NAMES="$(mktemp)"
|
|
PREV_NAMES="$(mktemp)"
|
|
cut -d' ' -f1 "${WORK}/packages-current.txt" | sort > "${CUR_NAMES}"
|
|
cut -d' ' -f1 "${WORK}/packages-previous.txt" | sort > "${PREV_NAMES}"
|
|
|
|
added_names="$(comm -13 "${PREV_NAMES}" "${CUR_NAMES}" || true)"
|
|
removed_names="$(comm -23 "${PREV_NAMES}" "${CUR_NAMES}" || true)"
|
|
common_names="$(comm -12 "${PREV_NAMES}" "${CUR_NAMES}" || true)"
|
|
|
|
ver_of() { awk -v p="$1" '$1 == p {print $2}' "$2"; }
|
|
|
|
added=()
|
|
while read -r n; do
|
|
[[ -z "${n}" ]] && continue
|
|
added+=("${n} $(ver_of "${n}" "${WORK}/packages-current.txt")")
|
|
done <<< "${added_names}"
|
|
|
|
removed=()
|
|
while read -r n; do
|
|
[[ -z "${n}" ]] && continue
|
|
removed+=("${n} $(ver_of "${n}" "${WORK}/packages-previous.txt")")
|
|
done <<< "${removed_names}"
|
|
|
|
updated=()
|
|
while read -r n; do
|
|
[[ -z "${n}" ]] && continue
|
|
old="$(ver_of "${n}" "${WORK}/packages-previous.txt")"
|
|
new="$(ver_of "${n}" "${WORK}/packages-current.txt")"
|
|
[[ -z "${old}" || -z "${new}" ]] && continue
|
|
[[ "${old}" != "${new}" ]] && updated+=("${n} ${old} -> ${new}")
|
|
done <<< "${common_names}"
|
|
|
|
{
|
|
echo "Automated build of \`${IMAGE}\` \`${DISTRO}\` (\`${BUILDID}\`)."
|
|
echo
|
|
echo "- Added: ${#added[@]}"
|
|
echo "- Removed: ${#removed[@]}"
|
|
echo "- Updated: ${#updated[@]}"
|
|
echo
|
|
if (( ${#added[@]} )); then
|
|
echo "### Added"
|
|
printf -- '- %s\n' "${added[@]}"
|
|
echo
|
|
fi
|
|
if (( ${#removed[@]} )); then
|
|
echo "### Removed"
|
|
printf -- '- %s\n' "${removed[@]}"
|
|
echo
|
|
fi
|
|
if (( ${#updated[@]} )); then
|
|
echo "### Updated"
|
|
printf -- '- %s\n' "${updated[@]}"
|
|
echo
|
|
fi
|
|
} > "${WORK}/changelog.md"
|
|
|
|
# --- create the release -----------------------------------------------------
|
|
echo "Creating release ${RELEASE_TAG} ..."
|
|
payload="$(jq -n \
|
|
--arg tag "${RELEASE_TAG}" \
|
|
--arg name "${RELEASE_TITLE}" \
|
|
--arg body "$(cat "${WORK}/changelog.md")" \
|
|
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')"
|
|
|
|
response="$(curl -fsSL -X POST \
|
|
-H "Authorization: token ${TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "${payload}" \
|
|
"${API}/releases")"
|
|
release_id="$(jq -r '.id' <<< "${response}")"
|
|
|
|
echo "Uploading ${ASSET_NAME} ..."
|
|
curl -fsSL -X POST \
|
|
-H "Authorization: token ${TOKEN}" \
|
|
-F "attachment=@${WORK}/packages-current.txt" \
|
|
"${API}/releases/${release_id}/assets?name=${ASSET_NAME}"
|
|
|
|
echo "Published release ${RELEASE_TAG}."
|
|
|
|
# --- prune older releases (and their assets/tags) for this image/distro -----
|
|
echo "Pruning older ${IMAGE} ${DISTRO} releases ..."
|
|
old_releases=""
|
|
page=1
|
|
while :; do
|
|
page_json="$(curl -fsSL -H "Authorization: token ${TOKEN}" \
|
|
"${API}/releases?limit=50&page=${page}")"
|
|
[[ "$(jq 'length' <<< "${page_json}")" -eq 0 ]] && break
|
|
old_releases+="$(jq -r --arg prefix "${IMAGE}-${DISTRO}." --arg keep "${release_id}" \
|
|
'.[] | select(.tag_name | startswith($prefix)) | select((.id | tostring) != $keep) | "\(.id)\t\(.tag_name)"' \
|
|
<<< "${page_json}")"$'\n'
|
|
page=$((page + 1))
|
|
[[ "${page}" -gt 20 ]] && break
|
|
done
|
|
|
|
while IFS=$'\t' read -r old_id old_tag; do
|
|
[[ -z "${old_id}" ]] && continue
|
|
echo " removing release ${old_tag}"
|
|
curl -fsSL -X DELETE -H "Authorization: token ${TOKEN}" \
|
|
"${API}/releases/${old_id}" >/dev/null || true
|
|
curl -fsSL -X DELETE -H "Authorization: token ${TOKEN}" \
|
|
"${API}/tags/${old_tag}" >/dev/null || true
|
|
done <<< "${old_releases}"
|
|
|
|
echo "Done."
|