gitaiflow / Deployment / Binary Distribution
DocsgitaiflowDeploymentBinary Distribution

Binary Distribution

Operational reference for compiling, packaging, and releasing gitaiflow as a native standalone binary across four platforms, via the GitLab Generic Package Registry and Cloudflare R2.

7 min readApplies to v1.1.3
On this page ▾
  1. Distribution Architecture
  2. Overview
  3. Complete Asset Coverage on R2
  4. Section A: Cross-Platform Binary Builds
  5. 1. macOS Apple Silicon (darwin-arm64) — make compile
  6. 2. Linux x86_64 (linux-amd64) — make build-linux-amd64
  7. 3. Linux ARM64 (linux-arm64) — make build-linux-arm64
  8. 4. Windows x86_64 (windows-amd64) — make build-windows
  9. 5. Verify Artifact Architecture Headers
  10. Section B: Cloudflare R2 Sync via Dynamic Manifest
  11. 1. Structure
  12. 2. Generate Manifest
  13. 3. Sync
  14. Section C: Release Process
  15. 1. make all
  16. 2. registry_update.sh — Package Registry Upload + R2 Sync
  17. 3. macOS-Specific Upload
  18. 4. Git Release Tagging
  19. Section D: Verification & Integration Testing
  20. 1. Local Machine Install Verification (macOS ARM64)
  21. 2. Linux Container Install Verification
  22. 3. Windows PowerShell Install Verification
  23. 4. Functional Smoke Test (all platforms)
  24. Related

Distribution Architecture

Overview

  • Source Repository: GitLab (clizero/gitaiflow) — private repository.
  • Public Edge Distribution: standalone binaries hosted on Cloudflare R2 under djangoplay-data/gitaiflow/installers/.
  • Public Domains:
    • macOS/Linux: https://install.djangoplay.org/gitaiflow
    • Windows: https://install.djangoplay.org/gitaiflow.ps1

Complete Asset Coverage on R2

Binary File Target OS / Environment Target Architecture
gitaiflow-darwin-arm64 macOS (Apple Silicon: M1, M2, M3, M4) ARM 64-bit (arm64)
gitaiflow-linux-amd64 Linux (Ubuntu, Debian, RedHat, standard cloud VMs) Intel/AMD 64-bit (x86_64)
gitaiflow-linux-arm64 Linux (AWS Graviton, Raspberry Pi, Apple Silicon Docker) ARM 64-bit (aarch64/arm64)
gitaiflow-windows-amd64.exe Windows 10, 11, Server 64-bit (x86_64)

Section A: Cross-Platform Binary Builds

All four platform targets are wired as Makefile targets, run from the repository root:

bash
make build-all
# runs, in sequence: compile (macOS) → build-linux-amd64 → build-linux-arm64 → build-windows

1. macOS Apple Silicon (darwin-arm64) — make compile

Runs scripts/install/compile.sh locally (not in a container — this target must run on actual macOS ARM64 hardware), producing bin/gitaiflow, then copies it to installers/gitaiflow-darwin-arm64.

2. Linux x86_64 (linux-amd64) — make build-linux-amd64

bash
docker run --rm --platform linux/amd64 -v "$PWD":/workspace -w /workspace python:3.11-bookworm bash -c '
  apt-get update && apt-get install -y --no-install-recommends gcc g++ ccache patchelf
  pip install -U nuitka zstandard
  pip install -e .
  python -m nuitka --standalone --onefile --remove-output \
    --include-package=gitaiflow \
    --include-package-data=gitaiflow \
    --include-package=httpx \
    --include-package-data=certifi \
    --output-dir=installers \
    --output-filename=gitaiflow-linux-amd64 \
    gitaiflow/generate_summary.py
'

3. Linux ARM64 (linux-arm64) — make build-linux-arm64

Identical container recipe to the amd64 build, run under --platform linux/arm64, producing installers/gitaiflow-linux-arm64.

4. Windows x86_64 (windows-amd64) — make build-windows

