diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml index bd97bce47..9bf289a8c 100644 --- a/.github/workflows/make-release.yml +++ b/.github/workflows/make-release.yml @@ -3,6 +3,11 @@ name: Make Release on: workflow_dispatch: inputs: + commit: + description: 'Commit SHA to release (empty = branch HEAD)' + required: false + default: '' + type: string dry_run: description: 'Dry run - validate without creating the tag' required: true @@ -24,12 +29,15 @@ jobs: uses: actions/checkout@v6 with: ssh-key: ${{ secrets.DEPLOY_KEY_RELEASE }} + ref: ${{ inputs.commit != '' && inputs.commit || github.ref_name }} + fetch-depth: 0 - name: Run release checks id: checks run: bash scripts/make-release-checks.sh ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }} env: GITHUB_REPOSITORY: ${{ github.repository }} + RELEASE_BRANCH: ${{ github.ref_name }} - name: Create release tag if: ${{ github.event.inputs.dry_run == 'false' }} diff --git a/docs/release.md b/docs/release.md index 4335ef9d4..e0c9c486b 100644 --- a/docs/release.md +++ b/docs/release.md @@ -29,6 +29,12 @@ identify which PRs require a version bump before cutting a release._ Releases are created by running the [make-release](.github/workflows/make-release.yml) which is a manual workflow. +The workflow runs against the branch selected in the "Run workflow" dialog +(default `master`) and takes an optional `commit` SHA. When a commit is given, +the workflow validates that the commit belongs to the branch and is not older +than 3 days from the branch HEAD, then releases that commit instead of the +branch HEAD. + The workflow creates an annotated git tag (e.g. `v0.1.0`) and pushes it to the remote. No GitHub Release object is created, the tag is the release artifact. diff --git a/scripts/make-release-checks.sh b/scripts/make-release-checks.sh index 8d0e5490b..6b2d54786 100755 --- a/scripts/make-release-checks.sh +++ b/scripts/make-release-checks.sh @@ -4,7 +4,10 @@ # Usage: make-release-checks.sh [--dry-run] # --dry-run: warn on failures instead of aborting # -# Env (when running in GitHub Actions): GH_TOKEN, GITHUB_REPOSITORY, GITHUB_OUTPUT +# Env (when running in GitHub Actions): +# GH_TOKEN, GITHUB_REPOSITORY, GITHUB_OUTPUT +# RELEASE_BRANCH: when set, HEAD must belong to origin/RELEASE_BRANCH and must +# not be older than 3 days from the branch HEAD (skipped when unset) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -28,6 +31,39 @@ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then echo "version=${VERSION}" >> "$GITHUB_OUTPUT" fi +SHA=$(git rev-parse HEAD) + +echo "Checking that commit ${SHA} belongs to the release branch..." +if [[ -z "${RELEASE_BRANCH:-}" ]]; then + echo "Warning: RELEASE_BRANCH not set - skipping commit check (local run)" +else + TIP="origin/${RELEASE_BRANCH}" + COMMIT_ERR="" + if ! git rev-parse --verify "${TIP}" >/dev/null 2>&1; then + COMMIT_ERR="branch ${RELEASE_BRANCH} not found on remote" + elif ! git merge-base --is-ancestor "${SHA}" "${TIP}"; then + COMMIT_ERR="commit ${SHA} is not part of branch ${RELEASE_BRANCH}" + else + COMMIT_TS=$(git show -s --format=%ct "${SHA}") + TIP_TS=$(git show -s --format=%ct "${TIP}") + AGE_DAYS=$(( (TIP_TS - COMMIT_TS) / 86400 )) + if (( TIP_TS - COMMIT_TS > 3 * 86400 )); then + COMMIT_ERR="commit ${SHA} is ${AGE_DAYS} day(s) older than the HEAD of ${RELEASE_BRANCH} (max: 3)" + fi + fi + if [[ -n "${COMMIT_ERR}" ]]; then + if [[ "$DRY_RUN" == "true" ]]; then + echo "Warning: ${COMMIT_ERR} (dry run, continuing)." + CHECKS_PASSED=false + else + echo "Error: ${COMMIT_ERR}" + exit 1 + fi + else + echo "Commit ${SHA} is on branch ${RELEASE_BRANCH} and within 3 days of its HEAD - OK" + fi +fi + echo "Checking that tag ${VERSION} does not already exist..." if git ls-remote --tags origin "${VERSION}" | grep -q "${VERSION}"; then echo "Error: tag ${VERSION} already exists on remote" @@ -35,27 +71,6 @@ if git ls-remote --tags origin "${VERSION}" | grep -q "${VERSION}"; then fi echo "Tag ${VERSION} does not exist on remote - OK" -SHA=$(git rev-parse HEAD) -echo "Checking release.yml status for commit ${SHA}..." -if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then - echo "Warning: GITHUB_REPOSITORY not set - skipping CI check (local run)" -else - RUNS=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/release.yml/runs" \ - --jq "[.workflow_runs[] | select(.head_sha == \"${SHA}\" and .conclusion == \"success\")] | length") - if [[ "$RUNS" -eq 0 ]]; then - if [[ "$DRY_RUN" == "true" ]]; then - echo "Warning: no successful release.yml run found for HEAD (${SHA}) (dry run, continuing)." - CHECKS_PASSED=false - else - echo "Error: no successful release.yml run found for HEAD (${SHA})" - echo "The nightly build must complete successfully before making a release." - exit 1 - fi - else - echo "Found successful release.yml run for HEAD." - fi -fi - MAJOR=$(grep "set(GGML_VERSION_MAJOR" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+') MINOR=$(grep "set(GGML_VERSION_MINOR" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+') PATCH=$(grep "set(GGML_VERSION_PATCH" "$REPO_ROOT/ggml/CMakeLists.txt" | grep -oP '\d+')