Skip to content

Add release automation and installation tooling#19

Merged
jfox85 merged 3 commits intomainfrom
codex/add-release-process-for-devx
Oct 1, 2025
Merged

Add release automation and installation tooling#19
jfox85 merged 3 commits intomainfrom
codex/add-release-process-for-devx

Conversation

@jfox85
Copy link
Owner

@jfox85 jfox85 commented Oct 1, 2025

Summary

  • add a GoReleaser configuration and GitHub Actions workflow to build tagged releases and update the Homebrew tap
  • add a universal install script and HTML redirect to support curl-based installs and binary downloads
  • document Homebrew, go install, curl script, direct download, and source build options in the README

Testing

  • gofmt -w .
  • go vet ./...
  • golangci-lint run --timeout=5m (fails: existing errcheck/staticcheck issues)
  • go test -v -race ./...
  • go mod tidy

https://chatgpt.com/codex/tasks/task_e_68da029468e483339cc24643d7d93332

Summary by CodeRabbit

  • New Features

    • Added cross-platform install script, install redirect page, and prebuilt binaries for macOS and Linux.
    • Added Homebrew tap support for streamlined macOS installation.
  • Documentation

    • Expanded README with comprehensive installation options: Homebrew, go install, install script, direct download, and build-from-source.
    • Renamed and clarified the alternative build targets section.
  • Chores

    • Added automated release and packaging workflow using GoReleaser and GitHub Actions.

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

Walkthrough

Adds a GitHub Actions release workflow and GoReleaser config, provides an install script and HTML redirect, and expands README installation instructions. The workflow triggers on tag pushes (v*), builds releases via Goreleaser, and the installer fetches and installs release binaries.

Changes

Cohort / File(s) Summary
CI/CD Workflow
.github/workflows/release.yml
New GitHub Actions workflow triggered on tag pushes v*; checks out repo, sets up Go and module cache, installs goreleaser action, and runs goreleaser release --clean with GITHUB_TOKEN.
Release Packaging
.goreleaser.yaml
New GoReleaser configuration for devx: builds darwin/linux (amd64, arm64), static CGO off, embeds version metadata, creates tar.gz archives, generates checksums, uses git changelog, defines Homebrew tap, and skips announce.
Installer Assets
install/install.sh, install/install-redirect.html
New installer script: detects OS/arch, resolves version (latest or specified), downloads devx_<version>_<os>_<arch>.tar.gz, extracts and installs binary to target dir; HTML file redirects to latest install script.
Documentation
README.md
Expanded Installation section with Homebrew, go install, universal install script, direct binary download, and build-from-source instructions; renamed a section to “Alternative build targets.”

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant GH as GitHub
  participant GA as GitHub Actions
  participant GR as GoReleaser

  Dev->>GH: Push tag vX.Y.Z
  GH-->>GA: Trigger release workflow (on tag v*)
  GA->>GA: Checkout repo (fetch-depth: 0)
  GA->>GA: Setup Go (version from go.mod)
  GA->>GA: Restore/setup Go module cache
  GA->>GR: Install goreleaser action
  GA->>GR: Run `goreleaser release --clean` (GITHUB_TOKEN)
  GR-->>GH: Create GitHub release & upload artifacts
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant SH as install.sh
  participant GH as GitHub Releases
  participant OS as Local System

  U->>SH: Execute install.sh (DEVX_VERSION?, DEVX_INSTALL_DIR?)
  SH->>SH: Detect OS & arch, normalize inputs
  alt DEVX_VERSION == "latest"
    SH->>GH: Query latest tag
    GH-->>SH: vX.Y.Z
  end
  SH->>GH: Download release tarball `devx_<ver>_<os>_<arch>.tar.gz`
  GH-->>SH: Return tarball
  SH->>SH: Extract binary to temp dir
  SH->>OS: Create install dir and install binary (0755)
  SH-->>U: Print installed path and verify version
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop where tags and changelogs meet,
I fetch the builds and make them neat.
A script, a tap, a tarball cheer—
I leave a binary on your tier.
Hooves of code, I stamp and steer. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the core focus of the changeset by highlighting both the addition of release automation and the new installation tooling, which align directly with the workflows, GoReleaser configuration, install scripts, and documentation introduced in this pull request.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/add-release-process-for-devx

