Cut the weekly release on GitLab
release.yml stopped running with the GitHub account, and nothing replaced it: no tag has been cut since, so the publish job had nothing to build. This ports its decision unchanged -- release only when main has commits since the newest published release, and only if the tag does not already exist -- to a job run by a Monday 09:17 UTC pipeline schedule. The schedule lives on the project and sets RELEASE_WEEKLY=1; DRY_RUN=1 stops after the decision. The release, and so the tag, is created with a project access token rather than the job token. That makes the tag an ordinary push, which starts the tag pipeline and its publish job, replacing release.yml's direct call of publish.yml. The checks are skipped in the release pipeline, as they were on GitHub: main has already passed them.
This commit is contained in:
+75
-1
@@ -15,7 +15,7 @@
|
||||
# https://git.coffeylabs.org -- that name is Cloudflare-proxied on the Free
|
||||
# plan, which caps request bodies at 100 MB and would break artifact uploads.
|
||||
|
||||
stages: [test, build, publish]
|
||||
stages: [test, build, publish, release]
|
||||
|
||||
variables:
|
||||
# Jobs talk to the registry directly on its DNS-only name, never through the
|
||||
@@ -64,6 +64,8 @@ node:
|
||||
paths: [dist/]
|
||||
expire_in: 1 week
|
||||
rules:
|
||||
- if: $RELEASE_WEEKLY == "1"
|
||||
when: never
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_TAG
|
||||
@@ -83,6 +85,8 @@ docker-build:
|
||||
- docker build -t ihasmail:ci-$CI_COMMIT_SHORT_SHA .
|
||||
- docker image rm ihasmail:ci-$CI_COMMIT_SHORT_SHA
|
||||
rules:
|
||||
- if: $RELEASE_WEEKLY == "1"
|
||||
when: never
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
@@ -145,3 +149,73 @@ publish:
|
||||
- docker logout "$CI_REGISTRY" || true
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
||||
# ----------------------------------------------------------- weekly release --
|
||||
# Port of .github/workflows/release.yml: cut a release once a week, but only
|
||||
# when there is something in it. The decision is the workflow's, unchanged --
|
||||
# count the commits on main since the newest published release, and skip the
|
||||
# week if there are none or if the tag already exists (the version comes from
|
||||
# the commit, so an unchanged commit is an existing tag).
|
||||
#
|
||||
# It runs from a pipeline schedule (Mondays 09:17 UTC, the same odd minute as
|
||||
# before) that sets RELEASE_WEEKLY=1. GitLab keeps schedules on the project,
|
||||
# not in this file, so the schedule and this job only work as a pair. Run it by
|
||||
# hand with RELEASE_WEEKLY=1, adding DRY_RUN=1 to see the decision and stop.
|
||||
#
|
||||
# The release -- and with it the tag -- is created with RELEASE_TOKEN, a
|
||||
# project access token (protected, masked), not CI_JOB_TOKEN. A tag pushed that
|
||||
# way is an ordinary push, so it starts the tag pipeline, and the version and
|
||||
# publish jobs above build the image from it. That replaces release.yml's
|
||||
# direct call of publish.yml, which only existed because a tag created with
|
||||
# GITHUB_TOKEN raises no event. The token expires; when it does this job fails
|
||||
# at the API call, loudly, and a new one goes in the same variable.
|
||||
weekly-release:
|
||||
stage: release
|
||||
image: node:26-bookworm-slim@sha256:582460f614631b59b824ac6020533b9bf339c7fdf3a6d7db31abb6b4065f0212 # 26-bookworm-slim
|
||||
# One at a time: two overlapping runs would race to create the same tag.
|
||||
resource_group: weekly-release
|
||||
variables:
|
||||
GIT_DEPTH: "0"
|
||||
before_script:
|
||||
- apt-get update -qq && apt-get install -y -qq --no-install-recommends git curl jq >/dev/null
|
||||
script:
|
||||
- |
|
||||
set -euo pipefail
|
||||
# Internal address, as for everything else CI does: never through the proxy.
|
||||
API="http://gitlab/api/v4/projects/${CI_PROJECT_ID}"
|
||||
auth=(--header "PRIVATE-TOKEN: ${RELEASE_TOKEN}")
|
||||
# The newest published release, or empty on a project that has never had
|
||||
# one -- in which case everything counts as new.
|
||||
previous="$(curl -fsS "${auth[@]}" "${API}/releases?order_by=released_at&sort=desc&per_page=1" | jq -r '.[0].tag_name // ""')"
|
||||
# A release can outlive its tag. Falling back to the whole history
|
||||
# over-counts, which cuts a release that was due anyway; under-counting
|
||||
# would skip one that was.
|
||||
if [ -n "$previous" ] && git rev-parse -q --verify "refs/tags/${previous}" >/dev/null; then
|
||||
count="$(git rev-list --count "${previous}..HEAD")"; range="${previous}..HEAD"
|
||||
else
|
||||
count="$(git rev-list --count HEAD)"; range="HEAD"
|
||||
fi
|
||||
version="$(node scripts/version.mjs)"
|
||||
# A Docker tag may not contain '+', and neither should the git tag, so
|
||||
# the two always agree about what to call a build.
|
||||
tag="v${version/+/-}"
|
||||
title="v${version%%+*}"
|
||||
sha="$(git rev-parse HEAD)"
|
||||
if [ "$count" -eq 0 ]; then
|
||||
echo "Nothing to release: no commits since ${previous}."; exit 0
|
||||
fi
|
||||
if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then
|
||||
echo "Nothing to release: tag ${tag} already exists."; exit 0
|
||||
fi
|
||||
echo "Releasing ${tag} -- ${count} commit(s) since ${previous:-the beginning}, at ${sha}."
|
||||
if [ "${DRY_RUN:-0}" = "1" ]; then echo "DRY_RUN=1: stopping here."; exit 0; fi
|
||||
# Notes bounded to what is new, from the first-parent history of main --
|
||||
# one line per merge, which is what GitHub's generated notes listed.
|
||||
notes="$(git log --first-parent --format='- %s' "$range")"
|
||||
jq -n --arg tag "$tag" --arg ref "$sha" --arg name "$title" \
|
||||
--arg desc "$(printf '%s commit(s) since %s.\n\n%s' "$count" "${previous:-the beginning}" "$notes")" \
|
||||
'{tag_name:$tag, ref:$ref, name:$name, description:$desc}' > release.json
|
||||
curl -fsS "${auth[@]}" --header "Content-Type: application/json" \
|
||||
--data @release.json "${API}/releases" | jq -r '"created release " + .tag_name'
|
||||
rules:
|
||||
- if: $RELEASE_WEEKLY == "1" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
Reference in New Issue
Block a user