Skip to content

[WIP] Add binary releases in GitHub for standalone hackagent executable - #531

Merged
Nicola Franco (franconicola) merged 3 commits into
mainfrom
claude/add-binary-releases-for-github
Jul 26, 2026
Merged

[WIP] Add binary releases in GitHub for standalone hackagent executable#531
Nicola Franco (franconicola) merged 3 commits into
mainfrom
claude/add-binary-releases-for-github

Conversation

@Claude

@Claude Claude AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>Binary releases in GitHub</issue_title>
<issue_description>## Goal

Ship a standalone hackagent executable per OS/arch attached to each GitHub Release, so a user can download one file and run hackagent with no Python/pip/uv install — the same experience uv, ruff, and gh itself provide. Today publish.yml already creates a GitHub Release on every v* tag (via actions/create-release@v1, right after uv build + uv publish to PyPI), but it carries no binary assets — the release page just links back to PyPI. This issue is about adding those assets to that same release.

Proposed approach: PyInstaller one-file build in a CI matrix

hackagent is a Click app ([project.scripts] hackagent = "hackagent.cli.main:cli" in pyproject.toml) that defaults to launching a Textual TUI (_launch_tui_default in hackagent/cli/bootstrap.py) when invoked with no subcommand. PyInstaller is the standard tool for this shape of app (pure-Python CLI + native-extension deps + TUI) and is what most comparable projects use for one-off no-python-required binaries.

Add a build-binaries job to .github/workflows/publish.yml, gated on the same v* tag push, running in parallel with the existing publish job:

build-binaries:
  strategy:
    matrix:
      include:
        - os: ubuntu-latest
          target: linux-x86_64
        - os: macos-latest
          target: macos-arm64
        - os: macos-13          # last Intel macos-* runner
          target: macos-x86_64
        - os: windows-latest
          target: windows-x86_64
  runs-on: ${{ matrix.os }}
  steps:
    - uses: actions/checkout@v7
    - uses: astral-sh/setup-uv@v7
      with: { version: "0.10.11" }
    - run: uv sync --extra pyinstaller   # see "new dependency" below
    - run: uv run pyinstaller packaging/hackagent.spec
    - run: <rename dist artifact to hackagent-${{ matrix.target }}[.exe]>
    - uses: actions/upload-release-asset@v1   # or softprops/action-gh-release, see note

The publish job's actions/create-release@v1 step is deprecated/unmaintained and only returns an upload URL awkwardly for matrix jobs; switching both jobs to softprops/action-gh-release@v2 (actively maintained, supports a files: glob and can be called once per matrix leg with append_body: true) is worth doing as part of this issue rather than fighting the old action across 4 parallel jobs.

Known packaging gotchas specific to this repo's dependency set

These need to be handled in the PyInstaller spec, not discovered at release time:

  • importlib.metadata.version("hackagent") is used both for --version and the version command (cli/main.py:72, hackagent/cli/main.py:260). A frozen PyInstaller binary has no installed distribution metadata by default — this raises PackageNotFoundError unless the spec adds --copy-metadata hackagent (or the code falls back to a version baked in at build time via an env var).
  • Textual ships its own CSS/asset files that PyInstaller's default import hook doesn't always collect — needs --collect-data textual. The app's own TUI code lives under hackagent/cli/tui/; worth double-checking during the build whether it loads any non-.py assets at runtime (fonts, icons) that also need --add-data.
  • faiss-cpu and other compiled-extension deps need the matrix build to run natively per target (no cross-compiling) — the matrix above already does this by using per-OS runners rather than QEMU/cross builds.
  • playwright (used by the WEB provider) requires a separate playwright install browser download regardless of packaging method — the binary cannot embed Chromium/Firefox. This should be called out explicitly wherever the binary is documented/installed (README/release notes), same caveat that exists for pip install hackagent today.
  • nicegui (local dashboard) serves its own static web assets from its package data — needs --collect-data nicegui, same class of issue as Textual.
  • One-file mode (--onefile) unpacks to a temp dir on every launch, which is slow for an app this size (~15 MB of first-party code before deps); --onedir (a folder + launcher) starts faster and is what most CLI-shaped PyInstaller distributions actually ship, at the cost of shipping a .zip/.tar.gz instead of a single file. Worth deciding onedir-in-an-archive vs true onefile before wiring the workflow, since it changes the release-asset naming and the install instructions.

New dev dependency

Add pyinstaller as an optional dependency group (e.g. [dependency-groups] packaging = ["pyinstaller>=6.0"] in pyproject.toml), not to the default dev group — it's a release-time tool the day-to-day contributor doesn't need.

Release asset naming

Match the convention CLI tools like uv/ripgrep use so an install script can predict the filename from uname:

hackagent-${VERSION}-linux-x86_64.tar.gz
hackagent-${VERSION}-macos-arm64.tar.gz
hackagent-${VERSION}-macos-x86_64.tar.gz
hackagent-${VERSION}-windows-x86_64.zip

Optional follow-up: install script

Once assets exist with predictable names, a curl -fsSL https://.../install.sh | bash one-liner (resolve OS/arch, download the matching release asset, extract to ~/.local/bin or similar) is a natural next step — but it's additive and shouldn't block getting the binaries themselves published. Track it as a follow-up rather than in this issue's acceptance criteria.

Acceptance criteria

  • packaging/hackagent.spec PyInstaller spec covering the metadata/Textual/nicegui data-collection gotchas above
  • pyinstaller added as its own dependency group, not dev
  • build-binaries matrix job in publish.yml (Linux x86_64, macOS arm64 + x86_64, Windows x86_64) producing the 4 archives above
  • Release job switched to an actively-maintained action that both publish and build-binaries can attach assets through (e.g. softprops/action-gh-release)
  • hackagent --version / hackagent version work correctly from the frozen binary (no PackageNotFoundError)
  • hackagent (no subcommand → TUI) and at least one subcommand (e.g. hackagent scan) smoke-tested from the built binary on each OS, not just import-tested
  • README/release notes call out the separate playwright install step needed for the WEB provider
    </issue_description>
  • Closes Binary releases in GitHub #477

Comments on the Issue (you are @claude[agent] in this section)

@Claude Claude AI linked an issue Jul 26, 2026 that may be closed by this pull request
7 tasks
@franconicola
Nicola Franco (franconicola) marked this pull request as ready for review July 26, 2026 20:01
Copilot AI review requested due to automatic review settings July 26, 2026 20:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
hackagent/cli/bootstrap.py 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@franconicola
Nicola Franco (franconicola) force-pushed the claude/add-binary-releases-for-github branch from 45a4e9b to 0e57750 Compare July 26, 2026 20:16
@franconicola
Nicola Franco (franconicola) force-pushed the claude/add-binary-releases-for-github branch from 97ddfb5 to 28834b1 Compare July 26, 2026 21:36
@franconicola
Nicola Franco (franconicola) force-pushed the claude/add-binary-releases-for-github branch from 28834b1 to 859c303 Compare July 26, 2026 21:49
@franconicola
Nicola Franco (franconicola) merged commit 5454994 into main Jul 26, 2026
23 checks passed
@franconicola
Nicola Franco (franconicola) deleted the claude/add-binary-releases-for-github branch July 26, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Binary releases in GitHub

3 participants