Skip to content

feat(release): bootstrap first-time winget submissions - #28

Merged
rianjs merged 4 commits into
mainfrom
feat/27-winget-first-submission-bootstrap
Jun 4, 2026
Merged

feat(release): bootstrap first-time winget submissions#28
rianjs merged 4 commits into
mainfrom
feat/27-winget-first-submission-bootstrap

Conversation

@rianjs

@rianjs rianjs commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a reusable winget-submit action that chooses update vs first-submission mode based on microsoft/winget-pkgs package existence
  • add packages.winget.bootstrap to normalized identity output with boolean validation
  • wire the reusable release workflow to pass separate GitHub and winget submission tokens

Tests

  • uv run --with pytest>=8,<9 --with PyYAML==6.0.2 --with defusedxml==0.7.1 python -m pytest -q actions/identity-check actions/winget-submit
  • uv run --with pytest>=8,<9 --with PyYAML==6.0.2 --with defusedxml==0.7.1 python -m pytest -q actions/identity-check
  • uv run --with pytest>=8,<9 --with PyYAML==6.0.2 python -m pytest -q actions/winget-submit
  • uv run --with pytest>=8,<9 --with PyYAML==6.0.2 python -m pytest -q actions/release-preflight
  • bash actions/auto-release/test_gate.sh
  • bash actions/darwin-gate/test_darwin_gate.sh
  • bash actions/macos-codesign-setup/test_gate.sh
  • bash actions/homebrew-alias/test_alias.sh

Closes #27

@rianjs

rianjs commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Findings

  • Minor: actions/winget-submit/test_winget_submit.py covers helper decisions, but not the run_submit orchestration that wires those decisions to the actual wingetcreate update vs wingetcreate submit commands. A small monkeypatched test for existing-package and missing/bootstrap-package paths would protect the most important release behavior: token separation, mode selection, urlretrieve, render_bootstrap_manifests, and the final subprocess args.

  • Minor: actions/winget-submit/winget_submit.py:192 and actions/winget-submit/winget_submit.py:232 perform network calls without timeouts. Since winget is best-effort, a slow GitHub/API/aka.ms connection should fail the channel promptly rather than hold the release workflow until the runner/job timeout.

Architecturally, the PR matches the plan: bootstrap is explicit and fail-closed, existing packages still use update mode, consumer repos are not special-cased, and codereview-cli can safely consume this after .github@v1 is moved.

Verification note: I could not rerun the reported tests in this read-only session because uv could not initialize its cache and this Python environment lacks pytest/PyYAML.

Reference checked: https://github.com/microsoft/winget-create

@rianjs

rianjs commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Findings

  • Major: The new winget-submit coverage only exercises run_submit() on mocked success paths. There is no test for the actual fail-closed behavior the ticket asks for: bootstrap=false with a missing package, or a non-404 / timeout while checking microsoft/winget-pkgs. That means a regression in the orchestration layer could still pass if it ignored the bootstrap gate or proceeded after an unverifiable lookup. See actions/winget-submit/test_winget_submit.py:184 and the unit-only CI addition in /.github/workflows/test-actions.yml:156.

  • Major: The first-submission rendering test only inspects the installer manifest. It would still pass if render_bootstrap_manifests() forgot to emit the version manifest, forgot the locale manifest, or only rendered a subset of templates into the temp directory. That is a real gap for the bootstrap path, because the ticket explicitly requires rendering the full manifest set without mutating source files. See actions/winget-submit/test_winget_submit.py:80.

  • Minor: The update-path assertion never checks the --urls payload. A broken build_update_command() that omits the URLs, swaps x64/arm64, or points at the wrong assets would still pass because the test only checks the command prefix and token suffix. See actions/winget-submit/test_winget_submit.py:184.

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

Automated PR Review

Reviewed commit: 32d51e1

Summary

Reviewer Findings
harness-engineering:harness-architecture-reviewer 2
harness-engineering:harness-enforcement-reviewer 2
harness-engineering:harness-knowledge-reviewer 1
harness-engineering:harness-self-documenting-code-reviewer 3
harness-engineering:harness-architecture-reviewer (2 findings)

