Skip to content

feat: add 'uvbox git' subcommand for git repository sources (#19) - #21

Closed
hasansezertasan wants to merge 12 commits into
AmadeusITGroup:mainfrom
hasansezertasan:feat/hasansezertasan/brainstrorming-lets-address-this-issue-httpsgithub
Closed

feat: add 'uvbox git' subcommand for git repository sources (#19)#21
hasansezertasan wants to merge 12 commits into
AmadeusITGroup:mainfrom
hasansezertasan:feat/hasansezertasan/brainstrorming-lets-address-this-issue-httpsgithub

Conversation

@hasansezertasan

Copy link
Copy Markdown

Closes #19.

Summary

Adds a new uvbox git <git-spec> subcommand alongside the existing pypi and wheel source types. The git spec is passed through to uv tool install --from verbatim, so any form uv accepts works (@main, @v1.0.0, @<commit>, ssh://, etc.).

uvbox git git+https://github.com/org/repo
uvbox git git+https://github.com/org/repo@v1.0.0
uvbox git git+ssh://git@github.com/org/private-repo

Implements the approach @Coruscant11 chose in this comment: subcommand with the git spec as a positional CLI argument, no changes to the uvbox.toml schema.

The runtime install path runs uv tool install --from <spec> <name> --upgrade on every install/update — no version-discovery infrastructure, no tag scanning. auto-update = true re-installs on every run, delivering the pycrucible delete_after_run-equivalent behavior the issue requested.

Design notes

  • Additive only. The pypi and wheel code paths are untouched. A regression test in box/config_identifier_test.go locks in a byte-stable golden hash for ComputeIdentifier to guarantee existing binaries find the same XDG dir.
  • Distinct identifier per git source. The git spec participates in ComputeIdentifier (only when set), so binaries built from git+url@main and git+url@v1.0.0 get separate isolated install dirs. No cross-contamination with pypi-built binaries of the same package.
  • Source embedding via file, not ldflag. See "Pre-existing bug discovered" below.
  • Validation is minimal: the spec must be non-empty and start with git+. Everything else is delegated to uv, which surfaces malformed specs loudly on first run.

Pre-existing bug discovered (and worked around)

The original spec proposed embedding the git source via -ldflags \"-X main.GIT_SOURCE=...\". The smoke test surfaced that this does not work because of a pre-existing bug introduced in #14 (commit f19680f, "fix: Use environment variables for ldflags"):

Go's `GOFLAGS` parser uses `strings.Fields` (whitespace split, no quote handling). Any ldflag string containing `-X main.foo=bar` is split across whitespace, and Go rejects `-X` as an unknown top-level flag with `go: parsing $GOFLAGS: unknown flag -X`.

This silently broke the uvbox wheel path in main since #14 was merged — it sets -X main.INSTALL_WHEELS=yes, which fails the same way. No test exercises wheel mode end-to-end so nobody noticed. Reproduction:

GOFLAGS='-ldflags=-s -w -X main.INSTALL_WHEELS=yes' go build -o hello .
# go: parsing $GOFLAGS: unknown flag -X

This PR does not fix the wheel bug — touching it requires re-validating the original Windows-via-uvx fix from #14 on Windows, which is out of scope here. The wheel path remains broken in main and should be addressed in a separate PR.

For git, this PR sidesteps GOFLAGS entirely by embedding the source in a small file (box/git_source.txt) read at compile time via //go:embed. boxer's writeGitSourceFile writes the spec into this file before go build. Empty file = pypi/wheel build, non-empty = git build. Same end result, different transport.

Files

Runtime (`box/`):

  • box/box_package_git.go (new) — GIT_SOURCE (loaded from embedded git_source.txt), uvToolInstallGit, buildUvToolInstallFromArgs pure helper
  • box/box_package.go — one new branch in uvToolInstall dispatching to git when GIT_SOURCE != \"\" (existing pypi/wheel logic untouched)
  • box/config.goComputeIdentifier conditionally appends GIT_SOURCE (no-op when empty)
  • box/generate.go — adds empty git_source.txt placeholder generation, mirroring the existing wheels/placeholder pattern
  • box/config_identifier_test.go (new) — golden-hash regression test + git distinction tests
  • box/box_package_git_test.go (new) — pure-helper command-shape tests

Build-time (`boxer/`):

  • boxer/git.go (new) — buildGoBuildLdflags pure helper (now without git, since git uses file embed), validateGitSource, writeGitSourceFile
  • boxer/main.go — new gitCmd cobra command, GitSource CLI var, validateGitSourceFlag in preRun, writeGitSourceFile call in insertFilesIntoBoxRepository
  • boxer/git_test.go (new) — ldflag regression tests for pypi/wheel, writeGitSourceFile tests, validateGitSource tests

Docs:

  • README.md — feature bullet updated, new "Build from a Git Repository" section, behavior of [package.version] for git builds, private repo auth note
  • examples/git/simple-app.toml (new) — minimal example
  • .gitignore — adds box/git_source.txt

Test Plan

  • Full box test suite passes (go test ./...) including the ComputeIdentifier golden-hash regression guard
  • Full boxer test suite passes including ldflag regression guards for the existing pypi/wheel paths
  • Manual smoke test: uvbox git git+https://github.com/VaasuDevanS/cowsay-python --darwin --arm builds, the resulting binary runs end-to-end (./cowsay -t \"hi\" prints the cow), self update reports "Already up-to-date", self path shows a git-distinct identifier
  • Regression: uvbox pypi still works, produces a different identifier hash (separate XDG dir from the git build of the same package)

Reviewers can reproduce the smoke test:

cd boxer && go generate && go build -o /tmp/uvbox . && cd ..
mkdir /tmp/smoke && cd /tmp/smoke
cat > uvbox.toml <<TOML
[package]
name = \"cowsay\"
script = \"cowsay\"
TOML
/tmp/uvbox git git+https://github.com/VaasuDevanS/cowsay-python --darwin --arm  # adjust to host
cd dist && tar xzf cowsay-*.tar.gz && ./cowsay -t \"git works!\"

🤖 Generated with Claude Code

@hasansezertasan
hasansezertasan marked this pull request as draft April 10, 2026 14:30
hasansezertasan and others added 12 commits April 10, 2026 17:33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extracts ldflag construction into a pure, testable function and adds
validateGitSource to enforce the git+ prefix constraint. Regression
tests lock in pypi and wheel ldflag output; new tests cover git source
injection behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oBuild

- Replace inline ldflags construction in goBuild with buildGoBuildLdflags(GitSource, WheelsToEmbed)
- Add GitSource package-level var alongside Config/Output/Nfpm/ReleaseVersion
- Register gitCmd cobra subcommand (ExactArgs(1), same flags as pypiCmd/wheelCmd)
- Add validateGitSourceFlag helper, called from preRun

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The smoke test surfaced a pre-existing bug in boxer's ldflag handling:
Go's GOFLAGS parser uses strings.Fields (whitespace split, no quote
handling), so any ldflag string containing -X flags fails to parse with
"unknown flag -X". This silently broke the wheel path in commit f19680f
when ldflags moved to GOFLAGS for Windows compatibility, and would have
broken git the same way.

To unblock the git feature without touching the wheel path (which
remains broken in main and needs a separate fix), embed the git source
via a committed placeholder file box/git_source.txt + //go:embed in
box/box_package_git.go. boxer writes the git spec into this file before
go build via writeGitSourceFile, replacing the empty placeholder for
git builds and leaving it empty for pypi/wheel builds.

This sidesteps GOFLAGS entirely for git. The wheel path's GOFLAGS bug
is left untouched and should be addressed in a separate PR by the
original author of f19680f, who can verify the Windows fix properly.

Verified end-to-end:
  uvbox git git+https://github.com/VaasuDevanS/cowsay-python --darwin --arm
  ./dist/cowsay -t "uvbox git works!"   # prints cow ✓
  ./cowsay self update                  # "Already up-to-date" ✓
  ./cowsay self path                    # cowsay-637049bd...  ✓
  uvbox pypi (regression)               # cowsay-2fea8a21... ✓ different dir

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match the existing wheels/placeholder pattern: gitignore the dynamically
generated file and create it on demand via go generate. Keeps the box
package free of empty committed files and lets `mise run generate:box`
handle bootstrapping for tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hasansezertasan
hasansezertasan force-pushed the feat/hasansezertasan/brainstrorming-lets-address-this-issue-httpsgithub branch from d01fcef to 89bc268 Compare April 10, 2026 14:34
@hasansezertasan

Copy link
Copy Markdown
Author

Closing in favor of a renamed branch — the auto-generated branch name was unwieldy. Reopening as a draft from feat/git-subcommand with the same commits and description. New PR link to follow.

@hasansezertasan
hasansezertasan deleted the feat/hasansezertasan/brainstrorming-lets-address-this-issue-httpsgithub branch April 10, 2026 14:39
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.

Feature request: Support git URLs as package source for self-update

1 participant