Skip to content

Verify update release assets#84

Merged
anandh8x merged 1 commit into
mainfrom
feat/update-release-verification
Jun 6, 2026
Merged

Verify update release assets#84
anandh8x merged 1 commit into
mainfrom
feat/update-release-verification

Conversation

@anandh8x

@anandh8x anandh8x commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • parse latest-release assets and validate the expected platform archive plus matching .sha256 file
  • add releaseAsset details to zero update --check JSON/text output
  • document that update checks now validate release archive metadata

Scope

This is metadata verification only. It does not download release assets or replace the local binary.

Tests

  • GOCACHE=/tmp/zero-go-cache go test ./internal/update -run 'Normalize|CheckReportsAvailableUpdate|CheckReturnsFetchError|CheckRespectsContextCancellation|CheckRejectsNegativeTimeout|CheckRejectsMissingTagName|CheckRejectsInvalidLatestVersion|CheckFallsBackReleaseURL|CheckFetchesDataEndpoint|CheckRejectsMissingReleaseAssets|ExpectedAssetCheck|ResolveEndpoint|FormatResult'
  • GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run Update
  • go test ./...
  • env GOCACHE=/tmp/zero-go-cache ZERO_UPDATE_RELEASE_URL='data:application/json,%7B%22tag_name%22%3A%22v0.2.0%22%2C%22html_url%22%3A%22https%3A%2F%2Fexample.test%2Frelease%22%2C%22assets%22%3A%5B%7B%22name%22%3A%22zero-v0.2.0-linux-x64.tar.gz%22%2C%22browser_download_url%22%3A%22https%3A%2F%2Fexample.test%2Fzero-v0.2.0-linux-x64.tar.gz%22%7D%2C%7B%22name%22%3A%22zero-v0.2.0-linux-x64.tar.gz.sha256%22%2C%22browser_download_url%22%3A%22https%3A%2F%2Fexample.test%2Fzero-v0.2.0-linux-x64.tar.gz.sha256%22%7D%5D%7D' go run -ldflags '-X github.com/Gitlawb/zero/internal/cli.version=0.1.0' ./cmd/zero update --check --json

Summary by CodeRabbit

  • New Features

    • update --check now validates platform-specific release assets (archive and checksum) before reporting available updates.
    • Release asset details (filename and checksum information) are displayed in command output and JSON responses.
  • Documentation

    • Clarified update --check return codes and release validation requirements.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Typecheck: bun run typecheck
  • [pass] Tests: bun run test
  • [pass] Build: bun run build
  • [pass] Smoke build: bun run smoke:build

Scope

Head: 3833c3343f98
Changed files (4): docs/UPDATE.md, internal/cli/app_test.go, internal/update/update.go, internal/update/update_test.go

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Too much diff to scan? Review this PR in Change Stack to start with the highest-impact changes.

Review Change Stack

Walkthrough

The PR implements platform-specific release asset validation for the update checker. It adds GOOS/GOARCH configuration to Options, extends Result with ReleaseAsset tracking, validates that GitHub releases contain expected archive and checksum assets before reporting updates, and updates output formatting and test coverage to reflect the richer asset metadata.

Changes

Release Asset Validation

Layer / File(s) Summary
Data model and platform configuration
internal/update/update.go
AssetCheck struct tracks archive/checksum names, URLs, and found/verified status. Result now includes ReleaseAsset field. Options gains GOOS/GOARCH overrides. Runtime import added for default platform detection.
Asset verification and expected-asset computation
internal/update/update.go
verifyReleaseAssets scans release assets and marks presence/URLs. expectedAssetCheck derives platform/arch-specific archive and checksum names (handling extension differences). releasePlatform/releaseArch validate and map platform strings. Asset verification integrated into Check before semver comparison. Result populated with computed ReleaseAsset.
Output formatting with asset details
internal/update/update.go
Format appends asset information via appendAssetLines for both update-available and up-to-date branches. Output now shows archive and checksum asset details consistently.
Core asset verification and format tests
internal/update/update_test.go
TestCheckReportsAvailableUpdate and TestCheckFallsBackReleaseURL updated to use platform-specific options and releaseForTarget helper. TestCheckRejectsMissingReleaseAssets validates error handling for missing assets. TestExpectedAssetCheckUsesInstallerArchiveNames table-driven test covers platform-specific archive naming. TestFormatResult assertions expanded for asset line presence. releaseForTarget and assetCheckForTest helpers added for fixture construction.
CLI integration tests and JSON serialization
internal/cli/app_test.go
TestRunUpdateCheckTextAndJSON mock fixture extended with ReleaseAsset fields. Stdout assertions added for asset filenames. JSON payload struct expanded to include releaseAsset object with platform/arch and archive/checksum details. JSON assertions verify archiveName, checksumName, and verified match result.
Documentation updates
docs/UPDATE.md
Clarified that release validation requires platform-specific archive and checksum assets. Documented Options.Endpoint resolution order for update.Check code-based calls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Verify update release assets' directly and clearly summarizes the main change: adding validation of platform-specific release assets before reporting an update.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/update-release-verification

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

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

