~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

A workflow keeps a release pull open as change files land

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ci/release-pull
.knotless/workflows/release-pull.ymlnew+124−0
@@ -0,0 +1,124 @@
# Keeps one pull open that releases the client, as release-please and
# changesets' bot do: each day, when main has change files, it runs `knope
# release` on main's tip and force-pushes the commit to `release/next`,
# opening a pull titled `Release <version>`, or bringing the open one up
# to date. Queueing that pull is the release: once it lands, release.yml
# publishes the version and tags it. Nothing is queued here, so when to
# release stays the owner's call.
#
# The version is worked out here and given to knope with
# --override-version: a change file's `minor` or `major` bumps 0.x's minor,
# where knope would bump the patch, and a `patch` the patch; from 1.0 on,
# each bumps its own part. A pull already holding main's change files, and
# still merging with main, is left alone, so its checks run again only
# when what it releases changes. The open pull is found by its title,
# `Release x.y.z` by the owner, so one cut by hand is taken over, its head
# pointed at release/next.
#
# Ask for it at once with `knotless run rerun <repo> --ref refs/heads/main
# --workflow release-pull`. Asked for on any other branch, it makes the
# release commit there and prints the pull's body, pushing nothing.
#
# RELEASE_PASSWORD is release.yml's app password of the owner's, granted
# to main alone.
# Its secrets are on ci.knotless.dev alone, the runner that runs no one
# else's code (deploy/runner-host/README.md, The trusted runner).
runners: [ci.knotless.dev]
on:
schedule:
- cron: '0 6 * * *'
asked: {refs: [refs/heads/*]}
image: node:24.22.1
secrets: [RELEASE_PASSWORD]
env:
REPO: knotless://chadtmiller.com/knotless
OWNER: chadtmiller.com
BRANCH: release/next
KNOTLESS_APPVIEW: https://knotless.dev
KNOPE_VERSION: 0.23.2
KNOPE_SHA256: c64f6281e012fc8e046ebd6f30be76c583e88b9ad55b00c7b1809b030db92eee
steps:
- name: changes
run: |
set -- .changeset/*.md
if [ ! -e "$1" ]; then
echo "no change files at $KNOTLESS_RUN_REF: nothing to release"
touch ~/.done
fi
- name: tools
run: |
[ ! -e ~/.done ] || exit 0
cd /tmp
curl -fsSLO "https://github.com/knope-dev/knope/releases/download/knope/v$KNOPE_VERSION/knope-x86_64-unknown-linux-musl.tgz"
echo "$KNOPE_SHA256 knope-x86_64-unknown-linux-musl.tgz" | sha256sum -c -
tar -xzf knope-x86_64-unknown-linux-musl.tgz
install knope-x86_64-unknown-linux-musl/knope /usr/local/bin/
curl -fsSL https://knotless.dev/install.sh | KNOTLESS_INSTALL_DIR=/usr/local/bin sh
- name: release commit
run: |
[ ! -e ~/.done ] || exit 0
set -- .changeset/*.md
count=$#
bumps=$(sed -n 's/^default: *//p' "$@")
current=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
IFS=. read -r major minor patch <<EOF
$current
EOF
if echo "$bumps" | grep -qx major && [ "$major" != 0 ]; then
version=$((major + 1)).0.0
elif echo "$bumps" | grep -qxE 'major|minor'; then
version=$major.$((minor + 1)).0
else
version=$major.$minor.$((patch + 1))
fi
echo "$version" > ~/.version
knope release --override-version "$version"
git -c user.name=knotless -c user.email=knotless@knotless.invalid commit -aqm "Release $version"
at=$(echo "$KNOTLESS_RUN_SHA" | cut -c1-12)
notes=$(awk -v v="$version" '/^## / { on = ($2 == v); next } on' CHANGELOG.md)
cat > ~/.body <<EOF
Releases knotless $version, from the $count change files on main: its changelog is below. Queue this pull to release it; once it lands, release.yml publishes the version and tags it. release-pull.yml keeps it up to date as change files land.
<details>
<summary>release-pull.yml</summary>
## What changes
knotless $version, as \`knope release --override-version $version\` makes it from main at $at: the workspace version from $current to $version in Cargo.toml and the crates' Cargo.lock entries, the version's CHANGELOG.md section, and the change files removed.
$notes
## Why
Users get the client side of main's changes only from a release. A 0.x minor change bumps the minor here, where knope alone would bump the patch.
## How it was checked
The build is left to CI. \`client.minimum\` is left as it is: raise it by hand if a release in between harms repositories.
</details>
EOF
cat ~/.body
- name: pull
on: {refs: [refs/heads/main]}
run: |
[ ! -e ~/.done ] || exit 0
version=$(cat ~/.version)
export KNOTLESS_IDENTIFIER="$OWNER" KNOTLESS_PASSWORD="$RELEASE_PASSWORD"
pull=$(knotless pull list "$REPO" -q "is:pull state:open author:$OWNER Release" |
awk -F '\t' '$4 ~ /^Release [0-9]+\.[0-9]+\.[0-9]+$/ { print $3; exit }')
if [ -n "$pull" ] && git fetch -q "$REPO" "refs/heads/$BRANCH" &&
[ "$(git rev-parse "FETCH_HEAD^:.changeset")" = "$(git rev-parse "$KNOTLESS_RUN_SHA:.changeset")" ] &&
git merge-tree --write-tree "$KNOTLESS_RUN_SHA" FETCH_HEAD > /dev/null; then
echo "$pull releases main's change files already and merges with main: nothing to push"
exit 0
fi
git push -q --force "$REPO" "HEAD:refs/heads/$BRANCH"
if [ -z "$pull" ]; then
knotless pull create "$REPO" main "$REPO" "$BRANCH" "Release $version" -m - < ~/.body
else
knotless pull retarget "$REPO" "$pull" --head "$BRANCH"
knotless pull edit "$REPO" "$pull" --title "Release $version" -m - < ~/.body
echo "updated $pull"
fi
.knotless/workflows/release.yml+4−3
@@ -3,9 +3,10 @@
# release yet and this commit's checks all pass, it builds every target
# deploy/build-client.sh builds and publishes them with `knotless release publish`,
# as the repository's owner, with the version's CHANGELOG.md section as its
# notes, and tags its commit `v<version>`. `knope release` bumps the
# version in a pull; once that lands, this publishes it. A version
# published already is left alone, so the run passes at once.
# notes, and tags its commit `v<version>`. release-pull.yml keeps a pull
# open that bumps the version with `knope release`; once that lands, this
# publishes it. A version published already is left alone, so the run
# passes at once.
#
# RELEASE_PASSWORD is an app password of the owner's, granted to pushes to
# main alone in the runner's secrets file. Asked for on any other branch,
docs/design/packages.md+13−0
@@ -405,6 +405,19 @@ release` gathers them: it bumps the workspace version in `Cargo.toml` and
`Cargo.lock`, writes `CHANGELOG.md`, and deletes the change files. That
goes to main as a pull like any other.
The `release-pull` workflow cuts that pull each day at 06:00 UTC, as
release-please and changesets' bot do, when main has change files. It
works out the version itself and gives it to knope: a `minor` or `major`
change file bumps 0.x's minor, where knope would bump the patch, and a
`patch` the patch; from 1.0 on, each bumps its own part. It force-pushes
the release commit to `release/next` and opens a pull titled `Release
<version>`, or brings the open one up to date, pointing one cut by hand
at that branch. A pull already holding main's change files, and still
merging with main, is left as it is, so its checks run again only when
what it releases changes. It queues nothing: queueing the pull is the
release, and when to release stays the owner's call. Asked for by name
at main, it does the same at once.
A change file is easy to forget, so the `check` check asks for one: a
pull that changes the client's code, `crates/knotless/src` or
`crates/knotless-mcp/src`, fails without a new file under `.changeset/`,
AGENTS.md+4−1
@@ -130,7 +130,10 @@ what the client does) adds a change file under `.changeset/`, as
`knope document-change` writes one (https://knope.tech): `default:
patch`, `minor` or `major` in its front matter, then a heading and a
line for them. `knope release` turns them into the version and
CHANGELOG.md. Commit titles stay prose: no conventional commits. The
CHANGELOG.md, in the `Release <version>` pull that release-pull.yml
keeps open each day there are any: queueing it releases. Rather than cut
one by hand, ask for that workflow by name at main. Commit titles stay
prose: no conventional commits. The
`check` check fails a pull that changes `crates/knotless/src` or
`crates/knotless-mcp/src` and adds no change file, unless its body says,
on a line of its own, `Changeset: none`: for a change no one using