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
140 changes: 140 additions & 0 deletions .github/workflows/build-linux-packages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: build-linux-packages

on:
release:
types:
- published
workflow_dispatch:
inputs:
version:
description: 'Package version (e.g., 3.2.1)'
required: false
default: ''

jobs:
build-packages:
name: Build ${{ matrix.format }} for ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
arch: [amd64, arm64]
format: [deb, rpm]

steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'

- name: Determine version
id: version
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'])")
fi

echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Package version: ${VERSION}"

- name: Install nfpm
run: |
set -euo pipefail
echo "deb [trusted=yes] https://repo.goreleaser.com/apt/ /" | sudo tee /etc/apt/sources.list.d/goreleaser.list
sudo apt-get update
sudo apt-get install -y nfpm

- name: Install Python build dependencies
run: |
python -m pip install --upgrade pip
pip install build virtualenv

- name: Build Python wheel
run: |
python -m build --wheel

- name: Create vendored virtual environment
run: |
set -euo pipefail

# Create a fresh venv in dist/venv
mkdir -p dist
python -m virtualenv dist/venv

# Install structkit and its dependencies into the venv
dist/venv/bin/pip install --upgrade pip
dist/venv/bin/pip install dist/*.whl

# Clean up unnecessary files to reduce package size
find dist/venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find dist/venv -type f -name "*.pyc" -delete 2>/dev/null || true
find dist/venv -type f -name "*.pyo" -delete 2>/dev/null || true
rm -rf dist/venv/lib/python*/site-packages/pip* || true
rm -rf dist/venv/lib/python*/site-packages/setuptools* || true

- name: Set architecture for nfpm
id: arch
run: |
if [ "${{ matrix.arch }}" = "amd64" ]; then
echo "nfpm_arch=amd64" >> $GITHUB_OUTPUT
elif [ "${{ matrix.arch }}" = "arm64" ]; then
echo "nfpm_arch=arm64" >> $GITHUB_OUTPUT
fi

- name: Build ${{ matrix.format }} package
env:
VERSION: ${{ steps.version.outputs.version }}
ARCH: ${{ steps.arch.outputs.nfpm_arch }}
run: |
set -euo pipefail

# Build the package using nfpm
if [ "${{ matrix.format }}" = "deb" ]; then
nfpm package \
--packager deb \
--config packaging/debian/nfpm.yaml \
--target dist/
elif [ "${{ matrix.format }}" = "rpm" ]; then
nfpm package \
--packager rpm \
--config packaging/rpm/nfpm.yaml \
--target dist/
fi

