Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/readme-source.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"sha256": "c492ea98e92ec200d47523e13a7cc8af93cd6f081c7352ff1959f202b8622c50",
"sha256": "fded3f18b290aa29d4d317358c5296e9477be3c5b785449f0b0066a6e3d3f08c",
"history": {
"forbidden_commits": [
"3cbdabb71650fbca119b17e89ceed577f6bf81cd"
Expand Down
45 changes: 26 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
name: CI

on:
'on':
pull_request:
push:
branches:
- main

branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
cli:
native-cli:
name: Native CLI
uses: ./.github/workflows/cli-native.yml
permissions:
contents: read
quality:
name: Rust formatting and lint
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Test CLI
run: go test ./...

- name: Build CLI
run: make build
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install pinned Rust
run: rustup toolchain install 1.94.0 --profile minimal --component rustfmt --component clippy
- name: Check source
run: |
set -euo pipefail
cargo +1.94.0 fmt --check
cargo +1.94.0 clippy --locked --all-targets -- -D warnings
107 changes: 107 additions & 0 deletions .github/workflows/cli-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: Native CLI
'on':
workflow_call:
inputs:
release:
type: boolean
default: false
version:
type: string
default: dev
date:
type: string
default: unknown
permissions:
contents: read
jobs:
native:
name: CLI ${{ matrix.platform }} ${{ matrix.arch }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
rust-target: x86_64-unknown-linux-musl
platform: Linux
arch: x86_64
extension: tar.gz
- runner: ubuntu-24.04-arm
rust-target: aarch64-unknown-linux-musl
platform: Linux
arch: arm64
extension: tar.gz
- runner: macos-15-intel
rust-target: x86_64-apple-darwin
platform: Darwin
arch: x86_64
extension: tar.gz
- runner: macos-15
rust-target: aarch64-apple-darwin
platform: Darwin
arch: arm64
extension: tar.gz
- runner: windows-2025
rust-target: x86_64-pc-windows-msvc
platform: Windows
arch: x86_64
extension: zip
- runner: windows-11-arm
rust-target: aarch64-pc-windows-msvc
platform: Windows
arch: arm64
extension: zip
defaults:
run:
shell: bash
env:
VERSION: ${{ inputs.version }}
COMMIT: ${{ github.sha }}
DATE: ${{ inputs.date }}
CARGO_INCREMENTAL: '0'
RUST_TARGET: ${{ matrix.rust-target }}
CC_x86_64_unknown_linux_musl: musl-gcc
CC_aarch64_unknown_linux_musl: musl-gcc
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install pinned Rust
run: rustup toolchain install 1.94.0 --profile minimal --target "$RUST_TARGET"
- name: Link Windows C runtime statically
if: runner.os == 'Windows'
run: echo 'RUSTFLAGS=-C target-feature=+crt-static' >> "$GITHUB_ENV"
- name: Install native musl linker
if: runner.os == 'Linux'
run: |
set -euo pipefail
sudo apt-get update -qq
sudo apt-get install -y musl-tools
- name: Test native CLI
run: |
set -euo pipefail
cargo +1.94.0 test --locked --target "$RUST_TARGET"
cargo +1.94.0 build --locked --target "$RUST_TARGET"
binary="target/$RUST_TARGET/debug/pxlb"
[ "$RUNNER_OS" != Windows ] || binary+=.exe
PXLB_TEST_BINARY="$binary" python3 tests/cli_test.py
- name: Build release archive
if: inputs.release
env:
RELEASE_PLATFORM: ${{ matrix.platform }}
RELEASE_ARCH: ${{ matrix.arch }}
RELEASE_EXTENSION: ${{ matrix.extension }}
run: |
set -euo pipefail
cargo +1.94.0 build --release --locked --target "$RUST_TARGET"
python3 scripts/package-cli.py
- name: Upload native archive
if: inputs.release
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: native-cli-${{ matrix.platform }}-${{ matrix.arch }}
path: dist/pxlb_${{ matrix.platform }}_${{ matrix.arch }}.${{ matrix.extension }}
if-no-files-found: error
retention-days: 7
215 changes: 106 additions & 109 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,120 +1,117 @@
name: Release

on:
'on':
workflow_dispatch:
inputs:
bump:
description: Version bump
required: true
type: choice
options:
- patch
- minor
- major

permissions:
contents: write

options: [patch, minor, major]
permissions: {}
concurrency: release-${{ github.repository }}

jobs:
release:
identity:
name: Select immutable release identity
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
outputs:
version: ${{ steps.release.outputs.version }}
tag: ${{ steps.release.outputs.tag }}
date: ${{ steps.release.outputs.date }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Determine release version
id: release
env:
BUMP: ${{ inputs.bump }}
run: |
set -euo pipefail
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo 'Run releases from the main branch.' >&2
exit 1
fi
git fetch --tags --force
latest="$(git tag --list 'v[0-9]*' | sed -E 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)"
if [ -z "$latest" ]; then
VERSION="0.0.1"
else
IFS=. read -r major minor patch <<< "$latest"
case "$BUMP" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
*) echo "Unsupported bump: $BUMP" >&2; exit 1 ;;
esac
VERSION="${major}.${minor}.${patch}"
fi
TAG="v${VERSION}"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag already exists: $TAG" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Test CLI
run: go test ./...

- name: Build release artifacts
env:
VERSION: ${{ steps.release.outputs.version }}
COMMIT: ${{ github.sha }}
run: |
set -euo pipefail
DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
mkdir -p dist

build() {
goos="$1"
goarch="$2"
os_label="$3"
arch_label="$4"
archive_ext="$5"
exe=""
if [ "$goos" = "windows" ]; then
exe=".exe"
fi

name="pxlb_${os_label}_${arch_label}"
out_dir="dist/$name"
mkdir -p "$out_dir"
out_path="$PWD/$out_dir/pxlb$exe"
GOOS="$goos" GOARCH="$goarch" CGO_ENABLED=0 go build \
-ldflags "-s -w -X main.version=$VERSION -X main.commit=$COMMIT -X main.date=$DATE" \
-o "$out_path" .
cp README.md "$out_dir/README.md"

if [ "$archive_ext" = "zip" ]; then
(cd "$out_dir" && zip -q "../$name.zip" "pxlb$exe" README.md)
else
tar -C "$out_dir" -czf "dist/$name.tar.gz" "pxlb$exe" README.md
fi
}

build linux amd64 Linux x86_64 tar.gz
build linux arm64 Linux arm64 tar.gz
build darwin amd64 Darwin x86_64 tar.gz
build darwin arm64 Darwin arm64 tar.gz
build windows amd64 Windows x86_64 zip
build windows arm64 Windows arm64 zip

(cd dist && sha256sum *.tar.gz *.zip > checksums.txt)

- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release.outputs.tag }}
run: |
set -euo pipefail
gh release create "$TAG" dist/*.tar.gz dist/*.zip dist/checksums.txt --target "$GITHUB_SHA" --title "$TAG" --notes "Release $TAG"
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0
persist-credentials: false
- name: Determine release version
id: release
env:
BUMP: ${{ inputs.bump }}
run: |
set -euo pipefail
test "$GITHUB_REF" = refs/heads/main || { echo 'Run releases from main.' >&2; exit 1; }
prefix=v
git fetch --tags --force
latest="$(git tag --list "${prefix}[0-9]*" | sed -E "s/^${prefix}//" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)"
test -n "$latest" || latest=0.0.0
IFS=. read -r major minor patch <<< "$latest"
case "$BUMP" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
*) exit 1 ;;
esac
version="$major.$minor.$patch"
tag="$prefix$version"
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "Release tag already exists; refusing to replace it." >&2
exit 1
fi
printf 'version=%s\ntag=%s\ndate=%s\n' "$version" "$tag" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
build-cli:
name: Build native CLI
needs: identity
uses: ./.github/workflows/cli-native.yml
permissions:
contents: read
with:
release: true
version: ${{ needs.identity.outputs.version }}
date: ${{ needs.identity.outputs.date }}
accept-cli:
name: Accept CLI release archives
needs: build-cli
permissions:
contents: read
attestations: write
id-token: write
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Download native archives
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333
with:
pattern: native-cli-*
merge-multiple: true
path: dist
- name: Require all six platforms and record checksums
run: |
set -euo pipefail
cd dist
for platform in Linux Darwin Windows; do
extension=tar.gz
[ "$platform" != Windows ] || extension=zip
for arch in x86_64 arm64; do
test -s "pxlb_${platform}_${arch}.${extension}"
done
done
test "$(find . -maxdepth 1 -type f | wc -l)" -eq 6
sha256sum -- *.tar.gz *.zip > checksums.txt
- name: Attest accepted release archives
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8
with:
subject-path: dist/*
- name: Upload accepted archives
id: accepted
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: accepted-cli-${{ github.sha }}-${{ github.run_attempt }}
path: dist/*
if-no-files-found: error
retention-days: 7
outputs:
artifact-id: ${{ steps.accepted.outputs.artifact-id }}
publish:
name: Publish verified archives
needs: [identity, accept-cli]
uses: nicodes/tools/.github/workflows/cli-publish.yml@9590742a455154ac5967a67952c5758d79d476b9
permissions:
contents: write
with:
tools-ref: 9590742a455154ac5967a67952c5758d79d476b9
binary: pxlb
tag: ${{ needs.identity.outputs.tag }}
artifact-id: ${{ needs.accept-cli.outputs.artifact-id }}
Loading
Loading