OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop commits / d6c4c51

Prepare v0.6.1 corrective release
d6c4c5160f3743db7628a91d9bb0a0b9097c2f48
by Local LLM <localllm@local> · 2026-07-15T15:24:23.000Z

Changed files (17) +323 -57

.github/workflows/desktop-native-release.yml +9 -5
……@@ -237,8 +237,9 @@ jobs:
237237 if: runner.os == 'Linux'
238238 run: |
239239 set -euo pipefail
240 rel="$(find appling/build -maxdepth 5 -name '*.AppImage' -type f | head -n1)"
241 [ -n "$rel" ] || { echo "::error::No .AppImage found to bundle"; exit 1; }
240 mapfile -t product_images < <(find appling/build -maxdepth 5 -name 'PearBrowser.AppImage' -type f -not -path '*/_deps/*')
241 [ "${#product_images[@]}" -eq 1 ] || { echo "::error::Expected exactly one PearBrowser.AppImage, found ${#product_images[@]}"; printf '%s\n' "${product_images[@]}"; exit 1; }
242 rel="${product_images[0]}"
242243 abs="$PWD/$rel"; chmod +x "$abs"
243244 work="$RUNNER_TEMP/appimage-gtk"; rm -rf "$work"; mkdir -p "$work"
244245 ( cd "$work" && "$abs" --appimage-extract >/dev/null )
……@@ -248,8 +249,9 @@ jobs:
248249 chmod +x "$tools/linuxdeploy" "$tools/linuxdeploy-plugin-gtk.sh"
249250 export PATH="$tools:$PATH"
250251 ( cd "$work" && linuxdeploy --appimage-extract-and-run --appdir squashfs-root --plugin gtk --output appimage )
251 out="$(find "$work" -maxdepth 1 -name '*.AppImage' -type f | head -n1)"
252 [ -n "$out" ] || { echo "::error::linuxdeploy did not produce an AppImage"; exit 1; }
252 mapfile -t bundled_images < <(find "$work" -maxdepth 1 -name '*.AppImage' -type f)
253 [ "${#bundled_images[@]}" -eq 1 ] || { echo "::error::Expected exactly one bundled AppImage, found ${#bundled_images[@]}"; printf '%s\n' "${bundled_images[@]}"; exit 1; }
254 out="${bundled_images[0]}"
253255 mv -f "$out" "$abs"; chmod +x "$abs"
254256
255257 # Deterministic gate: assert libgtk-4 is now inside the image, in a clean
……@@ -259,7 +261,9 @@ jobs:
259261 if: runner.os == 'Linux'
260262 run: |
261263 set -euo pipefail
262 rel="$(find appling/build -maxdepth 5 -name '*.AppImage' -type f | head -n1)"
264 mapfile -t product_images < <(find appling/build -maxdepth 5 -name 'PearBrowser.AppImage' -type f -not -path '*/_deps/*')
265 [ "${#product_images[@]}" -eq 1 ] || { echo "::error::Expected exactly one PearBrowser.AppImage, found ${#product_images[@]}"; printf '%s\n' "${product_images[@]}"; exit 1; }
266 rel="${product_images[0]}"
263267 docker run --rm -v "$PWD:/w" -w /w ubuntu:22.04 bash -lc '
264268 set -e
265269 apt-get update -qq && apt-get install -y -qq libfuse2 >/dev/null
CHANGELOG.md +22 -0
……@@ -1,5 +1,27 @@
11# Changelog
22
3## v0.6.1 — 2026-07-15
4
5Corrective distribution release aligning the catalogue, Linux package, website,
6GitHub release, and stable Pear channel on one verified version.
7
8### Fixed
9
10- Updated the PearBrowser catalogue row from the stale `0.4.5` label to `0.6.1`
11 and aligned its homepage with the current `pearbrowser.com` Hyperdrive mirror.
12- Excluded the CMake-generated AppImageTool utility from release collection so
13 the normalized Linux download contains PearBrowser itself.
14- Made Linux packaging, metadata inspection, release-asset checks, and download
15 resolution fail closed when more than one product AppImage is present.
16- Made live-catalogue verification assert the PearBrowser version, stable Pear
17 link, and public homepage from the versioned source contract.
18
19### Distribution note
20
21- The normalized Linux AppImage attached to `v0.6.0` contained AppImageTool and
22 is superseded by the verified `v0.6.1` PearBrowser AppImage. macOS, Windows,
23 and stable-Pear users should also use `v0.6.1` so every public surface agrees.
24
325## v0.6.0 — 2026-07-15
426
527Privacy-first browser release: Content Shield (Brave-grade ad/tracker blocking for `hyper://` and proxied clearnet), Pear Plugins foundation, optional history, zero telemetry, and on-device Ask Browser / QVAC AI kept operational.
README.md +16 -16
……@@ -4,21 +4,21 @@ A local-first peer-to-peer browser, app store, search engine, naming layer, Nost
44
55**No accounts. No DNS. No manual app updates.** Sites and apps are addressed by stable Pear/Hyperdrive keys and pinned 24/7 on the [HiveRelay](https://github.com/bigdestiny2/P2P-Hiverelay) backbone. The publisher's laptop being offline doesn't matter — the relays carry the bytes, and users launch the current release from the catalogue without hunting for a download or applying an updater.
66
7**Current release:** `v0.6.0` · stable Pear production length `63158` · fresh-peer metadata/content scan verified `8515` entries with release-only exclusions intact · five HiveRelay pin acceptances and five live remote replication peers observed. The publisher-side fresh-peer blob listing timed out behind the local NAT, so the release claim is backed by the successful metadata/content scan plus durable remote replication rather than a same-box blob sample. Desktop packages default to npm `latest` for HiveRelay, with the current dist-tag resolving to `0.20.2` and runtime compatibility checked through relay capability documents. See [docs/RELEASE_v0.6.0_OPERATOR.md](./docs/RELEASE_v0.6.0_OPERATOR.md) for the full publish checklist.
7**Current release candidate:** `v0.6.1` · the stable Pear production length and fresh-peer evidence will be recorded here immediately after publication. This corrective release aligns the catalogue, public website, GitHub assets, native metadata, and stable Pear channel, and replaces the incorrect normalized Linux AppImage attached to `v0.6.0`. Desktop packages default to npm `latest` for HiveRelay, with the current dist-tag resolving to `0.20.2` and runtime compatibility checked through relay capability documents. See [docs/RELEASE_v0.6.1_OPERATOR.md](./docs/RELEASE_v0.6.1_OPERATOR.md) for the coordinated publish checklist.
88
99**Current architecture:** start with [docs/ARCHITECTURE_AND_CAPABILITIES.md](./docs/ARCHITECTURE_AND_CAPABILITIES.md). The deeper catalogue/search/naming/Nostr audit is in [docs/DEEP_AUDIT_CATALOG_SEARCH_NAMING_NOSTR_2026-06-21.md](./docs/DEEP_AUDIT_CATALOG_SEARCH_NAMING_NOSTR_2026-06-21.md).
1010
1111## Install it
1212
1313Primary desktop distribution is now native GitHub release packages. The current
14`v0.6.0` release has attached macOS, Windows, and Linux assets with SHA-256
14`v0.6.1` release has attached macOS, Windows, and Linux assets with SHA-256
1515sidecars and platform manifests. Download from the
16[`v0.6.0` release](https://github.com/bigdestiny2/pearbrowser-desktop/releases/tag/v0.6.0),
16[`v0.6.1` release](https://github.com/bigdestiny2/pearbrowser-desktop/releases/tag/v0.6.1),
1717follow the [native install guide](./docs/INSTALL_NATIVE_PACKAGES.md), or resolve
1818the recommended asset for your machine from a source checkout:
1919
2020```sh
21npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
21npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
2222```
2323
2424The current package targets match the `cmake-pear` appling toolchain:
……@@ -41,7 +41,7 @@ The key above is **content-addressed and stable**: when we ship a new release, t
4141
4242## What's inside
4343
44### Browse (v0.4.x)
44### Browse
4545- Multi-tab browsing with proper keyboard shortcuts: `⌘T` new tab · `⌘W` close · `⌘L` focus URL bar · `⌘1`–`⌘9` switch · `⌘R` reload · `⌘⇧I` devtools
4646- `hyper://` URL bar accepting hex (64-char) or z-base-32 (52-char) keys
4747- Localhost HTTP proxy resolves Hyperdrive content for the Chromium engine
……@@ -220,29 +220,29 @@ node scripts/verify-live-catalog.js --expect-app peercord --expect-app peerit --
220220
221221## Distribution
222222
223The `appling/` directory contains the multi-architecture native shell — Bare + CMake builds for macOS / Windows / Linux. GitHub release assets are produced by `.github/workflows/desktop-native-release.yml`, which builds the appling on hosted macOS, Windows, and Linux runners, collects the native artifacts, writes SHA-256 sidecars, and attaches them to the matching release tag. Run the workflow manually with tag `v0.6.0` and `source_ref` set to the release commit to produce or refresh the attached release assets.
223The `appling/` directory contains the multi-architecture native shell — Bare + CMake builds for macOS / Windows / Linux. GitHub release assets are produced by `.github/workflows/desktop-native-release.yml`, which builds the appling on hosted macOS, Windows, and Linux runners, collects the native artifacts, writes SHA-256 sidecars, and attaches them to the matching release tag. Run the workflow manually with tag `v0.6.1` and `source_ref` set to the release commit to produce or refresh the attached release assets.
224224
225225Current generated artifacts are `.app.zip` on macOS, `.msix` on Windows, and `.AppImage` on Linux. The workflow uses `npm ci --prefix appling`, so update `appling/package-lock.json` deliberately when the native wrapper toolchain changes.
226226
227227```sh
228npm run check:appling-release -- --tag v0.6.0
228npm run check:appling-release -- --tag v0.6.1
229229npm run check:linux-appimage-metadata
230npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
231npm run -s generate:native-signing-secret-plan -- --repo bigdestiny2/pearbrowser-desktop --tag v0.6.0 --source-ref <release-commit>
232npm run -s generate:native-install-snippet -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
233npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
234npm run -s generate:native-install-smoke-plan -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit>
235npm run generate:package-manager-manifests -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --trust-mode package-proof
230npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
231npm run -s generate:native-signing-secret-plan -- --repo bigdestiny2/pearbrowser-desktop --tag v0.6.1 --source-ref <release-commit>
232npm run -s generate:native-install-snippet -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
233npm run -s generate:native-install-guide -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
234npm run -s generate:native-install-smoke-plan -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit>
235npm run generate:package-manager-manifests -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --trust-mode package-proof
236236npm run check:native-signing -- --require-public-trust --secret-source github --repo bigdestiny2/pearbrowser-desktop
237npm run check:public-trust-readiness -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github
238npm run -s generate:public-trust-operator-report -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github
237npm run check:public-trust-readiness -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github
238npm run -s generate:public-trust-operator-report -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github
239239npm run -s generate:release-evidence-handoff
240240cd appling
241241npm ci
242242npm run generate
243243npm run build
244244cd ..
245npm run package:appling -- --tag v0.6.0
245npm run package:appling -- --tag v0.6.1
246246```
247247
248248Code signing is per-platform:
appling/CMakeLists.txt +1 -1
……@@ -332,7 +332,7 @@ add_pear_appling(
332332 pearbrowser_desktop
333333 ID "tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty"
334334 NAME "PearBrowser"
335 VERSION 0.6.0
335 VERSION 0.6.1
336336 AUTHOR "pearbrowser-desktop contributors"
337337 DESCRIPTION "P2P browser, app store, and publishing platform — no servers required."
338338
appling/assets/linux/io.github.bigdestiny2.pearbrowser.metainfo.xml +1 -0
……@@ -20,6 +20,7 @@
2020 </categories>
2121 <url type="homepage">https://github.com/bigdestiny2/pearbrowser-desktop</url>
2222 <releases>
23 <release version="0.6.1" date="2026-07-15"/>
2324 <release version="0.6.0" date="2026-07-13"/>
2425 <release version="0.5.2" date="2026-07-01"/>
2526 </releases>
backend/catalogue-seed.js +1 -1
……@@ -26,7 +26,7 @@ module.exports = {
2626 "p2p"
2727 ],
2828 "description": "P2P browser, decentralized app store, and site publisher. macOS / Windows / Linux. Talks to peers directly over Hyperswarm. Pinned 24/7 on HiveRelay.",
29 "version": "0.4.5",
29 "version": "0.6.1",
3030 "homepage": "hyper://03f0060a35451cfb6b68ad1dda1b8474ebb43fd9100071ccf7d67679a83ebb4f/",
3131 "sourceUrl": "https://github.com/bigdestiny2/pearbrowser-desktop",
3232 "license": "MIT",
catalog-source/pearbrowser-network.catalog.json +4 -1
……@@ -10,7 +10,7 @@
1010 "name": "PearBrowser Desktop",
1111 "description": "P2P browser, decentralized app store, and site publisher. macOS / Windows / Linux. Talks to peers directly over Hyperswarm. Pinned 24/7 on HiveRelay.",
1212 "author": "bigdestiny2",
13 "version": "0.4.5",
13 "version": "0.6.1",
1414 "categories": [
1515 "browser",
1616 "tools",
……@@ -83,8 +83,11 @@
8383 "featured"
8484 ],
8585 "publishedAt": "2026-06-23T00:00:00Z",
86 "type": "hypersite",
87 "link": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/",
8688 "url": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/",
8789 "driveKey": "ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74",
90 "homepage": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/",
8891 "source": "pear-ecosystem/02-apps/p2pbuilders",
8992 "license": "MIT",
9093 "verification": "relay-listed"
docs/INSTALL_NATIVE_PACKAGES.md +17 -18
……@@ -1,6 +1,6 @@
11# Install Native Packages
22
3Current release: `v0.6.0`.
3Current release: `v0.6.1`.
44
55These are package-proof desktop builds. Linux uses checksums only. macOS is
66ad-hoc signed but not notarized, and Windows packages are unsigned until the
……@@ -12,18 +12,17 @@ PearBrowser is free of malware until a signed and notarized DMG is published.
1212## Choose A Package
1313
1414Download directly from the
15[`v0.6.0` GitHub release](https://github.com/bigdestiny2/pearbrowser-desktop/releases/tag/v0.6.0).
15[`v0.6.1` GitHub release](https://github.com/bigdestiny2/pearbrowser-desktop/releases/tag/v0.6.1).
1616
1717| Machine | Recommended package | Checksum sidecar |
1818| --- | --- | --- |
19| macOS Apple Silicon | [PearBrowser-0.6.0-macos-arm64.app.zip](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-macos-arm64.app.zip) | [PearBrowser-0.6.0-macos-arm64.app.zip.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-macos-arm64.app.zip.sha256) |
20| macOS Intel | [PearBrowser-0.6.0-macos-x64.app.zip](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-macos-x64.app.zip) | [PearBrowser-0.6.0-macos-x64.app.zip.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-macos-x64.app.zip.sha256) |
21| Windows x64 | [PearBrowser-0.6.0-windows-x64.msix](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-windows-x64.msix) | [PearBrowser-0.6.0-windows-x64.msix.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-windows-x64.msix.sha256) |
22| Linux x64 | [PearBrowser-0.6.0-linux-x64.AppImage](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-linux-x64.AppImage) | [PearBrowser-0.6.0-linux-x64.AppImage.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.0/PearBrowser-0.6.0-linux-x64.AppImage.sha256) |
19| macOS Apple Silicon | [PearBrowser-0.6.1-macos-arm64.app.zip](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-macos-arm64.app.zip) | [PearBrowser-0.6.1-macos-arm64.app.zip.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-macos-arm64.app.zip.sha256) |
20| macOS Intel | [PearBrowser-0.6.1-macos-x64.app.zip](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-macos-x64.app.zip) | [PearBrowser-0.6.1-macos-x64.app.zip.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-macos-x64.app.zip.sha256) |
21| Windows x64 | [PearBrowser-0.6.1-windows-x64.msix](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-windows-x64.msix) | [PearBrowser-0.6.1-windows-x64.msix.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-windows-x64.msix.sha256) |
22| Linux x64 | [PearBrowser-0.6.1-linux-x64.AppImage](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-linux-x64.AppImage) | [PearBrowser-0.6.1-linux-x64.AppImage.sha256](https://github.com/bigdestiny2/pearbrowser-desktop/releases/download/v0.6.1/PearBrowser-0.6.1-linux-x64.AppImage.sha256) |
2323
24The extra Linux AppImage artifact remains attached for package validation, but
25the resolver selects the Windows `.msix` and normalized `.AppImage` as the
26user-facing defaults.
24The Linux release contains exactly one product AppImage. Packaging now rejects
25ambiguous AppImages and excludes build tools such as AppImageTool.
2726
2827For the future public-trust macOS lane, the resolver will prefer notarized
2928`.dmg` assets over `.app.zip` once those assets are attached by the signed
……@@ -32,26 +31,26 @@ native release workflow.
3231From a source checkout, ask the resolver for the current machine:
3332
3433```sh
35npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
34npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
3635```
3736
3837Or specify a target:
3938
4039```sh
41npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --platform macos --arch x64
40npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --platform macos --arch x64
4241```
4342
4443Release operators can verify every recommended package download and checksum
4544sidecar in one pass:
4645
4746```sh
48npm run verify:native-downloads -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --all
47npm run verify:native-downloads -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --all
4948```
5049
5150Release operators can regenerate this guide from the same resolver rules:
5251
5352```sh
54npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop
53npm run -s generate:native-install-guide -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop
5554```
5655
5756## Verify The Download
……@@ -59,7 +58,7 @@ npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pear
5958macOS and Linux:
6059
6160```sh
62shasum -a 256 -c PearBrowser-0.6.0-macos-arm64.app.zip.sha256
61shasum -a 256 -c PearBrowser-0.6.1-macos-arm64.app.zip.sha256
6362```
6463
6564Use the matching filename for your package. A passing check prints `OK`.
……@@ -67,7 +66,7 @@ Use the matching filename for your package. A passing check prints `OK`.
6766Windows PowerShell:
6867
6968```powershell
70$package = "PearBrowser-0.6.0-windows-x64.msix"
69$package = "PearBrowser-0.6.1-windows-x64.msix"
7170$expected = (Get-Content "$($package).sha256").Split(" ")[0].ToLowerInvariant()
7271$actual = (Get-FileHash $package -Algorithm SHA256).Hash.ToLowerInvariant()
7372if ($actual -ne $expected) { throw "SHA-256 mismatch for $package" }
……@@ -89,7 +88,7 @@ macOS:
8988
9089Windows:
9190
921. Install `PearBrowser-0.6.0-windows-x64.msix`.
911. Install `PearBrowser-0.6.1-windows-x64.msix`.
93922. For package-proof builds, Windows SmartScreen may warn because the installer
9493 is not yet Authenticode-signed. Continue only if you intentionally trust this
9594 package and its checksum.
……@@ -97,8 +96,8 @@ Windows:
9796Linux:
9897
9998```sh
100chmod +x PearBrowser-0.6.0-linux-x64.AppImage
101./PearBrowser-0.6.0-linux-x64.AppImage
99chmod +x PearBrowser-0.6.1-linux-x64.AppImage
100./PearBrowser-0.6.1-linux-x64.AppImage
102101```
103102
104103## Recovery Fallback
docs/RELEASE_v0.6.1_OPERATOR.md +76 -0
……@@ -0,0 +1,76 @@
1# PearBrowser Desktop v0.6.1 — corrective release runbook
2
3Date: 2026-07-15
4Tag: `v0.6.1`
5Repo: `bigdestiny2/pearbrowser-desktop`
6Production link: `pear://tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty`
7Catalogue key: `f5fb7500bccd60a976d2b1d24246108f4444a210b9ca591533114dffc089934d`
8Website Hyperdrive key: `03f0060a35451cfb6b68ad1dda1b8474ebb43fd9100071ccf7d67679a83ebb4f`
9
10This patch is complete only when source, stable Pear bytes, live catalogue,
11GitHub release, native packages, `pearbrowser.com`, and its Hyperdrive mirror
12all advertise the same version and pass fresh-reader checks.
13
14## Release blockers being corrected
15
16- The live catalogue and embedded offline seed advertised PearBrowser `0.4.5`.
17- The normalized Linux asset for `v0.6.0` contained AppImageTool; the actual
18 PearBrowser image was attached under an extra suffixed filename.
19
20## Preflight
21
22```sh
23npm test
24npm audit --audit-level=high
25git diff --check
26npm run check:appling-release -- --tag v0.6.1
27npm run check:linux-appimage-metadata -- --json
28```
29
30The Linux packaging fixture must prove that an adjacent
31`appimagetool-*.AppImage` is ignored and `PearBrowser.AppImage` becomes the only
32normalized Linux product. The live catalogue verifier is expected to fail until
33the publisher is updated to `0.6.1`.
34
35## Coordinated publication order
36
371. Merge the tested source candidate to `main` and record `SOURCE_REF`.
382. Publish the stable Pear production link with `./scripts/release-prod.sh`.
393. Record the new Pear length in README and this runbook; merge that record.
404. Publish the live Hyperbee catalogue from
41 `catalog-source/pearbrowser-network.catalog.json` using the persistent
42 publisher storage.
435. Tag the exact release-source commit `v0.6.1` and create the GitHub release.
446. Run `desktop-native-release.yml` in `package-proof` mode.
457. Inspect the Linux AppImage contents and verify all package checksums and
46 embedded versions before publishing website download metadata.
478. Update and deploy `pearbrowser.com`, then republish its stable Hyperdrive
48 mirror.
49
50## Required fresh-reader evidence
51
52```sh
53node scripts/verify-live-catalog.js --expect-app peercord --expect-app peerit --expect-app hiveworm
54npm run check:native-release-assets -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --require-backfill-formats
55npm run verify:native-downloads -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --all
56```
57
58Additionally verify:
59
60- stable Pear package metadata reports `0.6.1` at the newly released length;
61- the live catalogue row reports `0.6.1`, the stable Pear link, and the current
62 website Hyperdrive homepage;
63- the Linux archive extracts to `usr/bin/pearbrowser` and is not AppImageTool;
64- macOS and Windows package metadata reports `0.6.1`;
65- GitHub README, release page, `pearbrowser.com`, and the website Hyperdrive
66 mirror expose the same version, links, file sizes, and SHA-256 values.
67
68## Final record
69
70- Release source: pending
71- Stable Pear length: pending
72- GitHub release: pending
73- Native workflow: pending
74- Live catalogue length: pending
75- Website deployment: pending
76- Website Hyperdrive publication: pending
package-lock.json +2 -2
……@@ -1,12 +1,12 @@
11{
22 "name": "pearbrowser-desktop",
3 "version": "0.6.0",
3 "version": "0.6.1",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "pearbrowser-desktop",
9 "version": "0.6.0",
9 "version": "0.6.1",
1010 "hasInstallScript": true,
1111 "license": "Apache-2.0 AND MIT",
1212 "dependencies": {
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "pearbrowser-desktop",
3 "version": "0.6.0",
3 "version": "0.6.1",
44 "description": "P2P browser + decentralized app store + site builder, desktop Pear fork of PearBrowser (jj10133/Vinjari family)",
55 "license": "Apache-2.0 AND MIT",
66 "type": "module",
scripts/check-linux-appimage-metadata.mjs +10 -6
……@@ -75,19 +75,23 @@ function validateBuildDir (buildDir) {
7575 }
7676
7777 const appDirs = findPaths(buildDir, (path) => path.endsWith('.AppDir') && isDirectory(path))
78 if (appDirs.length) {
79 for (const appDir of appDirs) validateAppDir(appDir, 'build-dir')
80 return
81 }
78 for (const appDir of appDirs) validateAppDir(appDir, 'build-dir')
8279
83 const appImages = findPaths(buildDir, (path) => /\.AppImage$/i.test(path) && isFile(path))
84 if (appImages.length === 0) {
80 const appImages = findPaths(buildDir, (path) => isProductAppImage(path) && isFile(path))
81 if (appDirs.length === 0 && appImages.length === 0) {
8582 errors.push(`Linux build directory has no .AppDir or .AppImage to inspect: ${buildDir}`)
8683 return
8784 }
85 if (appImages.length > 1) {
86 errors.push(`Linux build directory must contain exactly one PearBrowser AppImage, found ${appImages.length}: ${appImages.join(', ')}`)
87 }
8888 for (const appImage of appImages) validateAppImage(appImage)
8989}
9090
91function isProductAppImage (path) {
92 return /^PearBrowser(?:[-_.].*)?\.AppImage$/i.test(basename(path))
93}
94
9195function validateAppImage (appImage) {
9296 if (!validateFile(appImage, 'Linux AppImage')) return
9397 if (process.platform !== 'linux') {
scripts/check-native-release-assets.mjs +6 -0
……@@ -246,6 +246,12 @@ function verifyPlatform (platform, assets, names, version, errors, options = {})
246246 errors.push(`missing required v0.5.0 backfill ${required.label} for ${platform}/${arch}`)
247247 }
248248 }
249 if (platform === 'linux') {
250 const appImages = (artifactsByArch.get(arch) || []).filter((asset) => /\.AppImage$/i.test(asset.name))
251 if (appImages.length !== 1) {
252 errors.push(`expected exactly one Linux .AppImage for ${platform}/${arch}, found ${appImages.length}: ${appImages.map((asset) => asset.name).join(', ') || '(none)'}`)
253 }
254 }
249255 }
250256
251257 const sidecars = []
scripts/collect-appling-artifacts.mjs +15 -1
……@@ -56,6 +56,12 @@ if (artifacts.length === 0) {
5656 console.error(' run `npm run --prefix appling generate` and `npm run --prefix appling build` first')
5757 process.exit(1)
5858}
59if (platform === 'linux') {
60 const appImages = artifacts.filter((artifact) => /\.AppImage$/i.test(artifact.path))
61 if (appImages.length > 1) {
62 fail(`expected exactly one PearBrowser AppImage under ${buildDir}, found ${appImages.length}: ${appImages.map((artifact) => relative(resolve('.'), artifact.path)).join(', ')}`)
63 }
64}
5965
6066const outputs = []
6167const extCounts = new Map()
……@@ -195,7 +201,7 @@ function findArtifacts (root, targetPlatform) {
195201 }
196202 if (shouldSkipDir(entry.name)) continue
197203 walk(path)
198 } else if (entry.isFile() && isReleaseFile(entry.name, targetPlatform) && !isRawWindowsLauncher(entry.name, targetPlatform)) {
204 } else if (entry.isFile() && isReleaseFile(entry.name, targetPlatform) && !isPackagingToolArtifact(entry.name, targetPlatform) && !isRawWindowsLauncher(entry.name, targetPlatform)) {
199205 const stats = statSync(path)
200206 if (stats.size > 0) found.push({ kind: 'file', path })
201207 }
……@@ -211,6 +217,14 @@ function isReleaseFile (name, targetPlatform) {
211217 return filePatterns[targetPlatform].some((pattern) => pattern.test(name))
212218}
213219
220// cmake-pear builds AppImageTool as an implementation detail beside the actual
221// PearBrowser image. It is executable and ends in .AppImage, but it is not an
222// application release artifact. Collecting it previously made the tool image
223// the normalized Linux download because it sorted before PearBrowser.AppImage.
224function isPackagingToolArtifact (name, targetPlatform) {
225 return targetPlatform === 'linux' && /^(?:appimagetool|linuxdeploy)(?:[-_.].*)?\.AppImage$/i.test(name)
226}
227
214228// The bare-pear Windows build emits a raw "<AppName>.exe" launcher next to the
215229// .msix. It is the app executable, not an installer, and shipping it alongside
216230// the package confuses the download surface — drop it. A real installer (e.g.
scripts/resolve-native-release-asset.mjs +6 -0
……@@ -155,6 +155,12 @@ function resolveReleaseAsset (release, options) {
155155 throw new Error(`no ${options.platform}/${options.arch} native artifact found for ${options.tag}`)
156156 }
157157
158 const bestRank = artifactRank(options.platform, candidates[0].name)
159 const equallyPreferred = candidates.filter((candidate) => artifactRank(options.platform, candidate.name) === bestRank)
160 if (equallyPreferred.length > 1) {
161 throw new Error(`ambiguous ${options.platform}/${options.arch} native artifacts for ${options.tag}: ${equallyPreferred.map((candidate) => candidate.name).join(', ')}`)
162 }
163
158164 const asset = candidates[0]
159165 if (asset.size <= 0) throw new Error(`${asset.name} is empty or missing a size`)
160166
scripts/verify-live-catalog.js +26 -2
……@@ -15,12 +15,17 @@ import Corestore from 'corestore'
1515import Hyperswarm from 'hyperswarm'
1616import Hyperbee from 'hyperbee'
1717import b4a from 'b4a'
18import { mkdtempSync, rmSync } from 'node:fs'
18import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
1919import { join } from 'node:path'
2020import { tmpdir } from 'node:os'
2121import { readCatalogBee } from './lib/catalog-bee.js'
2222
2323const DEFAULT_KEY = 'f5fb7500bccd60a976d2b1d24246108f4444a210b9ca591533114dffc089934d'
24const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
25const pearConfig = JSON.parse(readFileSync(new URL('../pear.json', import.meta.url), 'utf8'))
26const catalogSource = JSON.parse(readFileSync(new URL('../catalog-source/pearbrowser-network.catalog.json', import.meta.url), 'utf8'))
27const browserSource = catalogSource.apps.find((app) => app.id === 'pearbrowser-desktop')
28if (!browserSource) throw new Error('catalogue source is missing pearbrowser-desktop')
2429const PEER_TIMEOUT_MS = 30_000
2530const UPDATE_TIMEOUT_MS = 20_000
2631const READ_TIMEOUT_MS = 20_000
……@@ -30,7 +35,10 @@ function parseArgs (argv) {
3035 key: DEFAULT_KEY,
3136 expectApps: [],
3237 expectCount: 14,
33 expectName: 'PearBrowser Network'
38 expectName: 'PearBrowser Network',
39 expectBrowserVersion: pkg.version,
40 expectBrowserLink: pearConfig.links.production,
41 expectBrowserHomepage: browserSource.homepage
3442 }
3543 for (let i = 0; i < argv.length; i++) {
3644 const arg = argv[i]
……@@ -38,6 +46,9 @@ function parseArgs (argv) {
3846 else if (arg === '--expect-app') args.expectApps.push(argv[++i])
3947 else if (arg === '--expect-count') args.expectCount = Number(argv[++i])
4048 else if (arg === '--expect-name') args.expectName = argv[++i]
49 else if (arg === '--expect-browser-version') args.expectBrowserVersion = argv[++i]
50 else if (arg === '--expect-browser-link') args.expectBrowserLink = argv[++i]
51 else if (arg === '--expect-browser-homepage') args.expectBrowserHomepage = argv[++i]
4152 }
4253 return args
4354}
……@@ -111,6 +122,18 @@ async function main () {
111122 if (!byId.has(id)) fail(`missing expected app: ${id}`)
112123 }
113124
125 const pearbrowser = byId.get('pearbrowser-desktop')
126 if (!pearbrowser) fail('missing expected app: pearbrowser-desktop')
127 if (pearbrowser.version !== args.expectBrowserVersion) {
128 fail(`PearBrowser version mismatch: expected ${args.expectBrowserVersion}, got ${pearbrowser.version || '(missing)'}`)
129 }
130 if (pearbrowser.link !== args.expectBrowserLink) {
131 fail(`PearBrowser link mismatch: expected ${args.expectBrowserLink}, got ${pearbrowser.link || '(missing)'}`)
132 }
133 if (pearbrowser.homepage !== args.expectBrowserHomepage) {
134 fail(`PearBrowser homepage mismatch: expected ${args.expectBrowserHomepage}, got ${pearbrowser.homepage || '(missing)'}`)
135 }
136
114137 const peercord = byId.get('peercord')
115138 if (peercord) {
116139 if (peercord.link !== 'pear://wmir47w7mai3b1skj66mx7fzso6k6o91kipaney7gtt69npimouy') fail('Peercord link mismatch')
……@@ -135,6 +158,7 @@ async function main () {
135158
136159 console.log(' → signed meta signature:', signedMeta.value.signature.slice(0, 16) + '…')
137160 console.log(' → catalogue:', data.name, '· apps:', data.apps.length)
161 console.log(' → PearBrowser:', pearbrowser.version, '·', pearbrowser.link, '·', pearbrowser.homepage)
138162 if (peercord) console.log(' → Peercord:', peercord.type, '·', peercord.sourceUrl, '·', peercord.license)
139163 console.log(' → expected apps:', args.expectApps.length ? args.expectApps.join(', ') : '(none)')
140164 console.log()
test/release-packaging.test.js +110 -3
……@@ -1,6 +1,7 @@
11import test from 'node:test'
22import assert from 'node:assert/strict'
33import { createHash } from 'node:crypto'
4import { createRequire } from 'node:module'
45import { execFileSync, spawnSync } from 'node:child_process'
56import {
67 copyFileSync,
……@@ -17,8 +18,11 @@ import { join } from 'node:path'
1718import { fileURLToPath, pathToFileURL } from 'node:url'
1819
1920const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
21const require = createRequire(import.meta.url)
2022const packageLock = JSON.parse(readFileSync(new URL('../package-lock.json', import.meta.url), 'utf8'))
2123const pearConfig = JSON.parse(readFileSync(new URL('../pear.json', import.meta.url), 'utf8'))
24const catalogSource = JSON.parse(readFileSync(new URL('../catalog-source/pearbrowser-network.catalog.json', import.meta.url), 'utf8'))
25const { SEED_APPS } = require('../backend/catalogue-seed.js')
2226const rootLicense = readFileSync(new URL('../LICENSE', import.meta.url), 'utf8')
2327const applingPkg = JSON.parse(readFileSync(new URL('../appling/package.json', import.meta.url), 'utf8'))
2428const applingLock = JSON.parse(readFileSync(new URL('../appling/package-lock.json', import.meta.url), 'utf8'))
……@@ -266,6 +270,24 @@ test('Pear stage ignore excludes local release/operator scratch files', () => {
266270 assert.ok(ignored.includes('/test'))
267271})
268272
273test('PearBrowser catalogue release row stays in sync with package and public addresses', () => {
274 const source = catalogSource.apps.find((app) => app.id === 'pearbrowser-desktop')
275 const seed = SEED_APPS.find((app) => app.name === 'PearBrowser Desktop')
276 const homepageKey = '03f0060a35451cfb6b68ad1dda1b8474ebb43fd9100071ccf7d67679a83ebb4f'
277
278 assert.ok(source, 'catalogue source row missing')
279 assert.ok(seed, 'offline catalogue seed row missing')
280 assert.equal(source.version, pkg.version)
281 assert.equal(source.pearLink, pearConfig.links.production)
282 assert.equal(source.link, pearConfig.links.production)
283 assert.equal(source.driveKey, homepageKey)
284 assert.equal(source.homepage, `hyper://${homepageKey}/`)
285 assert.equal(seed.version, source.version)
286 assert.equal(seed.link, source.link)
287 assert.equal(seed.driveKey, source.driveKey)
288 assert.equal(seed.homepage, source.homepage)
289})
290
269291test('release script purges ignored files from previous Pear stages', () => {
270292 assert.match(releaseScript, /pear stage --purge/)
271293})
……@@ -488,6 +510,9 @@ test('Linux AppImage metadata checker is exposed for desktop integration gates',
488510 assert.match(linuxAppImageMetadata, /io\.github\.bigdestiny2\.pearbrowser\.metainfo\.xml/)
489511 assert.match(linuxAppImageMetadata, /metadata_license/)
490512 assert.match(linuxAppImageMetadata, /--build-dir/)
513 assert.match(linuxAppImageMetadata, /isProductAppImage/)
514 assert.match(linuxAppImageMetadata, /exactly one PearBrowser AppImage/)
515 assert.doesNotMatch(linuxAppImageMetadata, /if \(appDirs\.length\) \{[\s\S]*?return\n\s*\}/)
491516 assert.match(linuxMetainfo, /<component type="desktop-application">/)
492517 assert.match(linuxMetainfo, /<launchable type="desktop-id">PearBrowser\.desktop<\/launchable>/)
493518 assert.match(linuxMetainfo, /<binary>pearbrowser<\/binary>/)
……@@ -771,6 +796,9 @@ test('native release workflow builds and attaches appling artifacts for every de
771796 assert.match(nativeReleaseWorkflow, /npm run --prefix appling build/)
772797 assert.match(nativeReleaseWorkflow, /Verify Linux AppImage metadata/)
773798 assert.match(nativeReleaseWorkflow, /check:linux-appimage-metadata -- --build-dir appling\/build/)
799 assert.match(nativeReleaseWorkflow, /-name 'PearBrowser\.AppImage'/)
800 assert.match(nativeReleaseWorkflow, /Expected exactly one PearBrowser\.AppImage/)
801 assert.doesNotMatch(nativeReleaseWorkflow, /AppImage' -type f \| head -n1/)
774802 assert.match(nativeReleaseWorkflow, /actions\/upload-artifact@v4/)
775803 assert.match(nativeReleaseWorkflow, /actions\/download-artifact@v4/)
776804 assert.match(nativeReleaseWorkflow, /release-platform: macos/)
……@@ -995,6 +1023,37 @@ test('appling artifact collector emits normalized assets and checksum manifests'
9951023 }
9961024})
9971025
1026test('appling artifact collector excludes AppImageTool and packages PearBrowser', () => {
1027 const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-linux-appling-release-')))
1028 try {
1029 const buildDir = join(fixture, 'appling', 'build', 'nested')
1030 mkdirSync(buildDir, { recursive: true })
1031 writeFileSync(join(buildDir, 'appimagetool-x86_64.AppImage'), 'packaging tool bytes')
1032 writeFileSync(join(buildDir, 'PearBrowser.AppImage'), 'pearbrowser product bytes')
1033
1034 execFileSync(process.execPath, [
1035 applingArtifactCollectorPath,
1036 '--tag',
1037 releaseTag,
1038 '--platform',
1039 'linux',
1040 '--arch',
1041 'x64',
1042 '--build-dir',
1043 join(fixture, 'appling', 'build')
1044 ], { cwd: fixture, encoding: 'utf8' })
1045
1046 const outDir = join(fixture, 'dist', 'appling-release', releaseTag, 'linux')
1047 const assetName = `PearBrowser-${releaseVersion}-linux-x64.AppImage`
1048 assert.equal(readFileSync(join(outDir, assetName), 'utf8'), 'pearbrowser product bytes')
1049 const manifest = JSON.parse(readFileSync(join(outDir, 'manifest-linux-x64.json'), 'utf8'))
1050 assert.equal(manifest.artifacts.length, 1)
1051 assert.equal(manifest.artifacts[0].source, 'appling/build/nested/PearBrowser.AppImage')
1052 } finally {
1053 rmSync(fixture, { recursive: true, force: true })
1054 }
1055})
1056
9981057test('appling artifact collector refuses unsafe output directories before clearing them', () => {
9991058 const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-appling-release-')))
10001059 try {
……@@ -1232,8 +1291,6 @@ test('native release asset resolver chooses the recommended package for each des
12321291 'PearBrowser-0.5.0-windows-x64.msix.sha256',
12331292 'PearBrowser-0.5.0-windows-x64.exe',
12341293 'PearBrowser-0.5.0-windows-x64.exe.sha256',
1235 'PearBrowser-0.5.0-linux-x64-PearBrowser.AppImage',
1236 'PearBrowser-0.5.0-linux-x64-PearBrowser.AppImage.sha256',
12371294 'PearBrowser-0.5.0-linux-x64.AppImage',
12381295 'PearBrowser-0.5.0-linux-x64.AppImage.sha256'
12391296 ].map((name, i) => ({
……@@ -1271,6 +1328,53 @@ test('native release asset resolver chooses the recommended package for each des
12711328 }
12721329})
12731330
1331test('native release asset resolver rejects ambiguous equally preferred packages', () => {
1332 const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-native-resolver-ambiguous-')))
1333 try {
1334 const releasePath = join(fixture, 'release.json')
1335 writeFileSync(releasePath, JSON.stringify({
1336 tagName: 'v0.5.0',
1337 isDraft: false,
1338 isPrerelease: false,
1339 assets: [
1340 'PearBrowser-0.5.0-linux-x64.AppImage',
1341 'PearBrowser-0.5.0-linux-x64.AppImage.sha256',
1342 'PearBrowser-0.5.0-linux-x64-PearBrowser.AppImage',
1343 'PearBrowser-0.5.0-linux-x64-PearBrowser.AppImage.sha256'
1344 ].map((name, i) => ({ name, size: i + 1 }))
1345 }, null, 2))
1346
1347 const assetCheck = spawnSync(process.execPath, [
1348 nativeReleaseAssetCheckPath,
1349 '--fixture',
1350 releasePath,
1351 '--tag',
1352 'v0.5.0',
1353 '--json'
1354 ], { encoding: 'utf8' })
1355 assert.notEqual(assetCheck.status, 0)
1356 assert.ok(JSON.parse(assetCheck.stdout).errors.some((error) => error.includes('expected exactly one Linux .AppImage')))
1357
1358 const result = spawnSync(process.execPath, [
1359 nativeReleaseAssetResolverPath,
1360 '--fixture',
1361 releasePath,
1362 '--tag',
1363 'v0.5.0',
1364 '--platform',
1365 'linux',
1366 '--arch',
1367 'x64',
1368 '--json'
1369 ], { encoding: 'utf8' })
1370
1371 assert.notEqual(result.status, 0)
1372 assert.match(JSON.parse(result.stdout).error, /ambiguous linux\/x64 native artifacts/)
1373 } finally {
1374 rmSync(fixture, { recursive: true, force: true })
1375 }
1376})
1377
12741378test('native install snippet generator emits release-note packages for every desktop target', () => {
12751379 const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-native-install-snippet-')))
12761380 try {
……@@ -2139,7 +2243,10 @@ test('release story smoke covers browse, catalogue, local stories, and opt-in si
21392243 assert.doesNotMatch(releaseStorySmoke, /CMD_RUN_APP_IN_TAB/)
21402244})
21412245
2142test('live catalogue verifier asserts Peercord provenance metadata', () => {
2246test('live catalogue verifier asserts PearBrowser release contract and Peercord provenance metadata', () => {
2247 assert.match(liveCatalogVerifier, /PearBrowser version mismatch/)
2248 assert.match(liveCatalogVerifier, /PearBrowser link mismatch/)
2249 assert.match(liveCatalogVerifier, /PearBrowser homepage mismatch/)
21432250 assert.match(liveCatalogVerifier, /Peercord sourceUrl mismatch/)
21442251 assert.match(liveCatalogVerifier, /https:\/\/git\.churchofmalware\.org\/mastercodeon\/Peercord/)
21452252 assert.match(liveCatalogVerifier, /Peercord license mismatch/)