Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7620ed4
Add macOS flasher support
billmcilhargey Sep 1, 2026
49dde51
Enhance macOS GUI functionality and improve drive detection logic
billmcilhargey Sep 1, 2026
ef33d82
Update UEFI firmware versioning and default settings for improved sta…
billmcilhargey Sep 1, 2026
7a26468
Fix macOS Pi 4 flashing workflow
billmcilhargey Sep 2, 2026
4ed88d8
Harden macOS flashing and validation
billmcilhargey Sep 3, 2026
75953f0
Share one engine between both front-ends, version it, add community f…
billmcilhargey Sep 4, 2026
fa8190c
Deliver the offline-OOBE answer file through WoR-PE's prefinalize hook
billmcilhargey Sep 4, 2026
8e45d41
Ask for the administrator password once, not twice
billmcilhargey Sep 4, 2026
f6c4147
Put the run log behind one variable and clear out generated debris
billmcilhargey Sep 4, 2026
b091b12
Deliver the Pi 4 RAM unlock to the installed OS, not just the media
billmcilhargey Sep 4, 2026
6250ea8
Check that the generated answer file is well-formed XML
billmcilhargey Sep 4, 2026
a6dfe98
Pin Pi 4 UEFI to v1.50, the only build with a working MAC and SD boot
billmcilhargey Sep 4, 2026
a1df9f3
Restore partnership banner
billmcilhargey Sep 4, 2026
e484c9b
Update partnership funding and installer safety checks
billmcilhargey Sep 4, 2026
83d772d
Refresh partnership banner and launch messaging
billmcilhargey Sep 4, 2026
a0aa536
Refresh partnership banner and launch messaging
billmcilhargey Sep 4, 2026
f16c2ce
Clarify project partnership in README
billmcilhargey Sep 4, 2026
6a6b501
Link Blackout Secure website in README
billmcilhargey Sep 4, 2026
a3054a7
Refresh partnership GUI links
billmcilhargey Sep 4, 2026
6627abf
Improve partnership UX and integration hooks
billmcilhargey Sep 5, 2026
ad1bc5c
Add WoR-Flasher integration hook
billmcilhargey Sep 5, 2026
34cf8a5
feat: add native cross-platform workflow
billmcilhargey Sep 5, 2026
2901da6
feat: package standalone macOS app runtime
billmcilhargey Sep 5, 2026
817a733
fix: advance macOS GUI after Proceed
billmcilhargey Sep 5, 2026
9b7682a
fix: macOS GUI window chrome, button layout, and Quit-vs-Back handling
billmcilhargey Sep 6, 2026
aa0e298
refactor: modernize release tooling, config parsing, and macOS app so…
billmcilhargey Sep 6, 2026
667de2e
fix(macOS): retry unmountDisk during disk preparation to prevent race…
billmcilhargey Sep 7, 2026
06438b0
feat(gui): show subprogress percentage for each installation step in …
billmcilhargey Sep 7, 2026
67a2e4f
feat: modernize the cross-platform flashing workflow
billmcilhargey Sep 9, 2026
e79092d
fix(macOS): tolerate malformed bytes in GUI data
billmcilhargey Sep 9, 2026
874a4d6
feat: prepare WoR-Flasher 2.0.0
billmcilhargey Sep 9, 2026
0226579
feat(gui): align Linux workflow with macOS
billmcilhargey Sep 9, 2026
b0d279a
fix(macOS): retry install.wim copy when FSKit unmounts the volume
billmcilhargey Sep 9, 2026
9b0d7b1
fix(gui): stack the Linux partnership announcement image above its text
billmcilhargey Sep 10, 2026
912ccef
feat: add automatic Pi 4 UEFI RAM unlock handoff
billmcilhargey Sep 10, 2026
5a7921f
fix: improve Ubuntu verification and progress UI
billmcilhargey Sep 10, 2026
078415c
fix(gui): simplify Linux progress and config flow
billmcilhargey Sep 10, 2026
152ac69
fix(gui): repair Ubuntu YAD field state
billmcilhargey Sep 10, 2026
7ea5757
fix: stabilize macos gui release flow
billmcilhargey Sep 17, 2026
adf3eaf
fix: polish linux gui flow
billmcilhargey Sep 17, 2026
24bdb22
fix: avoid linux usbredir gui popup
billmcilhargey Sep 17, 2026
e897794
fix(macos): stabilize flashing and ignore transient disk alerts
billmcilhargey Sep 26, 2026
fa4c023
fix(ci): unblock validated WoR-Flasher releases
billmcilhargey Sep 26, 2026
d998c6c
fix(release): address publication review findings
billmcilhargey Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
* text=auto eol=lf