bash
docker run --rm --platform linux/amd64 -v "$PWD":/workspace -w /workspace tobix/pywine:3.11 bash -c '
  wine pip install -U pyinstaller
  wine pip install -e .
  wine pyinstaller \
    --onefile \
    --clean \
    --noupx \
    --name gitaiflow-windows-amd64 \
    --add-data "gitaiflow/prompts;gitaiflow/prompts" \
    --add-data "gitaiflow/templates;gitaiflow/templates" \
    --collect-all gitaiflow \
    --collect-all httpx \
    --collect-all certifi \
    gitaiflow/generate_summary.py
'
mv dist/gitaiflow-windows-amd64.exe installers/
rm -rf build dist gitaiflow-windows-amd64.spec

As with astwire, the Windows build uses PyInstaller inside Wine rather than Nuitka — cross-compiling a native Windows PE with Nuitka from a Linux container isn't supported. The --add-data flags explicitly bundle gitaiflow/prompts/ and gitaiflow/templates/ (the AI system prompt and the changelog Markdown template), since PyInstaller doesn't pick up non-Python package data automatically the way --include-package-data does under Nuitka.

5. Verify Artifact Architecture Headers

bash
file installers/*

Expected output:

  • installers/gitaiflow-darwin-arm64: Mach-O 64-bit executable arm64
  • installers/gitaiflow-linux-amd64: ELF 64-bit LSB pie executable, x86-64
  • installers/gitaiflow-linux-arm64: ELF 64-bit LSB pie executable, ARM aarch64
  • installers/gitaiflow-windows-amd64.exe: PE32+ executable (console) x86-64, for MS Windows

Section B: Cloudflare R2 Sync via Dynamic Manifest

1. Structure

text
scripts/
├── install/
│   ├── compile.sh
│   ├── install.sh
│   └── install.ps1
└── r2/
    ├── gitai_manifest.json
    ├── gitai_r2.sh
    └── generate_gitai_manifest.py
installers/
├── gitaiflow-darwin-arm64
├── gitaiflow-linux-amd64
├── gitaiflow-linux-arm64
└── gitaiflow-windows-amd64.exe

2. Generate Manifest

bash
make manifest
# python3 scripts/r2/generate_gitai_manifest.py

Defaults to base directory = the current Git repository root, and default targets scripts/install/install.sh, scripts/install/install.ps1, and installers/. Installer scripts are flattened to top-level R2 keys (dropping their scripts/install/ prefix); everything under installers/ keeps its relative path. Output is written to scripts/r2/gitai_manifest.json.

3. Sync

bash
make sync
# runs `manifest`, then chmod +x scripts/r2/gitai_r2.sh && ./scripts/r2/gitai_r2.sh

gitai_r2.sh syncs to djangoplay-r2:djangoplay-data/gitaiflow (overridable via -r/--remote) using rclone copy with --checksum, so a re-run against unchanged files produces zero unnecessary writes to Cloudflare R2. It syncs, in order: the installers/ directory, install.sh, install.ps1, and the manifest file itself.


Section C: Release Process

1. make all

bash
make all
# test -> build-all -> release

release shells out to scripts/release/registry_update.sh $(VERSION), where VERSION is derived from pyproject.toml's [project].version (prefixed with v) unless overridden or resolved from the current Git tag.

2. registry_update.sh — Package Registry Upload + R2 Sync

Installers are now stored under a versioned path, installers/vX.Y.Z/, rather than flat — registry_update.sh resolves the version from pyproject.toml if not passed explicitly, and SKIP_R2=1 skips the R2 sync leg for a package-registry-only run. gitai_r2.sh also writes installers/latest.txt from the resolved version (or GITAIFLOW_LATEST_VERSION) so install.sh/install.ps1 can resolve "latest" without hardcoding it, and its R2 remote is configurable via R2_REMOTE.

For each file under installers/, uploads it to the GitLab Generic Package Registry twice — once under the release version, once under latest:

bash
BASE_URL="https://gitlab.com/api/v4/projects/${PROJECT_ID}/packages/generic/gitaiflow"

curl --fail-with-body --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
  --upload-file "installers/gitaiflow-linux-amd64" \
  "${BASE_URL}/${VERSION}/gitaiflow-linux-amd64"

curl --fail-with-body --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
  --upload-file "installers/gitaiflow-linux-amd64" \
  "${BASE_URL}/latest/gitaiflow-linux-amd64"

PROJECT_ID defaults to codefleet-labs%2Fgitaiflow, overridable via the GITLAB_PROJECT_ID environment variable — set this to match whichever GitLab project path is currently authoritative before running a release. GITLAB_TOKEN (a token with api scope) is required. After the package-registry upload, the script regenerates the R2 manifest and runs the R2 sync automatically — a single registry_update.sh invocation handles both distribution surfaces.

If bin/gitaiflow exists locally (from make compile) but installers/gitaiflow-darwin-arm64 doesn't yet, the script copies it into place first, so a fresh make compile && ./scripts/release/registry_update.sh vX.Y.Z works without a separate manual copy step.

3. macOS-Specific Upload

bash
make release-mac

Runs compile (builds bin/gitaiflow on macOS ARM64 hardware), uploads it directly to the GitLab Generic Package Registry at the clizero%2Fgitaiflow project path under both the current VERSION and latest, then runs sync. This exists because CI cannot cross-compile a macOS binary — the mac asset is always built and uploaded from real Apple Silicon hardware.

4. Git Release Tagging

bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

Makefile's VERSION variable is derived from pyproject.toml by default (VERSION ?= v$(PYPROJECT_VERSION)), but registry_update.sh also accepts an explicit tag as its first argument, or falls back to git describe --tags --exact-match if none is given — so tagging the release commit and running make release (or ./scripts/release/registry_update.sh vX.Y.Z directly) both work.

This step can now be delegated to scripts/release/tag_release.sh, which creates/pushes the annotated tag to both the GitLab and GitHub remotes, then creates the GitLab and GitHub releases themselves from RELEASE_NOTES.md (its ## Release Title / ## Release Draft sections) and links the built binaries from the GitLab package registry. Individual legs can be skipped with SKIP_TAG, SKIP_RELEASE, SKIP_GITLAB, or SKIP_GITHUB. Generate RELEASE_NOTES.md first with gitaiflow --release-notes (reads the current CHANGELOG.md entry and calls the AI) — the release Makefile target does this automatically, with SKIP_RELEASE_NOTES=1 as an escape hatch.


Section D: Verification & Integration Testing

1. Local Machine Install Verification (macOS ARM64)

bash
curl -fsSL https://install.djangoplay.org/gitaiflow | bash
gitaiflow --version
gitaiflow --help

2. Linux Container Install Verification

A. Linux x86_64 (amd64):

bash
docker run --rm -it --platform linux/amd64 python:3.11-slim bash -c "
  apt-get update && apt-get install -y curl sudo && \
  curl -fsSL https://install.djangoplay.org/gitaiflow | bash && \
  gitaiflow --version && \
  gitaiflow --help
"

B. Linux ARM64 (aarch64):

bash
docker run --rm -it --platform linux/arm64 python:3.11-slim bash -c "
  apt-get update && apt-get install -y curl sudo && \
  curl -fsSL https://install.djangoplay.org/gitaiflow | bash && \
  gitaiflow --version && \
  gitaiflow --help
"

3. Windows PowerShell Install Verification

powershell
irm https://install.djangoplay.org/gitaiflow.ps1 | iex
gitaiflow --version
gitaiflow --help

4. Functional Smoke Test (all platforms)

Installation succeeding is necessary but not sufficient — verify the compiled binary can actually reach an AI provider and write a real artifact, since bundled dependencies (httpx, certifi, and — on Windows — the prompts/templates data files) are the most likely thing to silently break in a from-source-to-binary compile:

bash
AI_PROVIDER=custom AI_BASE_URL=https://openrouter.ai/api/v1 \
AI_API_KEY=<key> AI_MODEL=openrouter/free \
gitaiflow --path . --change-summary