From 2409055c910e2e5b413a801f6074595e339d2d54 Mon Sep 17 00:00:00 2001 From: i2cjak Date: Tue, 3 Nov 2026 23:48:12 -0500 Subject: [PATCH 1/2] release: bind 0.14.1 producer and package version metadata --- .github/workflows/release-bootstrap.yml | 8 ++++---- .github/workflows/release.yml | 6 +++--- desktop/produce-release.cjs | 2 +- desktop/produce-web-release.cjs | 2 +- desktop/release-inputs.schema.json | 8 ++++---- scripts/release-bootstrap.py | 2 +- scripts/service-release.py | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release-bootstrap.yml b/.github/workflows/release-bootstrap.yml index 550ff271b1..dc73dafeb4 100644 --- a/.github/workflows/release-bootstrap.yml +++ b/.github/workflows/release-bootstrap.yml @@ -54,8 +54,8 @@ concurrency: env: BACKPLANE_FROZEN_COMMIT: ${{ inputs.ref }} - BACKPLANE_VERSION: v0.14.0 - BACKPLANE_VERSION_STAMP: '0.14.0' + BACKPLANE_VERSION: v0.14.1 + BACKPLANE_VERSION_STAMP: '0.14.1' BACKPLANE_TELEMETRY_CHANNEL: release # Source scanners and receipts must use UTF-8 on Windows too. PYTHONUTF8: '1' @@ -110,7 +110,7 @@ jobs: python3 build/bootstrap-tools/scripts/release-bootstrap.py run --source "$BACKPLANE_FROZEN_COMMIT" --operation js --target "$TARGET" --directory "$RUNNER_TEMP/release-inputs" # Public receipt for the telemetry release gate: the key ID only. Never the key, the # Google secret or the environment; written only after the build above succeeded. - python3 -c 'import json,os,sys; kid=os.environ.get("BACKPLANE_TELEMETRY_KID",""); kid or sys.exit("no telemetry key ID"); json.dump({"schema":1,"version":"0.14.0","channel":"release","baked":True,"capability":"signed-release-events","keyId":kid,"endpoint":os.environ["BACKPLANE_TELEMETRY_URL"],"target":os.environ["TARGET"],"sourceCommit":os.environ["BACKPLANE_FROZEN_COMMIT"]},open("build/bootstrap-js/TELEMETRY-CAPABILITY.json","w"),indent=2)' + python3 -c 'import json,os,sys; kid=os.environ.get("BACKPLANE_TELEMETRY_KID",""); kid or sys.exit("no telemetry key ID"); json.dump({"schema":1,"version":"0.14.1","channel":"release","baked":True,"capability":"signed-release-events","keyId":kid,"endpoint":os.environ["BACKPLANE_TELEMETRY_URL"],"target":os.environ["TARGET"],"sourceCommit":os.environ["BACKPLANE_FROZEN_COMMIT"]},open("build/bootstrap-js/TELEMETRY-CAPABILITY.json","w"),indent=2)' - uses: actions/upload-artifact@v4 if: always() with: @@ -167,7 +167,7 @@ jobs: chmod 755 "$SERVICE" # artifacts drop file modes v=$("$SERVICE" --version) echo "$v" - [ "$v" = "backplane 0.14.0" ] + [ "$v" = "backplane 0.14.1" ] - name: Measure matching-host system ABI and assemble genuine service component shell: bash env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64e4dd2b20..b8eb0a40b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ on: version: description: Exact stable tag spelling; this workflow creates no tag required: true - default: v0.14.0 + default: v0.14.1 type: string producer_run_id: description: Completed successful run containing owner-produced package artifacts @@ -105,8 +105,8 @@ jobs: e = os.environ commit, tag = e['SOURCE_COMMIT'], e['RELEASE_TAG'] runpy.run_path('scripts/release-assets.py')['identity'](commit, tag) - if tag != 'v0.14.0': - raise SystemExit('this stable preparation targets v0.14.0 only') + if tag != 'v0.14.1': + raise SystemExit('this stable preparation targets v0.14.1 only') if e['WORKFLOW_COMMIT'] != commit or subprocess.check_output(['git','rev-parse','HEAD'], text=True).strip() != commit: raise SystemExit('workflow and checkout must equal frozen source') if not re.fullmatch(r'[1-9][0-9]*', e['PRODUCER_RUN']) or not re.fullmatch(r'[1-9][0-9]*(,[1-9][0-9]*)*', e['PRODUCER_IDS']): diff --git a/desktop/produce-release.cjs b/desktop/produce-release.cjs index cba7959c02..22937a3744 100644 --- a/desktop/produce-release.cjs +++ b/desktop/produce-release.cjs @@ -3,7 +3,7 @@ const fs=require('node:fs'),path=require('node:path'),os=require('node:os'),crypto=require('node:crypto'); const {execFileSync}=require('node:child_process'); const browserOverlay=require('./selected-browser.cjs'); -const VERSION='0.14.0',ELECTRON='44.9.2',HEX=/^[a-f0-9]{64}$/,GIT=/^[a-f0-9]{40}$/; +const VERSION='0.14.1',ELECTRON='44.9.2',HEX=/^[a-f0-9]{64}$/,GIT=/^[a-f0-9]{40}$/; const TARGETS={ 'linux-x64':{suffix:'.tar.gz',root:'backplane-linux-x64',executable:'runtime/backplane-desktop',resources:'runtime/resources'}, 'linux-arm64':{suffix:'.tar.gz',root:'backplane-linux-arm64',executable:'runtime/backplane-desktop',resources:'runtime/resources'}, diff --git a/desktop/produce-web-release.cjs b/desktop/produce-web-release.cjs index beb3cc8840..3f4787e6f3 100644 --- a/desktop/produce-web-release.cjs +++ b/desktop/produce-web-release.cjs @@ -2,7 +2,7 @@ // Supplied, qualified web artifacts only. Never emits Bend, fetches or runs a payload. const fs=require('node:fs'),path=require('node:path'),os=require('node:os'); const p=require('./produce-release.cjs'); -const VERSION='0.14.0',GIT=/^[a-f0-9]{40}$/,HEX=/^[a-f0-9]{64}$/; +const VERSION='0.14.1',GIT=/^[a-f0-9]{40}$/,HEX=/^[a-f0-9]{64}$/; const check=(v,m)=>{if(!v)throw Error(m);}; function archive(requestFile,output){ check(path.isAbsolute(output)&&!fs.existsSync(output),'Distribution output must be a new absolute directory'); diff --git a/desktop/release-inputs.schema.json b/desktop/release-inputs.schema.json index afdd769b0a..822389a15c 100644 --- a/desktop/release-inputs.schema.json +++ b/desktop/release-inputs.schema.json @@ -300,10 +300,10 @@ ], "properties": { "version": { - "const": "0.14.0" + "const": "0.14.1" }, "tag": { - "const": "v0.14.0" + "const": "v0.14.1" }, "channel": { "const": "release" @@ -369,10 +369,10 @@ "const": "FROZEN" }, "version": { - "const": "0.14.0" + "const": "0.14.1" }, "tag": { - "const": "v0.14.0" + "const": "v0.14.1" }, "channel": { "const": "release" diff --git a/scripts/release-bootstrap.py b/scripts/release-bootstrap.py index bb70c549f2..ad98fdd541 100644 --- a/scripts/release-bootstrap.py +++ b/scripts/release-bootstrap.py @@ -21,7 +21,7 @@ import urllib.request import zipfile -VERSION = '0.14.0' +VERSION = '0.14.1' OPERATIONS = ('js', 'android') MAX_BYTES = 4 * 1024**3 # The Bun every JS service is built with, and each target's runtime it is diff --git a/scripts/service-release.py b/scripts/service-release.py index c453ed9708..4c2f47d866 100644 --- a/scripts/service-release.py +++ b/scripts/service-release.py @@ -13,7 +13,7 @@ import subprocess import sys -VERSION = "0.14.0" +VERSION = "0.14.1" ENDPOINT = "https://backplaneworks.com/api/v1/events" # The JS service (scripts/build-js.sh, `bun build --compile` against a # pinned Bun runtime of the target), cross-built from any host. From e564f904eb277ffbd8d0104d6d8afa90ff1aeb6f Mon Sep 17 00:00:00 2001 From: i2cjak Date: Tue, 3 Nov 2026 23:49:05 -0500 Subject: [PATCH 2/2] Gate native Windows packaging on standard-user cold startup --- .../workflows/windows-existing-desktop-package.yml | 12 ++++++++++++ scripts/windows-standard-user-smoke.ps1 | 11 ++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/windows-existing-desktop-package.yml b/.github/workflows/windows-existing-desktop-package.yml index bb052390c5..f629a80a7a 100644 --- a/.github/workflows/windows-existing-desktop-package.yml +++ b/.github/workflows/windows-existing-desktop-package.yml @@ -84,6 +84,14 @@ jobs: NODE - name: Stage existing component, brand on Windows, smoke and archive run: python tools/scripts/windows-existing-desktop-package.py --product product --out build/windows-existing + - name: Require packaged startup as a standard user without Developer Mode + shell: pwsh + run: | + $packages = @(Get-ChildItem build/windows-existing/dist -File -Filter '*windows-x64.zip') + if ($packages.Count -ne 1) { throw 'Expected one final Windows ZIP for standard-user qualification' } + $version = (Get-Content product/desktop/package.json -Raw | ConvertFrom-Json).version + $sha = (Get-FileHash $packages[0].FullName -Algorithm SHA256).Hash.ToLowerInvariant() + ./product/scripts/windows-standard-user-smoke.ps1 -PackagePath $packages[0].FullName -PackageSHA256 $sha -ExpectedVersion $version - uses: actions/upload-artifact@v4 if: always() with: @@ -96,5 +104,9 @@ jobs: build/windows-existing/dist/ build/windows-existing/stage/STAGE.json build/windows-existing/stage.request.json + product/build/windows-standard-user/result.json + product/build/windows-standard-user/package.json + product/build/windows-standard-user/unit-tests.txt + product/build/windows-standard-user/*.png if-no-files-found: warn retention-days: 30 diff --git a/scripts/windows-standard-user-smoke.ps1 b/scripts/windows-standard-user-smoke.ps1 index 5383787f37..7f2c933324 100644 --- a/scripts/windows-standard-user-smoke.ps1 +++ b/scripts/windows-standard-user-smoke.ps1 @@ -1,20 +1,21 @@ -param([string]$PackageUrl='', [string]$PackageSHA256='', [string]$ExpectedVersion='') +param([string]$PackageUrl='', [string]$PackagePath='', [string]$PackageSHA256='', [string]$ExpectedVersion='') $ErrorActionPreference = 'Stop' $root = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path $out = Join-Path $root 'build\windows-standard-user' New-Item -ItemType Directory -Force $out | Out-Null $exe = '' -if ($PackageUrl) { - if ($PackageUrl -notmatch '^https://' -or $PackageSHA256 -notmatch '^[a-f0-9]{64}$' -or !$ExpectedVersion) { throw 'Supply HTTPS package URL, SHA256 and expected version' } +if ($PackageUrl -or $PackagePath) { + if (($PackageUrl -and $PackageUrl -notmatch '^https://') -or ($PackageUrl -and $PackagePath) -or $PackageSHA256 -notmatch '^[a-f0-9]{64}$' -or !$ExpectedVersion) { throw 'Supply one HTTPS URL or local ZIP, SHA256 and expected version' } $zip = Join-Path $out 'package.zip' - Invoke-WebRequest $PackageUrl -OutFile $zip + if ($PackageUrl) { Invoke-WebRequest $PackageUrl -OutFile $zip } + else { Copy-Item (Resolve-Path $PackagePath).Path $zip } if ((Get-FileHash $zip -Algorithm SHA256).Hash.ToLowerInvariant() -ne $PackageSHA256) { throw 'Package SHA256 mismatch' } $package = Join-Path $out 'package' Expand-Archive $zip -DestinationPath $package $found = @(Get-ChildItem $package -Recurse -File -Filter Backplane.exe) if ($found.Count -ne 1) { throw 'Expected exactly one packaged Backplane.exe' } $exe = $found[0].FullName - @{url=$PackageUrl;sha256=$PackageSHA256;version=$ExpectedVersion} | ConvertTo-Json | Set-Content (Join-Path $out 'package.json') + @{url=$PackageUrl;sourcePath=$PackagePath;sha256=$PackageSHA256;version=$ExpectedVersion} | ConvertTo-Json | Set-Content (Join-Path $out 'package.json') } # This bootstrap runs on a disposable hosted runner. Only it is elevated; # every assertion, native filesystem effect and Electron process runs below