*.sh text eol=lf
terminal-run text eol=lf
config_txt_tips text eol=lf
*.mjs text eol=lf
*.json text eol=lf
*.md text eol=lf

# Stored with LF even though cmd.exe needs CRLF: install-wor.sh appends the CR when it
# writes the file into boot.wim, and a CRLF copy here would produce CRCRLF
*.cmd text eol=lf

# Binary assets
*.png binary

Expand Down
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [Botspot, blackoutsecure]
9 changes: 9 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
blank_issues_enabled: true
contact_links:
- name: Report it upstream instead
url: https://github.com/Botspot/wor-flasher/issues/new/choose
about: If the bug is not specific to macOS support, the native windows, or the test suite, upstream needs it more than this fork does
- name: Botspot Software Discord
url: https://discord.gg/RXSTvaUvuu
about: Questions and help with WoR-flasher itself
- name: Windows on Raspberry Discord
url: https://discord.gg/jQCpfVK
about: Real-time help with Windows on Raspberry, the operating system
- name: Windows on Raspberry support
url: https://worproject.com/contact
about: Problems with Windows, the drivers, or the UEFI firmware rather than the flasher
- name: Windows on Raspberry FAQ
url: https://worproject.com/faq
about: Covers the 3 GB RAM limit, supported Windows versions, and driver status
- name: Report a security issue privately
url: https://github.com/blackoutsecure/wor-flasher/security/advisories/new
about: Anything that should not be discussed in public. See SECURITY.md
8 changes: 6 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@

<!-- Which Raspberry Pi model, which Windows build, and what you observed. -->

- [ ] `bash -n install-wor.sh install-wor-gui.sh terminal-run tests/*.sh` passes
- [ ] `bash -n install-wor.sh install-wor-gui.sh install-wor-hook.sh tests/*.sh` passes
- [ ] `./tests/run-tests.sh` passes, skips only expected host/display/container checks, or the reason is explained
- [ ] GUI walkthrough checked with `./tests/run-tests-gui.sh` on the intended host (`yad` on Linux or `osascript` plus a removable drive on macOS)
- [ ] Tested with `DRY_RUN=1`, or flashed a real drive
- [ ] Raspberry Pi model tested:
- [ ] Windows build tested:

## Checklist

- [ ] The GUI and CLI still agree, if shared logic changed
- [ ] Shared logic lives in `install-wor.sh`, not duplicated into `install-wor-gui.sh`
- [ ] README updated, if behaviour or variables changed
- [ ] No new ShellCheck errors (`shellcheck --severity=error install-wor.sh install-wor-gui.sh terminal-run tests/*.sh`)
- [ ] Version history at the top of `install-wor.sh` and the Versions section of the README updated, if behaviour changed
- [ ] Sent upstream to Botspot/wor-flasher too, if the fix applies there
- [ ] No new ShellCheck errors (`shellcheck --severity=error install-wor.sh install-wor-gui.sh install-wor-hook.sh tests/*.sh`)
267 changes: 267 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
name: Publish Release

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag_name:
description: "auto for the next patch, or an explicit vX.Y.Z tag"
default: "auto"
required: false
type: string
skip_project_version_update:
description: "Do not update project version files before validating a new manual tag"
default: false
required: false
type: boolean

