| … | … | @@ -237,8 +237,9 @@ jobs: |
| 237 | 237 | if: runner.os == 'Linux' |
| 238 | 238 | run: | |
| 239 | 239 | 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]}" | |
| 242 | 243 | abs="$PWD/$rel"; chmod +x "$abs" |
| 243 | 244 | work="$RUNNER_TEMP/appimage-gtk"; rm -rf "$work"; mkdir -p "$work" |
| 244 | 245 | ( cd "$work" && "$abs" --appimage-extract >/dev/null ) |
| … | … | @@ -248,8 +249,9 @@ jobs: |
| 248 | 249 | chmod +x "$tools/linuxdeploy" "$tools/linuxdeploy-plugin-gtk.sh" |
| 249 | 250 | export PATH="$tools:$PATH" |
| 250 | 251 | ( 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]}" | |
| 253 | 255 | mv -f "$out" "$abs"; chmod +x "$abs" |
| 254 | 256 | |
| 255 | 257 | # Deterministic gate: assert libgtk-4 is now inside the image, in a clean |
| … | … | @@ -259,7 +261,9 @@ jobs: |
| 259 | 261 | if: runner.os == 'Linux' |
| 260 | 262 | run: | |
| 261 | 263 | 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]}" | |
| 263 | 267 | docker run --rm -v "$PWD:/w" -w /w ubuntu:22.04 bash -lc ' |
| 264 | 268 | set -e |
| 265 | 269 | apt-get update -qq && apt-get install -y -qq libfuse2 >/dev/null |
| … | … | @@ -1,5 +1,27 @@ |
| 1 | 1 | # Changelog |
| 2 | 2 | |
| 3 | ## v0.6.1 — 2026-07-15 | |
| 4 | ||
| 5 | Corrective distribution release aligning the catalogue, Linux package, website, | |
| 6 | GitHub 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 | ||
| 3 | 25 | ## v0.6.0 — 2026-07-15 |
| 4 | 26 | |
| 5 | 27 | Privacy-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. |
| … | … | @@ -4,21 +4,21 @@ A local-first peer-to-peer browser, app store, search engine, naming layer, Nost |
| 4 | 4 | |
| 5 | 5 | **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. |
| 6 | 6 | |
| 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. | |
| 8 | 8 | |
| 9 | 9 | **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). |
| 10 | 10 | |
| 11 | 11 | ## Install it |
| 12 | 12 | |
| 13 | 13 | Primary 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 | |
| 15 | 15 | sidecars 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), | |
| 17 | 17 | follow the [native install guide](./docs/INSTALL_NATIVE_PACKAGES.md), or resolve |
| 18 | 18 | the recommended asset for your machine from a source checkout: |
| 19 | 19 | |
| 20 | 20 | ```sh |
| 21 | npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 21 | npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 22 | 22 | ``` |
| 23 | 23 | |
| 24 | 24 | The 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 |
| 41 | 41 | |
| 42 | 42 | ## What's inside |
| 43 | 43 | |
| 44 | ### Browse (v0.4.x) | |
| 44 | ### Browse | |
| 45 | 45 | - Multi-tab browsing with proper keyboard shortcuts: `⌘T` new tab · `⌘W` close · `⌘L` focus URL bar · `⌘1`–`⌘9` switch · `⌘R` reload · `⌘⇧I` devtools |
| 46 | 46 | - `hyper://` URL bar accepting hex (64-char) or z-base-32 (52-char) keys |
| 47 | 47 | - 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 -- |
| 220 | 220 | |
| 221 | 221 | ## Distribution |
| 222 | 222 | |
| 223 | The `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. | |
| 223 | The `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. | |
| 224 | 224 | |
| 225 | 225 | Current 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. |
| 226 | 226 | |
| 227 | 227 | ```sh |
| 228 | npm run check:appling-release -- --tag v0.6.0 | |
| 228 | npm run check:appling-release -- --tag v0.6.1 | |
| 229 | 229 | npm run check:linux-appimage-metadata |
| 230 | npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 231 | npm run -s generate:native-signing-secret-plan -- --repo bigdestiny2/pearbrowser-desktop --tag v0.6.0 --source-ref <release-commit> | |
| 232 | npm run -s generate:native-install-snippet -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 233 | npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 234 | npm run -s generate:native-install-smoke-plan -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> | |
| 235 | npm run generate:package-manager-manifests -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --trust-mode package-proof | |
| 230 | npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 231 | npm run -s generate:native-signing-secret-plan -- --repo bigdestiny2/pearbrowser-desktop --tag v0.6.1 --source-ref <release-commit> | |
| 232 | npm run -s generate:native-install-snippet -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 233 | npm run -s generate:native-install-guide -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 234 | npm run -s generate:native-install-smoke-plan -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> | |
| 235 | npm run generate:package-manager-manifests -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --trust-mode package-proof | |
| 236 | 236 | npm run check:native-signing -- --require-public-trust --secret-source github --repo bigdestiny2/pearbrowser-desktop |
| 237 | npm run check:public-trust-readiness -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github | |
| 238 | npm run -s generate:public-trust-operator-report -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github | |
| 237 | npm run check:public-trust-readiness -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github | |
| 238 | npm run -s generate:public-trust-operator-report -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --source-ref <release-commit> --signing-secret-source github | |
| 239 | 239 | npm run -s generate:release-evidence-handoff |
| 240 | 240 | cd appling |
| 241 | 241 | npm ci |
| 242 | 242 | npm run generate |
| 243 | 243 | npm run build |
| 244 | 244 | cd .. |
| 245 | npm run package:appling -- --tag v0.6.0 | |
| 245 | npm run package:appling -- --tag v0.6.1 | |
| 246 | 246 | ``` |
| 247 | 247 | |
| 248 | 248 | Code signing is per-platform: |
| … | … | @@ -332,7 +332,7 @@ add_pear_appling( |
| 332 | 332 | pearbrowser_desktop |
| 333 | 333 | ID "tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty" |
| 334 | 334 | NAME "PearBrowser" |
| 335 | VERSION 0.6.0 | |
| 335 | VERSION 0.6.1 | |
| 336 | 336 | AUTHOR "pearbrowser-desktop contributors" |
| 337 | 337 | DESCRIPTION "P2P browser, app store, and publishing platform — no servers required." |
| 338 | 338 |
| … | … | @@ -20,6 +20,7 @@ |
| 20 | 20 | </categories> |
| 21 | 21 | <url type="homepage">https://github.com/bigdestiny2/pearbrowser-desktop</url> |
| 22 | 22 | <releases> |
| 23 | <release version="0.6.1" date="2026-07-15"/> | |
| 23 | 24 | <release version="0.6.0" date="2026-07-13"/> |
| 24 | 25 | <release version="0.5.2" date="2026-07-01"/> |
| 25 | 26 | </releases> |
| … | … | @@ -26,7 +26,7 @@ module.exports = { |
| 26 | 26 | "p2p" |
| 27 | 27 | ], |
| 28 | 28 | "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", | |
| 30 | 30 | "homepage": "hyper://03f0060a35451cfb6b68ad1dda1b8474ebb43fd9100071ccf7d67679a83ebb4f/", |
| 31 | 31 | "sourceUrl": "https://github.com/bigdestiny2/pearbrowser-desktop", |
| 32 | 32 | "license": "MIT", |
| … | … | @@ -10,7 +10,7 @@ |
| 10 | 10 | "name": "PearBrowser Desktop", |
| 11 | 11 | "description": "P2P browser, decentralized app store, and site publisher. macOS / Windows / Linux. Talks to peers directly over Hyperswarm. Pinned 24/7 on HiveRelay.", |
| 12 | 12 | "author": "bigdestiny2", |
| 13 | "version": "0.4.5", | |
| 13 | "version": "0.6.1", | |
| 14 | 14 | "categories": [ |
| 15 | 15 | "browser", |
| 16 | 16 | "tools", |
| … | … | @@ -83,8 +83,11 @@ |
| 83 | 83 | "featured" |
| 84 | 84 | ], |
| 85 | 85 | "publishedAt": "2026-06-23T00:00:00Z", |
| 86 | "type": "hypersite", | |
| 87 | "link": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/", | |
| 86 | 88 | "url": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/", |
| 87 | 89 | "driveKey": "ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74", |
| 90 | "homepage": "hyper://ac1977a75cc84b46af0af8bb559cd4ebbe10507eb0f51d863e289d09635f6d74/", | |
| 88 | 91 | "source": "pear-ecosystem/02-apps/p2pbuilders", |
| 89 | 92 | "license": "MIT", |
| 90 | 93 | "verification": "relay-listed" |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | # Install Native Packages |
| 2 | 2 | |
| 3 | Current release: `v0.6.0`. | |
| 3 | Current release: `v0.6.1`. | |
| 4 | 4 | |
| 5 | 5 | These are package-proof desktop builds. Linux uses checksums only. macOS is |
| 6 | 6 | ad-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. |
| 12 | 12 | ## Choose A Package |
| 13 | 13 | |
| 14 | 14 | Download 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). | |
| 16 | 16 | |
| 17 | 17 | | Machine | Recommended package | Checksum sidecar | |
| 18 | 18 | | --- | --- | --- | |
| 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) | | |
| 23 | 23 | |
| 24 | The extra Linux AppImage artifact remains attached for package validation, but | |
| 25 | the resolver selects the Windows `.msix` and normalized `.AppImage` as the | |
| 26 | user-facing defaults. | |
| 24 | The Linux release contains exactly one product AppImage. Packaging now rejects | |
| 25 | ambiguous AppImages and excludes build tools such as AppImageTool. | |
| 27 | 26 | |
| 28 | 27 | For the future public-trust macOS lane, the resolver will prefer notarized |
| 29 | 28 | `.dmg` assets over `.app.zip` once those assets are attached by the signed |
| … | … | @@ -32,26 +31,26 @@ native release workflow. |
| 32 | 31 | From a source checkout, ask the resolver for the current machine: |
| 33 | 32 | |
| 34 | 33 | ```sh |
| 35 | npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 34 | npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 36 | 35 | ``` |
| 37 | 36 | |
| 38 | 37 | Or specify a target: |
| 39 | 38 | |
| 40 | 39 | ```sh |
| 41 | npm run resolve:native-release -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --platform macos --arch x64 | |
| 40 | npm run resolve:native-release -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --platform macos --arch x64 | |
| 42 | 41 | ``` |
| 43 | 42 | |
| 44 | 43 | Release operators can verify every recommended package download and checksum |
| 45 | 44 | sidecar in one pass: |
| 46 | 45 | |
| 47 | 46 | ```sh |
| 48 | npm run verify:native-downloads -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop --all | |
| 47 | npm run verify:native-downloads -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --all | |
| 49 | 48 | ``` |
| 50 | 49 | |
| 51 | 50 | Release operators can regenerate this guide from the same resolver rules: |
| 52 | 51 | |
| 53 | 52 | ```sh |
| 54 | npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pearbrowser-desktop | |
| 53 | npm run -s generate:native-install-guide -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop | |
| 55 | 54 | ``` |
| 56 | 55 | |
| 57 | 56 | ## Verify The Download |
| … | … | @@ -59,7 +58,7 @@ npm run -s generate:native-install-guide -- --tag v0.6.0 --repo bigdestiny2/pear |
| 59 | 58 | macOS and Linux: |
| 60 | 59 | |
| 61 | 60 | ```sh |
| 62 | shasum -a 256 -c PearBrowser-0.6.0-macos-arm64.app.zip.sha256 | |
| 61 | shasum -a 256 -c PearBrowser-0.6.1-macos-arm64.app.zip.sha256 | |
| 63 | 62 | ``` |
| 64 | 63 | |
| 65 | 64 | Use 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`. |
| 67 | 66 | Windows PowerShell: |
| 68 | 67 | |
| 69 | 68 | ```powershell |
| 70 | $package = "PearBrowser-0.6.0-windows-x64.msix" | |
| 69 | $package = "PearBrowser-0.6.1-windows-x64.msix" | |
| 71 | 70 | $expected = (Get-Content "$($package).sha256").Split(" ")[0].ToLowerInvariant() |
| 72 | 71 | $actual = (Get-FileHash $package -Algorithm SHA256).Hash.ToLowerInvariant() |
| 73 | 72 | if ($actual -ne $expected) { throw "SHA-256 mismatch for $package" } |
| … | … | @@ -89,7 +88,7 @@ macOS: |
| 89 | 88 | |
| 90 | 89 | Windows: |
| 91 | 90 | |
| 92 | 1. Install `PearBrowser-0.6.0-windows-x64.msix`. | |
| 91 | 1. Install `PearBrowser-0.6.1-windows-x64.msix`. | |
| 93 | 92 | 2. For package-proof builds, Windows SmartScreen may warn because the installer |
| 94 | 93 | is not yet Authenticode-signed. Continue only if you intentionally trust this |
| 95 | 94 | package and its checksum. |
| … | … | @@ -97,8 +96,8 @@ Windows: |
| 97 | 96 | Linux: |
| 98 | 97 | |
| 99 | 98 | ```sh |
| 100 | chmod +x PearBrowser-0.6.0-linux-x64.AppImage | |
| 101 | ./PearBrowser-0.6.0-linux-x64.AppImage | |
| 99 | chmod +x PearBrowser-0.6.1-linux-x64.AppImage | |
| 100 | ./PearBrowser-0.6.1-linux-x64.AppImage | |
| 102 | 101 | ``` |
| 103 | 102 | |
| 104 | 103 | ## Recovery Fallback |
| … | … | @@ -0,0 +1,76 @@ |
| 1 | # PearBrowser Desktop v0.6.1 — corrective release runbook | |
| 2 | ||
| 3 | Date: 2026-07-15 | |
| 4 | Tag: `v0.6.1` | |
| 5 | Repo: `bigdestiny2/pearbrowser-desktop` | |
| 6 | Production link: `pear://tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty` | |
| 7 | Catalogue key: `f5fb7500bccd60a976d2b1d24246108f4444a210b9ca591533114dffc089934d` | |
| 8 | Website Hyperdrive key: `03f0060a35451cfb6b68ad1dda1b8474ebb43fd9100071ccf7d67679a83ebb4f` | |
| 9 | ||
| 10 | This patch is complete only when source, stable Pear bytes, live catalogue, | |
| 11 | GitHub release, native packages, `pearbrowser.com`, and its Hyperdrive mirror | |
| 12 | all 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 | |
| 23 | npm test | |
| 24 | npm audit --audit-level=high | |
| 25 | git diff --check | |
| 26 | npm run check:appling-release -- --tag v0.6.1 | |
| 27 | npm run check:linux-appimage-metadata -- --json | |
| 28 | ``` | |
| 29 | ||
| 30 | The Linux packaging fixture must prove that an adjacent | |
| 31 | `appimagetool-*.AppImage` is ignored and `PearBrowser.AppImage` becomes the only | |
| 32 | normalized Linux product. The live catalogue verifier is expected to fail until | |
| 33 | the publisher is updated to `0.6.1`. | |
| 34 | ||
| 35 | ## Coordinated publication order | |
| 36 | ||
| 37 | 1. Merge the tested source candidate to `main` and record `SOURCE_REF`. | |
| 38 | 2. Publish the stable Pear production link with `./scripts/release-prod.sh`. | |
| 39 | 3. Record the new Pear length in README and this runbook; merge that record. | |
| 40 | 4. Publish the live Hyperbee catalogue from | |
| 41 | `catalog-source/pearbrowser-network.catalog.json` using the persistent | |
| 42 | publisher storage. | |
| 43 | 5. Tag the exact release-source commit `v0.6.1` and create the GitHub release. | |
| 44 | 6. Run `desktop-native-release.yml` in `package-proof` mode. | |
| 45 | 7. Inspect the Linux AppImage contents and verify all package checksums and | |
| 46 | embedded versions before publishing website download metadata. | |
| 47 | 8. Update and deploy `pearbrowser.com`, then republish its stable Hyperdrive | |
| 48 | mirror. | |
| 49 | ||
| 50 | ## Required fresh-reader evidence | |
| 51 | ||
| 52 | ```sh | |
| 53 | node scripts/verify-live-catalog.js --expect-app peercord --expect-app peerit --expect-app hiveworm | |
| 54 | npm run check:native-release-assets -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --require-backfill-formats | |
| 55 | npm run verify:native-downloads -- --tag v0.6.1 --repo bigdestiny2/pearbrowser-desktop --all | |
| 56 | ``` | |
| 57 | ||
| 58 | Additionally 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 |
| … | … | @@ -1,12 +1,12 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "pearbrowser-desktop", |
| 3 | "version": "0.6.0", | |
| 3 | "version": "0.6.1", | |
| 4 | 4 | "lockfileVersion": 3, |
| 5 | 5 | "requires": true, |
| 6 | 6 | "packages": { |
| 7 | 7 | "": { |
| 8 | 8 | "name": "pearbrowser-desktop", |
| 9 | "version": "0.6.0", | |
| 9 | "version": "0.6.1", | |
| 10 | 10 | "hasInstallScript": true, |
| 11 | 11 | "license": "Apache-2.0 AND MIT", |
| 12 | 12 | "dependencies": { |
| … | … | @@ -1,6 +1,6 @@ |
| 1 | 1 | { |
| 2 | 2 | "name": "pearbrowser-desktop", |
| 3 | "version": "0.6.0", | |
| 3 | "version": "0.6.1", | |
| 4 | 4 | "description": "P2P browser + decentralized app store + site builder, desktop Pear fork of PearBrowser (jj10133/Vinjari family)", |
| 5 | 5 | "license": "Apache-2.0 AND MIT", |
| 6 | 6 | "type": "module", |
| … | … | @@ -75,19 +75,23 @@ function validateBuildDir (buildDir) { |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | 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') | |
| 82 | 79 | |
| 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) { | |
| 85 | 82 | errors.push(`Linux build directory has no .AppDir or .AppImage to inspect: ${buildDir}`) |
| 86 | 83 | return |
| 87 | 84 | } |
| 85 | if (appImages.length > 1) { | |
| 86 | errors.push(`Linux build directory must contain exactly one PearBrowser AppImage, found ${appImages.length}: ${appImages.join(', ')}`) | |
| 87 | } | |
| 88 | 88 | for (const appImage of appImages) validateAppImage(appImage) |
| 89 | 89 | } |
| 90 | 90 | |
| 91 | function isProductAppImage (path) { | |
| 92 | return /^PearBrowser(?:[-_.].*)?\.AppImage$/i.test(basename(path)) | |
| 93 | } | |
| 94 | ||
| 91 | 95 | function validateAppImage (appImage) { |
| 92 | 96 | if (!validateFile(appImage, 'Linux AppImage')) return |
| 93 | 97 | if (process.platform !== 'linux') { |
| … | … | @@ -246,6 +246,12 @@ function verifyPlatform (platform, assets, names, version, errors, options = {}) |
| 246 | 246 | errors.push(`missing required v0.5.0 backfill ${required.label} for ${platform}/${arch}`) |
| 247 | 247 | } |
| 248 | 248 | } |
| 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 | } | |
| 249 | 255 | } |
| 250 | 256 | |
| 251 | 257 | const sidecars = [] |
| … | … | @@ -56,6 +56,12 @@ if (artifacts.length === 0) { |
| 56 | 56 | console.error(' run `npm run --prefix appling generate` and `npm run --prefix appling build` first') |
| 57 | 57 | process.exit(1) |
| 58 | 58 | } |
| 59 | if (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 | } | |
| 59 | 65 | |
| 60 | 66 | const outputs = [] |
| 61 | 67 | const extCounts = new Map() |
| … | … | @@ -195,7 +201,7 @@ function findArtifacts (root, targetPlatform) { |
| 195 | 201 | } |
| 196 | 202 | if (shouldSkipDir(entry.name)) continue |
| 197 | 203 | 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)) { | |
| 199 | 205 | const stats = statSync(path) |
| 200 | 206 | if (stats.size > 0) found.push({ kind: 'file', path }) |
| 201 | 207 | } |
| … | … | @@ -211,6 +217,14 @@ function isReleaseFile (name, targetPlatform) { |
| 211 | 217 | return filePatterns[targetPlatform].some((pattern) => pattern.test(name)) |
| 212 | 218 | } |
| 213 | 219 | |
| 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. | |
| 224 | function isPackagingToolArtifact (name, targetPlatform) { | |
| 225 | return targetPlatform === 'linux' && /^(?:appimagetool|linuxdeploy)(?:[-_.].*)?\.AppImage$/i.test(name) | |
| 226 | } | |
| 227 | ||
| 214 | 228 | // The bare-pear Windows build emits a raw "<AppName>.exe" launcher next to the |
| 215 | 229 | // .msix. It is the app executable, not an installer, and shipping it alongside |
| 216 | 230 | // the package confuses the download surface — drop it. A real installer (e.g. |
| … | … | @@ -155,6 +155,12 @@ function resolveReleaseAsset (release, options) { |
| 155 | 155 | throw new Error(`no ${options.platform}/${options.arch} native artifact found for ${options.tag}`) |
| 156 | 156 | } |
| 157 | 157 | |
| 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 | ||
| 158 | 164 | const asset = candidates[0] |
| 159 | 165 | if (asset.size <= 0) throw new Error(`${asset.name} is empty or missing a size`) |
| 160 | 166 |
| … | … | @@ -15,12 +15,17 @@ import Corestore from 'corestore' |
| 15 | 15 | import Hyperswarm from 'hyperswarm' |
| 16 | 16 | import Hyperbee from 'hyperbee' |
| 17 | 17 | import b4a from 'b4a' |
| 18 | import { mkdtempSync, rmSync } from 'node:fs' | |
| 18 | import { mkdtempSync, readFileSync, rmSync } from 'node:fs' | |
| 19 | 19 | import { join } from 'node:path' |
| 20 | 20 | import { tmpdir } from 'node:os' |
| 21 | 21 | import { readCatalogBee } from './lib/catalog-bee.js' |
| 22 | 22 | |
| 23 | 23 | const DEFAULT_KEY = 'f5fb7500bccd60a976d2b1d24246108f4444a210b9ca591533114dffc089934d' |
| 24 | const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) | |
| 25 | const pearConfig = JSON.parse(readFileSync(new URL('../pear.json', import.meta.url), 'utf8')) | |
| 26 | const catalogSource = JSON.parse(readFileSync(new URL('../catalog-source/pearbrowser-network.catalog.json', import.meta.url), 'utf8')) | |
| 27 | const browserSource = catalogSource.apps.find((app) => app.id === 'pearbrowser-desktop') | |
| 28 | if (!browserSource) throw new Error('catalogue source is missing pearbrowser-desktop') | |
| 24 | 29 | const PEER_TIMEOUT_MS = 30_000 |
| 25 | 30 | const UPDATE_TIMEOUT_MS = 20_000 |
| 26 | 31 | const READ_TIMEOUT_MS = 20_000 |
| … | … | @@ -30,7 +35,10 @@ function parseArgs (argv) { |
| 30 | 35 | key: DEFAULT_KEY, |
| 31 | 36 | expectApps: [], |
| 32 | 37 | expectCount: 14, |
| 33 | expectName: 'PearBrowser Network' | |
| 38 | expectName: 'PearBrowser Network', | |
| 39 | expectBrowserVersion: pkg.version, | |
| 40 | expectBrowserLink: pearConfig.links.production, | |
| 41 | expectBrowserHomepage: browserSource.homepage | |
| 34 | 42 | } |
| 35 | 43 | for (let i = 0; i < argv.length; i++) { |
| 36 | 44 | const arg = argv[i] |
| … | … | @@ -38,6 +46,9 @@ function parseArgs (argv) { |
| 38 | 46 | else if (arg === '--expect-app') args.expectApps.push(argv[++i]) |
| 39 | 47 | else if (arg === '--expect-count') args.expectCount = Number(argv[++i]) |
| 40 | 48 | 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] | |
| 41 | 52 | } |
| 42 | 53 | return args |
| 43 | 54 | } |
| … | … | @@ -111,6 +122,18 @@ async function main () { |
| 111 | 122 | if (!byId.has(id)) fail(`missing expected app: ${id}`) |
| 112 | 123 | } |
| 113 | 124 | |
| 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 | ||
| 114 | 137 | const peercord = byId.get('peercord') |
| 115 | 138 | if (peercord) { |
| 116 | 139 | if (peercord.link !== 'pear://wmir47w7mai3b1skj66mx7fzso6k6o91kipaney7gtt69npimouy') fail('Peercord link mismatch') |
| … | … | @@ -135,6 +158,7 @@ async function main () { |
| 135 | 158 | |
| 136 | 159 | console.log(' → signed meta signature:', signedMeta.value.signature.slice(0, 16) + '…') |
| 137 | 160 | console.log(' → catalogue:', data.name, '· apps:', data.apps.length) |
| 161 | console.log(' → PearBrowser:', pearbrowser.version, '·', pearbrowser.link, '·', pearbrowser.homepage) | |
| 138 | 162 | if (peercord) console.log(' → Peercord:', peercord.type, '·', peercord.sourceUrl, '·', peercord.license) |
| 139 | 163 | console.log(' → expected apps:', args.expectApps.length ? args.expectApps.join(', ') : '(none)') |
| 140 | 164 | console.log() |
| … | … | @@ -1,6 +1,7 @@ |
| 1 | 1 | import test from 'node:test' |
| 2 | 2 | import assert from 'node:assert/strict' |
| 3 | 3 | import { createHash } from 'node:crypto' |
| 4 | import { createRequire } from 'node:module' | |
| 4 | 5 | import { execFileSync, spawnSync } from 'node:child_process' |
| 5 | 6 | import { |
| 6 | 7 | copyFileSync, |
| … | … | @@ -17,8 +18,11 @@ import { join } from 'node:path' |
| 17 | 18 | import { fileURLToPath, pathToFileURL } from 'node:url' |
| 18 | 19 | |
| 19 | 20 | const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) |
| 21 | const require = createRequire(import.meta.url) | |
| 20 | 22 | const packageLock = JSON.parse(readFileSync(new URL('../package-lock.json', import.meta.url), 'utf8')) |
| 21 | 23 | const pearConfig = JSON.parse(readFileSync(new URL('../pear.json', import.meta.url), 'utf8')) |
| 24 | const catalogSource = JSON.parse(readFileSync(new URL('../catalog-source/pearbrowser-network.catalog.json', import.meta.url), 'utf8')) | |
| 25 | const { SEED_APPS } = require('../backend/catalogue-seed.js') | |
| 22 | 26 | const rootLicense = readFileSync(new URL('../LICENSE', import.meta.url), 'utf8') |
| 23 | 27 | const applingPkg = JSON.parse(readFileSync(new URL('../appling/package.json', import.meta.url), 'utf8')) |
| 24 | 28 | const 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', () => { |
| 266 | 270 | assert.ok(ignored.includes('/test')) |
| 267 | 271 | }) |
| 268 | 272 | |
| 273 | test('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 | ||
| 269 | 291 | test('release script purges ignored files from previous Pear stages', () => { |
| 270 | 292 | assert.match(releaseScript, /pear stage --purge/) |
| 271 | 293 | }) |
| … | … | @@ -488,6 +510,9 @@ test('Linux AppImage metadata checker is exposed for desktop integration gates', |
| 488 | 510 | assert.match(linuxAppImageMetadata, /io\.github\.bigdestiny2\.pearbrowser\.metainfo\.xml/) |
| 489 | 511 | assert.match(linuxAppImageMetadata, /metadata_license/) |
| 490 | 512 | 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*\}/) | |
| 491 | 516 | assert.match(linuxMetainfo, /<component type="desktop-application">/) |
| 492 | 517 | assert.match(linuxMetainfo, /<launchable type="desktop-id">PearBrowser\.desktop<\/launchable>/) |
| 493 | 518 | assert.match(linuxMetainfo, /<binary>pearbrowser<\/binary>/) |
| … | … | @@ -771,6 +796,9 @@ test('native release workflow builds and attaches appling artifacts for every de |
| 771 | 796 | assert.match(nativeReleaseWorkflow, /npm run --prefix appling build/) |
| 772 | 797 | assert.match(nativeReleaseWorkflow, /Verify Linux AppImage metadata/) |
| 773 | 798 | 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/) | |
| 774 | 802 | assert.match(nativeReleaseWorkflow, /actions\/upload-artifact@v4/) |
| 775 | 803 | assert.match(nativeReleaseWorkflow, /actions\/download-artifact@v4/) |
| 776 | 804 | assert.match(nativeReleaseWorkflow, /release-platform: macos/) |
| … | … | @@ -995,6 +1023,37 @@ test('appling artifact collector emits normalized assets and checksum manifests' |
| 995 | 1023 | } |
| 996 | 1024 | }) |
| 997 | 1025 | |
| 1026 | test('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 | ||
| 998 | 1057 | test('appling artifact collector refuses unsafe output directories before clearing them', () => { |
| 999 | 1058 | const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-appling-release-'))) |
| 1000 | 1059 | try { |
| … | … | @@ -1232,8 +1291,6 @@ test('native release asset resolver chooses the recommended package for each des |
| 1232 | 1291 | 'PearBrowser-0.5.0-windows-x64.msix.sha256', |
| 1233 | 1292 | 'PearBrowser-0.5.0-windows-x64.exe', |
| 1234 | 1293 | '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', | |
| 1237 | 1294 | 'PearBrowser-0.5.0-linux-x64.AppImage', |
| 1238 | 1295 | 'PearBrowser-0.5.0-linux-x64.AppImage.sha256' |
| 1239 | 1296 | ].map((name, i) => ({ |
| … | … | @@ -1271,6 +1328,53 @@ test('native release asset resolver chooses the recommended package for each des |
| 1271 | 1328 | } |
| 1272 | 1329 | }) |
| 1273 | 1330 | |
| 1331 | test('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 | ||
| 1274 | 1378 | test('native install snippet generator emits release-note packages for every desktop target', () => { |
| 1275 | 1379 | const fixture = realpathSync(mkdtempSync(join(tmpdir(), 'pear-native-install-snippet-'))) |
| 1276 | 1380 | try { |
| … | … | @@ -2139,7 +2243,10 @@ test('release story smoke covers browse, catalogue, local stories, and opt-in si |
| 2139 | 2243 | assert.doesNotMatch(releaseStorySmoke, /CMD_RUN_APP_IN_TAB/) |
| 2140 | 2244 | }) |
| 2141 | 2245 | |
| 2142 | test('live catalogue verifier asserts Peercord provenance metadata', () => { | |
| 2246 | test('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/) | |
| 2143 | 2250 | assert.match(liveCatalogVerifier, /Peercord sourceUrl mismatch/) |
| 2144 | 2251 | assert.match(liveCatalogVerifier, /https:\/\/git\.churchofmalware\.org\/mastercodeon\/Peercord/) |
| 2145 | 2252 | assert.match(liveCatalogVerifier, /Peercord license mismatch/) |