Skip to content

Add verified cross-platform release installers - #30

Merged
merefield merged 4 commits into
mainfrom
codex/release-installers
Aug 23, 2026
Merged

Add verified cross-platform release installers#30
merefield merged 4 commits into
mainfrom
codex/release-installers

Conversation

@merefield

Copy link
Copy Markdown
Owner

Summary

  • add GoReleaser packaging for Linux, macOS, and Windows on AMD64 and ARM64
  • publish checksum-protected tarballs and Windows ZIPs only after tag validation and the full OS test matrix
  • add Unix and PowerShell installers that resolve releases, verify SHA-256, validate the embedded version, and install safely
  • make release installers the recommended user path while retaining go install for source builds

Validation

  • go test ./...
  • go test -race ./...
  • go vet ./...
  • ShellCheck and six Bats installer tests
  • actionlint
  • GoReleaser v2.17 config check
  • GoReleaser snapshot: six archives, all checksums verified, archive contents and embedded version inspected

The Windows installer integration test runs on windows-latest in CI because PowerShell is not installed in the local environment.

@merefield
merefield marked this pull request as ready for review August 23, 2026 12:37

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.

🟡 Changes recommended

Two critical findings and two moderate installer issues remain, plus one documentation nit.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds cross-platform release packaging and verified Unix/PowerShell installers with CI validation and documentation.

Changes:

  • Configures GoReleaser archives and checksums for six platform/architecture targets.
  • Adds guarded release workflows and installer tests.
  • Updates installation documentation, Make targets, and version metadata.
File summaries
File Summary and final review notes
test/install-release.ps1 Windows integration test. Critical (1 vote): create missing nested directories recursively before setup.
test/install-release.bats Unix installer tests; no final comments.
README.md Installation documentation. Nit (3 votes): document handling for PowerShell’s Restricted execution policy.
Makefile Adds check, integration, and snapshot targets; no final comments.
internal/version/version.go Updates fallback version; no final comments.
internal/version/version_test.go Updates version expectations; no final comments.
internal/ui/view_test.go Updates displayed-version expectations; no final comments.
install-release.sh Unix installer. Moderate (3 votes): accept SemVer + build metadata (also line 165). Critical (1 vote): stage and atomically replace the executable to prevent corruption.
install-release.ps1 PowerShell installer. Moderate (2 votes): accept SemVer + build metadata (also line 100).
.goreleaser.yaml Defines cross-platform archives and checksums; no final comments.
.github/workflows/release.yml Validates tags, tests platforms, and publishes releases; no final comments.
.github/workflows/ci.yml Adds installer coverage; no final comments.
Review details

Suppressed comments (7)

.github/workflows/release.yml:44

  • This is not a strict SemVer check: values such as v1.2.3-, v1.2.3+. and the numeric prerelease v1.2.3-01 all match. They pass the validation gate and are only rejected later (or produce invalid release metadata), so the workflow does not provide the promised semantic-tag validation; use a strict SemVer parser or regex with nonempty dot-separated identifiers and the numeric prerelease rules.
          if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then

.github/workflows/release.yml:44

  • This validation accepts SemVer build metadata such as v1.2.3+build, but both release installers reject + in an explicit or latest-resolved tag (install-release.sh:116-118 and install-release.ps1:100-102). Such a tag can pass validation and be published while neither documented installer can download it; make the tag grammar consistent across the workflow and installers, or reject build metadata here.
          if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then

install-release.ps1:100

  • For latest, the earlier explicit-version check is skipped, but the resolved tag is rejected by this second validator for the same reason: valid workflow tags containing + cannot be installed. Keep the resolved-tag grammar consistent with the release workflow.
    if ($releaseTag -notmatch '^[A-Za-z0-9._-]+$') {

install-release.ps1:175

  • If %LOCALAPPDATA%\Programs\codexometer\bin\codexometer.exe already exists as a directory, Move-Item -Destination $target moves the staged file inside that directory and the script then prints a successful installation to the directory path. Check the target type and fail (or handle it explicitly) before staging/moving.
        Move-Item -LiteralPath $stagedTarget -Destination $target -Force

install-release.sh:65

  • repository=owner passes this validation because both repository_owner and repository_name remain equal to the whole string when no slash is present. The installer then constructs /repos/owner/... instead of rejecting the documented owner/repository format; require that a separator is present before building the download URLs.
if [ -z "$repository_owner" ] || [ -z "$repository_name" ] || [ "$repository_name" != "${repository_name#*/}" ]; then

install-release.sh:169

  • When CODEXOMETER_BIN_DIR is a new nested path such as $HOME/.local/bin, its immediate parent may also be missing, so [ -w "$(dirname "$bin_dir")" ] is false. This routes a user-writable destination through the sudo branch (or fails when sudo is absent), contrary to the documented no-elevation install, and can leave the user's directory root-owned. Try mkdir -p "$bin_dir" as the current user and fall back to sudo only if that attempt fails.
if [ -w "$bin_dir" ] || { [ ! -e "$bin_dir" ] && [ -w "$(dirname "$bin_dir")" ]; }; then
  mkdir -p "$bin_dir"
  install -m 0755 "$candidate" "$target"
else
  command -v sudo >/dev/null 2>&1 || fail "$bin_dir is not writable and sudo is unavailable; set CODEXOMETER_BIN_DIR to a writable directory"

install-release.sh:167

  • There is no check that the final target is not already a directory. On Unix, install treats a directory destination as a container and can place the candidate inside $target/codexometer, after which this script reports success for $target even though $target is still a directory. Fail before either the normal or sudo install branch when the target is a directory.
  install -m 0755 "$candidate" "$target"
  • Files reviewed: 13/13 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread README.md
Comment thread install-release.ps1 Outdated
Comment thread install-release.sh Outdated
Comment thread install-release.sh Outdated
Comment thread test/install-release.ps1 Outdated
@merefield
merefield merged commit dc4c50c into main Aug 23, 2026
6 checks passed
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.

2 participants