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
125 changes: 110 additions & 15 deletions .github/workflows/build-linux-packages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ on:
- published
workflow_dispatch:
inputs:
version:
description: 'Package version (e.g., 3.3.0)'
required: false
default: ''
tag:
description: 'Release tag to check out, build, and attach packages to (e.g. v3.3.1)'
required: true
type: string

# GITHUB_TOKEN can attach assets to a release published by release-drafter.
permissions:
contents: write

env:
RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.tag }}

jobs:
build-packages:
Expand All @@ -23,6 +30,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
ref: ${{ env.RELEASE_TAG }}

- name: Set up Python
uses: actions/setup-python@v6
Expand All @@ -34,17 +43,16 @@ jobs:
run: |
set -euo pipefail

# Use workflow input if provided, otherwise extract from pyproject.toml
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
elif [ "${GITHUB_EVENT_NAME}" = "release" ]; then
VERSION="${GITHUB_REF_NAME#v}"
else
VERSION=$(python -c "import tomllib; f = open('pyproject.toml', 'rb'); data = tomllib.load(f); f.close(); print(data['project']['version'])")
TAG="${RELEASE_TAG}"
if [ -z "${TAG}" ]; then
echo "A release tag is required (release event or workflow_dispatch tag input)"
exit 1
fi

echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Package version: ${VERSION}"
VERSION="${TAG#v}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Package version: ${VERSION} (tag ${TAG})"

