Polls UUP-dump, rebuilds Windows 10/11 ISOs for a matrix of editions whenever Microsoft ships cumulative updates. Injects Intel RST/VMD NVMe drivers, drops in an autounattend.xml for hands-off install, bypasses the Win11 system-requirement check (registry + DLL patch), and uploads finished ISOs to gofile.io (and optionally a pool of Google Drive accounts via rclone).
This repo (win-forge/winforge) is the tool. It contains:
- The reusable workflow (
.github/workflows/build.yml) - The build scripts (
scripts/) - The vendored default autounattend template (
autounattend/base.xml) - The catalog of products/editions (
config/products.yaml,config/editions.yaml)
You do not fork this repo to use WinForge.
win-forge/winforge-configs is the config repo. It contains:
- Your build profiles (
config/profiles/*.yaml) - Your autounattend templates (
autounattend/*.xml) - A thin workflow that calls winforge's reusable workflow
- Your secrets (set at the repo level)
You fork winforge-configs (or use it as a template) and edit it. The winforge
repo stays up-to-date independently and your config repo just tracks a
@v1 (or @main, or @<sha>) ref.
┌─────────────────────────┐ ┌──────────────────────────┐
│ winforge-configs (you) │ workflow_call │ winforge (the tool) │
│ │ ────────────────▶│ │
│ config/profiles/*.yaml │ │ scripts/ │
│ autounattend/*.xml │ │ reusable workflow │
│ secrets: RCLONE_CONF, │ │ (does the heavy lifting)│
│ ACCOUNTS_YAML │ │ │
└─────────────────────────┘ └──────────────────────────┘
# 1. Create your config repo from the template
gh repo create myorg/my-winforge-configs --template win-forge/winforge-configs --public
# 2. Set secrets on YOUR repo (not on winforge)
gh secret set RCLONE_CONF -R myorg/my-winforge-configs < ~/.config/rclone/rclone.conf
gh secret set ACCOUNTS_YAML -R myorg/my-winforge-configs < my-accounts.yaml
gh secret set LOCAL_ADMIN_NAME -R myorg/my-winforge-configs
gh secret set LOCAL_ADMIN_PASS -R myorg/my-winforge-configs
# 3. Edit your profile
$EDITOR my-winforge-configs/config/profiles/win11-prod.yaml
# 4. Trigger a build
gh workflow run build.yml -R myorg/my-winforge-configs -f profile=win11-prodYou can also run builds directly on this repo using repository_dispatch or
workflow_dispatch. This uses the bundled config/profiles/*.yaml and
autounattend/*.xml and is useful for:
- Testing winforge changes end-to-end
- Building ISOs without spinning up a config repo
# Trigger via the winforge repo itself
gh api repos/win-forge/winforge/dispatches \
-f event_type=build-request \
-f client_payload[profile]=win11-prod- Frees disk space (
easimon/maximize-build-space@master) — concatenates/and/mntvia LVM to give ~100GB usable (UUP→WIM conversion can hit 8GB+ intermediate files; GitHub's default 14GB temp disk + ~29GB root is not enough). - Dual checkout — when called from a config repo (
workflow_call), checks out the caller's repo (forconfig/,autounattend/) AND this repo (forscripts/) into.winforge/. In self-build mode, just the standard checkout. - Renders
{{PLACEHOLDER}}tokens in the autounattend template against GitHub Actions Secrets from the caller's repo. - Downloads UUPs → runs the UUP-dump converter → produces a stock ISO.
- Injects Intel RST drivers into the WIM (gracefully skips if Intel's CDN WAF-blocks the request).
- Bypasses the Win11 system-requirement check (TPM 2.0 / Secure Boot / 4 GB RAM / CPU) — always via the
LabConfigregistry keys in the autounattend, plus a DLL patch (vendored atbypass/<product>/) for editions where Microsoft blocks the registry trick. Seedocs/bypass.md. - Repacks the ISO with the rendered autounattend baked in.
- Uploads to gofile.io (new — primary destination) and optionally to a pool of Google Drive accounts via rclone.
- Uploads ISO as a debug artifact (7-day retention).
Warning
The rendered autounattend.xml embeds your local-admin password in plaintext inside the ISO.
Every ISO built by this pipeline contains LOCAL_ADMIN_PASS (and optionally PRODUCT_KEY) baked
into autounattend.xml on the image. That means the gofile.io upload link is publicly downloadable
by anyone who has it, and the debug artifact copies carry it too. Only distribute finished-ISO links
to people you trust with those credentials, rotate the admin password if a link leaks, and prefer
oobe-skip.xml (no placeholders) when credentials aren't needed.
| Secret | Required? | Used for |
|---|---|---|
RCLONE_CONF |
yes | rclone config (Google Drive account pool) |
ACCOUNTS_YAML |
yes | config/accounts.yaml content (account pool metadata) |
GOFILE_TOKEN |
optional | gofile.io JWT (free account). If empty, uploads as guest and content expires in ~10 days. |
BYPASS_DLLS_B64 |
removed | no longer used — bypass DLLs are vendored at bypass/<product>/ in this repo (see bypass/README.md) |
LOCAL_ADMIN_NAME |
if your autounattend uses {{LOCAL_ADMIN_NAME}} |
|
LOCAL_ADMIN_PASS |
if your autounattend uses {{LOCAL_ADMIN_PASS}} (PlainText) |
|
COMPUTER_NAME |
optional | {{COMPUTER_NAME}} in autounattend |
PRODUCT_KEY |
optional | {{PRODUCT_KEY}} in autounattend |
*Required if your autounattend template uses those placeholders. If you only
use oobe-skip.xml (no placeholders), the build skips rendering and uses
the template as-is.
WinForge uses major-version tags (v1, v2, ...) for stable releases.
Bump the ref in your config repo's .github/workflows/build.yml:
# Stable (recommended for production)
uses: win-forge/winforge/.github/workflows/build.yml@v1
# Bleeding edge
uses: win-forge/winforge/.github/workflows/build.yml@main
# Exact pin
uses: win-forge/winforge/.github/workflows/build.yml@a1b2c3dRenovate / Dependabot will detect new tags and open PRs on your config repo.
pip install -e ".[dev]"
pytest -q
See .github/workflows/ for CI entry points. build.yml is the reusable
workflow (consumed by config repos); check-updates.yml runs daily to detect
new UUP-dump builds; ci.yml is PR-time linting; boot-smoke.yml boots
every built ISO under QEMU (OVMF + SeaBIOS) and fails if Windows Setup
never appears on the framebuffer.
- Structural —
build.ymlrunsverify_iso_bootable.pyon every produced ISO: parses the El Torito boot catalog and requires a non-empty EFI section (filealone can't see this). - Behavioral —
boot-smoke.ymlactually boots the ISO in QEMU after every main-branch build: OVMF leg proves the UEFI chain executes, SeaBIOS leg proves BIOS. Screenshots + firmware logs are uploaded asboot-smoke-evidence. Manual re-runs: dispatch it with a build run ID. - Install-level — not automated; autounattend correctness beyond the first splash screen is verified by hand on real hardware.
Additional docs:
docs/gofile.md— gofile.io upload (guest vs account mode, API quirks)docs/bypass.md— Win11 system-requirement bypass (registry + DLL patch)
The original easimon/maximize-build-space action handles / and /mnt
but the runner's _diag log volume is separate — that fills up on long
builds and crashes the runner. WinForge adds a second step that
truncates old _diag/Worker_*.log files.
Also: the action creates a 100GB LVM image mounted at $GITHUB_WORKSPACE,
which leaves /tmp (on /dev/root) with only ~2GB. WinForge's convert.sh
and repack.sh use ${GITHUB_WORKSPACE} for temp dirs (via the
WORKDIR env var override) so the UUP download + ISO repack happen on
the LVM volume.