Skip to content

feat: publish releases to WinGet and Scoop - #137

Merged
vriesdemichael merged 9 commits into
mainfrom
copilot/publish-using-winget-manifest
Apr 11, 2026
Merged

vriesdemichael merged 9 commits into
mainfrom
copilot/publish-using-winget-manifest

Conversation

Copilot AI commented Apr 8, 2026 •

Copy link
Copy Markdown
Contributor

Adds automated publishing of the CLI to Windows package managers (WinGet and Scoop) as part of the release workflow, and documents the new installation paths for Windows users.

Changes Made

  • WinGet: Added winget-release job to .github/workflows/release.yml using vedantmgoyal9/winget-releaser@v2 with identifier vriesdemichael.bb
  • Scoop: Added scoop-release job to .github/workflows/release.yml that downloads sha256sums.txt from the GitHub release, generates a bb.json Scoop manifest (amd64 + arm64, with autoupdate URLs), and pushes it to vriesdemichael/scoop using SCOOP_BUCKET_TOKEN
  • Security: Scoop job clones without credentials and uses a git credential helper so the token is never embedded in a URL
  • Python: Added explicit actions/setup-python@v5 step to the scoop-release job
  • Docs: Updated README.md and docs/site/installation-and-quickstart.md with WinGet and Scoop install commands:
    scoop bucket add vriesdemichael https://github.com/vriesdemichael/scoop
    scoop install vriesdemichael/bb
  • License: Added LICENSE file (Apache 2.0, copyright Michael de Vries); Scoop manifest license field set to Apache-2.0
  • Windows binary hardening: Added -s -w -buildid= to Go ldflags to strip debug/DWARF tables and produce reproducible builds, reducing Windows Defender/SmartScreen heuristic false-positive risk. Added a Windows-only go-winres step that embeds a proper PE VERSIONINFO resource (product name, file description, copyright, version) into bb.exe before compilation, making it identifiable as a legitimate Windows application rather than a bare Go binary.

Copilot AI linked an issue Apr 8, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add YAML manifest for publishing with winget feat: publish releases to WinGet via vedantmgoyal9/winget-releaser Apr 8, 2026
Copilot AI requested a review from vriesdemichael April 8, 2026 18:07
@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot I'd like to wait a bit, lets also pick up the issue for installation with scoop. Can you guide me on how to make that possible?

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot I'd like to wait a bit, lets also pick up the issue for installation with scoop. Can you guide me on how to make that possible?

Added Scoop support in commit 0c5d456. Here's what was added and what you need to do once before the first release:

What the PR now does automatically on each release:

  • Downloads sha256sums.txt from the GitHub release
  • Generates a bb.json Scoop manifest (amd64 + arm64, with proper autoupdate URLs) and pushes it to vriesdemichael/scoop-bb

One-time manual setup required:

  1. Create the bucket repo — create vriesdemichael/scoop-bb on GitHub (public, no special files needed initially; you can git init an empty repo or use the GitHub UI).

  2. Add an initial bb.json — the CI will overwrite it on every release, but the repo needs to exist. You can push a minimal placeholder:

    { "version": "0.0.0", "description": "placeholder", "bin": "bb.exe" }
  3. Add the SCOOP_BUCKET_TOKEN secret — a classic PAT with repo scope on vriesdemichael/scoop-bb. Add it to this repo's Actions secrets as SCOOP_BUCKET_TOKEN.

Users install via:

scoop bucket add scoop-bb https://github.com/vriesdemichael/scoop-bb
scoop install scoop-bb/bb

Copilot AI changed the title feat: publish releases to WinGet via vedantmgoyal9/winget-releaser feat: publish releases to WinGet and Scoop Apr 8, 2026
@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot Lets make the repo vriesdemichael/scoop instead of scoop-bb to enable future cli tools as well.
What should be in the placehold bb.json? Any requirements to it? Or just empty with a json containing {}

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Lets make the repo vriesdemichael/scoop instead of scoop-bb to enable future cli tools as well.
What should be in the placehold bb.json? Any requirements to it? Or just empty with a json cont...