⚠️ Should Fix - actions/winget-submit/winget_submit.py:241

Both subprocess.run calls for wingetcreate (update path ~line 241, submit path ~line 256) have no timeout parameter. If wingetcreate hangs during GitHub API calls or PR creation against microsoft/winget-pkgs, the release job blocks until the runner job limit (~6 hours). WINGETCREATE_TIMEOUT_SECONDS is defined but only applied to the download step. Add timeout= to both subprocess.run calls and handle subprocess.TimeoutExpired.

💡 Suggestion - actions/winget-submit/action.yml:1

The composite action downloads and executes wingetcreate.exe, a Windows-only binary, but neither the description nor any runtime guard documents or enforces a Windows runner requirement. A non-Windows caller receives an opaque exec-format error rather than a clear failure. Add a platform assertion (e.g. checking $RUNNER_OS == 'Windows') and note the requirement in the action description.

harness-engineering:harness-enforcement-reviewer (2 findings)

💡 Suggestion - actions/winget-submit/winget_submit.py:225

load_release_assets and resolve_windows_assets are called before the package_exists check in run_submit. For the MODE_MISSING_BOOTSTRAP_DISABLED fast-fail path (bootstrap=false, package absent from winget-pkgs), these API round-trips are wasted. Moving package_exists before the asset-loading calls allows early exit without unnecessary I/O.

💡 Suggestion - actions/winget-submit/action.yml:51

The github-token input is mapped to the env var GITHUB_TOKEN, which conflicts with the GitHub Actions built-in default token. While there is no current bug since both values are the same in release.yml, a future caller passing a PAT would silently shadow the built-in token for all steps within this composite action. Use a distinct name such as WINGET_GITHUB_TOKEN to eliminate ambiguity.

harness-engineering:harness-knowledge-reviewer (1 findings)

⚠️ Should Fix - .github/workflows/release.yml:352

References open-cli-collective/.github/actions/winget-submit@v1, but this tag does not exist until it is explicitly advanced after this PR merges. A release triggered in the window between merge and tag-update will fail with 'uses: action not found'. Confirm the tag-advance process is in place before this workflow runs in production.

harness-engineering:harness-self-documenting-code-reviewer (3 findings)

⚠️ Should Fix - actions/winget-submit/winget_submit.py:232

wingetcreate.exe is downloaded from aka.ms/wingetcreate/latest and executed with the winget submission token without any integrity verification (no hash check). A tampered CDN redirect or MITM could deliver an attacker-controlled binary that receives the token. Consider pinning to a versioned release URL and verifying a known SHA-256 before executing.

💡 Suggestion - actions/identity-check/identity.py:86

_winget_bootstrap() raises ManifestError for a non-boolean value while validate() appends the same condition as a collected error string. A caller invoking normalize() on an unvalidated manifest with bootstrap: 'yes' receives an unhandled exception instead of a structured error list. The duplicate paths can also silently diverge if one message is updated without the other. Either document that normalize() requires pre-validated input, or remove the raise from _winget_bootstrap and rely solely on the validate() path.

💡 Suggestion - actions/winget-submit/winget_submit.py:59

The parameter package_exists: bool in select_mode shadows the module-level package_exists function. Any future modification that calls package_exists(...) inside select_mode will get TypeError: 'bool' object is not callable. Rename the parameter to pkg_exists or exists to remove the shadow.

3 info-level observations excluded. Run with --verbose to include.

2 PR discussion threads considered.


