Skip to content

Commit 84cb1fe

Browse files
ralyodioclaude
andcommitted
Publish the Gentoo overlay, and stop winget pointing at a file that does not exist
Gentoo turns out not to need anyone's review. An overlay is a git repo laid out the way portage expects, so it is ours to push like the tap and the bucket, not a pull request into someone else's tree. profullstack/gentoo-tronbrowser now exists with net-misc/tronbrowser-bin 3.15.1, and carries its own write deploy key (GENTOO_OVERLAY_SSH_KEY). It is a complete overlay: metadata/layout.conf and profiles/repo_name included, both of which gentoo-pairux is missing, so portage can actually consume this one. The ebuild is entirely ${PV}/${P}, so a release needs no edits to its contents -- only the filename carries the version. The templater renames it and drops the stale one, instead of printing a note and leaving tronbrowser-bin-0.1.1.ebuild in the tree forever. The Manifest is generated: portage wants BLAKE2B and SHA512 next to the byte count rather than the sha256 every other channel uses, and node's blake2b512 is byte-identical to b2sum. It names the tarball by the ${P}.tar.gz that SRC_URI renames it to, not by the release asset's filename. winget is a different story and the blocker is the artifact, not the manifest. tronbrowser-win-x64.zip contains no tronbrowser.exe at all: the Windows entry point is tronbrowser.cmd, a shim that hunts for Python 3.9+ and then drives a copy of Ungoogled Chromium the user must install separately. A portable winget package around that would install for somebody and then fail to start, so it would be rejected on review or, worse, accepted and broken. The manifest pointed RelativeFilePath at that non-existent exe; corrected to the file that does exist, with the real blocker written down. PairUX ships a nullsoft .exe and is in winget-pkgs today, which is the shape to copy when someone does the Windows build. Verified against the live overlay: running the submitter for real reports "already current, nothing to push", which is only true because the generated Manifest matches the one computed by hand with b2sum and sha512sum byte for byte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent a8d9d46 commit 84cb1fe

4 files changed

Lines changed: 147 additions & 25 deletions

File tree

‎.github/workflows/submit-packages.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ jobs:
8888
# from that repo.
8989
HOMEBREW_TAP_SSH_KEY: ${{ secrets.HOMEBREW_TAP_SSH_KEY }}
9090
SCOOP_BUCKET_SSH_KEY: ${{ secrets.SCOOP_BUCKET_SSH_KEY }}
91+
GENTOO_OVERLAY_SSH_KEY: ${{ secrets.GENTOO_OVERLAY_SSH_KEY }}
9192
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
9293
run: |
9394
ARGS="-v ${{ steps.v.outputs.version }}"
File renamed without changes.
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
# winget manifest (installer). Submit to microsoft/winget-pkgs as
2-
# manifests/p/Profullstack/TronBrowser/<version>/. Needs a Windows installer
3-
# (.exe/.msi) or a portable zip artifact — pending the Windows build.
2+
# manifests/p/Profullstack/TronBrowser/<version>/.
3+
#
4+
# NOT SUBMITTABLE YET, and the blocker is the artifact rather than the manifest.
5+
# tronbrowser-win-x64.zip contains no tronbrowser.exe: the Windows entry point is
6+
# tronbrowser.cmd, a shim that searches for Python 3.9+ and then drives a copy of
7+
# Ungoogled Chromium the user has to install separately. A winget portable
8+
# package shimming that would install for someone and then fail to start, so it
9+
# would be rejected on review or, worse, accepted and broken.
10+
#
11+
# RelativeFilePath below named tronbrowser.exe, which has never existed in the
12+
# zip; corrected to the file that does. Submitting still needs a real Windows
13+
# build — a self-contained .exe/.msi, or a zip whose entry point runs without
14+
# the user installing an interpreter and a browser first. PairUX ships a nullsoft
15+
# .exe and is in winget-pkgs today, which is the shape to copy.
416
PackageIdentifier: Profullstack.TronBrowser
517
PackageVersion: 3.15.1
618
InstallerType: zip
@@ -10,7 +22,7 @@ Installers:
1022
InstallerSha256: 2AA36BB55E6E45EB1ACE91435B1B696CB6BFAD929B876B85702F3AD51F51ADDE
1123
NestedInstallerType: portable
1224
NestedInstallerFiles:
13-
- RelativeFilePath: tronbrowser\tronbrowser.exe
25+
- RelativeFilePath: tronbrowser\tronbrowser.cmd
1426
PortableCommandAlias: tron
1527
ManifestType: installer
1628
ManifestVersion: 1.6.0

‎scripts/submit-packages.mjs‎

Lines changed: 131 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@
1818
// ones that are a pull request into somebody else's monorepo (winget, flathub,
1919
// nixpkgs, gentoo, freebsd) still cannot be automated end to end and say so
2020
// rather than reporting success.
21-
import { readFileSync, writeFileSync, existsSync, mkdtempSync } from "node:fs";
21+
import {
22+
readFileSync,
23+
writeFileSync,
24+
existsSync,
25+
mkdtempSync,
26+
readdirSync,
27+
rmSync,
28+
} from "node:fs";
2229
import { join, dirname } from "node:path";
2330
import { fileURLToPath } from "node:url";
2431
import { createHash } from "node:crypto";
@@ -72,6 +79,24 @@ async function sha256(asset) {
7279
return createHash("sha256").update(buf).digest("hex");
7380
}
7481