- name: Install nfpm
run: |
Expand Down Expand Up @@ -113,6 +121,26 @@ jobs:
# List generated packages
ls -lh dist/*.{deb,rpm} 2>/dev/null || true

VERSION="${{ steps.version.outputs.version }}"
ARCH="${{ steps.arch.outputs.nfpm_arch }}"
shopt -s nullglob
packages=(dist/*.deb dist/*.rpm)
if [ "${#packages[@]}" -eq 0 ]; then
echo "nfpm did not produce a .deb or .rpm"
exit 1
fi
for pkg in "${packages[@]}"; do
base="$(basename "${pkg}")"
if [[ "${base}" != *"${VERSION}"* ]]; then
echo "Package name is missing version ${VERSION}: ${base}"
exit 1
fi
if [[ "${base}" != *"${ARCH}"* && "${base}" != *x86_64* && "${base}" != *aarch64* ]]; then
echo "Package name is missing architecture: ${base}"
exit 1
fi
done

- name: Upload package artifacts
uses: actions/upload-artifact@v7
with:
Expand All @@ -122,9 +150,76 @@ jobs:
dist/*.rpm
retention-days: 90

upload-release-assets:
name: Upload packages to GitHub Release
needs: build-packages
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all package artifacts
uses: actions/download-artifact@v7
with:
path: dist
pattern: structkit-*
merge-multiple: true

- name: Generate checksums and upload to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
set -euo pipefail
shopt -s nullglob

TAG="${RELEASE_TAG}"
if [ -z "${TAG}" ]; then
echo "A release tag is required (release event or workflow_dispatch tag input)"
exit 1
fi
VERSION="${TAG#v}"

packages=(dist/*.deb dist/*.rpm)
if [ "${#packages[@]}" -eq 0 ]; then
echo "No .deb or .rpm packages found to upload"
exit 1
fi

for pkg in "${packages[@]}"; do
base="$(basename "${pkg}")"
if [[ "${base}" != *"${VERSION}"* ]]; then
echo "Package name is missing version ${VERSION}: ${base}"
exit 1
fi
if [[ "${base}" != *amd64* && "${base}" != *arm64* && "${base}" != *x86_64* && "${base}" != *aarch64* ]]; then
echo "Package name is missing architecture: ${base}"
exit 1
fi
done

(
cd dist
sha256sum -- *.deb *.rpm | sort -k2 > SHA256SUMS
echo "SHA256SUMS:"
cat SHA256SUMS
)

echo "Uploading to release ${TAG}:"
ls -lh dist/*.deb dist/*.rpm dist/SHA256SUMS

gh release upload "${TAG}" dist/*.deb dist/*.rpm dist/SHA256SUMS --clobber

- name: Summarize release assets
run: |
{
echo "## Packages attached to ${RELEASE_TAG}"
echo ""
echo "Uploaded .deb, .rpm, and SHA256SUMS to https://github.com/${{ github.repository }}/releases/tag/${RELEASE_TAG}"
} >> "$GITHUB_STEP_SUMMARY"

summary:
name: Build Summary
needs: build-packages
needs: [build-packages, upload-release-assets]
runs-on: ubuntu-latest
if: always()

Expand All @@ -137,4 +232,4 @@ jobs:
echo "- Debian/Ubuntu (.deb) - amd64, arm64" >> $GITHUB_STEP_SUMMARY
echo "- Fedora/RHEL (.rpm) - amd64, arm64" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Download artifacts from the Actions tab above." >> $GITHUB_STEP_SUMMARY
echo "Packages are attached to the GitHub release (when the upload job succeeds) and also available as Actions artifacts." >> $GITHUB_STEP_SUMMARY
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ These are install paths for [StructKit](https://github.com/httpdss/structkit), n
- [Agent skills](https://github.com/httpdss/structkit-skills) — inspect, preview, generate, validate from an assistant
- [pre-commit](https://github.com/httpdss/structkit-pre-commit) — validate structures before they land
- [Homebrew](https://github.com/httpdss/homebrew-structkit) — `brew install httpdss/structkit/structkit`
- Linux `.deb` / `.rpm` — download from [build-linux-packages](https://github.com/httpdss/structkit/actions/workflows/build-linux-packages.yaml) workflow artifacts (not attached to GitHub Releases), or build locally per [packaging/README.md](packaging/README.md)
- Linux `.deb` / `.rpm` — download from the [GitHub release](https://github.com/httpdss/structkit/releases) (attached from 3.3.1 on), or build locally per [packaging/README.md](packaging/README.md)
- [Backstage](https://github.com/httpdss/backstage-plugin-structkit) — scaffolder actions for platform teams
- [Hermes plugin](https://github.com/httpdss/hermes-plugin-structkit) — safe StructKit tools inside Hermes

Expand Down
27 changes: 14 additions & 13 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,31 @@ Refer to the [Quick Start](quickstart.md) guide for more options.

## Linux packages (.deb / .rpm)

Native `.deb` (Debian/Ubuntu) and `.rpm` (Fedora/RHEL) packages are built by the [`build-linux-packages`](https://github.com/httpdss/structkit/actions/workflows/build-linux-packages.yaml) GitHub Actions workflow on each published release and on manual workflow dispatch. They are uploaded as **Actions workflow artifacts** (90-day retention). They are **not** attached to GitHub Releases.
Native `.deb` (Debian/Ubuntu) and `.rpm` (Fedora/RHEL) packages are attached to each [GitHub Release](https://github.com/httpdss/structkit/releases) starting with **3.3.1**. The [`build-linux-packages`](https://github.com/httpdss/structkit/actions/workflows/build-linux-packages.yaml) workflow builds them on every published release (and on manual `workflow_dispatch` with a tag) and uploads them to that release. A `SHA256SUMS` file is published alongside the packages. The same files are also kept as Actions artifacts (90-day retention).

To install a release package:
Asset names include the version and architecture, for example:

1. Open the [build-linux-packages workflow runs](https://github.com/httpdss/structkit/actions/workflows/build-linux-packages.yaml) and select the run for the release you want.
2. Download the artifact for your architecture:
- `structkit-VERSION-amd64-deb` / `structkit-VERSION-arm64-deb`
- `structkit-VERSION-amd64-rpm` / `structkit-VERSION-arm64-rpm`
3. Install from the downloaded file.
- `structkit_3.3.1_amd64.deb` / `structkit_3.3.1_arm64.deb`
- `structkit-3.3.1-1.x86_64.rpm` / `structkit-3.3.1-1.aarch64.rpm`

Debian/Ubuntu:
Debian/Ubuntu (amd64):

```sh
sudo dpkg -i structkit_VERSION_amd64.deb
# if needed: sudo apt-get install -f
curl -fsSL -o structkit_3.3.1_amd64.deb \
https://github.com/httpdss/structkit/releases/download/v3.3.1/structkit_3.3.1_amd64.deb
sudo apt install ./structkit_3.3.1_amd64.deb
```

Fedora/RHEL:
Fedora/RHEL (x86_64):

```sh
sudo dnf install ./structkit-VERSION-1.x86_64.rpm
# or: sudo rpm -ivh structkit-VERSION-1.x86_64.rpm
curl -fsSL -o structkit-3.3.1-1.x86_64.rpm \
https://github.com/httpdss/structkit/releases/download/v3.3.1/structkit-3.3.1-1.x86_64.rpm
sudo dnf install ./structkit-3.3.1-1.x86_64.rpm
```

For arm64, download `structkit_3.3.1_arm64.deb` or `structkit-3.3.1-1.aarch64.rpm` from the same release URL and install the local file the same way. Replace `3.3.1` / `v3.3.1` with the version you want.

To build packages locally instead, follow [packaging/README.md](https://github.com/httpdss/structkit/blob/main/packaging/README.md).

## GitHub Actions
Expand Down
36 changes: 20 additions & 16 deletions packaging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ The built packages will be in the `dist/` directory:

## Installing Packages

Packages are **not** attached to GitHub Releases. Download them from the [build-linux-packages](https://github.com/httpdss/structkit/actions/workflows/build-linux-packages.yaml) workflow run artifacts for the release, or build them locally with the steps above.
Packages are attached to each [GitHub Release](https://github.com/httpdss/structkit/releases) starting with 3.3.1. Download the `.deb` / `.rpm` (and `SHA256SUMS`) from the release, or build them locally with the steps above.

### Debian/Ubuntu

```bash
# Download the .deb artifact from the workflow run, then:
sudo dpkg -i structkit_VERSION_amd64.deb
# Download the .deb from the GitHub release, then:
sudo apt install ./structkit_VERSION_amd64.deb

# If there are dependency issues, resolve them with:
sudo apt-get install -f
Expand All @@ -110,7 +110,7 @@ sudo apt-get install -f
### Fedora/RHEL

```bash
# Download the .rpm artifact from the workflow run, then:
# Download the .rpm from the GitHub release, then:
sudo dnf install ./structkit-VERSION-1.x86_64.rpm

# Or using rpm directly:
Expand All @@ -131,32 +131,36 @@ structkit --help
The `.github/workflows/build-linux-packages.yaml` workflow automatically builds packages when:

1. **On release** — Triggered when a new GitHub release is published
2. **Manual dispatch** — Can be triggered manually from the Actions tab
2. **Manual dispatch** — Can be triggered from the Actions tab with a required `tag` input (for example `v3.3.1`) to rebuild that tag and attach packages to that release

The workflow:
- Checks out the release tag (or the `workflow_dispatch` tag)
- Builds packages for both `amd64` and `arm64` architectures
- Creates both `.deb` and `.rpm` packages (4 total artifacts)
- Uploads packages as GitHub Actions artifacts (90-day retention)
- Does **not** attach packages to GitHub Releases
- Can be triggered with a custom version via workflow dispatch
- Attaches the packages and a `SHA256SUMS` file to the corresponding GitHub Release

### Triggering a Manual Build

1. Go to **Actions** → **build-linux-packages**
2. Click **Run workflow**
3. Optionally specify a version (defaults to version in `pyproject.toml`)
3. Enter the release tag to backfill (for example `v3.3.1`)
4. Click **Run workflow**

### Downloading Built Packages

After the workflow completes:
1. Go to the workflow run page
2. Scroll to **Artifacts** section
3. Download the desired package(s):
- `structkit-VERSION-amd64-deb`
- `structkit-VERSION-arm64-deb`
- `structkit-VERSION-amd64-rpm`
- `structkit-VERSION-arm64-rpm`
After the workflow completes, download packages from the [GitHub release](https://github.com/httpdss/structkit/releases) for that tag:

- `structkit_VERSION_amd64.deb` / `structkit_VERSION_arm64.deb`
- `structkit-VERSION-1.x86_64.rpm` / `structkit-VERSION-1.aarch64.rpm`
- `SHA256SUMS`

The same files are also available as Actions artifacts:

- `structkit-VERSION-amd64-deb`
- `structkit-VERSION-arm64-deb`
- `structkit-VERSION-amd64-rpm`
- `structkit-VERSION-arm64-rpm`

## Package Contents

Expand Down
Loading