Comment @coderabbitai help to get the list of available commands and usage tips.

@jfox85
Copy link
Owner Author

jfox85 commented Oct 1, 2025

@coderabbitai help

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@jfox85
Copy link
Owner Author

jfox85 commented Oct 1, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43e89d3 and 1796da8.

📒 Files selected for processing (5)
  • .github/workflows/release.yml (1 hunks)
  • .goreleaser.yaml (1 hunks)
  • README.md (2 hunks)
  • install/install-redirect.html (1 hunks)
  • install/install.sh (1 hunks)

Comment on lines 42 to 59
brews:
- name: devx
homepage: https://github.com/jfox85/devx
description: "A macOS development environment manager"
license: "MIT"
commit_author:
name: toneclone-bot
email: opensource@toneclone.dev
tap:
owner: toneclone
name: toneclone
test: |
system "#{bin}/devx", "--version"
install: |
bin.install "devx"
dependencies:
- name: go
type: optional
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Point the Homebrew tap at the correct repository

Lines 51‑52 set the tap to toneclone/toneclone, but the Homebrew tap repo is typically toneclone/homebrew-toneclone (matching the documented brew tap toneclone/toneclone, which strips the homebrew- prefix). GoReleaser expects the actual GitHub repo name here; with the current value it will try to push the formula to github.com/toneclone/toneclone, so tap updates will fail. Update the tap name to the real repo.

   brews:
     - name: devx
       homepage: https://github.com/jfox85/devx
       description: "A macOS development environment manager"
       license: "MIT"
       commit_author:
         name: toneclone-bot
         email: opensource@toneclone.dev
       tap:
         owner: toneclone
-        name: toneclone
+        name: homebrew-toneclone
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
brews:
- name: devx
homepage: https://github.com/jfox85/devx
description: "A macOS development environment manager"
license: "MIT"
commit_author:
name: toneclone-bot
email: opensource@toneclone.dev
tap:
owner: toneclone
name: toneclone
test: |
system "#{bin}/devx", "--version"
install: |
bin.install "devx"
dependencies:
- name: go
type: optional
brews:
- name: devx
homepage: https://github.com/jfox85/devx
description: "A macOS development environment manager"
license: "MIT"
commit_author:
name: toneclone-bot
email: opensource@toneclone.dev
tap:
owner: toneclone
name: homebrew-toneclone
test: |
system "#{bin}/devx", "--version"
install: |
bin.install "devx"
dependencies:
- name: go
type: optional
🤖 Prompt for AI Agents
.goreleaser.yaml lines 42 to 59: the Homebrew tap is incorrectly set to
toneclone/toneclone so GoReleaser will push the formula to
github.com/toneclone/toneclone; change the tap name to the actual repository
name by setting tap.owner to toneclone and tap.name to homebrew-toneclone (so
GoReleaser targets github.com/toneclone/homebrew-toneclone) and keep the rest of
the brew config as-is.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (8)
install/install.sh (5)

18-19: Add dependency check for install.

Script uses install but only checks for curl/tar.

 command -v curl >/dev/null 2>&1 || { echo "curl is required to install devx" >&2; exit 1; }
 command -v tar >/dev/null 2>&1 || { echo "tar is required to install devx" >&2; exit 1; }
+command -v install >/dev/null 2>&1 || { echo "install is required to install devx" >&2; exit 1; }

77-79: Preflight writable install dir; guide user if not.

Avoid silent failures when INSTALL_DIR isn’t writable (common for /usr/local/bin).

-mkdir -p "${INSTALL_DIR}"
-install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"
+mkdir -p "${INSTALL_DIR}"
+if [[ ! -w "${INSTALL_DIR}" ]]; then
+  echo "No write access to ${INSTALL_DIR}. Re-run with sudo or set DEVX_INSTALL_DIR (e.g., \$HOME/.local/bin)." >&2
+  exit 1
+fi
+install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"

51-56: Handle GitHub API rate limits and add a fallback.