82+
/**
83+
* Size plus the digests a Gentoo Manifest names.
84+
*
85+
* Portage wants BLAKE2B and SHA512 alongside the byte count, not the sha256
86+
* everything else uses. Node's blake2b512 is byte-identical to b2sum.
87+
*/
88+
async function gentooDigests(asset) {
89+
const url = `https://github.com/${REPO}/releases/download/v${version}/${asset}`;
90+
const res = await fetch(url);
91+
if (!res.ok) return null;
92+
const buf = Buffer.from(await res.arrayBuffer());
93+
return {
94+
size: buf.length,
95+
blake2b: createHash("blake2b512").update(buf).digest("hex"),
96+
sha512: createHash("sha512").update(buf).digest("hex"),
97+
};
98+
}
99+
75100
function patch(file, replacers) {
76101
const path = join(ROOT, file);
77102
if (!existsSync(path)) return console.log(` skip (missing): ${file}`);
@@ -135,11 +160,36 @@ for (const pm of targets) {
135160
: []),
136161
]);
137162
break;
138-
case "gentoo":
139-
console.log(
140-
` rename distribution/gentoo/tronbrowser-bin-${version}.ebuild and bump SRC_URI (uses \${PV})`,
163+
case "gentoo": {
164+
// The ebuild is entirely ${PV}/${P}, so a release needs no edits to it —
165+
// only the filename carries the version. Keep exactly one ebuild in the
166+
// tree, renamed to this release, instead of printing a note and leaving
167+
// the 0.1.1 file there forever.
168+
const existing = readdirSync(join(ROOT, "distribution/gentoo")).filter((f) =>
169+
f.endsWith(".ebuild"),
141170
);
171+
const want = `tronbrowser-bin-${version}.ebuild`;
172+
if (existing.includes(want) && existing.length === 1) {
173+
console.log(` distribution/gentoo/${want} already current`);
174+
break;
175+
}
176+
const source = existing[0];
177+
if (!source) {
178+
console.log(" skip (missing): no ebuild in distribution/gentoo");
179+
break;
180+
}
181+
if (dryRun) {
182+
console.log(` [dry-run] would rename ${source} -> ${want}`);
183+
break;
184+
}
185+
const body = readFileSync(join(ROOT, "distribution/gentoo", source), "utf8");
186+
writeFileSync(join(ROOT, "distribution/gentoo", want), body);
187+
for (const f of existing) {
188+
if (f !== want) rmSync(join(ROOT, "distribution/gentoo", f));
189+
}
190+
console.log(` renamed ${source} -> ${want}`);
142191
break;
192+
}
143193
case "scoop":
144194
patch("distribution/scoop/tronbrowser.json", [
145195
[/"version": "[^"]+"/, `"version": "${version}"`],
@@ -241,6 +291,8 @@ for (const pm of targets) {
241291

242292
const TAP_REPO = process.env.HOMEBREW_TAP_REPO || "profullstack/homebrew-tap";
243293
const SCOOP_REPO = process.env.SCOOP_BUCKET_REPO || "profullstack/scoop-bucket";
294+
const GENTOO_REPO =
295+
process.env.GENTOO_OVERLAY_REPO || "profullstack/gentoo-tronbrowser";
244296

245297
function run(cmd, args, opts = {}) {
246298
return execFileSync(cmd, args, {
@@ -280,7 +332,22 @@ function hasCommand(cmd) {
280332
* created through the API, and is revoked by deleting it from that repo.
281333
*/
282334
function pushFileToRepo({ repo, sshKey, file, dest, message }) {
283-
const dir = tmp(dest.replace(/[^a-z0-9]+/gi, "-"));
335+
return pushFilesToRepo({
336+
repo,
337+
sshKey,
338+
message,
339+
files: [{ from: file, dest }],
340+
});
341+
}
342+
343+
/**
344+
* Write several files into a repo we own and push once.
345+
*
346+
* `files` are copied from the working tree, `write` are generated in place (the
347+
* Gentoo Manifest has no on-disk source).
348+
*/
349+
function pushFilesToRepo({ repo, sshKey, files = [], write = [], message }) {
350+
const dir = tmp(repo.replace(/[^a-z0-9]+/gi, "-"));
284351
const keyfile = join(dir, "..", `key-${repo.replace(/\W+/g, "-")}`);
285352
writeFileSync(keyfile, sshKey.endsWith("\n") ? sshKey : `${sshKey}\n`, {
286353
mode: 0o600,
@@ -294,8 +361,14 @@ function pushFileToRepo({ repo, sshKey, file, dest, message }) {
294361
});
295362
// The tap holds Casks/ and Formula/ side by side and the bucket holds bucket/;
296363
// a first push into a directory that does not exist yet has to create it.
297-
run("mkdir", ["-p", dirname(join(dir, dest))]);
298-
run("cp", [join(ROOT, file), join(dir, dest)], { cwd: ROOT });
364+
for (const f of files) {
365+
run("mkdir", ["-p", dirname(join(dir, f.dest))]);
366+
run("cp", [join(ROOT, f.from), join(dir, f.dest)], { cwd: ROOT });
367+
}
368+
for (const w of write) {
369+
run("mkdir", ["-p", dirname(join(dir, w.dest))]);
370+
writeFileSync(join(dir, w.dest), w.content);
371+
}
299372
run("git", ["config", "user.name", "github-actions[bot]"], { cwd: dir });
300373
run("git", [
301374
"config",
@@ -310,10 +383,11 @@ function pushFileToRepo({ repo, sshKey, file, dest, message }) {
310383
console.log(` ${repo} already current, nothing to push`);
311384
return;
312385
}
313-
run("git", ["add", dest], { cwd: dir });
386+
run("git", ["add", "-A"], { cwd: dir });
314387
run("git", ["commit", "-m", message], { cwd: dir });
315388
run("git", ["push"], { cwd: dir, env });
316-
console.log(` pushed ${dest} to ${repo}`);
389+
const names = [...files, ...write].map((f) => f.dest).join(", ");
390+
console.log(` pushed ${names} to ${repo}`);
317391
}
318392

319393
/** Channels that are a PR into a third party's monorepo. */
@@ -429,12 +503,18 @@ const SUBMITTERS = {
429503
console.log(" uploaded to the Snap Store");
430504
},
431505

432-
winget: () =>
433-
upstreamPr(
434-
"winget",
435-
"microsoft/winget-pkgs",
436-
"https://github.com/microsoft/winget-pkgs/blob/master/CONTRIBUTING.md",
437-
),
506+
// Blocked on the artifact, not on credentials or review: the Windows zip has
507+
// no exe, only a .cmd that needs the user to already have Python and Ungoogled
508+
// Chromium. A portable package around that installs and then fails to start.
509+
winget: () => {
510+
console.log(
511+
" winget: the Windows zip has no self-contained executable, so a portable",
512+
);
513+
console.log(
514+
" package would install and fail to start. Needs a real .exe/.msi build",
515+
);
516+
console.log(" before submitting to microsoft/winget-pkgs.");
517+
},
438518
flatpak: () =>
439519
upstreamPr(
440520
"flatpak",
@@ -447,12 +527,39 @@ const SUBMITTERS = {
447527
"NixOS/nixpkgs",
448528
"https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md",
449529
),
450-
gentoo: () =>
451-
upstreamPr(
452-
"gentoo",
453-
"an ebuild overlay",
454-
"https://wiki.gentoo.org/wiki/Ebuild_repository",
455-
),
530+
// Gentoo has no central submission: an overlay is just a git repo laid out the
531+
// way portage expects, and ours is profullstack/gentoo-tronbrowser. So unlike
532+
// winget or flathub this one is entirely ours to push.
533+
gentoo: async () => {
534+
const sshKey = process.env.GENTOO_OVERLAY_SSH_KEY;
535+
if (!sshKey) return skip("gentoo", "GENTOO_OVERLAY_SSH_KEY");
536+
const src = `distribution/gentoo/tronbrowser-bin-${version}.ebuild`;
537+
if (!existsSync(join(ROOT, src))) {
538+
console.log(` gentoo: ${src} is missing, nothing to push`);
539+
return;
540+
}
541+
const d = await gentooDigests(ASSET.linux);
542+
if (!d) {
543+
console.log(" gentoo: could not read the release tarball, skipping");
544+
return;
545+
}
546+
// SRC_URI renames the tarball to ${P}.tar.gz, and the Manifest must name it
547+
// by that renamed filename, not the release asset's.
548+
const manifest =
549+
`DIST tronbrowser-bin-${version}.tar.gz ${d.size}` +
550+
` BLAKE2B ${d.blake2b} SHA512 ${d.sha512}\n`;
551+
pushFilesToRepo({
552+
repo: GENTOO_REPO,
553+
sshKey,
554+
message: `net-misc/tronbrowser-bin: ${version}`,
555+
files: [
556+
{ from: src, dest: `net-misc/tronbrowser-bin/tronbrowser-bin-${version}.ebuild` },
557+
],
558+
write: [
559+
{ dest: "net-misc/tronbrowser-bin/Manifest", content: manifest },
560+
],
561+
});
562+
},
456563
freebsd: () =>
457564
upstreamPr(
458565
"freebsd",
@@ -506,7 +613,9 @@ if (!dryRun) {
506613
const submit = SUBMITTERS[pm];
507614
if (!submit) continue;
508615
console.log(`\n== submit ${pm} ==`);
509-
submit();
616+
// gentoo fetches the tarball to build its Manifest, so submitters may be
617+
// async; awaiting a sync one is harmless.
618+
await submit();
510619
}
511620
}
512621

0 commit comments

Comments
 (0)