# List generated packages
ls -lh dist/*.{deb,rpm} 2>/dev/null || true

- name: Upload package artifacts
uses: actions/upload-artifact@v7
with:
name: structkit-${{ steps.version.outputs.version }}-${{ matrix.arch }}-${{ matrix.format }}
path: |
dist/*.deb
dist/*.rpm
retention-days: 90

summary:
name: Build Summary
needs: build-packages
runs-on: ubuntu-latest
if: always()

steps:
- name: Generate summary
run: |
echo "## Linux Package Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Built packages for:" >> $GITHUB_STEP_SUMMARY
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
229 changes: 229 additions & 0 deletions packaging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Linux Distribution Packages

This directory contains the configuration and tooling for building `.deb` (Debian/Ubuntu) and `.rpm` (Fedora/RHEL) packages for the structkit CLI.

## Package Structure

- `debian/` — Debian/Ubuntu package configuration
- `rpm/` — Fedora/RHEL package configuration
- `structkit-shim.sh` — Wrapper script installed at `/usr/bin/structkit`

## Architecture

The packages install structkit in a vendored Python virtual environment to handle dependencies that may not be available in distribution repositories:

- **Vendored environment**: `/usr/lib/structkit/venv/` — Complete Python environment with all dependencies
- **CLI wrapper**: `/usr/bin/structkit` — Shim script that invokes the vendored structkit
- **Documentation**: `/usr/share/doc/structkit/` — README and LICENSE files

This approach ensures:
- No conflicts with system Python packages
- Consistent dependency versions across distributions
- Simple installation without requiring pip or virtual environment management

## Building Packages Locally

### Prerequisites

Install required tools:

**Debian/Ubuntu:**
```bash
sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-virtualenv
```

**Fedora/RHEL:**
```bash
sudo dnf install -y python3 python3-pip python3-virtualenv
```

**Install nfpm** (cross-platform package builder):
```bash
# Option 1: Using apt (Debian/Ubuntu)
echo "deb [trusted=yes] https://repo.goreleaser.com/apt/ /" | sudo tee /etc/apt/sources.list.d/goreleaser.list
sudo apt-get update
sudo apt-get install -y nfpm

# Option 2: Using yum/dnf (Fedora/RHEL)
echo '[goreleaser]
name=GoReleaser
baseurl=https://repo.goreleaser.com/yum/
enabled=1
gpgcheck=0' | sudo tee /etc/yum.repos.d/goreleaser.repo
sudo dnf install -y nfpm

# Option 3: Direct download
# See: https://nfpm.goreleaser.com/install/
```

### Build Steps

1. **Install Python build dependencies:**
```bash
python3 -m pip install --upgrade pip build virtualenv
```

2. **Build the Python wheel:**
```bash
python3 -m build --wheel
```

3. **Create vendored virtual environment:**
```bash
mkdir -p dist
python3 -m virtualenv dist/venv
dist/venv/bin/pip install --upgrade pip
dist/venv/bin/pip install dist/*.whl
```

4. **Build Debian package (amd64):**
```bash
VERSION=$(python3 -c "import tomllib; f = open('pyproject.toml', 'rb'); data = tomllib.load(f); f.close(); print(data['project']['version'])")
ARCH=amd64 nfpm package --packager deb --config packaging/debian/nfpm.yaml --target dist/
```

5. **Build RPM package (amd64):**
```bash
VERSION=$(python3 -c "import tomllib; f = open('pyproject.toml', 'rb'); data = tomllib.load(f); f.close(); print(data['project']['version'])")
ARCH=amd64 nfpm package --packager rpm --config packaging/rpm/nfpm.yaml --target dist/
```

The built packages will be in the `dist/` directory:
- `structkit_${VERSION}_amd64.deb`
- `structkit-${VERSION}-1.x86_64.rpm`

## Installing Packages

### Debian/Ubuntu

```bash
# Download the .deb package, then:
sudo dpkg -i structkit_VERSION_amd64.deb

# If there are dependency issues, resolve them with:
sudo apt-get install -f
```

### Fedora/RHEL

```bash
# Download the .rpm package, then:
sudo dnf install ./structkit-VERSION-1.x86_64.rpm

# Or using rpm directly:
sudo rpm -ivh structkit-VERSION-1.x86_64.rpm
```

### Verify Installation

After installation, verify that structkit is working:

```bash
structkit info
structkit --help
```

## CI/CD: GitHub Actions Workflow

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

The workflow:
- 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)
- Can be triggered with a custom version via workflow dispatch

### 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`)
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`

## Package Contents

Each package includes:

- `/usr/bin/structkit` — CLI entry point (wrapper script)
- `/usr/lib/structkit/venv/` — Vendored Python environment with:
- Python interpreter
- structkit and all dependencies
- Site packages (PyYAML, requests, openai, jinja2, etc.)
- `/usr/share/doc/structkit/README.md` — Project README
- `/usr/share/doc/structkit/LICENSE` — MIT License

## Maintenance

### Updating Package Metadata

Package metadata is defined in:
- `packaging/debian/nfpm.yaml` — Debian package configuration
- `packaging/rpm/nfpm.yaml` — RPM package configuration

Common fields to update:
- `maintainer` — Package maintainer contact
- `description` — Package description
- `depends` — Runtime dependencies
- `recommends` — Recommended packages

### Version Synchronization

The package version is automatically extracted from `pyproject.toml` during builds. Ensure the version in `pyproject.toml` is updated before building or releasing.

## Troubleshooting

### Package Installation Issues

If you encounter dependency issues:

**Debian/Ubuntu:**
```bash
sudo apt-get update
sudo apt-get install -f
```

**Fedora/RHEL:**
```bash
sudo dnf install ca-certificates
```

### CLI Not Working After Install

Verify the installation:
```bash
which structkit # Should show /usr/bin/structkit
ls -la /usr/lib/structkit/venv/ # Should show Python environment
/usr/lib/structkit/venv/bin/structkit --help # Direct invocation
```

If the shim script fails, you can directly invoke:
```bash
/usr/lib/structkit/venv/bin/structkit [command]
```

## Future Enhancements

This is Phase 1 of Linux distribution packaging. Not included in this PR:

- Submission to Debian mentors or Fedora review
- PPA/COPR repository setup
- Official distribution repository inclusion
- Package signing/verification
- Marketplace/catalog integration

These may be addressed in future phases based on community adoption and requirements.
Loading
Loading