Unauthenticated calls can hit rate limits. Allow using GITHUB_TOKEN and add a fallback to the releases/latest redirect.

-if [[ "$VERSION" == "latest" ]]; then
-  VERSION="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | \
+if [[ "$VERSION" == "latest" ]]; then
+  auth_hdr=()
+  [[ -n "${GITHUB_TOKEN:-}" ]] && auth_hdr=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
+  VERSION="$(curl -fsSL "${auth_hdr[@]}" "https://api.github.com/repos/${REPO}/releases/latest" | \
     grep -m1 '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')"
   if [[ -z "$VERSION" ]]; then
-    echo "Unable to determine latest release version" >&2
-    exit 1
+    # Fallback: follow redirect to tag URL and extract the tag
+    VERSION="$(curl -fsI "https://github.com/${REPO}/releases/latest" | tr -d '\r' | awk -F/ '/^location: /{print $NF}' )"
+    [[ -z "$VERSION" ]] && { echo "Unable to determine latest release version" >&2; exit 1; }
   fi
 fi

71-73: Harden downloads with retries and clearer failures.

Improve resilience and error messages.

-echo "Downloading ${asset}..."
-curl -fsSL "${url}" -o "${workdir}/${asset}"
+echo "Downloading ${asset}..."
+curl --fail --location --retry 3 --retry-delay 1 --connect-timeout 10 \
+  --output "${workdir}/${asset}" "${url}"

71-79: Optional: Verify archive integrity using GoReleaser checksums.

If you publish *_checksums.txt, verify before installing.

 echo "Downloading ${asset}..."
 curl --fail --location --retry 3 --retry-delay 1 --connect-timeout 10 \
   --output "${workdir}/${asset}" "${url}"
+
+# Optional integrity verification (requires published checksums file)
+checksums="devx_${asset_version}_checksums.txt"
+checksums_url="https://github.com/${REPO}/releases/download/${VERSION}/${checksums}"
+if curl -fsSL "${checksums_url}" -o "${workdir}/${checksums}"; then
+  (cd "${workdir}" && grep " ${asset}\$" "${checksums}" | shasum -a 256 -c -) || {
+    echo "Checksum verification failed for ${asset}" >&2
+    exit 1
+  }
+fi
README.md (3)

49-60: Add a pinned install example and sudo guidance.