Updated to vriesdemichael/scoop in d810dff.

Placeholder bb.json: {} isn't enough — Scoop will error when scanning the bucket if version is missing. Use this minimal placeholder that satisfies the parser:

{
  "version": "0.0.0",
  "url": "https://github.com/vriesdemichael/bitbucket-server-cli/releases/download/v0.0.0/bb_0.0.0_windows_amd64.zip",
  "bin": "bb.exe"
}

The CI will overwrite it with the real manifest (including proper hashes and both amd64/arm64 entries) on the first release, so the URL and hash in the placeholder don't need to be real.

@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot scoop bucket add scoop https://github.com/vriesdemichael/scoop
This makes no sense... why would you name your added bucket scoop? That wont work if anybody else did it.

Make it
scoop bucket add vriesdemichael https://github.com/vriesdemichael/scoop
scoop install vriesdemichael/bb

Do i also need to add a token to the repo for scoop? What permissions should it have, describe how i should make it

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot scoop bucket add scoop https://github.com/vriesdemichael/scoop
This makes no sense... why would you name your added bucket scoop? That wont work if anybody else did it.
...

Fixed the install commands in d90226f:

scoop bucket add vriesdemichael https://github.com/vriesdemichael/scoop
scoop install vriesdemichael/bb

Token setup for SCOOP_BUCKET_TOKEN:

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Click Generate new token (classic)
  3. Give it a name like scoop-bucket-push
  4. Select only the repo scope (full control of private repositories — needed to push commits to vriesdemichael/scoop)
  5. Click Generate token and copy it
  6. In the vriesdemichael/bitbucket-server-cli repo → Settings → Secrets and variables → Actions → New repository secret
  7. Name: SCOOP_BUCKET_TOKEN, Value: the token you just copied

That's it — no other permissions needed. The token only needs to be able to push to vriesdemichael/scoop.

Copilot AI commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot The token is in and the bucket repo is created with the placeholder.
Lets finish this PR by adding an apache 2 license (since we dont have any configured yet)

Done in 6eee6c3:

  • Added LICENSE (Apache 2.0, copyright Michael de Vries)
  • Also fixed the two workflow issues flagged in review: the Scoop clone no longer embeds the token in the URL (uses a credential helper instead), and actions/setup-python@v5 is now explicitly set up before the manifest generation step
  • Updated the Scoop manifest's license field from "MIT" to "Apache-2.0" to match

@vriesdemichael

Copy link
Copy Markdown
Owner

Waiting for microsoft/winget-pkgs#356818 to merge so we wont trigger more PRs during the initial release

@vriesdemichael vriesdemichael linked an issue Apr 8, 2026 that may be closed by this pull request
@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot I got this messsage back.
Automatic Validation ended with:

Installation failed with exit code -1978335187
2026-04-08 18:12:57.834 [FAIL] Installer failed security check. Url: https://github.com/vriesdemichael/bitbucket-server-cli/releases/download/v1.15.0/bb_1.15.0_windows_amd64.zip Result: 0x80004005

Hex Dec Inverted Dec Symbol Description
80004005 -2147467259 2147500037 E_Fail Resource missing
8A15002D -1978335187 2316632109 APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED Installer failed security check. Another script should retry this about every 18 hours.

(Automated response - build 1244.)

Can you give me the next steps?

Copilot AI commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot I got this messsage back.
Automatic Validation ended with:

...

This is a known transient WinGet validation failure — no code changes are needed. The error message itself says "Another script should retry this about every 18 hours," and that's exactly what happens. WinGet's automated pipeline uses Windows Defender / SmartScreen to scan new installer URLs, and newly published GitHub releases can take a few scan cycles to be cleared.