Completed in 6m 23s | $1.83 | sonnet | daemon 0.2.121 | Glorfindel
Field Value
Model sonnet
Reviewers hybrid-synthesis, database:database-reviewer, harness-engineering:harness-architecture-reviewer, harness-engineering:harness-enforcement-reviewer, harness-engineering:harness-knowledge-reviewer, harness-engineering:harness-self-documenting-code-reviewer, security:security-code-auditor
Engine claude · sonnet
Reviewed by pr-review-daemon · monit-pr-reviewer
Duration 6m 23s wall · 18m 30s compute (Reviewers: 5m 16s · Synthesis: 1m 04s)
Cost $1.83
Tokens 340.6k in / 71.5k out
Turns 12

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost
hybrid-synthesis sonnet 31.0k 4.2k 17.4k 13.6k (1h) $0.12
database:database-reviewer sonnet 16.8k 242 4.1k 12.7k (1h) $0.06
harness-engineering:harness-architecture-reviewer sonnet 66.7k 19.2k 28.3k 38.4k (1h) $0.46
harness-engineering:harness-enforcement-reviewer sonnet 62.1k 20.6k 28.3k 33.8k (1h) $0.46
harness-engineering:harness-knowledge-reviewer sonnet 99.5k 15.0k 45.3k 54.2k (1h) $0.46
harness-engineering:harness-self-documenting-code-reviewer sonnet 41.7k 8.6k 21.0k 20.7k (1h) $0.22
security:security-code-auditor haiku 22.7k 3.6k 4.2k 18.5k (1h) $0.05

Re-reviews only run when @monit-reviewer is re-requested as a reviewer — push as many commits as you need, then re-request when ready. PRs targeting branches other than main, master are skipped, even when @monit-reviewer is re-requested.

Comment thread actions/winget-submit/winget_submit.py
Comment thread .github/workflows/release.yml
Comment thread actions/winget-submit/winget_submit.py
Comment thread actions/winget-submit/winget_submit.py Outdated
Comment thread actions/winget-submit/action.yml
Comment thread actions/winget-submit/action.yml
Comment thread actions/identity-check/identity.py
Comment thread actions/winget-submit/winget_submit.py Outdated
@monit-reviewer
monit-reviewer dismissed their stale review June 4, 2026 13:27

Superseded by updated review

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

Automated PR Review

Reviewed commit: 1907036 | Previous: 32d51e1 (incremental)

Summary

No issues found.

10 PR discussion threads considered.


Completed in 2m 49s | $1.36 | sonnet | daemon 0.2.121 | Glorfindel
Field Value
Model sonnet
Mode Re-review · Cycle 2 · Session resumed
Reviewers hybrid-synthesis, database:database-reviewer, harness-engineering:harness-architecture-reviewer, harness-engineering:harness-enforcement-reviewer, harness-engineering:harness-knowledge-reviewer, harness-engineering:harness-self-documenting-code-reviewer, security:security-code-auditor
Engine claude · sonnet
Reviewed by pr-review-daemon · monit-pr-reviewer
Duration 2m 49s wall · 4m 49s compute (Reviewers: 2m 01s · Synthesis: 15s)
Cost $1.36
Tokens 340.0k in / 18.3k out
Turns 7

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost
hybrid-synthesis sonnet 39.2k 801 17.4k 21.8k (1h) $0.10
database:database-reviewer sonnet 24.8k 46 4.1k 20.6k (1h) $0.08
harness-engineering:harness-architecture-reviewer sonnet 59.7k 7.1k 4.1k 55.6k (1h) $0.32
harness-engineering:harness-enforcement-reviewer sonnet 61.1k 5.6k 4.1k 57.0k (1h) $0.30
harness-engineering:harness-knowledge-reviewer sonnet 55.5k 88 4.1k 51.3k (1h) $0.20
harness-engineering:harness-self-documenting-code-reviewer sonnet 33.3k 104 4.1k 29.1k (1h) $0.11
security:security-code-auditor haiku 40.6k 3.1k 4.2k 36.4k (1h) $0.06
discussion-summarizer 25.9k 1.4k 0 21.2k (1h) $0.19

Re-reviews only run when @monit-reviewer is re-requested as a reviewer — push as many commits as you need, then re-request when ready. PRs targeting branches other than main, master are skipped, even when @monit-reviewer is re-requested.

@rianjs
rianjs merged commit 6efe5b2 into main Jun 4, 2026
19 checks passed
@rianjs
rianjs deleted the feat/27-winget-first-submission-bootstrap branch June 4, 2026 13:29
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.

feat(release): bootstrap first-time winget package submissions

2 participants