Skip to content

feat(self): replace GitHub release install with go install#92

Open
geowa4 wants to merge 4 commits into
openshift:mainfrom
geowa4:feat/go-install-self-update
Open

feat(self): replace GitHub release install with go install#92
geowa4 wants to merge 4 commits into
openshift:mainfrom
geowa4:feat/go-install-self-update

Conversation

@geowa4
Copy link
Copy Markdown
Contributor

@geowa4 geowa4 commented Apr 28, 2026

Summary

  • Replace the GitHub release-based self-install with go install github.com/openshift/backplane-tools@main
  • Add a new base.GoBin base type that handles the full install flow: go install, GOBIN resolution, commit hash extraction from binary build info, copy to backplane directory, and symlink
  • Self tool (pkg/tools/self/self.go) is now a minimal constructor embedding base.GoBin

Motivation: Maintainers aren't regularly creating GitHub releases, making self-updates via release assets unreliable. go install from main always reflects the latest source.

How it works:

  • Install() runs go install ...@main, copies the binary from GOBIN into ~/.local/bin/backplane/backplane-tools/<commit-hash>/, and symlinks to latest/
  • Version is the git commit hash extracted from the binary's embedded build info (vcs.revision) via debug/buildinfo
  • LatestVersion() returns "main" (never matches a commit hash), so upgrades always re-run go install
  • Requires Go on PATH; fails with a clear error if missing

Test plan

  • go vet ./... and go build ./... pass
  • backplane-tools install backplane-tools installs via go install and creates the expected directory structure
  • backplane-tools list installed shows the commit hash as the installed version
  • backplane-tools upgrade backplane-tools always re-installs from main
  • Without go on PATH, a clear error is shown

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a Go-based installer that builds Go modules, verifies embedded build metadata, installs binaries into versioned directories, and maintains a “latest” pointer.
  • Refactor

    • The self tool now uses the new Go-based installer; prior release/download-based installation logic was removed.

Maintainers aren't regularly creating GitHub releases, so self-updates
via release assets are unreliable. Switch to `go install` from the main
branch, which always reflects the latest source.

Add a new GoBin base type that runs `go install`, copies the resulting
binary from GOBIN into the backplane directory structure, and uses the
VCS commit hash embedded in the binary as the version identifier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 28, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: geowa4
Once this PR has been reviewed and has the lgtm label, please assign tnierman for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a2772269-7581-4583-8461-595d39c2e92a

📥 Commits

Reviewing files that changed from the base of the PR and between f16b886 and 0e28acd.

📒 Files selected for processing (1)
  • pkg/tools/base/gobin.go

Walkthrough

Adds an exported Go-based installer type GoBin that installs Go modules via go install, validates build info, stores binaries in versioned directories with a latest symlink, and refactors the self tool to embed GoBin (removing its previous GitHub-based install logic).

Changes

Cohort / File(s) Summary
Go Module Installer
pkg/tools/base/gobin.go
Add exported GoBin type with LatestVersion() and Install() that runs go install <Module>@<Branch>, resolves GOBIN/GOPATH/bin, verifies binary, reads debug/buildinfo to confirm module/version, copies binary into a versioned directory, and updates a latest symlink.
Self Tool Migration
pkg/tools/self/self.go
Replace embedded base.Github with base.GoBin configured with Module and Branch; remove custom Install() so installation is delegated to GoBin.

Sequence Diagram

sequenceDiagram
    participant User
    participant SelfTool as Self Tool
    participant GoBin as GoBin.Install
    participant GoCmd as go executable
    participant FS as Filesystem
    participant BuildInfo as debug/buildinfo

    User->>SelfTool: invoke Install()
    SelfTool->>GoBin: delegate Install()
    GoBin->>GoCmd: lookup 'go' executable
    GoCmd-->>GoBin: path to go
    GoBin->>GoCmd: run 'go install Module@Branch'
    GoCmd->>FS: compile & write binary to GOBIN/GOPATH/bin
    FS-->>GoCmd: binary created
    GoBin->>GoCmd: run 'go env GOBIN' / 'go env GOPATH'
    GoCmd-->>GoBin: output directory
    GoBin->>FS: verify binary exists
    FS-->>GoBin: binary confirmed
    GoBin->>BuildInfo: read debug/buildinfo from binary
    BuildInfo-->>GoBin: module path and version
    GoBin->>FS: create versioned dir, copy binary, set exec perms
    GoBin->>FS: remove old 'latest' and create new symlink to versioned binary
    FS-->>GoBin: filesystem ops complete
    GoBin-->>SelfTool: Install complete
    SelfTool-->>User: success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: replacing GitHub release-based installation with go install for the self tool. It is specific, descriptive, and directly reflected in the code changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Repository contains no Ginkgo test files, imports, or constructs. The PR modifies only regular Go implementation code with no tests.
