The release page got the newest <release> block as the plain text of its paragraphs, which fit two paragraphs of prose and nothing more. From 3.0.0 the notes group the changes under New, Fixes and Breaking, each in descending order of importance, and AppStream's description markup has lists but no headings to carry that. The metainfo stays the one source of the notes, since the Flatpak installs it and the tag check reads it. A category is written there as a paragraph that introduces a list, <p>New</p><ul>...</ul>, which is how AppStream notes lead into a list anyway, and the CI step renders such a paragraph as a Markdown heading over its items. A paragraph on its own stays a paragraph, so the older entries render as before. The step parses the XML with ElementTree instead of two regexes: entities are decoded, <em> and <code> get their Markdown spelling, and a code span keeps text like lib/<triple>/eh from being read as an HTML tag by the forge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
233 lines
10 KiB
YAML
233 lines
10 KiB
YAML
name: Flatpak
|
|
|
|
# One job, three outcomes:
|
|
# pull request -> build the Flatpak and run the suite inside it
|
|
# push master -> also publish the result as the `beta` branch of the
|
|
# Catcrafts repo and refresh the rolling `latest` release
|
|
# tag X.Y.Z -> publish as `stable` and create release X.Y.Z
|
|
#
|
|
# The `crafter-flatpak` runner label is the privileged runner on the Hetzner
|
|
# box (bubblewrap needs it) with four volumes allowed: the Sdk install, the
|
|
# flatpak-builder state (ccache and the LLVM module, so only crafter-build
|
|
# itself rebuilds on a normal run), the published repo's web root, and the
|
|
# repo signing key, which lives on the runner host and never in the forge.
|
|
# No other runner carries that label. Plain `flatpak` also matches the
|
|
# organisation's runner, which mounts none of those volumes; a job it picks
|
|
# up fails before building anything.
|
|
on:
|
|
pull_request:
|
|
branches: [master]
|
|
push:
|
|
branches: [master]
|
|
# Bare semver tags. Spelled with globs so it means the same whether the
|
|
# forge reads it as a workflow pattern or as a plain glob; `latest`, the
|
|
# rolling tag this workflow moves itself, does not start with a digit.
|
|
tags: ['[0-9]*.[0-9]*.[0-9]*']
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
APP_ID: net.catcrafts.CrafterBuild
|
|
MANIFEST: packaging/flatpak/net.catcrafts.CrafterBuild.yaml
|
|
CI_MANIFEST: packaging/flatpak/ci-manifest.yaml
|
|
RUNTIME: org.freedesktop.Sdk//26.08
|
|
REPO_DIR: /srv/flatpak/repo
|
|
WEB_DIR: /srv/flatpak
|
|
|
|
jobs:
|
|
flatpak:
|
|
runs-on: crafter-flatpak
|
|
container:
|
|
image: archlinux:latest
|
|
options: >-
|
|
-v crafter-flatpak-system:/var/lib/flatpak
|
|
-v crafter-flatpak-builder:/root/.cache/flatpak-builder
|
|
-v /var/www/flatpak:/srv/flatpak
|
|
-v /etc/crafter-flatpak:/run/crafter-flatpak:ro
|
|
steps:
|
|
- name: Install tools
|
|
run: |
|
|
pacman-key --init
|
|
pacman-key --populate archlinux
|
|
pacman -Sy --noconfirm --needed archlinux-keyring
|
|
pacman -Syu --noconfirm --needed flatpak flatpak-builder ccache git nodejs jq gnupg go-yq ostree python
|
|
git config --global --add safe.directory '*'
|
|
|
|
- name: Sdk
|
|
# A no-op once the volume is warm; `update` follows point releases.
|
|
run: |
|
|
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
flatpak install -y --noninteractive flathub "$RUNTIME"
|
|
flatpak update -y --noninteractive
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: true
|
|
|
|
- name: Version and channel
|
|
id: meta
|
|
run: |
|
|
set -eu
|
|
if [ "${GITHUB_REF_TYPE}" = tag ]; then
|
|
version="$GITHUB_REF_NAME"
|
|
branch=stable
|
|
# The metainfo's newest release is the release notes and must be
|
|
# the version being tagged.
|
|
top=$(grep -m1 -oP '<release version="\K[^"]+' packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml)
|
|
if [ "$top" != "$version" ]; then
|
|
echo "metainfo lists $top as the newest release, but the tag is $version" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
# A master build names itself after the release it follows, the
|
|
# way a local checkout does: 2.0.0-3-g21746b1 is three commits
|
|
# past 2.0.0. Before the first tag there is nothing to describe
|
|
# from and the bare hash gets a placeholder in front.
|
|
described=$(git describe --tags --always --exclude=latest)
|
|
case "$described" in
|
|
[0-9]*.[0-9]*.[0-9]*) version="$described" ;;
|
|
*) version="0.0.0-dev+$described" ;;
|
|
esac
|
|
branch=beta
|
|
fi
|
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
echo "branch=$branch" >> "$GITHUB_OUTPUT"
|
|
echo "version $version, flatpak branch $branch"
|
|
|
|
- name: Build
|
|
# The manifest is rewritten beside the original so its relative
|
|
# source paths still resolve. The suite runs inside this build.
|
|
env:
|
|
VERSION: ${{ steps.meta.outputs.version }}
|
|
run: |
|
|
set -eu
|
|
yq '(.modules[] | select(.name == "crafter-build") | ."build-options".env.CRAFTER_BUILD_VERSION) = strenv(VERSION)' "$MANIFEST" > "$CI_MANIFEST"
|
|
grep -n 'CRAFTER_BUILD_VERSION' "$CI_MANIFEST"
|
|
flatpak-builder --force-clean --ccache --disable-rofiles-fuse \
|
|
--state-dir=/root/.cache/flatpak-builder \
|
|
--repo=repo --default-branch="${{ steps.meta.outputs.branch }}" \
|
|
build-dir "$CI_MANIFEST"
|
|
|
|
- name: Smoke test
|
|
run: |
|
|
flatpak-builder --run build-dir "$CI_MANIFEST" crafter-build --version
|
|
flatpak-builder --run build-dir "$CI_MANIFEST" sh -c 'cd examples/hello-world && crafter-build && ls bin/*/hello*'
|
|
|
|
# Everything below publishes and only runs on master and on tags.
|
|
- name: Bundle
|
|
if: github.event_name != 'pull_request'
|
|
run: |
|
|
mkdir -p dist
|
|
flatpak build-bundle repo "dist/crafter-build-${{ steps.meta.outputs.version }}.flatpak" "$APP_ID" "${{ steps.meta.outputs.branch }}"
|
|
cp packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref dist/
|
|
(cd dist && sha256sum * > SHA256SUMS)
|
|
ls -la dist
|
|
|
|
- name: Upload bundle
|
|
if: github.event_name != 'pull_request'
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: crafter-build-${{ steps.meta.outputs.version }}
|
|
path: dist/*.flatpak
|
|
if-no-files-found: error
|
|
|
|
- name: Publish to the Catcrafts repo
|
|
if: github.event_name != 'pull_request'
|
|
run: |
|
|
set -eu
|
|
# The signing key is generated once on the runner host
|
|
# (/etc/crafter-flatpak: signing-key.asc and key-id) and mounted
|
|
# read-only; it is imported into a throwaway keyring per run.
|
|
if [ ! -s /run/crafter-flatpak/signing-key.asc ] || [ ! -s /run/crafter-flatpak/key-id ]; then
|
|
echo "no signing key under /etc/crafter-flatpak on the runner host; generate it as described in AGENTS.md (CI and publishing)" >&2
|
|
exit 1
|
|
fi
|
|
export GNUPGHOME=$(mktemp -d)
|
|
gpg --batch --quiet --import /run/crafter-flatpak/signing-key.asc
|
|
FLATPAK_GPG_KEY_ID=$(cat /run/crafter-flatpak/key-id)
|
|
# The live repo is the canonical copy; builds are promoted into it,
|
|
# never built into it.
|
|
# Keyed on the repo's config file, not the directory: the web root
|
|
# may exist as an empty directory before the first publish.
|
|
[ -f "$REPO_DIR/config" ] || ostree init --repo="$REPO_DIR" --mode=archive
|
|
flatpak build-commit-from --src-repo=repo \
|
|
--gpg-sign="$FLATPAK_GPG_KEY_ID" --gpg-homedir="$GNUPGHOME" \
|
|
"$REPO_DIR" "app/$APP_ID/x86_64/${{ steps.meta.outputs.branch }}"
|
|
flatpak build-update-repo --gpg-sign="$FLATPAK_GPG_KEY_ID" --gpg-homedir="$GNUPGHOME" \
|
|
--generate-static-deltas --prune "$REPO_DIR"
|
|
# The repo description and the app ref carry the public key.
|
|
key=$(gpg --export "$FLATPAK_GPG_KEY_ID" | base64 --wrap=0)
|
|
sed "s|GPGKey=.*|GPGKey=$key|" packaging/flatpak/catcrafts.flatpakrepo > "$WEB_DIR/catcrafts.flatpakrepo"
|
|
sed "s|GPGKey=.*|GPGKey=$key|" packaging/flatpak/net.catcrafts.CrafterBuild.flatpakref > "$WEB_DIR/net.catcrafts.CrafterBuild.flatpakref"
|
|
|
|
- name: Rolling 'latest' release
|
|
if: github.event_name != 'pull_request' && github.ref_type != 'tag'
|
|
run: |
|
|
git config user.email "ci@catcrafts.net"
|
|
git config user.name "Crafter Build CI"
|
|
git tag -f latest
|
|
git push origin latest --force
|
|
- name: Publish rolling 'latest' release
|
|
if: github.event_name != 'pull_request' && github.ref_type != 'tag'
|
|
uses: https://code.forgejo.org/actions/forgejo-release@v2
|
|
with:
|
|
direction: upload
|
|
url: ${{ github.server_url }}
|
|
repo: ${{ github.repository }}
|
|
tag: latest
|
|
title: Latest master build
|
|
prerelease: true
|
|
override: true
|
|
release-dir: dist
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Release notes
|
|
if: github.ref_type == 'tag'
|
|
run: |
|
|
# The newest <release> block's description as Markdown, for the
|
|
# release page. AppStream has no headings, so a paragraph that
|
|
# introduces a list is one: <p>New</p><ul>...</ul> comes out as
|
|
# "## New" over its items, the shape the notes take since 3.0.0.
|
|
# A paragraph on its own stays a paragraph, as the older entries
|
|
# are written. <em> and <code> get their Markdown spelling; a code
|
|
# span also keeps text such as lib/<triple>/eh from being read as
|
|
# an HTML tag by the forge.
|
|
python3 - > release-notes.md <<'EOF'
|
|
import xml.etree.ElementTree as ET
|
|
|
|
desc = ET.parse("packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml").getroot().find("releases/release/description")
|
|
|
|
def text(el):
|
|
parts = [el.text or ""]
|
|
for child in el:
|
|
inner = text(child)
|
|
parts.append({"em": f"*{inner}*", "code": f"`{inner}`"}.get(child.tag, inner))
|
|
parts.append(child.tail or "")
|
|
return " ".join("".join(parts).split())
|
|
|
|
blocks = list(desc)
|
|
out = []
|
|
for i, el in enumerate(blocks):
|
|
heads_list = i + 1 < len(blocks) and blocks[i + 1].tag in ("ul", "ol")
|
|
if el.tag == "p":
|
|
out.append(f"## {text(el)}" if heads_list else text(el))
|
|
elif el.tag == "ul":
|
|
out.append("\n".join(f"- {text(li)}" for li in el))
|
|
elif el.tag == "ol":
|
|
out.append("\n".join(f"{n}. {text(li)}" for n, li in enumerate(el, 1)))
|
|
print("\n\n".join(out))
|
|
print("\nInstall:\n\n flatpak remote-add --if-not-exists catcrafts https://flatpak.catcrafts.net/catcrafts.flatpakrepo\n flatpak install catcrafts net.catcrafts.CrafterBuild\n")
|
|
EOF
|
|
- name: Publish release
|
|
if: github.ref_type == 'tag'
|
|
uses: https://code.forgejo.org/actions/forgejo-release@v2
|
|
with:
|
|
direction: upload
|
|
url: ${{ github.server_url }}
|
|
repo: ${{ github.repository }}
|
|
tag: ${{ github.ref_name }}
|
|
title: crafter-build ${{ github.ref_name }}
|
|
release-notes-file: release-notes.md
|
|
release-dir: dist
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|