JP builds with 3.14 now, so the workflows do too. The lint job moves from 3.10, the packaging build job and the release's site rebuild and PyPI build from 3.12, and the installers from 3.10. The test matrix is untouched: it still runs every version from 3.10 up, because 3.10 is still the floor. The installers' comment said the oldest Python was chosen on purpose, for reach. The reach that matters on Linux comes from glibc, and that comes from the runner, so the Linux legs stay on ubuntu-22.04 and the comment now says that. The binaries carry 3.14 from the next release; 0.9.0's were built on 3.10. The changelog gains the 3.14 entry JP wrote in another session, plus a line for the downloads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
267 lines
11 KiB
YAML
267 lines
11 KiB
YAML
# Puts the downloadable builds on the repository's Releases page.
|
|
#
|
|
# Nothing here leaves GitHub: a release is exactly as visible as the repository it sits
|
|
# on. This is *not* the PyPI publish — that is `release.yml`, it is a separate act, and it
|
|
# does not run from here.
|
|
#
|
|
# Triggered by pushing a version tag, which is the moment a release exists in git anyway:
|
|
#
|
|
# git tag -a v0.3.0 -m "MeshTerm 0.3.0"
|
|
# git push origin v0.3.0
|
|
#
|
|
# The binaries are built by `installers.yml`, called rather than copied, so the files
|
|
# people download are made exactly the way the ones CI checks on every push are.
|
|
#
|
|
# Once the release exists, `website` puts it on meshterm.net: it rebuilds that site from
|
|
# the tagged tree — the landing and About pages carry the version — and pushes the result
|
|
# to jpmartineau/meshterm.net, which GitHub Pages serves. It writes with a deploy key
|
|
# scoped to that one repository (secret SITE_DEPLOY_KEY); that repo's README makes one.
|
|
name: GitHub release
|
|
|
|
on:
|
|
push:
|
|
tags: ["v*"]
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: "Existing tag to build a release from (e.g. v0.2.0)"
|
|
required: true
|
|
type: string
|
|
prerelease:
|
|
description: "Mark as a pre-release (keeps it out of 'Latest')"
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
uses: ./.github/workflows/installers.yml
|
|
|
|
publish:
|
|
name: create the release
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
# The one job that needs it: creating a release writes to the repository.
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.tag || github.ref }}
|
|
|
|
# Every platform's artifact, flattened into one directory of files to attach.
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
# The build uploads one artifact nobody should ever download: the unsigned
|
|
# Windows binary it hands to SignPath. Everything meant for the release page is
|
|
# named `meshterm-…`, so the pattern is the filter.
|
|
pattern: meshterm-*
|
|
path: artifacts
|
|
merge-multiple: true
|
|
|
|
- name: Gather the binaries and their checksums
|
|
id: gather
|
|
shell: bash
|
|
run: |
|
|
tag="${{ inputs.tag || github.ref_name }}"
|
|
version="${tag#v}"
|
|
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
|
|
mkdir -p release
|
|
find artifacts -type f -exec mv {} release/ \;
|
|
ls -lh release/
|
|
cd release
|
|
|
|
# A second, version-less copy of every binary. GitHub's evergreen download URL,
|
|
# `releases/latest/download/<name>`, resolves against an exact asset name and
|
|
# nothing else — so a link that always fetches the newest build needs a name
|
|
# that never moves. That is what lets the README, the website and anyone's
|
|
# install script say `meshterm-macos-arm64` once instead of naming a version
|
|
# that goes stale the moment the next release lands. The version-stamped file
|
|
# stays beside it: it is the one you want on disk when you keep several builds
|
|
# around, and it is what the release-notes install block points at, since that
|
|
# page is about this release rather than the latest one.
|
|
made=0
|
|
for versioned in "meshterm-$version"-*; do
|
|
case "$versioned" in *.sha256) continue ;; esac
|
|
[ -e "$versioned" ] || continue
|
|
stable="meshterm-${versioned#meshterm-$version-}"
|
|
cp "$versioned" "$stable"
|
|
sha256sum "$stable" > "$stable.sha256"
|
|
made=$((made + 1))
|
|
done
|
|
# Nothing matched means the tag and `meshterm.__version__` disagree, which is a
|
|
# broken release either way: the notes below are about to name files by version
|
|
# that the build did not produce. Fail here, where it is one line to read.
|
|
if [ "$made" -eq 0 ]; then
|
|
echo "::error::No binary named meshterm-$version-*; $tag and meshterm.__version__ disagree."
|
|
exit 1
|
|
fi
|
|
echo "$made version-less aliases made"
|
|
|
|
# One combined checksum file is easier to verify against than ten loose ones.
|
|
cat *.sha256 > SHA256SUMS && rm -f *.sha256
|
|
|
|
- name: Take the release notes from the changelog
|
|
id: notes
|
|
shell: bash
|
|
env:
|
|
VERSION: ${{ steps.gather.outputs.version }}
|
|
run: |
|
|
# The section for this version, between its own heading and the next one. If the
|
|
# changelog has nothing for it, say so plainly rather than inventing notes.
|
|
#
|
|
# Extracted once, into the file, and the *file* is what gets tested. It used to
|
|
# run twice — once down a `| sed | head -1 | grep -q .` pipeline to decide, then
|
|
# again for real — and this step runs under `bash -e -o pipefail`, where that
|
|
# pipeline is a race it can lose. `head -1` exits the moment it has its line and
|
|
# closes the pipe; if `sed` still has bytes to write it dies on SIGPIPE, pipefail
|
|
# promotes that to a failed condition, and a version with a perfectly good
|
|
# changelog section publishes "No changelog entry" instead. It only bites once a
|
|
# section outgrows one buffered write, which is why every entry under 4 KB was
|
|
# fine and 0.3.1 was not — while 0.3.0, the same size, happened to win.
|
|
awk -v v="$VERSION" '
|
|
$0 ~ "^## \\[" v "\\]" {found=1; next}
|
|
found && /^## \[/ {exit}
|
|
found {print}
|
|
' CHANGELOG.md > notes.md
|
|
if ! grep -q '[^[:space:]]' notes.md; then
|
|
echo "No changelog entry for $VERSION." > notes.md
|
|
fi
|
|
echo "--- notes ---"; cat notes.md
|
|
|
|
# Most people who open a release page came to install the thing, so the commands go
|
|
# above the changelog rather than in a link to a README that names some other
|
|
# version. Written here rather than kept as prose anywhere means they can only ever
|
|
# name *this* release: the tag interpolates, so the URLs are the assets attached to
|
|
# the very page they are printed on, and there is no example version to go stale.
|
|
#
|
|
# The heredoc is quoted, so the markdown's own backticks stay backticks instead of
|
|
# becoming command substitution, and the two placeholders are filled by `sed`
|
|
# afterwards. Neither a tag nor a version can contain a slash, so the substitution
|
|
# needs no escaping.
|
|
- name: Open the notes with this release's install commands
|
|
shell: bash
|
|
env:
|
|
TAG: ${{ steps.gather.outputs.tag }}
|
|
VERSION: ${{ steps.gather.outputs.version }}
|
|
run: |
|
|
cat > install.md.in <<'INSTALL_MD'
|
|
## Install
|
|
|
|
One file, no Python needed.
|
|
|
|
**macOS** — paste these into Terminal. Use `curl`, **not your browser**: a browser
|
|
flags the download and macOS then refuses to open it at all, and on Sequoia and
|
|
later right-click → Open no longer gets you past it. `curl` flags nothing, so it
|
|
simply runs.
|
|
|
|
```bash
|
|
curl -fL -o meshterm https://github.com/jpmartineau/MeshTerm/releases/download/@TAG@/meshterm-@VERSION@-macos-arm64
|
|
chmod +x meshterm
|
|
./meshterm
|
|
```
|
|
|
|
That is the Apple silicon build — every Mac since 2020. On an Intel Mac, swap
|
|
`arm64` for `x64`.
|
|
|
|
**Linux** — the same three lines with `linux-x64`, or `linux-arm64` on a uConsole
|
|
or another 64-bit ARM handheld.
|
|
|
|
```bash
|
|
curl -fL -o meshterm https://github.com/jpmartineau/MeshTerm/releases/download/@TAG@/meshterm-@VERSION@-linux-x64
|
|
chmod +x meshterm
|
|
./meshterm
|
|
```
|
|
|
|
**Windows** — download [meshterm-@VERSION@-windows-x64.exe](https://github.com/jpmartineau/MeshTerm/releases/download/@TAG@/meshterm-@VERSION@-windows-x64.exe), then run it from Windows
|
|
Terminal or PowerShell rather than by double-clicking, so you can read anything it
|
|
says on the way up.
|
|
|
|
```powershell
|
|
.\meshterm-@VERSION@-windows-x64.exe
|
|
```
|
|
|
|
Every file below is listed in `SHA256SUMS`. The version-less copies —
|
|
`meshterm-macos-arm64` and its four siblings — are the same binaries under a name
|
|
that never changes, for anything that wants whichever release is newest without
|
|
being told its number. [Installing with pip or pipx, and the rest of the
|
|
detail](https://github.com/jpmartineau/MeshTerm#install).
|
|
|
|
---
|
|
|
|
## What changed
|
|
INSTALL_MD
|
|
sed -e "s/@TAG@/$TAG/g" -e "s/@VERSION@/$VERSION/g" install.md.in > install.md
|
|
cat install.md notes.md > body.md
|
|
echo "--- body ---"; cat body.md
|
|
|
|
- name: Create it
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
TAG: ${{ steps.gather.outputs.tag }}
|
|
PRERELEASE: ${{ inputs.prerelease && '--prerelease' || '' }}
|
|
run: |
|
|
gh release create "$TAG" release/* \
|
|
--title "MeshTerm ${TAG#v}" \
|
|
--notes-file body.md \
|
|
--verify-tag \
|
|
$PRERELEASE
|
|
echo "https://github.com/${{ github.repository }}/releases/tag/$TAG"
|
|
|
|
website:
|
|
name: put the version on meshterm.net
|
|
needs: publish
|
|
# A pre-release stays out of "Latest", so it stays off the landing page too.
|
|
if: ${{ !inputs.prerelease }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Check for the deploy key
|
|
env:
|
|
SITE_DEPLOY_KEY: ${{ secrets.SITE_DEPLOY_KEY }}
|
|
run: |
|
|
if [ -z "$SITE_DEPLOY_KEY" ]; then
|
|
echo "::error::SITE_DEPLOY_KEY is not set; the meshterm.net README says how to make it."
|
|
exit 1
|
|
fi
|
|
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.tag || github.ref }}
|
|
path: meshterm
|
|
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
repository: jpmartineau/meshterm.net
|
|
ssh-key: ${{ secrets.SITE_DEPLOY_KEY }}
|
|
path: site
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.14"
|
|
|
|
- name: Rebuild the site from this release
|
|
run: |
|
|
python -m pip install --quiet -r site/requirements.txt
|
|
python site/build.py --meshterm meshterm
|
|
|
|
- name: Publish it
|
|
working-directory: site
|
|
env:
|
|
TAG: ${{ inputs.tag || github.ref_name }}
|
|
run: |
|
|
git add -A
|
|
if git diff --cached --quiet; then
|
|
echo "meshterm.net already shows ${TAG#v}."
|
|
exit 0
|
|
fi
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "41898341+github-actions[bot]@users.noreply.github.com"
|
|
git commit -m "MeshTerm ${TAG#v}"
|
|
git push
|