Test Structure And Quality ✅ Passed No Ginkgo test code found in this PR; therefore no test quality requirements can be assessed.
Microshift Test Compatibility ✅ Passed This pull request does not add new Ginkgo e2e tests; it only modifies regular Go implementation files for a GoBin tool installer.
Single Node Openshift (Sno) Test Compatibility ✅ Passed This pull request does not add any Ginkgo e2e tests. The modified files are pure Go implementation code with no test code or Ginkgo test patterns.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces Go package code for local CLI tool installation via go install with no Kubernetes deployment manifests, operator code, controllers, or topology-aware scheduling constraints.
Ote Binary Stdout Contract ✅ Passed backplane-tools is a standalone CLI tool manager, not an OTE test runner, and does not implement OTE Binary Stdout Contract requirements.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The custom check for IPv6 and disconnected network test compatibility applies specifically to new Ginkgo e2e tests. This PR does not add any e2e tests; it only adds regular Go code implementing a tool installer and refactors existing code. Since no Ginkgo tests are introduced, the requirements do not apply.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Review rate limit: 8/10 reviews remaining, refill in 7 minutes and 34 seconds.

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

@geowa4 geowa4 marked this pull request as draft April 28, 2026 14:44
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 28, 2026
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/tools/base/gobin.go`:
- Around line 14-24: Default.InstalledVersion() is unreliable for GoBin because
GoBin.Install writes into a revision-specific directory; implement
GoBin.InstalledVersion() to return the actual installed vcs.revision instead of
inheriting Default behaviour by locating the installed binary directory used by
Install (use the same path logic as GoBin.Install), read the revision metadata
file that Install writes (or parse the revision from the directory name), and
return that revision string (ensure signature InstalledVersion() (string, error)
on type GoBin).
- Around line 82-100: The resolveGOBIN function currently treats the entire
GOPATH string as one path (variable gopath) and does filepath.Join(gopath,
"bin"), which breaks when GOPATH contains multiple entries; update resolveGOBIN
to call filepath.SplitList(gopath), select the first non-empty entry
(firstGOPATH := split[0] or iterate to find first non-empty), validate it is not
empty, and return filepath.Join(firstGOPATH, "bin") instead of joining the full
GOPATH string; keep existing error handling for exec.Command and use the same
variable names (gobin, gopath) so the change is minimal and localized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fc9b6608-ee3e-47a7-8419-a46ce94c23d8

📥 Commits

Reviewing files that changed from the base of the PR and between 289db43 and 944d5d6.

📒 Files selected for processing (2)
  • pkg/tools/base/gobin.go
  • pkg/tools/self/self.go

Comment thread pkg/tools/base/gobin.go
Comment on lines +14 to +24
type GoBin struct {
Default
Module string
Branch string
}

func (g *GoBin) LatestVersion() (string, error) {
return g.Branch, nil
}

func (g *GoBin) Install() error {
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Implement InstalledVersion() for GoBin.

GoBin now installs into a revision-specific directory, but it still inherits Default.InstalledVersion(), which scrapes a hash out of the binary bytes. That won't reliably return the vcs.revision you extract during install, so list installed and upgrade decisions can be wrong.

🔧 Proposed fix
 type GoBin struct {
 	Default
 	Module string
 	Branch string
 }
 
 func (g *GoBin) LatestVersion() (string, error) {
 	return g.Branch, nil
 }
+
+func (g *GoBin) InstalledVersion() (string, error) {
+	return extractVCSRevision(g.SymlinkPath())
+}
📝 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
type GoBin struct {
Default
Module string
Branch string
}
func (g *GoBin) LatestVersion() (string, error) {
return g.Branch, nil
}
func (g *GoBin) Install() error {
type GoBin struct {
Default
Module string
Branch string
}
func (g *GoBin) LatestVersion() (string, error) {
return g.Branch, nil
}
func (g *GoBin) InstalledVersion() (string, error) {
return extractVCSRevision(g.SymlinkPath())
}
func (g *GoBin) Install() error {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tools/base/gobin.go` around lines 14 - 24, Default.InstalledVersion() is
unreliable for GoBin because GoBin.Install writes into a revision-specific
directory; implement GoBin.InstalledVersion() to return the actual installed
vcs.revision instead of inheriting Default behaviour by locating the installed
binary directory used by Install (use the same path logic as GoBin.Install),
read the revision metadata file that Install writes (or parse the revision from
the directory name), and return that revision string (ensure signature
InstalledVersion() (string, error) on type GoBin).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Default.InstalledVersion() works correctly here — it extracts the version from the directory name in the symlink path (splits on path separator and takes element [1]). Since GoBin.Install() uses the module pseudo-version as the directory name, the inherited method returns exactly that pseudo-version. Verified with backplane-tools list installed showing v1.3.1-0.20260423145906-289db43d04d1.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the Default.InstalledVersion() implementation
rg -n "InstalledVersion" --type=go -A 10