Next steps:

  1. Wait — the WinGet validation bot will automatically retry the PR (New package: vriesdemichael.bb version 1.15.0 microsoft/winget-pkgs#356818) in ~18 hours. Most first-submission failures like this pass on the second or third attempt once the file has been cached and scanned by Microsoft's infrastructure.
  2. Do nothing — no changes to the release or the workflow are needed.
  3. If it fails again after 2–3 retries (48–72 hours), you can add a comment on the winget-pkgs PR mentioning the repeated failure and a maintainer there can trigger a manual re-validation.

The v1.16.0 release itself is fine — the zip is accessible and correctly published.

@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot Wow, okay that is incredibly unclear. So we just wait, ok

@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot The communication on winget is kind of terrible.. do the included screenshots mean it resolved itself? Or should i still take action

Copilot AI commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot The communication on winget is kind of terrible.. do the included screenshots mean it resolved itself? Or should i still take action

Yes, it resolved itself. The winget-pkgs PR (microsoft/winget-pkgs#356818) now has both Azure-Pipeline-Passed and Validation-Completed labels — no action needed on your end. It just needs a WinGet maintainer to merge it.

@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot Process the pr comments posted on this branch and also take this into consideration (we had a bunch of issues with winget, this might help a little)

(from an ai conversation somewhere else, translate it into instructions for yourself before you blindly take it at face value

If you are about to merge this into your pipeline, you can absolutely make some strategic changes to your go build command.

While nothing is a 100% guaranteed silver bullet without a signature, the goal here is to make your binary look as "boring" and standard as possible to the Wacatac ML model. Machine learning heuristics don't just look at what the code does; they look at the physical structure of the Portable Executable (PE) file.

Here is the exact checklist to implement in your GitHub Actions workflow to aggressively lower your threat score.
1. The Essential Compiler Flags

Right now, your standard go build embeds a massive amount of metadata, debugging information, and absolute file paths from the GitHub Actions runner into your executable. ML models look at weird file paths (like /home/runner/work/bitbucket-server-cli/...) and flag them as anomalous.

Change your build command to this:
Bash

go build -trimpath -ldflags="-s -w -buildid=" -o dist/bb.exe ./cmd/bb

    -trimpath: This strips all the absolute paths from the GitHub Actions Linux runner out of the binary, replacing them with relative module paths.

    -s: Omits the symbol table and debug information.

    -w: Omits the DWARF symbol table. (Combining -s and -w shrinks the binary size and removes the exact debugging tables that Wacatac frequently trips over).

    -buildid=: This clears the unique build ID that the Go compiler generates on every run. Combined with -trimpath, this makes your build reproducible. If the source code hasn't changed, the output hash will be identical, which helps prevent unnecessary reputation resets.

2. The Secret Weapon: Add a Windows Resource File

This is the biggest mistake Go developers make when compiling for Windows.

A standard go build produces a "naked" executable. If you right-click a standard Go .exe and go to Properties > Details, it is completely blank. There is no icon, no company name, no copyright, and no version number. To an enterprise AI security model, a naked executable dropping onto a disk and immediately reading the keychain screams "malware payload."

You need to make your CLI look like a legitimate Windows application.
Add a tool like go-winres to your pipeline before the build step.

    Install it in your workflow: go install github.com/tc-hib/go-winres@latest

    Run go-winres make in your pipeline.

    This generates an embedded .syso file that gets compiled directly into your .exe.

Even if you just embed a generic icon, the string "Bitbucket Server CLI," and a 1.15.0 version number, the ML model sees a properly structured Windows PE manifest. This drastically lowers the heuristic "suspicious structure" score.
3. Keep CGO Disabled

Since you are cross-compiling from Linux (ubuntu-latest) to Windows, CGO is likely already disabled by default. Make sure you are explicitly setting CGO_ENABLED=0 in your environment variables.

If CGO is accidentally enabled and trying to link against cross-compilation C libraries, it introduces non-standard C-runtime wrappers into the Go binary that Windows Defender immediately flags as highly suspicious.
4. The "Do Not Do This" Reminder

Just to reiterate: as you are tweaking the pipeline, do not add UPX or any other executable packer to try and shrink the 25MB file size down. Packing an unsigned binary that does HTTP calls will instantly trigger an un-clearable "Severe" malware alert.

Do you currently use a standard bash script for your build steps, or are you utilizing a release automation tool like GoReleaser in your pipeline?

- Add -s -w -buildid= to Go ldflags: strips debug/DWARF tables and
  makes builds reproducible (same source → same hash), which prevents
  reputation resets in Microsoft SmartScreen/Wacatac heuristics
- Add go-winres step (Windows matrix only) to embed PE metadata into
  the .exe before go build: company name, product name, description,
  copyright, and version resource — making bb.exe look like a
  legitimate Windows application instead of a bare Go binary
- gitignore the generated rsrc_windows_*.syso files

Agent-Logs-Url: https://github.com/vriesdemichael/bitbucket-server-cli/sessions/e1bf7d7e-c04d-45f4-86af-9b34e8b066a0

Co-authored-by: vriesdemichael <9072548+vriesdemichael@users.noreply.github.com>

Copilot AI commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Process the pr comments posted on this branch and also take this into consideration (we had a bunch of issues with winget, this might help a little)

(from an ai conversat...

Done in aebd12e. Here's what was applied from the advice (after verifying against actual go-winres v0.3.3 flags):

Implemented:

  1. -s -w -buildid= added to Go ldflags — strips debug/DWARF tables and makes builds reproducible (same source → identical hash, which avoids SmartScreen reputation resets between retries). The build already had -trimpath and CGO_ENABLED=0, so those were already covered.
  2. go-winres PE metadata embedding — a new Windows-only step installs go-winres@v0.3.3 and embeds a proper Windows version resource into bb.exe before go build, including product name ("Bitbucket Server CLI"), file description, copyright, and the release version. This makes the exe look like a legitimate Windows application rather than a bare Go binary.

Not applied:

  • UPX — correctly excluded per the advice; would cause an instant "Severe" flag.
  • The advice mentioned --company and --no-icon flags that don't exist in go-winres — verified against the real CLI help before using.

These changes take effect on the next release build.

@vriesdemichael

Copy link
Copy Markdown
Owner

@copilot you did not mention the two comments on this pr (my guess is because you cannot read them). Here they are:
.github/workflows/release.yml
Outdated
Comment on lines +541 to +543
git clone
"https://x-access-token:${SCOOP_BUCKET_TOKEN}@github.com/vriesdemichael/scoop.git"
scoop-bucket
https://github.com/apps/copilot-pull-request-reviewer AI
3 days ago

The Scoop publish step embeds SCOOP_BUCKET_TOKEN directly in the git clone URL. If git clone/git push fails, git error output can include the full remote URL, which risks leaking the token in logs (masking is not guaranteed for all token formats). Prefer cloning without credentials and only supplying the token for the push (or configuring a credential helper / url.insteadOf) so the token is never part of a printed URL.
Copilot uses AI. Check for mistakes.
@vriesdemichael
.github/workflows/release.yml
scoop-bucket
cd scoop-bucket

      python - <<'PY'

https://github.com/apps/copilot-pull-request-reviewer AI
3 days ago

scoop-release relies on python being available on ubuntu-latest, but this job doesn’t set it up (unlike the other release jobs earlier in this workflow). Add an explicit actions/setup-python@v5 step (and pin/choose a version) to avoid breakage if the runner image changes.

Are those taken into account as well?

Copilot AI commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot you did not mention the two comments on this pr (my guess is because you cannot read them). Here they are:
[.github/workflows/release.yml](https://github.com/vriesdemichael/bitbucket-server-...

Yes, both were addressed in commit 6eee6c3:

  1. Token in clone URL — the Scoop job now clones the bucket repo without credentials (https://github.com/vriesdemichael/scoop.git) and configures the token via a git credential helper instead, so it is never part of a URL that could appear in git error output.

  2. actions/setup-python@v5 — an explicit setup-python step was added to the scoop-release job (line 552 in the current workflow).

@vriesdemichael
vriesdemichael enabled auto-merge (rebase) April 11, 2026 18:47
@vriesdemichael
vriesdemichael merged commit 9cdb88d into main Apr 11, 2026
7 checks passed
@vriesdemichael
vriesdemichael deleted the copilot/publish-using-winget-manifest branch April 11, 2026 18:52
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.

add a license publish using winget publish using scoop bucket

4 participants