Skip to content

Commit 491e0fb

Browse files
ralyodioclaude
andauthored
Actually publish to the package managers (#114)
TronBrowser has never reached a single package manager. The channels were scaffolded on 2026-06-25 and every manifest in distribution/ still said 0.1.0 while releases went out to 3.15.0. Four separate things had to be true for a release to ship downstream and none of them were. The trigger never fired. submit-packages.yml listened on `release: [published]`, but release.yml un-drafts with GITHUB_TOKEN, and GitHub raises no workflow events for what a GITHUB_TOKEN does. The workflow has zero runs in three months. release.yml now calls it directly through workflow_call, which needs no PAT. The dry-run flag was read from `github.event.inputs.dry_run`, which is empty on anything that is not a workflow_dispatch. The guard was `!= "false"`, so a release run appended --dry-run every time: even with a working trigger the submit path could not have executed. Chocolatey was worse, hardcoded --dry-run with no way to turn it off. The refreshed manifests were thrown away with the runner. Nothing committed them, which is why the tree never moved off 0.1.0. They are now opened as a PR, since those files are the input to every channel's submission and a stale tree means even a working submit publishes the wrong version. And there was no submission code at all. The header said submission was "gated on the relevant secret", but no push, PR or upload existed anywhere in the script -- it rewrote files and exited. Channels we own outright now really publish: homebrew to our tap, scoop to our bucket, AUR over ssh (with a generated .SRCINFO, since makepkg is not on a runner), Chocolatey via choco push, Snap via snapcraft upload. The five that are a pull request into someone else's monorepo (winget, flathub, nixpkgs, gentoo, freebsd) cannot be automated end to end and now say so with a link, rather than printing success. A channel with no secret refreshes its manifest, explains the skip and exits clean, so a credential-less repo still goes green. A channel that has its secret and fails is allowed to break the build, because a silent success here is exactly what hid this for three months. Verified against the real v3.15.0 release: manifests refresh to 3.15.0 and the generated AUR sha256 matches the published tarball byte for byte. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c8fb9a3 commit 491e0fb

9 files changed

Lines changed: 393 additions & 39 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,19 @@ jobs:
143143
env:
144144
GH_TOKEN: ${{ github.token }}
145145
run: gh release edit "${{ needs.create-release.outputs.tag }}" --draft=false --repo "${{ github.repository }}"
146+
147+
# 4) Push the release out to the downstream package managers.
148+
#
149+
# This is a direct call, NOT submit-packages.yml's own `release: [published]`
150+
# trigger. The un-draft above uses GITHUB_TOKEN, and GitHub raises no workflow
151+
# events for what a GITHUB_TOKEN does, so that trigger never fired once in the
152+
# three months it was wired up and no channel was ever updated past 0.1.0.
153+
# Calling the workflow keeps that guarantee without needing a PAT.
154+
distribute:
155+
needs: [create-release, publish]
156+
uses: ./.github/workflows/submit-packages.yml
157+
secrets: inherit
158+
with:
159+
version: ${{ needs.create-release.outputs.version }}
160+
dry_run: false
161+
package_managers: all
Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,46 @@
11
name: Submit to Package Managers
22

3-
# Refresh + submit downstream package managers after a release. Mirrors
4-
# pairux.com's submit-packages workflow but stays in this monorepo. Each channel
5-
# is gated on its secret; without the secret it dry-runs (no-op), so this never
6-
# fails for missing credentials.
3+
# Refresh + submit downstream package managers after a release. Each channel is
4+
# gated on its secret; without the secret it refreshes the manifest and skips the
5+
# submit, so this never fails for missing credentials.
6+
#
7+
# NOT triggered by `release: [published]`. release.yml un-drafts with
8+
# GITHUB_TOKEN, and GitHub deliberately raises no workflow events for anything a
9+
# GITHUB_TOKEN does, so that trigger never fired once between 2026-06-25 and
10+
# 2026-09-24 and every channel silently stayed on 0.1.0. release.yml now calls
11+
# this workflow directly instead, which needs no PAT.
712
on:
8-
release:
9-
types: [published]
13+
workflow_call:
14+
inputs:
15+
version:
16+
description: 'Version without v prefix (e.g. 0.1.0)'
17+
required: true
18+
type: string
19+
dry_run:
20+
description: 'Refresh manifests without submitting'
21+
type: boolean
22+
default: false
23+
package_managers:
24+
description: 'Comma-separated, or "all"'
25+
type: string
26+
default: all
1027
workflow_dispatch:
1128
inputs:
1229
version:
1330
description: 'Version without v prefix (e.g. 0.1.0)'
1431
required: true
1532
dry_run:
16-
description: 'Generate manifests without submitting'
33+
description: 'Refresh manifests without submitting'
1734
type: boolean
1835
default: true
1936
package_managers:
2037
description: 'Comma-separated, or "all"'
2138
default: all
2239

40+
permissions:
41+
contents: write
42+
pull-requests: write
43+
2344
jobs:
2445
# Everything except Chocolatey runs on Linux.
2546
submit-linux:
@@ -31,50 +52,74 @@ jobs:
3152

3253
- name: Resolve version
3354
id: v
34-
run: |
35-
if [ -n "${{ github.event.inputs.version }}" ]; then V="${{ github.event.inputs.version }}"; else V="${{ github.event.release.tag_name }}"; fi
36-
echo "version=${V#v}" >> "$GITHUB_OUTPUT"
55+
env:
56+
V_IN: ${{ inputs.version }}
57+
run: echo "version=${V_IN#v}" >> "$GITHUB_OUTPUT"
3758

3859
- name: Resolve package managers
3960
id: pms
61+
env:
62+
PMS_IN: ${{ inputs.package_managers }}
4063
run: |
41-
PMS="${{ github.event.inputs.package_managers }}"
64+
PMS="$PMS_IN"
4265
[ -z "$PMS" ] && PMS="all"
4366
if [ "$PMS" = "all" ]; then PMS="homebrew,scoop,winget,aur,apt,rpm,gentoo,nix,snap,flatpak,appimage,freebsd"; fi
4467
echo "list=$(echo "$PMS" | sed 's/chocolatey,*//g')" >> "$GITHUB_OUTPUT"
4568
4669
- name: Refresh + submit manifests
4770
env:
48-
DRY_RUN: ${{ github.event.inputs.dry_run }}
71+
# A release calls this with dry_run=false. This previously read
72+
# github.event.inputs.dry_run, which is empty on anything but a
73+
# workflow_dispatch, so the "!= false" test below appended --dry-run on
74+
# every release run: the submit path could never have executed even if
75+
# the trigger had worked.
76+
DRY_RUN: ${{ inputs.dry_run }}
4977
GH_TOKEN: ${{ github.token }}
5078
AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
5179
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
80+
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
81+
SCOOP_BUCKET_TOKEN: ${{ secrets.SCOOP_BUCKET_TOKEN }}
82+
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
5283
run: |
5384
ARGS="-v ${{ steps.v.outputs.version }}"
5485
for PM in $(echo "${{ steps.pms.outputs.list }}" | tr ',' ' '); do ARGS="$ARGS -p $PM"; done
55-
# Default to dry-run unless explicitly disabled AND submit secrets exist.
5686
if [ "$DRY_RUN" != "false" ]; then ARGS="$ARGS --dry-run"; fi
5787
node scripts/submit-packages.mjs $ARGS
5888
89+
# The refreshed manifests are the input to every future submission, so they
90+
# have to outlive the runner. Without this they were rewritten and thrown
91+
# away, which is why distribution/ still said 0.1.0 at release 3.15.0.
92+
- name: Open a PR with the refreshed manifests
93+
if: ${{ inputs.dry_run != true }}
94+
env:
95+
GH_TOKEN: ${{ github.token }}
96+
VERSION: ${{ steps.v.outputs.version }}
97+
run: bash scripts/open-distribution-pr.sh
98+
5999
# Chocolatey publishes from Windows.
60100
submit-chocolatey:
61101
runs-on: windows-latest
62102
name: Submit (Chocolatey)
63103
if: >-
64-
github.event.inputs.package_managers == 'all' ||
65-
github.event.inputs.package_managers == '' ||
66-
github.event_name == 'release' ||
67-
contains(github.event.inputs.package_managers, 'chocolatey')
104+
inputs.package_managers == 'all' ||
105+
inputs.package_managers == '' ||
106+
contains(inputs.package_managers, 'chocolatey')
68107
steps:
69108
- uses: actions/checkout@v5
70109
- name: Resolve version
71110
id: v
72111
shell: bash
73-
run: |
74-
if [ -n "${{ github.event.inputs.version }}" ]; then V="${{ github.event.inputs.version }}"; else V="${{ github.event.release.tag_name }}"; fi
75-
echo "version=${V#v}" >> "$GITHUB_OUTPUT"
76-
- name: Refresh chocolatey manifest
112+
env:
113+
V_IN: ${{ inputs.version }}
114+
run: echo "version=${V_IN#v}" >> "$GITHUB_OUTPUT"
115+
- name: Refresh + submit chocolatey manifest
77116
shell: bash
78117
env:
118+
# Was hardcoded --dry-run, so chocolatey could never publish regardless
119+
# of trigger or credentials.
120+
DRY_RUN: ${{ inputs.dry_run }}
79121
CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
80-
run: node scripts/submit-packages.mjs -v ${{ steps.v.outputs.version }} -p chocolatey --dry-run
122+
run: |
123+
ARGS="-v ${{ steps.v.outputs.version }} -p chocolatey"
124+
if [ "$DRY_RUN" != "false" ]; then ARGS="$ARGS --dry-run"; fi
125+
node scripts/submit-packages.mjs $ARGS

‎distribution/aur/PKGBUILD‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Maintainer: Profullstack, Inc. <security@profullstack.com>
22
# AUR binary package — consumes the GitHub release tarball.
33
pkgname=tronbrowser-bin
4-
pkgver=0.1.0
4+
pkgver=3.15.0
55
pkgrel=1
66
pkgdesc="Open-source, privacy-first, AI-native browser (Ungoogled Chromium fork)"
77
arch=('x86_64')
@@ -11,7 +11,7 @@ depends=('chromium')
1111
provides=('tronbrowser')
1212
conflicts=('tronbrowser')
1313
source=("tronbrowser-${pkgver}.tar.gz::https://github.com/profullstack/tronbrowser.dev/releases/download/v${pkgver}/tronbrowser-linux-x64.tar.gz")
14-
sha256sums=('d966a54a6369ec283203abd257f89e50844ff68551229a463f568dc638dcf4c7')
14+
sha256sums=('0870fc5fd4b7acdfa1679f542d038a34e932b65721918cb4c1071e7a6b008779')
1515

1616
package() {
1717
install -dm755 "${pkgdir}/usr/lib/tronbrowser"

‎distribution/homebrew/tronbrowser.rb‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
class Tronbrowser < Formula
55
desc "Open-source, privacy-first, AI-native browser (Ungoogled Chromium fork)"
66
homepage "https://tronbrowser.dev"
7-
version "0.1.0"
7+
version "3.15.0"
88
license "MIT"
99

1010
on_macos do
11-
url "https://github.com/profullstack/tronbrowser.dev/releases/download/v0.1.0/tronbrowser-macos.zip"
11+
url "https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-macos.zip"
1212
sha256 "4f928b90b83a34d90edf6f3b4f522c47b85090046424ce69cc535a0cb85d77d1"
1313
end
1414

1515
on_linux do
16-
url "https://github.com/profullstack/tronbrowser.dev/releases/download/v0.1.0/tronbrowser-linux-x64.tar.gz"
17-
sha256 "d966a54a6369ec283203abd257f89e50844ff68551229a463f568dc638dcf4c7"
16+
url "https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-linux-x64.tar.gz"
17+
sha256 "0870fc5fd4b7acdfa1679f542d038a34e932b65721918cb4c1071e7a6b008779"
1818
end
1919

2020
def install

‎distribution/scoop/tronbrowser.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
{
2-
"version": "0.1.0",
2+
"version": "3.15.0",
33
"description": "Open-source, privacy-first, AI-native browser (Ungoogled Chromium fork).",
44
"homepage": "https://tronbrowser.dev",
55
"license": "MIT",
66
"architecture": {
77
"64bit": {
8-
"url": "https://github.com/profullstack/tronbrowser.dev/releases/download/v0.1.0/tronbrowser-win-x64.zip",
9-
"hash": "TODO-needs-windows-build"
8+
"url": "https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-win-x64.zip",
9+
"hash": "ebb5b0c7675255accc9975e0ac31a542cb9c9eb1bb700eac18fce8cca681f82c"
1010
}
1111
},
1212
"bin": [["tronbrowser/tronbrowser.exe", "tron"], ["tronbrowser/tronbrowser.exe", "tronbrowser"]],
1313
"checkver": "github",
1414
"autoupdate": {
1515
"architecture": {
1616
"64bit": {
17-
"url": "https://github.com/profullstack/tronbrowser.dev/releases/download/v$version/tronbrowser-win-x64.zip"
17+
"url": "https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-win-x64.zip"
1818
}
1919
}
2020
}

‎distribution/snap/snapcraft.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# and exec the host's Chromium. `snapcraft` builds; publish with `snapcraft upload`.
33
name: tronbrowser
44
base: core24
5-
version: '0.1.1'
5+
version: '3.15.0'
66
summary: Privacy-first, AI-native browser (Ungoogled Chromium fork)
77
description: |
88
Open-source, privacy-first, AI-native web browser built on Ungoogled Chromium.
@@ -20,6 +20,6 @@ apps:
2020
parts:
2121
tronbrowser:
2222
plugin: dump
23-
source: https://github.com/profullstack/tronbrowser.dev/releases/download/v0.1.1/tronbrowser-linux-x64.tar.gz
23+
source: https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-linux-x64.tar.gz
2424
organize:
2525
'tronbrowser/*': './'

‎distribution/winget/Profullstack.TronBrowser.installer.yaml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# manifests/p/Profullstack/TronBrowser/<version>/. Needs a Windows installer
33
# (.exe/.msi) or a portable zip artifact — pending the Windows build.
44
PackageIdentifier: Profullstack.TronBrowser
5-
PackageVersion: 0.1.0
5+
PackageVersion: 3.15.0
66
InstallerType: zip
77
Installers:
88
- Architecture: x64
9-
InstallerUrl: https://github.com/profullstack/tronbrowser.dev/releases/download/v0.1.0/tronbrowser-win-x64.zip
10-
InstallerSha256: TODO-NEEDS-WINDOWS-BUILD
9+
InstallerUrl: https://github.com/profullstack/tronbrowser.dev/releases/download/v3.15.0/tronbrowser-win-x64.zip
10+
InstallerSha256: EBB5B0C7675255ACCC9975E0AC31A542CB9C9EB1BB700EAC18FCE8CCA681F82C
1111
NestedInstallerType: portable
1212
NestedInstallerFiles:
1313
- RelativeFilePath: tronbrowser\tronbrowser.exe

‎scripts/open-distribution-pr.sh‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env bash
2+
# Open a PR with the manifests submit-packages.mjs just refreshed.
3+
#
4+
# Why this exists: the refresh rewrites files under distribution/ on the runner
5+
# and nothing ever persisted them, so every manifest in git stayed frozen at the
6+
# version it was scaffolded with (0.1.0/0.1.1) while releases went out to 3.15.0.
7+
# Those manifests are the input to every channel's submission, so a stale tree
8+
# means even a working submit publishes the wrong version.
9+
#
10+
# A PR rather than a push to main: main is protected, and a bad checksum should
11+
# be reviewable before it reaches a package manager.
12+
set -euo pipefail
13+
14+
: "${VERSION:?VERSION must be set}"
15+
16+
if git diff --quiet -- distribution; then
17+
echo "distribution/ already current for v${VERSION}, nothing to commit"
18+
exit 0
19+
fi
20+
21+
git config user.name "github-actions[bot]"
22+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
23+
24+
BRANCH="chore/distribution-${VERSION}"
25+
26+
# A re-run of the same release must not fail on an existing branch.
27+
if git ls-remote --exit-code --heads origin "${BRANCH}" >/dev/null 2>&1; then
28+
echo "branch ${BRANCH} already exists on origin; force-updating it"
29+
git checkout -B "${BRANCH}"
30+
git add distribution
31+
git commit -m "chore(distribution): refresh manifests for v${VERSION}"
32+
git push --force-with-lease origin "${BRANCH}"
33+
else
34+
git checkout -b "${BRANCH}"
35+
git add distribution
36+
git commit -m "chore(distribution): refresh manifests for v${VERSION}"
37+
git push origin "${BRANCH}"
38+
fi
39+
40+
# `gh pr create` fails if one is already open for the branch, which is fine.
41+
gh pr create \
42+
--base main \
43+
--head "${BRANCH}" \
44+
--title "chore(distribution): refresh manifests for v${VERSION}" \
45+
--body "Automated manifest refresh from the release pipeline for v${VERSION}.
46+
47+
Version strings and sha256 checksums are rewritten from the published release
48+
assets by \`scripts/submit-packages.mjs\`." \
49+
|| echo "a PR for ${BRANCH} already exists; branch updated in place"

0 commit comments

Comments
 (0)