Length of output: 3423


@geowa4, you're right — I misread the implementation. Default.InstalledVersion() resolves the latest/ symlink and extracts the version from the directory name in the resolved path, not from binary bytes. Since GoBin.Install() names the version directory after the module pseudo-version, the inherited method returns exactly that value. My review comment was a false positive. Thanks for the clarification and for verifying it end-to-end!


📝 Learnings were identified and not saved (knowledge base disabled). Enable

Comment thread pkg/tools/base/gobin.go Outdated
…builds

Binaries built via `go install module@branch` don't have VCS build info
embedded since they're built from module cache, not a git checkout. Use
the module pseudo-version from build info instead, which includes the
commit hash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
pkg/tools/base/gobin.go (1)

92-100: ⚠️ Potential issue | 🟡 Minor

Handle multi-entry GOPATH before appending /bin.

On Line 100, filepath.Join(gopath, "bin") treats a path list as a single directory. If GOPATH has multiple entries, binary discovery can fail after go install.

🛠️ Proposed fix
 func resolveGOBIN(goBin string) (string, error) {
 	out, err := exec.Command(goBin, "env", "GOBIN").Output()
 	if err != nil {
 		return "", fmt.Errorf("failed to run 'go env GOBIN': %w", err)
 	}
 	gobin := strings.TrimSpace(string(out))
 	if gobin != "" {
 		return gobin, nil
 	}

 	out, err = exec.Command(goBin, "env", "GOPATH").Output()
 	if err != nil {
 		return "", fmt.Errorf("failed to run 'go env GOPATH': %w", err)
 	}
 	gopath := strings.TrimSpace(string(out))
-	if gopath == "" {
+	entries := filepath.SplitList(gopath)
+	first := ""
+	for _, e := range entries {
+		if e != "" {
+			first = e
+			break
+		}
+	}
+	if first == "" {
 		return "", fmt.Errorf("both GOBIN and GOPATH are empty; cannot determine where 'go install' placed the binary")
 	}
-	return filepath.Join(gopath, "bin"), nil
+	return filepath.Join(first, "bin"), nil
 }
#!/bin/bash
# Verify resolveGOBIN still uses whole GOPATH instead of first entry.
sed -n '82,105p' pkg/tools/base/gobin.go
rg -n 'filepath\.SplitList|filepath\.Join\(gopath,\s*"bin"\)' pkg/tools/base/gobin.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tools/base/gobin.go` around lines 92 - 100, The code treats a possibly
multi-entry GOPATH as a single directory by doing filepath.Join(gopath, "bin");
update the function that computes the GOBIN fallback (the block that calls
exec.Command(goBin, "env", "GOPATH").Output() and uses variable gopath) to split
gopath with filepath.SplitList, pick the first non-empty entry (or return an
error if none), then return filepath.Join(firstEntry, "bin") so the binary path
uses the first GOPATH element rather than the whole path list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/tools/base/gobin.go`:
- Around line 92-100: The code treats a possibly multi-entry GOPATH as a single
directory by doing filepath.Join(gopath, "bin"); update the function that
computes the GOBIN fallback (the block that calls exec.Command(goBin, "env",
"GOPATH").Output() and uses variable gopath) to split gopath with
filepath.SplitList, pick the first non-empty entry (or return an error if none),
then return filepath.Join(firstEntry, "bin") so the binary path uses the first
GOPATH element rather than the whole path list.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9e6c55bc-342d-48f6-a194-f3e4a746c8a8

📥 Commits

Reviewing files that changed from the base of the PR and between 944d5d6 and ad531ab.

📒 Files selected for processing (1)
  • pkg/tools/base/gobin.go

@geowa4 geowa4 marked this pull request as ready for review April 29, 2026 00:33
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 29, 2026
@openshift-ci openshift-ci Bot requested a review from typeid April 29, 2026 00:33
geowa4 and others added 2 commits April 28, 2026 20:57
Satisfies the perfsprint linter rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use filepath.SplitList to split GOPATH and take the first entry, since
go install places binaries in the first GOPATH's bin directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
pkg/tools/base/gobin.go (1)

93-101: ⚠️ Potential issue | 🟡 Minor

Split GOPATH list entries before deriving bin path.

At Line 101, joining the raw GOPATH string with "bin" breaks when GOPATH has multiple entries. Use filepath.SplitList(gopath) and choose the first non-empty entry.

🛠️ Proposed fix
 out, err = exec.Command(goBin, "env", "GOPATH").Output()
 if err != nil {
 	return "", fmt.Errorf("failed to run 'go env GOPATH': %w", err)
 }
 gopath := strings.TrimSpace(string(out))
-if gopath == "" {
+entries := filepath.SplitList(gopath)
+first := ""
+for _, e := range entries {
+	if strings.TrimSpace(e) != "" {
+		first = e
+		break
+	}
+}
+if first == "" {
 	return "", errors.New("both GOBIN and GOPATH are empty; cannot determine where 'go install' placed the binary")
 }
-return filepath.Join(gopath, "bin"), nil
+return filepath.Join(first, "bin"), nil
#!/bin/bash
# Verify current implementation still joins full GOPATH and does not split list entries.
rg -n -C2 'go env GOPATH|filepath\.Join\(gopath,\s*"bin"\)|filepath\.SplitList\(gopath\)' pkg/tools/base/gobin.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tools/base/gobin.go` around lines 93 - 101, The code currently uses the
raw gopath string and returns filepath.Join(gopath, "bin"), which fails when
GOPATH contains multiple entries; instead call filepath.SplitList(gopath),
iterate the returned list to pick the first non-empty entry (e.g., first entry
from split), and then return filepath.Join(firstEntry, "bin"); update the logic
around the gopath variable and the final return accordingly (keep the existing
error handling when gopath is empty).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/tools/base/gobin.go`:
- Around line 93-101: The code currently uses the raw gopath string and returns
filepath.Join(gopath, "bin"), which fails when GOPATH contains multiple entries;
instead call filepath.SplitList(gopath), iterate the returned list to pick the
first non-empty entry (e.g., first entry from split), and then return
filepath.Join(firstEntry, "bin"); update the logic around the gopath variable
and the final return accordingly (keep the existing error handling when gopath
is empty).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6a03c5f8-68ae-4017-adc2-d0689748b131

📥 Commits

Reviewing files that changed from the base of the PR and between ad531ab and f16b886.

📒 Files selected for processing (1)
  • pkg/tools/base/gobin.go

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 29, 2026

@geowa4: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 64 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (289db43) to head (0e28acd).

Files with missing lines Patch % Lines
pkg/tools/base/gobin.go 0.00% 60 Missing ⚠️
pkg/tools/self/self.go 0.00% 4 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main     #92   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files         34      35    +1     
  Lines       1594    1607   +13     
=====================================
- Misses      1594    1607   +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dustman9000
Copy link
Copy Markdown
Member

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants