Compare commits
2
Commits
v1.0.1-rc.1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b250794a7c
|
||
|
|
bf9c093bdd |
No files matched your search
@@ -7,7 +7,7 @@
|
||||
# macos native darwin-arm64 binary, built and smoke-tested on the macOS runner
|
||||
# windows native windows-x64 binary, built and smoke-tested on the Windows runner
|
||||
# publish SHA256SUMS; publish the release and check its downloads; the four npm packages to the
|
||||
# QRun-IO Gitea npm registry; for a final version the Homebrew tap and the Scoop bucket
|
||||
# QRun-IO Gitea npm registry; for a final version pull requests to the Homebrew tap and the Scoop bucket
|
||||
name: release
|
||||
on:
|
||||
push:
|
||||
@@ -296,38 +296,9 @@ jobs:
|
||||
echo "Published $name@$version ($tag): $(npm view "$name@$version" version --registry "$registry")"
|
||||
done
|
||||
|
||||
- name: Homebrew tap and Scoop bucket
|
||||
- name: Homebrew tap and Scoop bucket (pull requests)
|
||||
# Final versions: branch release/qqq-<version> and a pull request "qqq <version>" in QRun-IO/homebrew-tap
|
||||
# and QRun-IO/scoop-bucket (PACKAGES_TOKEN); a human merges. Prereleases, or a refused push, attach
|
||||
# qqq.rb / qqq.json to the release instead. Never fails the release.
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/update-packaging.mjs /tmp/assets/SHA256SUMS
|
||||
api="$GITHUB_SERVER_URL/api/v1"
|
||||
# Final versions only: the tap and the bucket follow the stable channel, like scripts/install.sh.
|
||||
# Pushed when PACKAGES_TOKEN can write the repository; otherwise the filled file goes on the release.
|
||||
can_write() {
|
||||
curl -fsS -H "Authorization: token $PACKAGES_TOKEN" "$api/repos/$1" 2>/dev/null |
|
||||
node -e 'try { process.stdout.write(String(JSON.parse(require("fs").readFileSync(0, "utf8")).permissions.push === true)) } catch { process.stdout.write("false") }' || echo false
|
||||
}
|
||||
push_file() {
|
||||
local repo=$1 src=$2 dest=$3 dir header
|
||||
dir=$(mktemp -d)
|
||||
header="Authorization: Basic $(printf 'ci:%s' "$PACKAGES_TOKEN" | base64 -w0)"
|
||||
git -c http.extraHeader="$header" clone -q --depth 1 "$GITHUB_SERVER_URL/$repo.git" "$dir"
|
||||
cp "$src" "$dir/$dest"
|
||||
git -C "$dir" add "$dest"
|
||||
if git -C "$dir" diff --cached --quiet; then echo "$repo already has qqq $VERSION"; return 0; fi
|
||||
git -C "$dir" -c user.name=CI -c user.email=ci@kof22.com commit -q -m "qqq $VERSION"
|
||||
git -C "$dir" -c http.extraHeader="$header" push -q origin HEAD:main
|
||||
echo "Pushed qqq $VERSION to $repo"
|
||||
}
|
||||
for spec in "QRun-IO/homebrew-tap docs/packaging/homebrew-tap/Formula/qqq.rb Formula/qqq.rb" \
|
||||
"QRun-IO/scoop-bucket docs/packaging/qqq.json bucket/qqq.json"; do
|
||||
read -r repo src dest <<< "$spec"
|
||||
if [[ $VERSION != *-* && $(can_write "$repo") == true ]]; then
|
||||
push_file "$repo" "$src" "$dest"
|
||||
else
|
||||
curl -fsS -o /dev/null -X POST -H "Authorization: token $TOKEN" -F "attachment=@$src" \
|
||||
"$api/repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID/assets?name=$(basename "$src")"
|
||||
echo "$repo not updated (prerelease, or PACKAGES_TOKEN cannot write it): $(basename "$src") is attached to the release for a pull request"
|
||||
fi
|
||||
done
|
||||
run: bash scripts/publish-packaging.sh
|
||||
+10
-7
@@ -14,8 +14,8 @@ What ships:
|
||||
|---|---|---|
|
||||
| `qqq` binaries with the terminal UI, 5 targets (linux x64/arm64, darwin x64/arm64, windows x64), plus `SHA256SUMS` | Gitea release `v<version>` of `QRun-IO/qqq-cli` | `release.yaml`: Linux targets and darwin-x64 (cross) in the container, darwin-arm64 on the macOS runner, windows-x64 on the Windows runner |
|
||||
| `@qrun-io/qqq-ui-api-client`, `qqq-core`, `qqq-ui-api-conformance`, `qqq-cli` | QRun-IO Gitea npm registry, `https://git.kof22.com/api/packages/QRun-IO/npm/` | `release.yaml` publish job |
|
||||
| Homebrew formula `qqq` | `QRun-IO/homebrew-tap`, `Formula/qqq.rb` | `release.yaml` (final versions), else a pull request |
|
||||
| Scoop manifest `qqq` | `QRun-IO/scoop-bucket`, `bucket/qqq.json` | `release.yaml` (final versions), else a pull request |
|
||||
| Homebrew formula `qqq` | `QRun-IO/homebrew-tap`, `Formula/qqq.rb` | pull request from `release.yaml` (final versions), merged by a human |
|
||||
| Scoop manifest `qqq` | `QRun-IO/scoop-bucket`, `bucket/qqq.json` | pull request from `release.yaml` (final versions), merged by a human |
|
||||
|
||||
## 1. Versions
|
||||
|
||||
@@ -63,9 +63,11 @@ prerelease: npm dist-tag `next` for prerelease packages, a Gitea prerelease, and
|
||||
3. **publish** (`container-build`): `SHA256SUMS` from the three jobs' checksums, uploaded; the release is
|
||||
published and every download is checked against `SHA256SUMS`; the four packages go to the Gitea npm
|
||||
registry with `PACKAGES_TOKEN`; `scripts/update-packaging.mjs` fills the formula and the manifest.
|
||||
For a final version they are pushed to `QRun-IO/homebrew-tap` and `QRun-IO/scoop-bucket` when
|
||||
`PACKAGES_TOKEN` can write those repositories; otherwise `qqq.rb` and `qqq.json` are attached to the
|
||||
release (step 4).
|
||||
For a final version `scripts/publish-packaging.sh` pushes branch `release/qqq-<version>` to
|
||||
`QRun-IO/homebrew-tap` and `QRun-IO/scoop-bucket` with `PACKAGES_TOKEN` and opens a pull request
|
||||
`qqq <version>` in each; a human checks the sums and merges. When the push or the pull request is refused
|
||||
(or for a prerelease) `qqq.rb` and `qqq.json` are attached to the release instead (step 4); the release
|
||||
never fails over this. Dry run, no writes: `DRY_RUN=1 VERSION=<version> SUMS_FILE=<SHA256SUMS> scripts/publish-packaging.sh`.
|
||||
|
||||
Watch it under Actions in `QRun-IO/qqq-cli`. The release page is
|
||||
`https://git.kof22.com/QRun-IO/qqq-cli/releases/tag/v<version>`.
|
||||
@@ -76,8 +78,9 @@ musl hosts use the npm package (`@qrun-io/qqq-cli`, CLI only) until a musl build
|
||||
|
||||
## 4. Homebrew tap and Scoop bucket (when CI could not push)
|
||||
|
||||
`PACKAGES_TOKEN` is the package-scoped publisher token; unless it is granted write on the tap and the
|
||||
bucket, CI attaches the filled files to the release instead. For a final version, download them and open a
|
||||
`PACKAGES_TOKEN` is the package-scoped publisher token (user `qqq-package-publisher`); unless it is granted
|
||||
write on the tap and the bucket, CI attaches the filled files to the release instead. Their `main` branches
|
||||
should be protected so CI can only propose. For a final version, download them and open a
|
||||
pull request on each:
|
||||
|
||||
```sh
|
||||
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2026 QRun.IO, Inc.
|
||||
# Licensed under the Apache License, Version 2.0 (see LICENSE).
|
||||
#
|
||||
# Offer the filled Homebrew formula and Scoop manifest (scripts/update-packaging.mjs) to the tap and the
|
||||
# bucket as pull requests; a human merges them. Called by the release workflow's publish job.
|
||||
#
|
||||
# final version push branch release/qqq-<version> to QRun-IO/homebrew-tap and QRun-IO/scoop-bucket with
|
||||
# PACKAGES_TOKEN and open a pull request "qqq <version>" in each
|
||||
# otherwise (prerelease, or the push or the pull request is refused) attach qqq.rb / qqq.json to the
|
||||
# release instead, for a hand-made pull request (docs/release-runbook.md step 4)
|
||||
#
|
||||
# Never fails the release over the tap or the bucket. DRY_RUN=1 clones and commits locally but pushes,
|
||||
# opens and attaches nothing; it needs only VERSION (and GITHUB_SERVER_URL, default https://git.kof22.com):
|
||||
# DRY_RUN=1 VERSION=1.2.3 scripts/publish-packaging.sh
|
||||
#
|
||||
# Environment: VERSION, GITHUB_SERVER_URL, GITHUB_REPOSITORY, RELEASE_ID, TOKEN (job token, release assets),
|
||||
# PACKAGES_TOKEN (tap and bucket), DRY_RUN.
|
||||
set -euo pipefail
|
||||
|
||||
server=${GITHUB_SERVER_URL:-https://git.kof22.com}
|
||||
api="$server/api/v1"
|
||||
dry=${DRY_RUN:-0}
|
||||
: "${VERSION:?VERSION is required}"
|
||||
[[ $dry == 1 ]] || : "${TOKEN:?TOKEN is required}" "${RELEASE_ID:?RELEASE_ID is required}" "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}"
|
||||
branch="release/qqq-$VERSION"
|
||||
title="qqq $VERSION"
|
||||
work=$(mktemp -d)
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
|
||||
json() { node -e 'const o = {}; for (let i = 1; i < process.argv.length; i += 2) o[process.argv[i]] = process.argv[i + 1];
|
||||
process.stdout.write(JSON.stringify(o))' -- "$@"; }
|
||||
field() { node -e 'try { const v = JSON.parse(require("fs").readFileSync(0, "utf8")); const r = [].concat(v)
|
||||
.find((p) => !process.argv[2] || (p.head && p.head.ref === process.argv[2])); process.stdout.write(String(r ? r[process.argv[1]] ?? "" : "")) }
|
||||
catch { }' -- "$@"; }
|
||||
|
||||
attach() { # attach <file>: the fallback, a hand-made pull request from the release asset
|
||||
local src=$1 name
|
||||
name=$(basename "$src")
|
||||
if [[ $dry == 1 ]]; then echo "DRY RUN: would attach $name to the release"; return 0; fi
|
||||
curl -fsS -o /dev/null -X POST -H "Authorization: token $TOKEN" -F "attachment=@$src" \
|
||||
"$api/repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID/assets?name=$name" ||
|
||||
{ echo "::warning::could not attach $name to the release"; return 0; }
|
||||
echo "Attached $name to the release for a hand-made pull request"
|
||||
}
|
||||
|
||||
propose() { # propose <repo> <src> <dest>: branch + pull request; non-zero means fall back
|
||||
local repo=$1 src=$2 dest=$3 dir="$work/${1##*/}" header=() base pr body
|
||||
[[ $dry == 1 || -n ${PACKAGES_TOKEN:-} ]] || { echo "PACKAGES_TOKEN is empty"; return 1; }
|
||||
[[ -n ${PACKAGES_TOKEN:-} ]] && header=(-c "http.extraHeader=Authorization: Basic $(printf 'ci:%s' "$PACKAGES_TOKEN" | base64 | tr -d '\n')")
|
||||
git "${header[@]}" clone -q --depth 1 "$server/$repo.git" "$dir" || return 1
|
||||
base=$(git -C "$dir" rev-parse --abbrev-ref HEAD)
|
||||
git -C "$dir" checkout -q -b "$branch"
|
||||
mkdir -p "$(dirname "$dir/$dest")"
|
||||
cp "$src" "$dir/$dest"
|
||||
git -C "$dir" add "$dest"
|
||||
if git -C "$dir" diff --cached --quiet; then echo "$repo $base already has qqq $VERSION"; return 0; fi
|
||||
git -C "$dir" -c user.name=CI -c user.email=ci@kof22.com commit -q -m "$title"
|
||||
git -C "$dir" --no-pager show --stat --format='%s' HEAD
|
||||
if [[ $dry == 1 ]]; then
|
||||
echo "DRY RUN: would push $branch to $repo and open pull request \"$title\" into $base"; return 0
|
||||
fi
|
||||
# CI owns release/qqq-<version>: a re-run replaces it.
|
||||
git -C "$dir" "${header[@]}" push -q --force origin "HEAD:refs/heads/$branch" || { echo "push to $repo refused"; return 1; }
|
||||
body="Formula/manifest for qqq $VERSION, filled by the qqq-cli release workflow from the release's SHA256SUMS: $server/$GITHUB_REPOSITORY/releases/tag/v$VERSION. Check the sums, then merge."
|
||||
pr=$(curl -fsS -X POST -H "Authorization: token $PACKAGES_TOKEN" -H 'Content-Type: application/json' \
|
||||
-d "$(json head "$branch" base "$base" title "$title" body "$body")" "$api/repos/$repo/pulls" 2>/dev/null | field html_url) || pr=''
|
||||
if [[ -z $pr ]]; then # a re-run: the pull request may already be open
|
||||
pr=$(curl -fsS -H "Authorization: token $PACKAGES_TOKEN" "$api/repos/$repo/pulls?state=open&limit=50" 2>/dev/null |
|
||||
field html_url "$branch") || pr=''
|
||||
fi
|
||||
[[ -n $pr ]] || { echo "pushed $branch to $repo but could not open the pull request"; return 1; }
|
||||
echo "Opened $pr"
|
||||
}
|
||||
|
||||
node "$(dirname "$0")/update-packaging.mjs" "${SUMS_FILE:-/tmp/assets/SHA256SUMS}"
|
||||
for spec in "QRun-IO/homebrew-tap docs/packaging/homebrew-tap/Formula/qqq.rb Formula/qqq.rb" \
|
||||
"QRun-IO/scoop-bucket docs/packaging/qqq.json bucket/qqq.json"; do
|
||||
read -r repo src dest <<< "$spec"
|
||||
# Final versions only: the tap and the bucket follow the stable channel, like scripts/install.sh.
|
||||
if [[ $VERSION == *-* ]]; then
|
||||
echo "$repo not updated: $VERSION is a prerelease"; attach "$src"; continue
|
||||
fi
|
||||
propose "$repo" "$src" "$dest" || { echo "$repo not updated: falling back to the release asset"; attach "$src"; }
|
||||
done
|
||||
Reference in new issue
Block a user