permissions:
contents: read

concurrency:
group: release-${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag_name) || github.ref }}
cancel-in-progress: false

jobs:
prepare:
name: Resolve release tag
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
create_tag: ${{ steps.release.outputs.create_tag }}
ref: ${{ steps.release.outputs.ref }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
fetch-depth: 0

- id: release
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.tag_name }}
REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
SKIP_PROJECT_VERSION_UPDATE: ${{ inputs.skip_project_version_update }}
SOURCE_SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = workflow_dispatch ];then
#Metadata is JSON-backed; the shell module no longer has a literal version assignment.
version="$(node -p 'require("./src/config/metadata.json").product.version')"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
echo "Invalid version: $version" >&2
exit 1
}
if [ "$INPUT_TAG" = auto ];then
latest_tag=''
while IFS= read -r candidate ;do
if [[ "$candidate" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]];then
latest_tag="$candidate"
break
fi
done < <(git tag --list 'v*' --sort=-version:refname)
if [ -n "$latest_tag" ];then
IFS=. read -r major minor patch <<< "${latest_tag#v}"
tag="v$major.$minor.$((10#$patch + 1))"
else
tag="v$version"
fi
else
tag="$INPUT_TAG"
fi
if git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null ;then
ref="refs/tags/$tag"
create_tag=false
else
if [ "$SKIP_PROJECT_VERSION_UPDATE" != true ];then
node src/set-version.mjs "${tag#v}"
if [ -n "$(git status --porcelain -- src/config/metadata.json src/lib/metadata.sh src/macos-app/Contents/Info.plist README.md install-wor.sh package.json)" ];then
source_branch="${REF#refs/heads/}"
[ "$source_branch" != "$REF" ] || {
echo "Manual releases must run from a branch." >&2
exit 1
}
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add src/config/metadata.json src/lib/metadata.sh src/macos-app/Contents/Info.plist README.md install-wor.sh package.json
git commit -m "chore: prepare $tag" \
-m "Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
git push origin "HEAD:$source_branch"
fi
fi
SOURCE_SHA="$(git rev-parse HEAD)"
ref="$SOURCE_SHA"
create_tag=true
fi
else
tag="$REF_NAME"
ref="$REF"
create_tag=false
fi
[[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
echo "Release tag must be vX.Y.Z (got: $tag)." >&2
exit 1
}
{
echo "create_tag=$create_tag"
echo "ref=$ref"
echo "tag=$tag"
} >> "$GITHUB_OUTPUT"

validate-linux:
name: Validate Linux release candidate
needs: prepare
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
fetch-depth: 0
ref: ${{ needs.prepare.outputs.ref }}

- name: Verify tag matches the release metadata
env:
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
set -euo pipefail
version="$(node -p 'require("./src/config/metadata.json").product.version')"
[ -n "$version" ] || { echo 'WOR_FLASHER_VERSION is missing.' >&2; exit 1; }
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid version: $version" >&2; exit 1; }
[ "$RELEASE_TAG" = "v$version" ] || { echo "Tag $RELEASE_TAG does not match v$version." >&2; exit 1; }

- name: Install ShellCheck and integration test prerequisites
run: |
set -euo pipefail
sudo apt-get update -qq
sudo apt-get install -y -qq shellcheck pv

- name: Check shell syntax
run: bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh

- name: Check ShellCheck errors
run: shellcheck --severity=error src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh

- name: Verify packaged macOS runtime
run: npm run build:macos && node src/package-macos-app.mjs --check

- name: Verify release packaging plan
run: npm run check

- name: Run Linux test suite
env:
TEST_MODELS: "4"
TEST_COMMAND_TIMEOUT: "120"
run: ./tests/run-tests.sh

validate-macos:
name: Validate macOS release candidate
needs: prepare
runs-on: macos-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
ref: ${{ needs.prepare.outputs.ref }}

- name: Check shell syntax
run: /bin/bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh

- name: Verify packaged macOS runtime
run: npm run build:macos && node src/package-macos-app.mjs --check

- name: Verify release packaging plan
run: npm run check

- name: Run macOS test suite
run: ./tests/run-tests.sh

publish:
name: Package and publish release
needs:
- prepare
- validate-linux
- validate-macos
runs-on: macos-latest
permissions:
contents: write
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
fetch-depth: 0
ref: ${{ needs.prepare.outputs.ref }}

- name: Package release artifacts
id: package
shell: bash
env:
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
set -euo pipefail
version="$(node -p 'require("./src/config/metadata.json").product.version')"
archive_dir="$RUNNER_TEMP/release-artifacts"
release_commit="$(git rev-parse HEAD)"
mkdir -p "$archive_dir"
npm run package:all
ditto -c -k --keepParent release/linux/wor-flasher "$archive_dir/wor-flasher-$version-linux-rpi.zip"
ditto -c -k --keepParent release/macos/WoR-Flasher.app "$archive_dir/WoR-Flasher-$version-macos.zip"
tar -czf "$archive_dir/macos-runtime-$version.tar.gz" \
-C release/macos/WoR-Flasher.app/Contents/Resources runtime runtime-manifest.json
runtime_digest="$(shasum -a 256 "$archive_dir/macos-runtime-$version.tar.gz" | awk '{print $1}')"
printf '{\n "version": "%s",\n "url": "https://github.com/%s/releases/download/%s/macos-runtime-%s.tar.gz",\n "sha256": "%s"\n}\n' \
"$version" "$GITHUB_REPOSITORY" "$RELEASE_TAG" "$version" "$runtime_digest" > "$archive_dir/macos-runtime.json"
(
cd "$archive_dir"
shasum -a 256 wor-flasher-"$version"-linux-rpi.zip WoR-Flasher-"$version"-macos.zip \
macos-runtime-"$version".tar.gz macos-runtime.json > SHA256SUMS
)
{
echo "directory=$archive_dir"
echo "commit=$release_commit"
} >> "$GITHUB_OUTPUT"

- name: Create immutable release tag
if: needs.prepare.outputs.create_tag == 'true'
env:
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
set -euo pipefail
git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null && {
echo "Tag already exists: $RELEASE_TAG" >&2
exit 1
}
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG" HEAD
git push origin "refs/tags/$RELEASE_TAG"

- name: Publish GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ needs.prepare.outputs.tag }}
target_commitish: ${{ steps.package.outputs.commit }}
generate_release_notes: true
body: |
`wor-flasher-*-linux-rpi.zip` is the portable Bash distribution for Debian, Ubuntu,
Raspberry Pi OS, and other supported Linux hosts. It includes the runtime source,
README, LICENSE, and NOTICE.

The `WoR-Flasher-*-macos.zip` application bundle is unsigned and unnotarized.
`macos-runtime.json` and its versioned runtime archive are consumed only by the macOS
launcher's verified runtime updater.

Verify downloaded artifacts against `SHA256SUMS` before use.

macOS GUI disk writes use one administrator authentication and an already-authorized
finalizer. Automatic Ignore handles only the exact unreadable-disk system alert
during active writes; Accessibility and Automation permission may be required.
Physical-media completion and Raspberry Pi boot compatibility are not established
by the automated tests. See README and NOTICE for limitations and attribution.
files: |
${{ steps.package.outputs.directory }}/wor-flasher-*-linux-rpi.zip
${{ steps.package.outputs.directory }}/WoR-Flasher-*-macos.zip
${{ steps.package.outputs.directory }}/macos-runtime-*.tar.gz
${{ steps.package.outputs.directory }}/macos-runtime.json
${{ steps.package.outputs.directory }}/SHA256SUMS
fail_on_unmatched_files: true
Loading
Loading