🧹 Nitpick comments (4)
docs/UPDATE.md (1)

20-20: ⚡ Quick win

Inconsistent capitalization of Options.Endpoint.

Line 16 uses Options.Endpoint (capitalized, suggesting the Go struct field), but this line uses options.endpoint (lowercase). For consistency and clarity, use the same capitalization as line 16.

📝 Proposed consistency fix
-`options.endpoint` and `ZERO_UPDATE_RELEASE_URL` may be a full URL or an `owner/repo` slug.
+`Options.Endpoint` and `ZERO_UPDATE_RELEASE_URL` may be a full URL or an `owner/repo` slug.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/UPDATE.md` at line 20, The text is inconsistent: change the lowercase
`options.endpoint` to match the earlier capitalization `Options.Endpoint` for
consistency with the Go struct reference; update the sentence so it reads using
`Options.Endpoint` (and leave `ZERO_UPDATE_RELEASE_URL` as-is) to match line
16's usage of `Options.Endpoint`.
internal/update/update_test.go (3)

73-75: ⚡ Quick win

Split compound assertion for better test failure diagnostics.

The single assertion checking multiple ReleaseAsset fields makes it difficult to identify which specific field caused a failure. Consider splitting into separate assertions:

-	if !result.ReleaseAsset.Verified || result.ReleaseAsset.ArchiveName != "zero-v0.2.0-linux-x64.tar.gz" || result.ReleaseAsset.ChecksumName != "zero-v0.2.0-linux-x64.tar.gz.sha256" {
-		t.Fatalf("unexpected release asset check: %#v", result.ReleaseAsset)
-	}
+	if !result.ReleaseAsset.Verified {
+		t.Fatalf("ReleaseAsset.Verified = false, want true")
+	}
+	if result.ReleaseAsset.ArchiveName != "zero-v0.2.0-linux-x64.tar.gz" {
+		t.Fatalf("ReleaseAsset.ArchiveName = %q, want zero-v0.2.0-linux-x64.tar.gz", result.ReleaseAsset.ArchiveName)
+	}
+	if result.ReleaseAsset.ChecksumName != "zero-v0.2.0-linux-x64.tar.gz.sha256" {
+		t.Fatalf("ReleaseAsset.ChecksumName = %q, want zero-v0.2.0-linux-x64.tar.gz.sha256", result.ReleaseAsset.ChecksumName)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/update/update_test.go` around lines 73 - 75, Split the compound
assertion that checks result.ReleaseAsset.Verified,
result.ReleaseAsset.ArchiveName and result.ReleaseAsset.ChecksumName into three
separate assertions so failures show which field failed; specifically replace
the single if that Fatalfs with three checks that call t.Fatalf (or t.Errorf)
with clear messages referencing each symbol (result.ReleaseAsset.Verified,
result.ReleaseAsset.ArchiveName, result.ReleaseAsset.ChecksumName) and the
expected values ("true", "zero-v0.2.0-linux-x64.tar.gz",
"zero-v0.2.0-linux-x64.tar.gz.sha256").

174-174: ⚡ Quick win

Consider improving JSON payload readability.

The URL-encoded JSON payload is difficult to read and maintain. Consider using a helper function or multi-line raw string:

Optional refactor for readability
+	releaseJSON := `{
+		"tag_name": "v0.2.0",
+		"html_url": "https://example.test/release",
+		"assets": [
+			{"name": "zero-v0.2.0-linux-x64.tar.gz", "browser_download_url": "https://example.test/zero-v0.2.0-linux-x64.tar.gz"},
+			{"name": "zero-v0.2.0-linux-x64.tar.gz.sha256", "browser_download_url": "https://example.test/zero-v0.2.0-linux-x64.tar.gz.sha256"}
+		]
+	}`
+	payload := url.QueryEscape(releaseJSON)
-	payload := url.QueryEscape(`{"tag_name":"v0.2.0","html_url":"https://example.test/release","assets":[{"name":"zero-v0.2.0-linux-x64.tar.gz","browser_download_url":"https://example.test/zero-v0.2.0-linux-x64.tar.gz"},{"name":"zero-v0.2.0-linux-x64.tar.gz.sha256","browser_download_url":"https://example.test/zero-v0.2.0-linux-x64.tar.gz.sha256"}]}`)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/update/update_test.go` at line 174, Replace the single-line,
URL-encoded JSON literal used to build payload in the test with a readable
multi-line raw string or a small helper function; e.g., extract the JSON into a
variable or helper (e.g., releaseJSON or buildReleasePayload) and then call
url.QueryEscape(releaseJSON) where payload is assigned, keeping the final value
assigned to payload via url.QueryEscape(...) so tests behave the same but the
JSON is easy to read and maintain.

258-258: ⚡ Quick win

Split compound assertion for clearer test failure diagnostics.

Similar to TestCheckReportsAvailableUpdate, this compound assertion makes it difficult to identify which field failed:

-			if check.ArchiveName != tt.archiveName || check.ChecksumName != tt.archiveName+".sha256" || check.Platform != tt.platform || check.Arch != tt.arch {
-				t.Fatalf("expectedAssetCheck = %#v, want archive %s", check, tt.archiveName)
-			}
+			if check.ArchiveName != tt.archiveName {
+				t.Errorf("ArchiveName = %q, want %q", check.ArchiveName, tt.archiveName)
+			}
+			if check.ChecksumName != tt.archiveName+".sha256" {
+				t.Errorf("ChecksumName = %q, want %q", check.ChecksumName, tt.archiveName+".sha256")
+			}
+			if check.Platform != tt.platform {
+				t.Errorf("Platform = %q, want %q", check.Platform, tt.platform)
+			}
+			if check.Arch != tt.arch {
+				t.Errorf("Arch = %q, want %q", check.Arch, tt.arch)
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/update/update_test.go` at line 258, The test currently uses a single
compound condition comparing check.ArchiveName, check.ChecksumName,
check.Platform, and check.Arch to tt.archiveName/tt.platform/tt.arch which hides
which field failed; update the assertion in Test (file
internal/update/update_test.go) to split into individual checks—one assertion
each for check.ArchiveName == tt.archiveName, check.ChecksumName ==
tt.archiveName+".sha256", check.Platform == tt.platform, and check.Arch ==
tt.arch—using t.Errorf or t.Fatalf with concise messages naming the expected and
actual values so failures indicate exactly which field mismatched.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/UPDATE.md`:
- Line 20: The text is inconsistent: change the lowercase `options.endpoint` to
match the earlier capitalization `Options.Endpoint` for consistency with the Go
struct reference; update the sentence so it reads using `Options.Endpoint` (and
leave `ZERO_UPDATE_RELEASE_URL` as-is) to match line 16's usage of
`Options.Endpoint`.

In `@internal/update/update_test.go`:
- Around line 73-75: Split the compound assertion that checks
result.ReleaseAsset.Verified, result.ReleaseAsset.ArchiveName and
result.ReleaseAsset.ChecksumName into three separate assertions so failures show
which field failed; specifically replace the single if that Fatalfs with three
checks that call t.Fatalf (or t.Errorf) with clear messages referencing each
symbol (result.ReleaseAsset.Verified, result.ReleaseAsset.ArchiveName,
result.ReleaseAsset.ChecksumName) and the expected values ("true",
"zero-v0.2.0-linux-x64.tar.gz", "zero-v0.2.0-linux-x64.tar.gz.sha256").
- Line 174: Replace the single-line, URL-encoded JSON literal used to build
payload in the test with a readable multi-line raw string or a small helper
function; e.g., extract the JSON into a variable or helper (e.g., releaseJSON or
buildReleasePayload) and then call url.QueryEscape(releaseJSON) where payload is
assigned, keeping the final value assigned to payload via url.QueryEscape(...)
so tests behave the same but the JSON is easy to read and maintain.
- Line 258: The test currently uses a single compound condition comparing
check.ArchiveName, check.ChecksumName, check.Platform, and check.Arch to
tt.archiveName/tt.platform/tt.arch which hides which field failed; update the
assertion in Test (file internal/update/update_test.go) to split into individual
checks—one assertion each for check.ArchiveName == tt.archiveName,
check.ChecksumName == tt.archiveName+".sha256", check.Platform == tt.platform,
and check.Arch == tt.arch—using t.Errorf or t.Fatalf with concise messages
naming the expected and actual values so failures indicate exactly which field
mismatched.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e59e1d4-611d-4ab9-af12-035ee3a6ebd2

📥 Commits

Reviewing files that changed from the base of the PR and between 1974a19 and 3833c33.

📒 Files selected for processing (4)
  • docs/UPDATE.md
  • internal/cli/app_test.go
  • internal/update/update.go
  • internal/update/update_test.go

@gnanam1990 gnanam1990 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the release-asset verification changes. The expected archive/checksum naming matches the current installer and package-release contract for linux, macOS, and Windows, and the CLI output/JSON coverage exercises the new releaseAsset payload.

Validation run locally:

  • GOCACHE=/tmp/zero-go-cache go test ./internal/update -run 'Normalize|CheckReportsAvailableUpdate|CheckReturnsFetchError|CheckRespectsContextCancellation|CheckRejectsNegativeTimeout|CheckRejectsMissingTagName|CheckRejectsInvalidLatestVersion|CheckFallsBackReleaseURL|CheckFetchesDataEndpoint|CheckRejectsMissingReleaseAssets|ExpectedAssetCheck|ResolveEndpoint|FormatResult'
  • GOCACHE=/tmp/zero-go-cache go test ./internal/cli -run Update
  • env GOCACHE=/tmp/zero-go-cache ZERO_UPDATE_RELEASE_URL=<macos-arm64 data release> go run -ldflags '-X github.com/Gitlawb/zero/internal/cli.version=0.1.0' ./cmd/zero update --check --json
  • git diff --check origin/main...HEAD
  • GOCACHE=/tmp/zero-go-cache go test ./...

One broad-suite run initially timed out in existing provider-command config tests, but the exact failing tests passed in isolation and the full suite passed on rerun. CI is also green across Ubuntu, macOS, Windows, Performance Smoke, Zero Review, and CodeRabbit.

@anandh8x
anandh8x merged commit 04808f6 into main Jun 6, 2026
6 checks passed
@Vasanthdev2004

Copy link
Copy Markdown
Collaborator

Blockers

None found.

Non-Blocking

  • None.

Looks Good

  • Clean update-check metadata guard: latest releases now have to expose both the expected platform archive and matching .sha256 before reporting an available update.
  • Asset naming matches the existing release/install contract across Linux, macOS, and Windows, and the JSON/text output exposes the verified asset details without downloading or replacing binaries.
  • The tests cover missing assets, data endpoints, CLI formatting, and platform/archive naming boundaries.
  • Validation passed locally: targeted go test for internal/update and update CLI paths, go test ./..., go build ./cmd/zero, Windows-targeted zero update --check --json data-endpoint smoke, bun install --frozen-lockfile, bun run typecheck, bun test ./tests --timeout 15000, bun run build, bun run smoke:build, and git diff --check origin/main...HEAD.

Verdict: Approve — Clean release-asset verification for update checks, aligned with the current archive/checksum naming contract.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved: clean release-asset verification aligned with the archive/checksum naming contract.

@gnanam1990

Copy link
Copy Markdown
Collaborator

Review: PR #84 — Verify update release assets (anandh8x / Anandan)

URL: #84 (MERGED)
Author: anandh8x
Changes: +291 / -26
Review focus: Ownership, runnable behavior, tests (post-merge spot check on main)

Ownership & Split Compliance

Direct hit on Anandan's explicit lane:

Anandan: feat/update-release-verification

  • zero update verification, release metadata validation, and install smoke tests.
  • Package and binary checksums.
  • "CI as product verification", "release trust".

Also fulfills the "Platform/release contract" DRI and the rule that his PRs must include "user-runnable command behavior" or "install/update/release artifact verification".

What Landed

  • internal/update now parses latest-release JSON for assets.
  • Validates presence of platform-specific archive + matching .sha256 (e.g. zero-vX.Y.Z-linux-x64.tar.gz + .sha256).
  • update --check (text + --json) now includes richer releaseAsset / checksum info in output.
  • New tests exercising happy path, missing assets, negative cases, context cancellation, custom endpoint via ZERO_UPDATE_RELEASE_URL=data:....
  • Runnable demo in PR body: go run ... ./cmd/zero update --check --json against synthetic release data.

No change to actual download/replace logic — this is metadata verification only (correct scope for "trust" without full updater yet).

Quality Notes (on current main)

  • Good use of table-driven / specific test names matching the cases called out (CheckRejectsMissingReleaseAssets, ExpectedAssetCheck, etc.).
  • Data-URL trick for hermetic tests is pragmatic.
  • Output now gives users (and CI) something concrete to assert on for release trust.
  • Integrates with the broader "release metadata validation" + checksum story (pairs with Move release packaging to Go #85 release packaging / checksums move to Go and feat: add M2 release checksum verification #25 earlier checksum work).

Minor Observations

  • The asset name matching logic (platform-specific) should be robust across the supported GOOS/GOARCH combos in the release matrix. Worth a comment or small table in docs/UPDATE.md or PERFORMANCE/NPM smoke if not already.
  • Since this touches the update path consumed by CI and users, any future change to the event or error surface here should coordinate with stream-json (Gnanam) and TUI display (Vasanth).

Verdict (Post-Merge)

Well executed, exactly the platform product behavior required by the split.

This (together with #81 sandbox platform report, #86/#85 build/release Go moves, #75 PR automation) shows Anandan delivering on the "not only docs/CI" rule.

If any follow-up (e.g. wiring the checksum verification into actual update apply path or stronger signature checks), it should stay in his lane.

No blocking issues observed on main.


Non-gnanam1990 PR review. See sibling reviews for open work and other platform PRs.
File: reviews/pr-84-anandan-update-verification.md

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.

3 participants