For reproducibility and least surprise:

  • Show a pinned install using a tagged script URL.
  • Note sudo or alternate INSTALL_DIR when writing to /usr/local/bin.
 ### Universal install script
 ```bash
-curl -fsSL https://raw.githubusercontent.com/jfox85/devx/main/install/install.sh | bash
+curl -fsSL https://raw.githubusercontent.com/jfox85/devx/main/install/install.sh | bash
+# Pinned to a specific version (reproducible):
+# curl -fsSL https://raw.githubusercontent.com/jfox85/devx/v1.2.3/install/install.sh | bash

Set DEVX_INSTALL_DIR or DEVX_VERSION to customize the installation:

curl -fsSL https://raw.githubusercontent.com/jfox85/devx/main/install/install.sh \
  | DEVX_INSTALL_DIR=$HOME/.local/bin DEVX_VERSION=v1.2.3 bash
+# If installing to /usr/local/bin and you lack write permission, use:
+# curl -fsSL .../install.sh | sudo DEVX_VERSION=v1.2.3 bash

62-66: Clarify archive naming for direct downloads.

Mention the archive pattern to help users pick the right file.

-Download the appropriate archive from the [releases page](https://github.com/jfox85/devx/releases), extract it, and move the
-`devx` binary into your `PATH`.
+Download the appropriate archive from the [releases page](https://github.com/jfox85/devx/releases)  
+(format: `devx_<version>_<os>_<arch>.tar.gz`, e.g., `devx_1.2.3_darwin_arm64.tar.gz`), extract it, and move the `devx` binary into your `PATH`.

36-41: Homebrew tip (optional).

Consider adding brew update before install for first-time tap users.

 brew tap jfox85/homebrew-devx
+brew update
 brew install devx
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1796da8 and 76653d9.

📒 Files selected for processing (3)
  • .goreleaser.yaml (1 hunks)
  • README.md (2 hunks)
  • install/install.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .goreleaser.yaml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build
  • GitHub Check: Go Vet
  • GitHub Check: Test Windows
  • GitHub Check: Test (1.23)
  • GitHub Check: Build (linux, arm64)
🔇 Additional comments (1)
install/install.sh (1)

64-66: Asset name construction fix looks good.

Stripping the leading “v” before composing the archive name resolves the 404 from mismatched asset names. Nice catch.

Comment on lines +74 to +79
echo "Extracting devx..."
tar -xzf "${workdir}/${asset}" -C "${workdir}"

mkdir -p "${INSTALL_DIR}"
install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Don’t assume archive layout; make extraction robust.

If the archive contains a leading directory (common with GoReleaser), ${workdir}/devx won’t exist and install will fail. Locate the binary after extraction.

Apply:

-echo "Extracting devx..."
-tar -xzf "${workdir}/${asset}" -C "${workdir}"
-
-mkdir -p "${INSTALL_DIR}"
-install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"
+echo "Extracting devx..."
+tar -xzf "${workdir}/${asset}" -C "${workdir}"
+# Find the devx binary regardless of archive layout (flat or nested)
+bin_path="$(cd "${workdir}" && find . -type f -name devx -perm -u+x -print -quit)"
+if [[ -z "${bin_path}" ]]; then
+  echo "devx binary not found in archive" >&2
+  exit 1
+fi
+mkdir -p "${INSTALL_DIR}"
+install -m 0755 "${workdir}/${bin_path#./}" "${INSTALL_DIR}/devx"

If your GoReleaser archives are guaranteed flat, confirm and we can keep the current approach. Otherwise, this makes installs resilient.
Based on learnings


🏁 Script executed:

#!/bin/bash
grep -R -n "asset=" install/install.sh
sed -n '1,200p' install/install.sh
grep -R -n "GoReleaser" .

Length of output: 2069


Don’t assume flat archive layout; locate the binary dynamically

GoReleaser’s tar.gz archives include a top-level directory by default, so ${workdir}/devx may not exist. Replace the hardcoded path with a find-and-install approach:

-echo "Extracting devx..."
-tar -xzf "${workdir}/${asset}" -C "${workdir}"
-
-mkdir -p "${INSTALL_DIR}"
-install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"
+echo "Extracting devx..."
+tar -xzf "${workdir}/${asset}" -C "${workdir}"
+# Find the devx binary regardless of archive layout
+bin_path="$(cd "${workdir}" && find . -type f -name devx -perm -u+x -print -quit)"
+if [[ -z "${bin_path}" ]]; then
+  echo "devx binary not found in archive" >&2
+  exit 1
+fi
+mkdir -p "${INSTALL_DIR}"
+install -m 0755 "${workdir}/${bin_path#./}" "${INSTALL_DIR}/devx"

install/install.sh:74-79

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo "Extracting devx..."
tar -xzf "${workdir}/${asset}" -C "${workdir}"
mkdir -p "${INSTALL_DIR}"
install -m 0755 "${workdir}/devx" "${INSTALL_DIR}/devx"
echo "Extracting devx..."
tar -xzf "${workdir}/${asset}" -C "${workdir}"
# Find the devx binary regardless of archive layout
bin_path="$(cd "${workdir}" && find . -type f -name devx -perm -u+x -print -quit)"
if [[ -z "${bin_path}" ]]; then
echo "devx binary not found in archive" >&2
exit 1
fi
mkdir -p "${INSTALL_DIR}"
install -m 0755 "${workdir}/${bin_path#./}" "${INSTALL_DIR}/devx"
🤖 Prompt for AI Agents
In install/install.sh around lines 74 to 79, the script assumes a flat archive
and uses "${workdir}/devx" which fails when the tarball contains a top-level
directory; change the install step to search the extracted workdir for the devx
binary (e.g., find under "${workdir}" for an executable file named "devx" or
matching pattern, take the first match) and then use that discovered path with
install -m 0755 into "${INSTALL_DIR}/devx" so the script works regardless of
archive layout.

@jfox85 jfox85 merged commit 76653d9 into main Oct 1, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant