diff --git a/.actrc b/.actrc
new file mode 100644
index 0000000..5650ddb
--- /dev/null
+++ b/.actrc
@@ -0,0 +1,13 @@
+# Default to large Ubuntu image for full compatibility (Go, Node, GTK/WebKit)
+-P ubuntu-latest=catthehacker/ubuntu:full-latest
+-P ubuntu-22.04=catthehacker/ubuntu:full-22.04
+-P ubuntu-24.04=catthehacker/ubuntu:full-24.04
+
+# macOS can't be emulated - skip or use self-hosted
+-P macos-latest=-self-hosted
+
+# Windows can't be emulated - skip or use self-hosted
+-P windows-latest=-self-hosted
+
+# Use ARM64 architecture (M3 native)
+--container-architecture linux/arm64
diff --git a/.github/workflows/build-tests.yml b/.github/workflows/build-tests.yml
deleted file mode 100644
index ab7787b..0000000
--- a/.github/workflows/build-tests.yml
+++ /dev/null
@@ -1,256 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [ "main", "v2", "v3", "dev", "*-*" ]
- pull_request:
- branches: [ "main", "v2", "v3", "dev", "*-*" ]
- # Allows you to run this workflow manually from the Actions tab
- workflow_dispatch:
-
-jobs:
- ubuntu2404:
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: linux/amd64
- wails-build-webview2: "embed"
- package: false # Do not try to upload to github
- ubuntu2204:
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: linux/amd64
- wails-build-webview2: "embed"
- package: false # Do not try to upload to github
- ubuntu2004:
- runs-on: ubuntu-20.04
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: linux/amd64
- wails-build-webview2: "embed"
- package: false # Do not try to upload to github
- ubuntu-obfuscate:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: linux/amd64
- wails-build-webview2: "embed"
- build-obfuscate: true
- package: false # Do not try to upload to github
- macos15arm64:
- runs-on: macos-15
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: darwin/universal
- sign: "false"
- wails-version: "v2.9.0"
- sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
- sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
- sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
- sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
- sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
- sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
- sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
- package: false # Do not try to upload to github
-# macos15amd64:
-# runs-on: macos-15-large
-# steps:
-# - uses: actions/checkout@v4
-# with:
-# submodules: recursive
-# - uses: dAppServer/wails-build-action@main
-# with:
-# build-name: wails
-# build-platform: darwin/universal
-# sign: "false"
-# sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
-# sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
-# sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
-# sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
-# sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
-# package: false # Do not try to upload to github
- macos14arm64:
- runs-on: macos-14
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: darwin/universal
- sign: "false"
- wails-version: "v2.9.0"
- sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
- sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
- sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
- sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
- sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
- sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
- sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
- package: false # Do not try to upload to github
-# macos14amd64:
-# runs-on: macos-14-large
-# steps:
-# - uses: actions/checkout@v4
-# with:
-# submodules: recursive
-# - uses: dAppServer/wails-build-action@main
-# with:
-# build-name: wails
-# build-platform: darwin/universal
-# sign: "false"
-# sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
-# sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
-# sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
-# sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
-# sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
-# package: false # Do not try to upload to github
-# macos13arm64:
-# runs-on: macos-13-xlarge
-# steps:
-# - uses: actions/checkout@v4
-# with:
-# submodules: recursive
-# - uses: dAppServer/wails-build-action@main
-# with:
-# build-name: wails
-# build-platform: darwin/universal
-# sign: "false"
-# sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
-# sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
-# sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
-# sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
-# sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
-# sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
-# package: false # Do not try to upload to github
- macos13amd64:
- runs-on: macos-13
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: darwin/universal
- sign: "false"
- wails-version: "v2.9.0"
- sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
- sign-macos-app-id: "Developer ID Application: Lethean LTD (W2DNA5L5DY)"
- sign-macos-app-cert: ${{ secrets.MAC_DEVELOPER_CERT }}
- sign-macos-app-cert-password: ${{ secrets.MAC_DEVELOPER_PASS }}
- sign-macos-installer-id: "Developer ID Installer: Lethean LTD (W2DNA5L5DY)"
- sign-macos-installer-cert: ${{ secrets.MAC_DEVELOPER_INSTALL_CERT }}
- sign-macos-installer-cert-password: ${{ secrets.MAC_DEVELOPER_INSTALL_PASS }}
- package: false # Do not try to upload to github
- macos-obfuscate:
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails
- build-platform: darwin/universal
- build-obfuscate: true
- wails-version: "v2.9.0"
- package: false # Do not try to upload to github
-
- windows2025:
- runs-on: windows-2025
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails.exe
- build-platform: windows/amd64
- wails-build-webview2: "embed"
- nsis: 'false'
- package: false # Do not try to upload to github
- windows2022:
- runs-on: windows-2022
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails.exe
- build-platform: windows/amd64
- wails-build-webview2: "embed"
- nsis: 'false'
- package: false # Do not try to upload to github
- windows2019:
- runs-on: windows-2019
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails.exe
- build-platform: windows/amd64
- wails-build-webview2: "embed"
- nsis: 'false'
- package: false # Do not try to upload to github
- windows-nsis:
- runs-on: windows-2022
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails.exe
- build-platform: windows/amd64
- wails-build-webview2: "embed"
- nsis: "true"
- package: false # Do not try to upload to github
- windows-obfuscate:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v4
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@main
- with:
- build-name: wails.exe
- build-platform: windows/amd64
- wails-build-webview2: "embed"
- build-obfuscate: true
- package: false # Do not try to upload to github
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a25231c
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,512 @@
+name: Build Action self-tests
+
+permissions:
+ contents: read
+
+on:
+ push:
+ branches: [ "main", "v3", "dev", "*-*" ]
+ pull_request:
+ branches: [ "main", "v3", "dev", "*-*" ]
+ workflow_dispatch:
+
+jobs:
+ discovery-tests:
+ name: Discovery sub-action tests on ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - id: disc
+ uses: ./actions/discovery
+ - name: Assert outputs (bash)
+ if: runner.os != 'Windows'
+ shell: bash
+ run: |
+ set -euo pipefail
+ : "${{ steps.disc.outputs.OS }}"
+ : "${{ steps.disc.outputs.ARCH }}"
+ : "${{ steps.disc.outputs.REF }}"
+ : "${{ steps.disc.outputs.SHORT_SHA }}"
+ echo "OS=${{ steps.disc.outputs.OS }} ARCH=${{ steps.disc.outputs.ARCH }} REF=${{ steps.disc.outputs.REF }} SHORT_SHA=${{ steps.disc.outputs.SHORT_SHA }}"
+ - name: Assert outputs (pwsh)
+ if: runner.os == 'Windows'
+ shell: powershell
+ run: |
+ if (-not "${{ steps.disc.outputs.OS }}") { throw 'OS output empty' }
+ if (-not "${{ steps.disc.outputs.ARCH }}") { throw 'ARCH output empty' }
+ if (-not "${{ steps.disc.outputs.REF }}") { throw 'REF output empty' }
+ if (-not "${{ steps.disc.outputs.SHORT_SHA }}") { throw 'SHORT_SHA output empty' }
+
+ discovery-fixture-tests:
+ name: Discovery fixture tests (project markers and stack suggestion)
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - { dir: 'tdd/wails2-root', expect: 'wails2', expect_root_pkg: '0', expect_frontend_pkg: '1', expect_go_mod: '1', expect_main_go: '1', expect_cmake: '0' }
+ - { dir: 'tdd/cpp-root', expect: 'cpp', expect_root_pkg: '0', expect_frontend_pkg: '0', expect_go_mod: '0', expect_main_go: '0', expect_cmake: '1' }
+ - { dir: 'tdd/node-only', expect: 'unknown', expect_root_pkg: '1', expect_frontend_pkg: '0', expect_go_mod: '0', expect_main_go: '0', expect_cmake: '0' }
+ - { dir: 'tdd/docs', expect: 'unknown', expect_root_pkg: '0', expect_frontend_pkg: '0', expect_go_mod: '0', expect_main_go: '0', expect_cmake: '0' }
+ steps:
+ - uses: actions/checkout@v4
+ - id: disc
+ uses: ./actions/discovery
+ with:
+ working-directory: ${{ matrix.dir }}
+ - name: Assert markers and suggestion
+ shell: bash
+ run: |
+ set -euo pipefail
+ echo "[DEBUG_LOG] Tested dir=${{ matrix.dir }}"
+ test "${{ steps.disc.outputs.PRIMARY_STACK_SUGGESTION }}" = "${{ matrix.expect }}"
+ test "${{ steps.disc.outputs.HAS_ROOT_PACKAGE_JSON }}" = "${{ matrix.expect_root_pkg }}"
+ test "${{ steps.disc.outputs.HAS_FRONTEND_PACKAGE_JSON }}" = "${{ matrix.expect_frontend_pkg }}"
+ test "${{ steps.disc.outputs.HAS_ROOT_GO_MOD }}" = "${{ matrix.expect_go_mod }}"
+ test "${{ steps.disc.outputs.HAS_ROOT_MAIN_GO }}" = "${{ matrix.expect_main_go }}"
+ test "${{ steps.disc.outputs.HAS_ROOT_CMAKELISTS }}" = "${{ matrix.expect_cmake }}"
+
+
+ options-tests:
+ name: Options sub-action matrix (Ubuntu)
+ needs: [discovery-tests,discovery-fixture-tests]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - { distro: '22.04', obf: 'false', nsis: 'false', tags: 'false', expect_webkit: 'false' }
+ - { distro: '24.04', obf: 'false', nsis: 'false', tags: 'false', expect_webkit: 'true' }
+ - { distro: '24.04', obf: 'true', nsis: 'true', tags: 'release', expect_webkit: 'true' }
+ - { distro: '24.04', obf: 'false', nsis: 'false', tags: 'release,custom,custom', expect_webkit: 'true', expect_dedupe: 'custom' }
+ - { distro: '24.04', obf: 'false', nsis: 'false', tags: 'release custom', expect_webkit: 'true', expect_dedupe: '' }
+ - { distro: '24.04', obf: 'false', nsis: 'false', tags: 'false', expect_webkit: 'false', disable_webkit: 'true' }
+ steps:
+ - uses: actions/checkout@v4
+ - id: opts
+ uses: ./actions/options
+ with:
+ build-obfuscate: ${{ matrix.obf }}
+ build-tags: ${{ matrix.tags }}
+ nsis: ${{ matrix.nsis }}
+ distro: ${{ matrix.distro }}
+ disable-webkit-auto-tag: ${{ matrix.disable_webkit || 'false' }}
+ - name: Assert BUILD_OPTIONS
+ shell: bash
+ run: |
+ set -euo pipefail
+ BO='${{ steps.opts.outputs.BUILD_OPTIONS }}'
+ echo "BUILD_OPTIONS=$BO"
+ if [ "${{ matrix.obf }}" = "true" ] && ! echo "$BO" | grep -q -- "-obfuscated"; then echo "missing -obfuscated"; exit 1; fi
+ if [ "${{ matrix.nsis }}" = "true" ] && ! echo "$BO" | grep -q -- "-nsis"; then echo "missing -nsis"; exit 1; fi
+ if [ "${{ matrix.expect_webkit }}" = "true" ] && ! echo "$BO" | grep -q -- "webkit2_41"; then echo "missing webkit2_41"; exit 1; fi
+ if [ "${{ matrix.disable_webkit || 'false' }}" = "true" ] && echo "$BO" | grep -q -- "webkit2_41"; then echo "webkit2_41 should be disabled"; exit 1; fi
+ if [ -n "${{ matrix.expect_dedupe || '' }}" ]; then
+ count=$(echo "$BO" | grep -o -- "${{ matrix.expect_dedupe }}" | wc -l | tr -d ' ')
+ if [ "$count" -ne 1 ]; then echo "expected tag '${{ matrix.expect_dedupe }}' to appear once, got $count"; exit 1; fi
+ fi
+
+
+
+
+
+ setup-go-tests:
+ needs: [options-tests, wails-env-mapping, wails-env-mapping-wrapper]
+ name: Setup Go/Wails sub-action on ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Go/Wails
+ uses: ./actions/setup/go
+ with:
+ go-version: '1.23'
+ build-cache: 'true'
+ build-obfuscate: 'false'
+ wails-version: 'latest'
+ wails-dev-build: 'false'
+ - name: Assert go and wails available
+ shell: bash
+ run: |
+ set -euo pipefail
+ go version
+ if command -v wails >/dev/null 2>&1; then wails version || true; else echo "wails not on PATH (may be Windows PATH nuance), continuing"; fi
+
+
+ setup-npm-tests:
+ name: Setup npm sub-action on ${{ matrix.os }}
+ needs: [options-tests, wails-env-mapping, wails-env-mapping-wrapper]
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Node/npm (no install)
+ uses: ./actions/setup/npm
+ with:
+ node-version: '18.x'
+ working-directory: '.'
+ install: 'false'
+ - name: Assert node/npm
+ shell: bash
+ run: |
+ node -v
+ npm -v
+
+ setup-conan-tests:
+ needs: [options-tests, wails-env-mapping, wails-env-mapping-wrapper]
+ name: Setup Conan sub-action (Linux only)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Conan
+ uses: ./actions/setup/conan
+ - name: Assert conan version present
+ shell: bash
+ run: |
+ if command -v conan >/dev/null 2>&1; then conan --version; else echo "Conan not found in PATH (ok for placeholder)"; fi
+
+
+ # setup-deno-tests:
+# name: Setup Deno sub-action behavior (skip vs enable)
+# runs-on: ubuntu-latest
+# steps:
+# - uses: actions/checkout@v4
+# - name: Skip path (no env)
+# uses: ./actions/setup/deno
+# - name: Enable via env and run a command
+# env:
+# DENO_ENABLE: 'true'
+# DENO_VERSION: 'v1.44.x'
+# DENO_WORKDIR: '.'
+# DENO_BUILD: 'deno --version'
+# uses: ./actions/setup/deno
+
+ package-tests:
+ needs: [setup-conan-tests,setup-go-tests,setup-npm-tests]
+ name: Package sub-action with dummy artifact (Ubuntu)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Prepare dummy artifact
+ shell: bash
+ run: |
+ mkdir -p tmp/bin
+ echo "hello" > tmp/bin/dummy.txt
+ - name: Package (no release on branches)
+ uses: ./actions/package
+ with:
+ package: 'true'
+ build-name: 'ci-test'
+ os: 'Linux'
+ arch: 'amd64'
+ short-sha: '${{ github.sha }}'
+ - name: Check log
+ run: echo "Ensure [DEBUG_LOG] ARTIFACT_NAME is visible above"
+
+ package-smoke-ubuntu:
+ name: Packaging smoke (Ubuntu, package:true, no release on non-tags)
+ runs-on: ubuntu-latest
+ needs: package-tests
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Prepare dummy artifact
+ shell: bash
+ run: |
+ mkdir -p tmp/bin
+ echo "dummy" > tmp/bin/dummy.txt
+ - name: Run root action with packaging
+ uses: ./
+ with:
+ build-name: wails-smoke-${{ github.run_id }}
+ build-platform: linux/amd64
+ app-working-directory: tdd/wails2-root
+ package: true
+ sign: false
+ nsis: false
+ - name: Note
+ run: |
+ echo "[DEBUG_LOG] Packaging smoke complete. Check previous step for ARTIFACT_NAME echo."
+
+ signing-diagnostics:
+ name: Signing diagnostics (dry-run)
+ needs: package-tests
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: macOS gon presence
+ if: runner.os == 'macOS'
+ shell: bash
+ run: |
+ if command -v gon >/dev/null 2>&1; then
+ echo "[DEBUG_LOG] gon present: $(gon --version)"
+ else
+ echo "[DEBUG_LOG] gon not found. It is installed during the Go setup step in real builds (macOS only)."
+ fi
+ - name: Windows signtool discovery
+ if: runner.os == 'Windows'
+ shell: powershell
+ run: |
+ $paths = @(
+ 'C:/Program Files (x86)/Windows Kits/10/bin',
+ 'C:/Program Files (x86)/Windows Kits/11/bin'
+ )
+ $found = $false
+ foreach ($base in $paths) {
+ if (Test-Path $base) {
+ $sig = Get-ChildItem -Path $base -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue | Select-Object -First 1
+ if ($sig) { Write-Host "[DEBUG_LOG] signtool found at $($sig.FullName)"; $found = $true; break }
+ }
+ }
+ if (-not $found) {
+ Write-Host "[DEBUG_LOG] signtool.exe not found in common SDK paths. It is typically available with Windows 10/11 SDK."
+ }
+
+ subactions-smoke:
+ needs: package-tests
+ name: Sub-actions smoke (workflow-local ./actions/*)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Discovery (workflow-local path)
+ id: disc
+ uses: ./actions/discovery
+ - name: Compute options (with distro)
+ id: opts
+ uses: ./actions/options
+ with:
+ build-obfuscate: 'true'
+ build-tags: 'release'
+ nsis: 'false'
+ distro: ${{ steps.disc.outputs.DISTRO }}
+ - name: Show computed options
+ run: echo "BUILD_OPTIONS='${{ steps.opts.outputs.BUILD_OPTIONS }}'"
+
+
+ auto-stack-smoke:
+ needs: package-tests
+ name: Auto stack routing smoke (root action on Wails2-like repo)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Run root action with AUTO_STACK in tdd/wails2-root
+ id: root
+ uses: ./
+ with:
+ build-name: wails
+ build-platform: linux/amd64
+ app-working-directory: tdd/wails2-root
+ build: false
+ package: false
+ sign: false
+ - name: Assert orchestrator selected wails2
+ shell: bash
+ run: |
+ set -euo pipefail
+ sel='${{ steps.root.outputs.SELECTED_STACK }}'
+ echo "[DEBUG_LOG] SELECTED_STACK=$sel"
+ test "$sel" = "wails2" || { echo "Expected SELECTED_STACK=wails2, got '$sel'"; exit 1; }
+
+ matrix-root-action:
+ name: Root action (./) on ${{ matrix.os }} for ${{ matrix.platform }}
+ runs-on: ${{ matrix.os }}
+ needs: [ package-tests]
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ platform: linux/amd64
+ - os: windows-latest
+ platform: windows/amd64
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Prepare dummy artifact
+ shell: bash
+ run: |
+ mkdir -p tmp/bin
+ echo "dummy" > tmp/bin/dummy.txt
+ - name: Run root action locally
+ uses: ./
+ with:
+ build-name: wails-root-action-${{ matrix.os }}
+ build-platform: ${{ matrix.platform }}
+ app-working-directory: tdd/wails2-root
+ build: false
+ package: true
+ sign: false
+
+ wrapper-wails2:
+ name: Wails2 wrapper (./actions/build/wails2) on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ needs: [package-tests]
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ platform: linux/amd64
+ - os: windows-latest
+ platform: windows/amd64
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Run wails2 wrapper locally
+ uses: ./actions/build/wails2
+ with:
+ build-name: wails-wrapper-${{ matrix.os }}
+ build-platform: ${{ matrix.platform }}
+ app-working-directory: tdd/wails2-root
+ package: true
+ sign: false
+ nsis: false
+
+
+# deno-env-path-test:
+# name: Deno env-first path (root action on Ubuntu)
+# runs-on: ubuntu-latest
+# steps:
+# - uses: actions/checkout@v4
+# - name: Configure Deno via $GITHUB_ENV
+# run: |
+# echo "DENO_ENABLE=true" >> "$GITHUB_ENV"
+# echo "DENO_VERSION=v1.44.x" >> "$GITHUB_ENV"
+# echo "DENO_WORKDIR=frontend" >> "$GITHUB_ENV"
+# echo "DENO_BUILD=deno --version" >> "$GITHUB_ENV"
+# - name: Run root action (should set up Deno and run command)
+# uses: ./
+# with:
+# build-name: wails
+# build-platform: linux/amd64
+# build: false
+# package: false
+
+ wails-env-mapping:
+ name: WAILS_* env mapping (root action)
+ needs: [discovery-tests,discovery-fixture-tests]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Configure WAILS_* via $GITHUB_ENV
+ run: |
+ echo "WAILS_OBFUSCATE=true" >> "$GITHUB_ENV"
+ echo "WAILS_NSIS=false" >> "$GITHUB_ENV"
+ echo "WAILS_BUILD_TAGS=release custom" >> "$GITHUB_ENV"
+ echo "WAILS_VERSION=latest" >> "$GITHUB_ENV"
+ echo "WAILS_GO_VERSION=1.23" >> "$GITHUB_ENV"
+ echo "WAILS_NODE_VERSION=18.x" >> "$GITHUB_ENV"
+ echo "WAILS_WEBVIEW2=download" >> "$GITHUB_ENV"
+ - name: Run root action (uses ENV mapping)
+ uses: ./
+ with:
+ build-name: wails
+ build-platform: linux/amd64
+ app-working-directory: tdd/wails2-root
+ build: false
+ package: false
+ sign: false
+
+ wails-env-mapping-wrapper:
+ name: WAILS_* env mapping (wrapper outputs)
+ needs: [discovery-tests,discovery-fixture-tests]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Configure WAILS_* via $GITHUB_ENV (wrapper)
+ run: |
+ echo "WAILS_OBFUSCATE=true" >> "$GITHUB_ENV"
+ echo "WAILS_NSIS=false" >> "$GITHUB_ENV"
+ echo "WAILS_BUILD_TAGS=release custom" >> "$GITHUB_ENV"
+ echo "WAILS_VERSION=latest" >> "$GITHUB_ENV"
+ echo "WAILS_GO_VERSION=1.23" >> "$GITHUB_ENV"
+ echo "WAILS_NODE_VERSION=18.x" >> "$GITHUB_ENV"
+ echo "WAILS_WEBVIEW2=download" >> "$GITHUB_ENV"
+ - name: Run wails2 wrapper (capture outputs)
+ id: wrap
+ uses: ./actions/build/wails2
+ with:
+ build-name: wails-wrapper-${{ github.run_id }}
+ build-platform: linux/amd64
+ app-working-directory: tdd/wails2-root
+ build: false
+ package: false
+ sign: false
+ nsis: false
+ - name: Assert wrapper resolved envs
+ shell: bash
+ run: |
+ set -euo pipefail
+ tags='${{ steps.wrap.outputs.BUILD_TAGS }}'
+ obf='${{ steps.wrap.outputs.OBFUSCATE }}'
+ nsis='${{ steps.wrap.outputs.NSIS }}'
+ echo "[DEBUG_LOG] WRAP_BUILD_TAGS=$tags OBFUSCATE=$obf NSIS=$nsis"
+ grep -q "release" <<< "$tags"
+ grep -q "custom" <<< "$tags"
+ test "$obf" = "true"
+ test "$nsis" = "false"
+
+ readme-snippets:
+ name: README snippets validation (non-signing, non-release)
+ needs: [package-tests]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Discovery example
+ id: disc
+ uses: ./actions/discovery
+ with:
+ working-directory: tdd/wails2-root
+ - name: Options example
+ id: opts
+ uses: ./actions/options
+ with:
+ build-obfuscate: 'true'
+ build-tags: 'release'
+ nsis: 'false'
+ distro: ${{ steps.disc.outputs.DISTRO }}
+ - name: Package example
+ uses: ./actions/package
+ with:
+ package: 'true'
+ build-name: 'ci-snippet'
+ os: 'Linux'
+ arch: 'amd64'
+ short-sha: '${{ github.sha }}'
+ include-meta: 'true'
+ - name: Note
+ run: echo "[DEBUG_LOG] README snippets validated"
+
+ build-wails2:
+ name: Build Wails2 project (actual compilation)
+ needs: [setup-go-tests, setup-npm-tests]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build with action
+ uses: ./
+ with:
+ build-name: hello-wails
+ build-platform: linux/arm64
+ app-working-directory: tdd/wails2-root
+ build: true
+ package: false
+ sign: false
+ - name: Verify binary
+ run: test -f tdd/wails2-root/build/bin/hello-wails
diff --git a/.github/workflows/wails-build.yml b/.github/workflows/wails-build.yml
new file mode 100644
index 0000000..e6f2bad
--- /dev/null
+++ b/.github/workflows/wails-build.yml
@@ -0,0 +1,120 @@
+name: Reusable Wails Build
+
+on:
+ workflow_call:
+ inputs:
+ build:
+ type: string
+ default: 'true'
+ sign:
+ type: string
+ default: 'false'
+ package:
+ type: string
+ default: 'true'
+ nsis:
+ type: string
+ default: 'false'
+ build-name:
+ type: string
+ required: true
+ build-cache:
+ type: string
+ default: 'true'
+ build-platform:
+ type: string
+ default: 'darwin/universal'
+ build-tags:
+ type: string
+ default: 'false'
+ build-obfuscate:
+ type: string
+ default: 'false'
+ wails-version:
+ type: string
+ default: 'latest'
+ wails-build-webview2:
+ type: string
+ default: 'download'
+ go-version:
+ type: string
+ default: '1.23'
+ deno-build:
+ type: string
+ default: ''
+ app-working-directory:
+ type: string
+ default: '.'
+ deno-working-directory:
+ type: string
+ default: '.'
+ deno-version:
+ type: string
+ default: 'v1.20.x'
+ sign-macos-app-id:
+ type: string
+ default: ''
+ sign-macos-apple-password:
+ type: string
+ default: ''
+ sign-macos-app-cert:
+ type: string
+ default: ''
+ sign-macos-app-cert-password:
+ type: string
+ default: ''
+ sign-macos-installer-id:
+ type: string
+ default: ''
+ sign-macos-installer-cert:
+ type: string
+ default: ''
+ sign-macos-installer-cert-password:
+ type: string
+ default: ''
+ sign-windows-cert:
+ type: string
+ default: ''
+ sign-windows-cert-password:
+ type: string
+ default: ''
+ wails-dev-build:
+ type: string
+ default: 'false'
+
+jobs:
+ build:
+ runs-on: ${{ inputs.build-platform == 'windows/amd64' && 'windows-latest' || inputs.build-platform == 'linux/amd64' && 'ubuntu-latest' || 'macos-latest' }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Wails build action (wrapper)
+ uses: ./actions/build/wails2
+ with:
+ build: ${{ inputs.build }}
+ sign: ${{ inputs.sign }}
+ package: ${{ inputs.package }}
+ nsis: ${{ inputs.nsis }}
+ build-name: ${{ inputs.build-name }}
+ build-cache: ${{ inputs.build-cache }}
+ build-platform: ${{ inputs.build-platform }}
+ build-tags: ${{ inputs.build-tags }}
+ build-obfuscate: ${{ inputs.build-obfuscate }}
+ wails-version: ${{ inputs.wails-version }}
+ wails-build-webview2: ${{ inputs.wails-build-webview2 }}
+ go-version: ${{ inputs.go-version }}
+ deno-build: ${{ inputs.deno-build }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+ deno-working-directory: ${{ inputs.deno-working-directory }}
+ deno-version: ${{ inputs.deno-version }}
+ sign-macos-app-id: ${{ inputs.sign-macos-app-id }}
+ sign-macos-apple-password: ${{ inputs.sign-macos-apple-password }}
+ sign-macos-app-cert: ${{ inputs.sign-macos-app-cert }}
+ sign-macos-app-cert-password: ${{ inputs.sign-macos-app-cert-password }}
+ sign-macos-installer-id: ${{ inputs.sign-macos-installer-id }}
+ sign-macos-installer-cert: ${{ inputs.sign-macos-installer-cert }}
+ sign-macos-installer-cert-password: ${{ inputs.sign-macos-installer-cert-password }}
+ sign-windows-cert: ${{ inputs.sign-windows-cert }}
+ sign-windows-cert-password: ${{ inputs.sign-windows-cert-password }}
+ wails-dev-build: ${{ inputs.wails-dev-build }}
diff --git a/.github/workflows/wails2.yml b/.github/workflows/wails2.yml
new file mode 100644
index 0000000..fe98886
--- /dev/null
+++ b/.github/workflows/wails2.yml
@@ -0,0 +1,120 @@
+name: Reusable Wails v2 Build
+
+on:
+ workflow_call:
+ inputs:
+ build:
+ type: string
+ default: 'true'
+ sign:
+ type: string
+ default: 'false'
+ package:
+ type: string
+ default: 'true'
+ nsis:
+ type: string
+ default: 'false'
+ build-name:
+ type: string
+ required: true
+ build-cache:
+ type: string
+ default: 'true'
+ build-platform:
+ type: string
+ default: 'darwin/universal'
+ build-tags:
+ type: string
+ default: 'false'
+ build-obfuscate:
+ type: string
+ default: 'false'
+ wails-version:
+ type: string
+ default: 'latest'
+ wails-build-webview2:
+ type: string
+ default: 'download'
+ go-version:
+ type: string
+ default: '1.23'
+ deno-build:
+ type: string
+ default: ''
+ app-working-directory:
+ type: string
+ default: '.'
+ deno-working-directory:
+ type: string
+ default: '.'
+ deno-version:
+ type: string
+ default: 'v1.20.x'
+ sign-macos-app-id:
+ type: string
+ default: ''
+ sign-macos-apple-password:
+ type: string
+ default: ''
+ sign-macos-app-cert:
+ type: string
+ default: ''
+ sign-macos-app-cert-password:
+ type: string
+ default: ''
+ sign-macos-installer-id:
+ type: string
+ default: ''
+ sign-macos-installer-cert:
+ type: string
+ default: ''
+ sign-macos-installer-cert-password:
+ type: string
+ default: ''
+ sign-windows-cert:
+ type: string
+ default: ''
+ sign-windows-cert-password:
+ type: string
+ default: ''
+ wails-dev-build:
+ type: string
+ default: 'false'
+
+jobs:
+ build:
+ runs-on: ${{ inputs.build-platform == 'windows/amd64' && 'windows-latest' || inputs.build-platform == 'linux/amd64' && 'ubuntu-latest' || 'macos-latest' }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Wails v2 build (wrapper)
+ uses: ./actions/build/wails2
+ with:
+ build: ${{ inputs.build }}
+ sign: ${{ inputs.sign }}
+ package: ${{ inputs.package }}
+ nsis: ${{ inputs.nsis }}
+ build-name: ${{ inputs.build-name }}
+ build-cache: ${{ inputs.build-cache }}
+ build-platform: ${{ inputs.build-platform }}
+ build-tags: ${{ inputs.build-tags }}
+ build-obfuscate: ${{ inputs.build-obfuscate }}
+ wails-version: ${{ inputs.wails-version }}
+ wails-build-webview2: ${{ inputs.wails-build-webview2 }}
+ go-version: ${{ inputs.go-version }}
+ deno-build: ${{ inputs.deno-build }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+ deno-working-directory: ${{ inputs.deno-working-directory }}
+ deno-version: ${{ inputs.deno-version }}
+ sign-macos-app-id: ${{ inputs.sign-macos-app-id }}
+ sign-macos-apple-password: ${{ inputs.sign-macos-apple-password }}
+ sign-macos-app-cert: ${{ inputs.sign-macos-app-cert }}
+ sign-macos-app-cert-password: ${{ inputs.sign-macos-app-cert-password }}
+ sign-macos-installer-id: ${{ inputs.sign-macos-installer-id }}
+ sign-macos-installer-cert: ${{ inputs.sign-macos-installer-cert }}
+ sign-macos-installer-cert-password: ${{ inputs.sign-macos-installer-cert-password }}
+ sign-windows-cert: ${{ inputs.sign-windows-cert }}
+ sign-windows-cert-password: ${{ inputs.sign-windows-cert-password }}
+ wails-dev-build: ${{ inputs.wails-dev-build }}
diff --git a/.gitignore b/.gitignore
index fb7a30c..832d661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
.idea
-build/bin
-node_modules
-frontend/dist
+build/wails2/build/bin
+build/wails2/node_modules
+build/wails2/frontend/dist
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..cdd1e0b
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,223 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Repository Overview
+
+This is `snider/build@v3` - a modular, multi-stack GitHub Actions build system. The primary use case is building Wails v2 desktop applications, with planned support for Wails v3 and C++.
+
+## The Pipeline Pattern
+
+The `actions/` folder implements a powerful compositional pipeline pattern. Each sub-action is a pure function that takes inputs, produces outputs, and can be composed with others. This pattern translates well to Go.
+
+### Data Flow Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ ROOT ACTION │
+│ action.yml (gateway) │
+│ - Calls Discovery first │
+│ - Delegates to Directory Orchestrator │
+│ - Calls Package last │
+└─────────────────────────────────────────────────────────────────────────────┘
+ │
+ ┌───────────────┼───────────────┐
+ ▼ ▼ ▼
+ ┌───────────┐ ┌─────────────┐ ┌─────────────┐
+ │ Discovery │ │ Orchestrator│ │ Package │
+ │ │ │ │ │ │
+ │ Outputs: │ │ Routes to │ │ Inputs: │
+ │ - OS │──▶│ stack- │ │ - Discovery │
+ │ - ARCH │ │ specific │ │ outputs │
+ │ - DISTRO │ │ wrapper │ │ - build- │
+ │ - IS_TAG │ │ │ │ name │
+ │ - etc. │ │ │ │ │
+ └───────────┘ └─────────────┘ └─────────────┘
+ │
+ ▼
+ ┌─────────────────────────────────────────────────┐
+ │ STACK WRAPPER (e.g., wails2) │
+ │ actions/build/wails2/action.yml │
+ │ │
+ │ Full pipeline for one stack: │
+ │ 1. Config Resolution (inputs > env > defaults) │
+ │ 2. Discovery (reused) │
+ │ 3. Options computation │
+ │ 4. Setup (toolchains) │
+ │ 5. Build │
+ │ 6. Sign │
+ │ 7. Package │
+ └─────────────────────────────────────────────────┘
+```
+
+### The Sub-Actions
+
+#### 1. Discovery (`actions/discovery/`)
+**Purpose**: Gather environmental context - runs first, outputs flow downstream.
+
+**Key Pattern**: Platform-specific implementations (bash vs powershell) with unified outputs.
+
+```
+Inputs:
+ - working-directory
+
+Outputs:
+ - OS, ARCH, DISTRO (environment)
+ - REF, BRANCH, TAG, IS_TAG, SHA, SHORT_SHA (git context)
+ - HAS_ROOT_PACKAGE_JSON, HAS_FRONTEND_PACKAGE_JSON, HAS_ROOT_GO_MOD,
+ HAS_ROOT_MAIN_GO, HAS_ROOT_CMAKELISTS (file markers)
+ - PRIMARY_STACK_SUGGESTION (computed: wails2|cpp|unknown)
+```
+
+**Go equivalent**: A `Discover()` function returning a `Context` struct.
+
+#### 2. Options (`actions/options/`)
+**Purpose**: Compute build flags from inputs + discovery outputs.
+
+**Key Pattern**: Pure transformation - no side effects, deterministic output.
+
+```
+Inputs:
+ - build-obfuscate, build-tags, nsis
+ - distro (from discovery)
+
+Outputs:
+ - BUILD_OPTIONS (string like "-obfuscated -tags release,webkit2_41 -nsis")
+```
+
+**Go equivalent**: A `ComputeOptions(config, discovery) Options` function.
+
+#### 3. Setup Orchestrator (`actions/setup/`)
+**Purpose**: Coordinate toolchain installation in correct order.
+
+**Key Pattern**: Thin orchestrator that delegates to specialised setup actions.
+
+```
+Delegates to:
+ - setup/go → Go + Wails CLI + Garble (optional) + gon (macOS)
+ - setup/npm → Node.js + npm dependencies (auto-detects frontend/)
+ - setup/deno → Optional, ENV-first configuration
+ - setup/conan → Placeholder for C++ builds
+```
+
+**Go equivalent**: A `Setup` interface with implementations per toolchain.
+
+#### 4. Individual Setup Actions (`actions/setup/{go,npm,deno,conan}/`)
+**Purpose**: Single-responsibility toolchain setup.
+
+**Key Patterns**:
+- **Conditional execution**: Only run what's needed (e.g., Garble only if obfuscating)
+- **ENV-first config**: Environment variables override inputs (Deno is the best example)
+- **Platform awareness**: macOS gets `gon` for signing
+
+#### 5. Stack Wrapper (`actions/build/wails2/`)
+**Purpose**: Complete pipeline for one technology stack.
+
+**Key Pattern**: Config resolution layer + orchestration of all other sub-actions.
+
+```
+Step 1: Resolve configuration (precedence: inputs > env > defaults)
+ - Normalises booleans, picks first non-empty value
+ - Outputs resolved config for downstream steps
+
+Step 2-7: Call other sub-actions in order:
+ Discovery → Options → Setup → Build → Sign → Package
+```
+
+**Go equivalent**: A `Pipeline` struct with `Run()` method that coordinates phases.
+
+#### 6. Build (`actions/build/wails2/build/`)
+**Purpose**: Execute the actual build command.
+
+**Key Pattern**: Minimal action - receives pre-computed options, just runs the command.
+
+```
+Inputs:
+ - build-platform, build-name
+ - wails-build-webview2
+ - build-options (pre-computed string)
+
+Does:
+ - wails build --platform X -webview2 Y -o Z $BUILD_OPTIONS
+ - chmod +x on outputs (platform-specific paths)
+```
+
+#### 7. Sign (`actions/sign/`)
+**Purpose**: Platform-specific code signing.
+
+**Key Pattern**: Conditional branches per OS, tag-gated for releases.
+
+```
+macOS flow (only on tags):
+ 1. Import code-signing certificates
+ 2. Import installer certificates
+ 3. Sign .app with gon
+ 4. Create .app.zip
+ 5. Build .pkg installer (signed or unsigned)
+ 6. Notarise with gon
+
+Windows flow:
+ 1. Decode certificate from base64
+ 2. Sign .exe with signtool
+ 3. Sign installer .exe
+```
+
+#### 8. Package (`actions/package/`)
+**Purpose**: Create artifacts and publish releases.
+
+**Key Pattern**: Smart naming from discovery outputs + tag-gated releases.
+
+```
+Artifact name: {build-name}_{OS}_{ARCH}_{TAG|SHORT_SHA}
+
+Does:
+ 1. Compute artifact name
+ 2. Write artifact_meta.json (optional)
+ 3. Upload artifact (always)
+ 4. Publish GitHub release (only on tags)
+```
+
+### Key Design Principles (Portable to Go)
+
+1. **Outputs flow downstream**: Each action's outputs become inputs to later actions. Discovery runs first and its outputs are passed to everything else.
+
+2. **Config resolution with precedence**: `inputs > environment > defaults`. The stack wrapper handles this once, then passes resolved values downstream.
+
+3. **ENV-first for optional features**: Features like Deno check environment variables first, allowing zero-config enablement via CI job `env:` blocks.
+
+4. **Platform-specific implementations, unified interface**: Discovery has separate bash/powershell steps but unified outputs. In Go, this is interfaces with platform-specific implementations.
+
+5. **Thin orchestrators**: The setup orchestrator and directory orchestrator just coordinate - they don't contain logic themselves.
+
+6. **Conditional execution**: Actions check conditions before running (e.g., sign only on tags, Garble only if obfuscating).
+
+7. **Stack wrappers own the full pipeline**: Each stack (wails2, future cpp) has its own wrapper that knows how to coordinate all phases for that technology.
+
+## Development Commands
+
+**Local testing with act** (if available):
+```bash
+act -j discovery-tests # Test discovery sub-action
+act -j options-tests # Test options computation
+act -j setup-go-tests # Test Go/Wails setup
+```
+
+**CI runs automatically** on push to `main`, `v3`, `dev`, and feature branches. The workflow gates app builds behind fast sub-action tests.
+
+## Testing Strategy
+
+CI uses fixture directories in `tdd/` to validate stack detection:
+- `tdd/wails2-root/` - Wails v2 project structure (go.mod + frontend/package.json)
+- `tdd/cpp-root/` - C++ project structure (CMakeLists.txt)
+- `tdd/node-only/` - Node.js only project (package.json, no Go)
+- `tdd/docs/` - Documentation-only project (mkdocs.yml)
+
+## Adding New Stacks
+
+1. Add file markers to `actions/discovery/` (detection logic)
+2. Create `actions/build/{stack}/` with stack-specific wrapper
+3. Create `actions/build/{stack}/build/` for the actual build step
+4. Add routing to `actions/action.yml` (directory orchestrator)
+5. Add setup sub-actions if new toolchains needed
+6. Create `tdd/{stack}/` fixture for CI validation
+7. Add test jobs to `.github/workflows/ci.yml`
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b4d920c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,267 @@
+# Makefile for testing GitHub Actions locally with act
+# Requires: act (brew install act), Docker
+
+.PHONY: help test test-all test-quick test-discovery test-options test-setup test-package test-smoke test-wails clean list \
+ build-all build-go build-node build-deno build-cpp build-wails
+
+# Default target
+help:
+ @echo "GitHub Actions Local Testing (via act)"
+ @echo ""
+ @echo "Quick start:"
+ @echo " make test-quick Run fast tests (discovery, options)"
+ @echo " make test-all Run all Linux tests"
+ @echo ""
+ @echo "Individual test targets:"
+ @echo " make test-discovery Discovery sub-action tests"
+ @echo " make test-fixtures Discovery fixture tests (stack detection)"
+ @echo " make test-options Options computation tests"
+ @echo " make test-setup-go Go/Wails setup tests"
+ @echo " make test-setup-npm Node/npm setup tests"
+ @echo " make test-setup-conan Conan setup tests"
+ @echo " make test-package Package sub-action tests"
+ @echo " make test-smoke Sub-actions smoke test"
+ @echo " make test-auto-stack Auto stack routing smoke test"
+ @echo " make test-wails-env Wails env mapping tests"
+ @echo " make test-wrapper Wails2 wrapper tests"
+ @echo ""
+ @echo "Local Build Tests (no Docker):"
+ @echo " make build-all Build all TDD hello worlds locally"
+ @echo " make build-go Build Go CLI and HTTP projects"
+ @echo " make build-cpp Build C++ project with CMake"
+ @echo " make build-node Install and check Node.js project"
+ @echo " make build-deno Type-check Deno project"
+ @echo " make build-wails Build full Wails v2 project"
+ @echo " make build-clean Remove build artifacts"
+ @echo ""
+ @echo "Utilities:"
+ @echo " make list List all available CI jobs"
+ @echo " make dry-run Dry run of all tests"
+ @echo " make pull-image Pull the large Ubuntu image"
+ @echo " make clean Remove all artifacts"
+ @echo ""
+ @echo "Options:"
+ @echo " ARCH=amd64 Run with x64 emulation (slower on M3)"
+ @echo " VERBOSE=1 Enable verbose output"
+ @echo ""
+ @echo "Examples:"
+ @echo " make build-all # Test local compilation"
+ @echo " make test-discovery # Test via act/Docker"
+ @echo " make test-quick VERBOSE=1"
+ @echo " make test-setup-go ARCH=amd64"
+
+# Architecture flag (default ARM64 for M3, can override with ARCH=amd64)
+ifeq ($(ARCH),amd64)
+ ARCH_FLAG := --container-architecture linux/amd64
+else
+ ARCH_FLAG := --container-architecture linux/arm64
+endif
+
+# Verbose flag
+ifeq ($(VERBOSE),1)
+ VERBOSE_FLAG := -v
+else
+ VERBOSE_FLAG :=
+endif
+
+# Base act command (specify workflow to avoid ambiguity with wails-build.yml and wails2.yml)
+ACT := act -W .github/workflows/ci.yml $(ARCH_FLAG) $(VERBOSE_FLAG)
+
+# Pull the large image first (saves time on subsequent runs)
+pull-image:
+ docker pull catthehacker/ubuntu:full-latest
+
+# List all jobs in the workflow
+list:
+ @$(ACT) -l
+
+# Dry run (shows what would execute without running)
+dry-run:
+ $(ACT) -n
+
+# =============================================================================
+# Quick Tests (fast, no heavy toolchain setup)
+# =============================================================================
+
+test-quick: test-discovery test-fixtures test-options
+ @echo "✓ Quick tests passed"
+
+# =============================================================================
+# Discovery Tests
+# =============================================================================
+
+test-discovery:
+ @echo "==> Running discovery tests..."
+ $(ACT) -j discovery-tests
+
+test-fixtures:
+ @echo "==> Running discovery fixture tests..."
+ $(ACT) -j discovery-fixture-tests
+
+# =============================================================================
+# Options Tests
+# =============================================================================
+
+test-options:
+ @echo "==> Running options tests..."
+ $(ACT) -j options-tests
+
+# =============================================================================
+# Setup Tests (require toolchain downloads)
+# =============================================================================
+
+test-setup: test-setup-go test-setup-npm test-setup-conan
+ @echo "✓ Setup tests passed"
+
+test-setup-go:
+ @echo "==> Running Go/Wails setup tests..."
+ $(ACT) -j setup-go-tests
+
+test-setup-npm:
+ @echo "==> Running npm setup tests..."
+ $(ACT) -j setup-npm-tests
+
+test-setup-conan:
+ @echo "==> Running Conan setup tests..."
+ $(ACT) -j setup-conan-tests
+
+# =============================================================================
+# Package Tests
+# =============================================================================
+
+test-package:
+ @echo "==> Running package tests..."
+ $(ACT) -j package-tests
+
+test-package-smoke:
+ @echo "==> Running package smoke test..."
+ $(ACT) -j package-smoke-ubuntu
+
+# =============================================================================
+# Integration / Smoke Tests
+# =============================================================================
+
+test-smoke:
+ @echo "==> Running sub-actions smoke test..."
+ $(ACT) -j subactions-smoke
+
+test-auto-stack:
+ @echo "==> Running auto-stack routing smoke test..."
+ $(ACT) -j auto-stack-smoke
+
+# =============================================================================
+# Wails-specific Tests
+# =============================================================================
+
+test-wails-env:
+ @echo "==> Running Wails env mapping tests..."
+ $(ACT) -j wails-env-mapping
+ $(ACT) -j wails-env-mapping-wrapper
+
+test-wrapper:
+ @echo "==> Running Wails2 wrapper tests..."
+ $(ACT) -j wrapper-wails2
+
+test-matrix:
+ @echo "==> Running matrix root action tests..."
+ $(ACT) -j matrix-root-action
+
+test-readme:
+ @echo "==> Running README snippets validation..."
+ $(ACT) -j readme-snippets
+
+test-build-wails2:
+ @echo "==> Running actual Wails2 build test..."
+ $(ACT) -j build-wails2
+
+# =============================================================================
+# Full Test Suite
+# =============================================================================
+
+test-all: test-discovery test-fixtures test-options test-setup test-package test-smoke test-auto-stack test-wails-env
+ @echo ""
+ @echo "=========================================="
+ @echo "✓ All Linux tests passed"
+ @echo "=========================================="
+
+# Alias
+test: test-quick
+
+# =============================================================================
+# Local Build Tests (test compilation without Docker)
+# =============================================================================
+
+TDD := tdd
+BUILD_DIR := $(TDD)/.build
+
+build-all: build-go build-cpp build-node build-deno
+ @echo ""
+ @echo "=========================================="
+ @echo "All TDD hello worlds compiled successfully"
+ @echo "=========================================="
+
+build-go:
+ @echo "==> Building Go projects..."
+ @mkdir -p $(BUILD_DIR)
+ cd $(TDD)/go-cli && go build -o ../../$(BUILD_DIR)/go-cli .
+ cd $(TDD)/go-http && go build -o ../../$(BUILD_DIR)/go-http .
+ @echo "Go CLI:"
+ @./$(BUILD_DIR)/go-cli "TDD Test"
+ @echo "Go HTTP: compiled successfully (not running server)"
+
+build-go-versions:
+ @echo "==> Testing Go version compatibility..."
+ @for v in 1.21 1.22 1.23; do \
+ echo "Testing Go $$v..."; \
+ cd $(TDD)/go-cli && go mod edit -go=$$v && go build -o /dev/null . && echo " Go $$v: OK"; \
+ done
+ cd $(TDD)/go-cli && go mod edit -go=1.21
+
+build-cpp:
+ @echo "==> Building C++ projects..."
+ @mkdir -p $(BUILD_DIR)/cpp
+ cd $(BUILD_DIR)/cpp && cmake ../../cpp-hello && make
+ @echo "C++ hello:"
+ @./$(BUILD_DIR)/cpp/hello "TDD Test"
+
+build-node:
+ @echo "==> Building Node.js projects..."
+ cd $(TDD)/node-http && npm install --silent
+ @echo "Node HTTP: dependencies installed, checking syntax..."
+ cd $(TDD)/node-http && node --check index.js
+ @echo "Node HTTP: OK"
+
+build-deno:
+ @echo "==> Checking Deno projects..."
+ @if command -v deno >/dev/null 2>&1; then \
+ cd $(TDD)/deno-http && deno check main.ts; \
+ echo "Deno HTTP: OK"; \
+ else \
+ echo "Deno not installed, skipping (install with: brew install deno)"; \
+ fi
+
+build-wails:
+ @echo "==> Building Wails v2 project (requires frontend build first)..."
+ @if [ ! -d "$(TDD)/wails2-root/frontend/dist" ]; then \
+ echo "Building frontend..."; \
+ cd $(TDD)/wails2-root/frontend && npm install --silent && npm run build; \
+ fi
+ cd $(TDD)/wails2-root && go build -o ../../$(BUILD_DIR)/wails2-app .
+ @echo "Wails v2: compiled successfully"
+
+build-clean:
+ @echo "Cleaning build artifacts..."
+ rm -rf $(BUILD_DIR)
+ rm -rf $(TDD)/node-http/node_modules
+ rm -rf $(TDD)/wails2-root/frontend/node_modules
+ rm -rf $(TDD)/wails2-root/frontend/dist
+ @echo "Done"
+
+# =============================================================================
+# Cleanup
+# =============================================================================
+
+clean: build-clean
+ @echo "Cleaning up act artifacts..."
+ rm -rf .act-*
+ @echo "Done"
diff --git a/README.md b/README.md
index 560f632..f0dc183 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,25 @@
-20/02/2025 - Wails Version 2.10.0 is problematic, please use `wails-version: "v2.9.0"` & report the bug if you get issues, tyvm <3
+# snider/build@v3
-07/02/2025 - Please use `dAppServer/wails-build-action@main` AND star the repo to get updated on V3, the readme refers to a future version
+[](https://github.com/snider/wails-build-action/actions/workflows/ci.yml)
+[](LICENSE)
-# dAppServer/wails-build-action@v2
-GitHub action to build Wails.io: the action will install GoLang and NodeJS and run a build.
-This will be used on a [Wails.io](https://wails.io) v2 project.
+> [!NOTE]
+> For comprehensive documentation, please visit the [docs](docs/) directory.
-By default, the action will build and upload the results to Git Hub; on a tagged build, it will also upload to the release.
+
+> I help on lots of open source projects, im tired of doing the same thing over and over again.\
+> so, I'm going to put them all together in one place.\
+> Hopefully it will help you too.
+
+General build action (multi-stack).
+
+By default, the root action will best guess the builds you might want to run and delegate to the appropriate sub-action. You can also explicitly select a stack and enable/disable setup steps.
+
+you should write out an action that cherry-picks the parts you need; the auto-detected method works for me, based on the file structures in the tdd/* folders
# Default build
```yaml
-- uses: dAppServer/wails-build-action@v3
+- uses: snider/build@v3
with:
build-name: wailsApp
build-platform: linux/amd64
@@ -19,142 +28,168 @@ By default, the action will build and upload the results to Git Hub; on a tagged
## Build with No uploading
```yaml
-- uses: dAppServer/wails-build-action@v3
+- uses: snider/build@v3
with:
build-name: wailsApp
build-platform: linux/amd64
package: false
```
-## GitHub Action Options
-
-| Name | Default | Description |
-|--------------------------------------|----------------------|----------------------------------------------------|
-| `build-name` | none, required input | The name of the binary |
-| `build-obfuscate` | `false` | Obfuscate the binary |
-| `build` | `true` | Runs `wails build` on your source |
-| `nsis` | `true` | Runs `wails build` with or without -nsis |
-| `sign` | `false` | After build, signs and creates signed installers |
-| `package` | `true` | Upload workflow artifacts & publish release on tag |
-| `build-platform` | `darwin/universal` | Platform to build for |
-| `build-tags` | '' | Build tags to pass to Go compiler. Must be quoted. |
-| `wails-version` | `latest` | Wails version to use |
-| `wails-build-webview2` | `download` | Webview2 installing [download,embed,browser,error] |
-| `go-version` | `1.18` | Version of Go to use |
-| `node-version` | `16.x` | Node js version |
-| `deno-build` | '' | Deno compile command |
-| `deno-working-directory` | `.` | Working directory of your [Deno](https://deno.land/) server|
-| `deno-version` | `v1.20.x` | Deno version to use |
-| `sign-macos-app-id` | '' | ID of the app signing cert |
-| `sign-macos-apple-password` | '' | MacOS Apple password |
-| `sign-macos-app-cert` | '' | MacOS Application Certificate |
-| `sign-macos-app-cert-password` | '' | MacOS Application Certificate Password |
-| `sign-macos-installer-id` | '' | MacOS Installer Certificate id |
-| `sign-macos-installer-cert` | '' | MacOS Installer Certificate |
-| `sign-macos-installer-cert-password` | '' | MacOS Installer Certificate Password |
-| `sign-windows-cert` | '' | Windows Signing Certificate |
-| `sign-windows-cert-passowrd` | '' | Windows Signing Certificate Password |
-
-
-
-## Example Build
+## Inputs (high level)
-```yaml
-name: Wails build
+This repository is multi-stack. The root action currently runs the Wails v2 pipeline by default. For full Wails-specific inputs and examples, see `actions/build/wails2/README.md`.
+
+Common high-level inputs on the root action include:
+- `build-name` — required; base name for outputs
+- `build-platform` — target platform (e.g., `linux/amd64`, `windows/amd64`, `darwin/universal`)
+- `build` — whether to build (default `true`)
+- `package` — upload artifacts and (on tags) publish a release (default `true`)
+- `sign` — enable platform signing when configured (default `false`)
+
+Stack-specific inputs (Wails flags, signing certs, etc.) are documented in the Wails v2 wrapper: `actions/wails2/README.md`.
+
+
+
+## Examples and stack-specific docs
+
+For Wails v2 end-to-end usage, examples, and advanced options, see:
+- Wails v2 wrapper: `actions/build/wails2/README.md`
+- Wails build sub-action: `actions/build/wails2/build/README.md`
+
+The root README focuses on multi-stack concepts. Stack-specific workflows are documented alongside each stack.
-on: [push, pull_request]
+## macOS code signing docs moved
+The detailed macOS code signing and notarization guide (including `gon` JSON examples and `entitlements.plist`) now lives with the Wails v2 stack docs:
+- See `actions/build/wails2/README.md` → “macOS Code Signing (Wails v2)”
+
+
+## Configure Deno via environment variables (optional)
+
+Deno is not required. If you want to run a Deno build/asset step before Wails, you can configure it entirely via env vars — no `deno-*` inputs are needed.
+
+Precedence used by the action (inside `actions/setup`):
+- Environment variables > action inputs > defaults.
+- If nothing is provided, Deno is skipped.
+
+Supported variables:
+- `DENO_ENABLE` — `true`/`1`/`yes`/`on` explicitly enables Deno even without a build command.
+- `DENO_BUILD` — full command to run (e.g., `deno task build`, `deno run -A build.ts`).
+- `DENO_VERSION` — e.g., `v1.44.x`.
+- `DENO_WORKDIR` — working directory for the Deno command (default `.`).
+- Pass-throughs (used by Deno if present): `DENO_AUTH_TOKEN`, `DENO_DIR`, `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, etc.
+
+Example (job-level env):
+```yaml
jobs:
build:
- strategy:
- fail-fast: false
- matrix:
- build: [
- {name: wailsTest, platform: linux/amd64, os: ubuntu-latest},
- {name: wailsTest, platform: windows/amd64, os: windows-latest},
- {name: wailsTest, platform: darwin/universal, os: macos-latest}
- ]
- runs-on: ${{ matrix.build.os }}
+ runs-on: ubuntu-latest
+ env:
+ DENO_ENABLE: 'true'
+ DENO_VERSION: 'v1.44.x'
+ DENO_WORKDIR: 'frontend'
+ DENO_BUILD: 'deno task build'
steps:
- - uses: actions/checkout@v2
- with:
- submodules: recursive
- - uses: dAppServer/wails-build-action@v3
+ - uses: actions/checkout@v4
+ - uses: snider/build@v3
with:
- build-name: ${{ matrix.build.name }}
- build-platform: ${{ matrix.build.platform }}
- build-obfuscate: true
+ build-name: wailsApp
+ build-platform: linux/amd64
```
-## MacOS Code Signing
-
-You need to make two gon configuration files, this is because we need to sign and notarize the .app before making an installer with it.
-
+Using `$GITHUB_ENV` in a prior step:
```yaml
- - uses: dAppServer/wails-build-action@v3
- with:
- build-name: wailsApp
- sign: true
- build-platform: darwin/universal
- sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
- sign-macos-app-id: ${{ secrets.MACOS_DEVELOPER_CERT_ID }}
- sign-macos-app-cert: ${{ secrets.MACOS_DEVELOPER_CERT }}
- sign-macos-app-cert-password: ${{ secrets.MACOS_DEVELOPER_CERT_PASSWORD }}
- sign-macos-installer-id: ${{ secrets.MACOS_INSTALLER_CERT_ID }}
- sign-macos-installer-cert: ${{ secrets.MACOS_INSTALLER_CERT }}
- sign-macos-installer-cert-password: ${{ secrets.MACOS_INSTALLER_CERT_PASSWORD }}
+- name: Configure Deno via $GITHUB_ENV
+ run: |
+ echo "DENO_ENABLE=true" >> "$GITHUB_ENV"
+ echo "DENO_VERSION=v1.44.x" >> "$GITHUB_ENV"
+ echo "DENO_WORKDIR=frontend" >> "$GITHUB_ENV"
+ echo "DENO_BUILD=deno task build" >> "$GITHUB_ENV"
+- uses: snider/build@v3
+ with:
+ build-name: wailsApp
+ build-platform: linux/amd64
```
-`build/darwin/gon-sign.json`
-```json
-{
- "source" : ["./build/bin/wailsApp.app"],
- "bundle_id" : "com.wails.app",
- "apple_id": {
- "username": "username",
- "password": "@env:APPLE_PASSWORD"
- },
- "sign" :{
- "application_identity" : "Developer ID Application: XXXXXXXX (XXXXXX)",
- "entitlements_file": "./build/darwin/entitlements.plist"
- },
- "dmg" :{
- "output_path": "./build/bin/wailsApp.dmg",
- "volume_name": "Lethean"
- }
-}
-```
-`build/darwin/gon-notarize.json`
-```json
-{
- "notarize": [{
- "path": "./build/bin/wailsApp.pkg",
- "bundle_id": "com.wails.app",
- "staple": true
- },{
- "path": "./build/bin/wailsApp.app.zip",
- "bundle_id": "com.wails.app",
- "staple": false
- }],
- "apple_id": {
- "username": "USER name",
- "password": "@env:APPLE_PASSWORD"
- }
-}
+Secrets example (private modules):
+```yaml
+env:
+ DENO_AUTH_TOKEN: ${{ secrets.DENO_AUTH_TOKEN }}
```
-`build/darwin/entitlements.plist`
-```xml
-
-
-
-
- com.apple.security.app-sandbox
-
- com.apple.security.network.client
-
- com.apple.security.network.server
-
- com.apple.security.files.user-selected.read-write
-
-
-
+
+
+## Sub-actions overview
+
+This repo is modular. You can call the root action, the Wails v2 wrapper, or any sub-action directly.
+
+- actions/discovery — detects OS/ARCH, Ubuntu version on Linux, and exposes repo/ref metadata.
+- actions/options — computes `BUILD_OPTIONS` (adds `-tags webkit2_41` on Ubuntu 24.04 when appropriate).
+- actions/setup — orchestrator that delegates to:
+ - actions/setup/go — Go, optional Garble, Wails CLI, and `gon` on macOS.
+ - actions/setup/npm — Node.js and npm install/ci in your app working directory.
+ - actions/setup/deno — optional; ENV-first Deno setup and command runner.
+ - actions/setup/conan — placeholder for future C++ builds.
+- actions/build/wails2/build — runs `wails build` and fixes executable permissions per-OS.
+- actions/sign — unified macOS and Windows signing; notarizes on tags.
+- actions/package — uploads artifacts; on tags, publishes a GitHub Release.
+
+## Stacks
+
+- Available:
+ - wails2 — `uses: snider/build/actions/build/wails2@v3` (or just call the root action)
+- Coming soon:
+ - wails3 — once upstream stabilizes
+ - cpp — via `setup/conan` and dedicated build/sign/pack steps
+
+## Setup orchestrator notes
+
+The `actions/setup` sub-action is a thin orchestrator that runs Go → npm → Deno (optional) → Conan (optional). It keeps Deno independent from Wails. Configure Deno via environment variables (ENV-first), or via inputs as a fallback. See the Deno section below and `actions/setup/deno/README.md` for details.
+
+## Orchestrator controls (root action)
+
+The root action can auto-detect your stack and auto-enable setup steps. This makes `snider/build@v3` “just work” for common layouts, while still allowing full control.
+
+- Inputs (root action):
+ - `AUTO_STACK` (default `true`) — auto-select a stack based on `actions/discovery` outputs.
+ - `AUTO_SETUP` (default `true`) — allow sub-setup enabling based on env toggles.
+ - `STACK` (optional) — force a stack (e.g., `wails2`). When set, it takes precedence over auto.
+- Environment toggles (read when `AUTO_SETUP == true`):
+ - `ENABLE_GO`, `ENABLE_NPM`, `ENABLE_DENO`, `ENABLE_CONAN` — `true`/`1`/`yes`/`on` to explicitly enable those setups; otherwise defaults are used.
+- Precedence and routing:
+ - If `STACK` is set, the root action routes to that stack wrapper directly.
+ - Else if `AUTO_STACK` is enabled, the root action uses `PRIMARY_STACK_SUGGESTION` from discovery and routes accordingly (currently `wails2`).
+ - You can fully opt out by setting `AUTO_STACK: 'false'` and `AUTO_SETUP: 'false'` and calling sub-actions directly in your workflow.
+- Debug logs:
+ - Look for `[DEBUG_LOG] Auto stack=...` and `[DEBUG_LOG] npm-install resolved=...` in the logs to see decisions made.
+
+## Smarter artifact naming (package)
+
+Starting in v3, the `actions/package` sub-action composes a descriptive artifact name using discovery metadata:
+
+```text
+___
```
+
+- On tag builds, the tag (e.g., `v1.2.3`) is used.
+- On branch/PR builds, the short commit SHA is used.
+- Example: `wailsApp_Ubuntu-22.04_amd64_ab12cd3` or `wailsApp_macos_arm64_v1.2.3`.
+
+When you call the root action or the `wails2` wrapper, discovery outputs are passed automatically to `actions/package`.
+
+
+## CI validations and gating
+
+The repository includes self-tests to surface issues early and gate app builds behind fast sub-action checks:
+- Sub-action tests (gating): `discovery`, `options`, `setup/*` (go, npm, deno, conan), `sign` diagnostics, and `package` run first. App build jobs depend on these via `needs:` and will not execute if any sub-test fails.
+- Packaging smoke (Ubuntu): runs the root action locally with `package: true` on branch/PR builds and verifies artifact upload. No release is created on non-tag refs. Look for `[DEBUG_LOG] ARTIFACT_NAME=...` in logs.
+- Matrix builds with packaging: root action and the `wails2` wrapper run on Ubuntu/macOS/Windows with `package: true` on branches/PRs to confirm cross-OS uploads. Signing remains disabled.
+- Signing diagnostics (dry-run):
+ - macOS: prints `gon --version` if available or guidance if not; always green.
+ - Windows: searches common Windows SDK locations for `signtool.exe` and logs the result; always green.
+
+These checks run on `push`/`pull_request` to branches and are safe on forks (no secrets required). On tag refs, real releases are only created when your workflow explicitly runs and `refs/tags/*` is detected.
+
+### Extending CI for new stacks (wails3/cpp)
+- Mirror the pattern: create stack-specific sub-action tests (e.g., `setup/wails3`, `setup/conan`, stack-specific build options) that are fast and deterministic.
+- Add the new test jobs to the app build job `needs:` so stack builds only run after sub-tests pass.
+- Prefer dummy artifacts with the `actions/package` sub-action for packaging checks; keep releases tag-gated.
+- Keep tests secrets-free; add tool presence diagnostics (similar to `gon`/`signtool`) for platform-specific tools.
diff --git a/action.yml b/action.yml
index 1060d9a..a819a70 100644
--- a/action.yml
+++ b/action.yml
@@ -1,289 +1,86 @@
-name: "Wails Build Action"
-description: "Creates a wails binary"
+name: "Build Action (orchestrated)"
+description: "General build action repo; root is minimal and delegates to directory orchestrator based on discovery/ENV"
branding:
icon: 'box'
color: 'purple'
inputs:
build:
- description: "Platform to build for"
+ description: "Whether to run the build"
required: false
default: "true"
sign:
- description: "Sign the build"
+ description: "Sign the build (delegated to stack’s signing flow)"
required: false
default: "false"
package:
- description: "Uploads workflow & uploads tag builds to a release"
+ description: "Upload artifacts and publish release on tags"
required: false
default: "true"
- nsis:
- description: "Build a Windows Installer"
- required: false
- default: "false"
build-name:
- description: "The name of the binary file"
+ description: "The name of the binary/app bundle"
required: true
- build-cache:
- description: "Cache the build"
- required: false
- default: "true"
build-platform:
- description: "Platform to build for"
+ description: "Platform to build for (e.g., linux/amd64, windows/amd64, darwin/universal)"
required: false
default: "darwin/universal"
- build-tags:
- description: "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"
- required: false
- default: "false"
- build-obfuscate:
- description: "Obfuscate the build"
- required: false
- default: "false"
- wails-version:
- description: "Wails version to use"
- required: false
- default: "latest"
- wails-build-webview2:
- description: "Webview2 installer method [download,embed,browser,error]"
- required: false
- default: "download"
- go-version:
- description: "Version of Go to use"
- required: false
- default: "1.23"
- node-version:
- description: "Node js version"
- required: false
- default: "18.x"
- deno-build:
- description: "This gets run into bash, use the full command"
- required: false
- default: ""
app-working-directory:
- description: "This gets run into bash, use the full command"
- required: false
- default: "."
- deno-working-directory:
- description: "This gets run into bash, use the full command"
+ description: "Root of the app being built"
required: false
default: "."
- deno-version:
- description: "Deno version to use"
- required: false
- default: "v1.20.x"
- sign-macos-app-id:
- description: "MacOS Application Certificate id"
- required: false
- default: ''
- sign-macos-apple-password:
- description: "MacOS Apple password"
- required: false
- default: ''
- sign-macos-app-cert:
- description: "MacOS Application Certificate"
- required: false
- default: ''
- sign-macos-app-cert-password:
- description: "MacOS Application Certificate Password"
- required: false
- default: ''
- sign-macos-installer-id:
- description: "MacOS Installer Certificate id"
- required: false
- default: ''
- sign-macos-installer-cert:
- description: "MacOS Installer Certificate"
- required: false
- default: ''
- sign-macos-installer-cert-password:
- description: "MacOS Installer Certificate Password"
- required: false
- default: ''
- sign-windows-cert:
- description: "Windows Signing Certificate"
+ # Orchestrator controls (stack-agnostic)
+ AUTO_STACK:
+ description: "Allow root action to auto-select a stack based on discovery"
required: false
- default: ''
- sign-windows-cert-password:
- description: "Windows Signing Certificate Password"
+ default: "true"
+ AUTO_SETUP:
+ description: "Allow orchestrators to auto-enable setup tools based on env toggles"
required: false
- default: ''
- wails-dev-build:
- description: "Use provided wails"
+ default: "true"
+ STACK:
+ description: "Explicit stack override (e.g., wails2)"
required: false
- default: "false"
+ default: ""
runs:
using: "composite"
steps:
- - name: Linux Discovery
- if: runner.os == 'Linux'
- id: linux_discovery
- run: |
- sudo apt-get -yq update
- DISTRO=$(lsb_release -rs) # Get the distribution version (e.g., "22.04", "24.04")
- if [[ "$DISTRO" == "20.04" ]]; then
- sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.0-dev gcc-aarch64-linux-gnu
- elif [[ "$DISTRO" == "22.04" ]]; then
- sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.0-dev gcc-aarch64-linux-gnu
- elif [[ "$DISTRO" == "24.04" ]]; then
- sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.1-dev gcc-aarch64-linux-gnu
- else
- echo "Unsupported Linux distribution: $DISTRO"
- exit 1 # Fail the workflow if the distribution is not supported
- fi
- echo "DISTRO=$DISTRO" >> "$GITHUB_OUTPUT"
- shell: bash
- - name: Setup Build Options
- id: build_options
- shell: bash
- env:
- DISTRO: ${{ steps.linux_discovery.outputs.DISTRO }}
- run: |
- build_options=""
- if ${{ inputs.build-obfuscate == 'true' }}; then
- build_options+=' -obfuscated'
- fi
- if [[ "${{ inputs.build-tags }}" != "false" ]]; then
- tags_string="${{ inputs.build-tags }}"
- if [[ "$DISTRO" == '24.04' ]]; then
- tags_string+=" webkit2_41"
- fi
- build_options+=" -tags $tags_string"
- elif [[ "${{ inputs.build-tags }}" == "false" && "$DISTRO" == '24.04' ]]; then
- build_options+=" -tags webkit2_41"
- fi
- if ${{ inputs.nsis == 'true' }}; then
- build_options+=' -nsis'
- fi
- echo "BUILD_OPTIONS=$build_options" >> "$GITHUB_OUTPUT"
- # Setup and configure GoLang
- - name: Setup GoLang
- uses: actions/setup-go@v5
- if: inputs.wails-dev-build == 'false'
+ - name: Discovery
+ id: discovery
+ uses: ./actions/discovery
with:
- check-latest: true
- cache: ${{ inputs.build-cache }}
- cache-dependency-path: 'go.sum'
- go-version: ${{ inputs.go-version }}
- - name: Install Garble
- if: inputs.build-obfuscate == 'true'
- run: go install mvdan.cc/garble@latest
- shell: bash
- - run: go version
- shell: bash
- # (Optional) Setup and configure Deno
- - name: Setup Deno
- uses: denoland/setup-deno@v2
- if: inputs.deno-build != ''
- with:
- deno-version: ${{inputs.deno-version}}
- - name: Run Deno Command
- if: inputs.deno-build != ''
- shell: bash
- working-directory: ${{inputs.deno-working-directory}}
- run: ${{inputs.deno-build}}
- # install wails
- - name: Install Wails
- if: inputs.build == 'true' && inputs.wails-dev-build == 'false'
- run: go install github.com/wailsapp/wails/v2/cmd/wails@${{inputs.wails-version}}
- shell: bash
- - name: Install macOS Wails deps
- if: runner.os == 'macOS'
- run: brew install mitchellh/gon/gon
- shell: bash
- # Building step
- - name: Build App
- if: inputs.build == 'true'
- env:
- BUILD_OPTIONS: ${{ steps.build_options.outputs.BUILD_OPTIONS }}
- working-directory: ${{ inputs.app-working-directory }}
- run: wails build --platform ${{inputs.build-platform}} -webview2 ${{inputs.wails-build-webview2}} -o ${{inputs.build-name}} $BUILD_OPTIONS
- shell: bash
- - name: Add macOS perms
- if: inputs.build == 'true' && runner.os == 'macOS'
- working-directory: ${{ inputs.app-working-directory }}
- run: chmod +x build/bin/*/Contents/MacOS/*
- shell: bash
- - name: Add Linux perms
- if: inputs.build == 'true' && runner.os == 'Linux'
- working-directory: ${{ inputs.app-working-directory }}
- run: chmod +x build/bin/*
- shell: bash
- # Package and Sign MacOS
- - name: Import Code-Signing Certificates for macOS
- if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
- uses: Apple-Actions/import-codesign-certs@v1
- with:
- keychain-password: ${{ inputs.sign-macos-apple-password }}
- p12-file-base64: ${{ inputs.sign-macos-app-cert }}
- p12-password: ${{ inputs.sign-macos-app-cert-password }}
- - name: Import Code-Signing Certificates for macOS Installer
- if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
- uses: Apple-Actions/import-codesign-certs@v1
+ working-directory: ${{ inputs.app-working-directory }}
+
+ - name: Delegate to directory orchestrator
+ id: dir_orch
+ uses: ./actions
with:
- keychain-password: ${{ inputs.sign-macos-apple-password }}
- p12-file-base64: ${{ inputs.sign-macos-installer-cert }}
- p12-password: ${{ inputs.sign-macos-installer-cert-password }}
- create-keychain: false
- - name: Sign our macOS binary
- if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
- shell: bash
- working-directory: ${{ inputs.app-working-directory }}
- env:
- APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }}
- run: |
- echo "Signing Package"
- gon -log-level=info ./build/darwin/gon-sign.json
- - name: Build .app zip file
- if: runner.os == 'macOS'
- working-directory: ${{ inputs.app-working-directory }}
- shell: bash
- run: |
- ditto -c -k --keepParent ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app.zip
- - name: Building Installer
- if: runner.os == 'macOS' && inputs.sign != 'false' && inputs.sign-macos-installer-id != '' && startsWith(github.ref, 'refs/tags/')
- shell: bash
- working-directory: ${{ inputs.app-working-directory }}
- run: |
- productbuild --sign '${{inputs.sign-macos-installer-id}}' --component ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.pkg
- - name: Building Installer
- if: runner.os == 'macOS' && inputs.sign-macos-installer-id == '' && startsWith(github.ref, 'refs/tags/')
- shell: bash
- working-directory: ${{ inputs.app-working-directory }}
- run: |
- productbuild --component ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{inputs.build-name}}.pkg
- - name: Notarising Installer and zip
- if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
+ build: ${{ inputs.build }}
+ sign: ${{ inputs.sign }}
+ package: ${{ inputs.package }}
+ build-name: ${{ inputs.build-name }}
+ build-platform: ${{ inputs.build-platform }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+ AUTO_STACK: ${{ inputs.AUTO_STACK }}
+ AUTO_SETUP: ${{ inputs.AUTO_SETUP }}
+ STACK: ${{ inputs.STACK }}
+ # Pass suggestion from discovery so orchestrator can skip re-scanning if it wants
+ PRIMARY_STACK_SUGGESTION: ${{ steps.discovery.outputs.PRIMARY_STACK_SUGGESTION }}
+
+ - name: Debug selected stack
shell: bash
- working-directory: ${{ inputs.app-working-directory }}
- env:
- APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }}
- run: |
- gon -log-level=info ${{ inputs.app-working-directory }}/build/darwin/gon-notarize.json
- # Windows signing
- - name: Sign Windows binaries
- shell: powershell
- if: runner.os == 'Windows' && inputs.sign != 'false' && inputs.sign-windows-cert != ''
- working-directory: ${{ inputs.app-working-directory }}
- run: |
- echo "Creating certificate file"
- New-Item -ItemType directory -Path certificate
- Set-Content -Path certificate\certificate.txt -Value '${{ inputs.sign-windows-cert }}'
- certutil -decode certificate\certificate.txt certificate\certificate.pfx
- echo "Signing our binaries"
- & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ inputs.sign-windows-cert-password }}' .\build\bin\${{inputs.build-name}}.exe
- echo "Signing Installer" & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ inputs.sign-windows-cert-password }}' .\build\bin\${{inputs.build-name}}-amd64-installer.exe
+ run: echo "[DEBUG_LOG] (root) Orchestrator selected stack=${{ steps.dir_orch.outputs.SELECTED_STACK }}"
- # Upload build assets
- - uses: actions/upload-artifact@v4
- if: inputs.package == 'true'
- with:
- name: Wails Build ${{runner.os}} ${{inputs.build-name}}
- path: |
- */bin/
- *\bin\*
- - name: Release
- uses: softprops/action-gh-release@v1
- if: inputs.package == 'true' && startsWith(github.ref, 'refs/tags/')
+ - name: Package & release
+ uses: ./actions/package
with:
- files: |
- */bin/*
+ package: ${{ inputs.package }}
+ build-name: ${{ inputs.build-name }}
+ os: ${{ steps.discovery.outputs.OS }}
+ arch: ${{ steps.discovery.outputs.ARCH }}
+ tag: ${{ steps.discovery.outputs.TAG }}
+ is-tag: ${{ steps.discovery.outputs.IS_TAG }}
+ short-sha: ${{ steps.discovery.outputs.SHORT_SHA }}
+ ref: ${{ steps.discovery.outputs.REF }}
+outputs:
+ SELECTED_STACK:
+ description: "Stack selected by the orchestrator (via directory orchestrator)"
+ value: ${{ steps.dir_orch.outputs.SELECTED_STACK }}
diff --git a/actions/README.md b/actions/README.md
new file mode 100644
index 0000000..82d41c1
--- /dev/null
+++ b/actions/README.md
@@ -0,0 +1,23 @@
+# actions/ index
+
+This directory contains modular composite actions that you can call directly from your workflows, or via the root orchestrator.
+
+Sub-actions overview
+- discovery — Detect OS/ARCH, Ubuntu distro (on Linux), repo/ref metadata, and project markers. Suggests a primary stack.
+- options — Compute `BUILD_OPTIONS` string for Wails v2 builds (adds `-tags webkit2_41` on Ubuntu 24.04 when appropriate).
+- setup — Orchestrator for toolchains (Go → npm → optional Deno → optional Conan).
+ - setup/go — Installs Go, optional Garble (when obfuscating), Wails CLI, and `gon` on macOS.
+ - setup/npm — Installs Node.js and optionally runs `npm ci`/`npm install` in your app directory (auto-detects `frontend/`).
+ - setup/deno — ENV-first Deno setup and command runner (`DENO_*` envs).
+ - setup/conan — Installs Conan via pip (placeholder for future C++ builds).
+- sign — Unified signing for macOS and Windows; notarizes on tag builds (macOS).
+- package — Upload artifacts and (on tags) publish a GitHub Release; includes smarter artifact naming and optional `artifact_meta.json`.
+
+Stacks under actions/build/
+- build/wails2 — Full Wails v2 pipeline wrapper (discovery → options → setup → build → sign → package).
+ - build/wails2/build — Runs `wails build` for the chosen platform and fixes executable permissions.
+
+Notes
+- In composite actions (inside this repo), reference other sub-actions via relative paths like `uses: ./actions/discovery`.
+- In workflows within this repo, reference local actions with `uses: ./actions/` (or the repo root with `uses: ./`).
+- For consumers of this repo, use the fully qualified path, for example: `uses: snider/build/actions/discovery@v3`.
diff --git a/actions/action.yml b/actions/action.yml
new file mode 100644
index 0000000..261d0fb
--- /dev/null
+++ b/actions/action.yml
@@ -0,0 +1,86 @@
+name: "Directory Orchestrator"
+description: "Selects and delegates to a stack wrapper based on discovery outputs and ENV"
+inputs:
+ build:
+ required: false
+ default: "true"
+ description: ""
+ sign:
+ required: false
+ default: "false"
+ description: ""
+ package:
+ required: false
+ default: "true"
+ description: ""
+ build-name:
+ required: true
+ description: ""
+ build-platform:
+ required: false
+ default: "darwin/universal"
+ description: ""
+ app-working-directory:
+ required: false
+ default: "."
+ description: ""
+ # Orchestrator controls
+ AUTO_STACK:
+ required: false
+ default: "true"
+ description: ""
+ AUTO_SETUP:
+ required: false
+ default: "true"
+ description: ""
+ STACK:
+ required: false
+ default: ""
+ description: ""
+ # Optional hint from a prior discovery step
+ PRIMARY_STACK_SUGGESTION:
+ required: false
+ default: ""
+ description: ""
+runs:
+ using: "composite"
+ steps:
+ - name: Resolve stack selection
+ id: sel
+ shell: bash
+ env:
+ AUTO_STACK: ${{ inputs.AUTO_STACK }}
+ FORCED_STACK: ${{ inputs.STACK }}
+ HINT: ${{ inputs.PRIMARY_STACK_SUGGESTION }}
+ run: |
+ set -euo pipefail
+ sel_stack="wails2"
+ if [ -n "${FORCED_STACK:-}" ]; then
+ sel_stack="$FORCED_STACK"
+ echo "[DEBUG_LOG] (orchestrator) Forced stack via input STACK=$sel_stack"
+ elif [ "${AUTO_STACK:-true}" = "true" ] || [ "${AUTO_STACK:-true}" = "1" ]; then
+ if [ -n "${HINT:-}" ] && [ "$HINT" != "unknown" ]; then
+ sel_stack="$HINT"
+ echo "[DEBUG_LOG] (orchestrator) Auto stack=${sel_stack} (from discovery hint)"
+ else
+ echo "[DEBUG_LOG] (orchestrator) No hint; defaulting to wails2"
+ fi
+ else
+ echo "[DEBUG_LOG] (orchestrator) AUTO_STACK disabled; defaulting to wails2"
+ fi
+ echo "SELECTED_STACK=$sel_stack" >> "$GITHUB_OUTPUT"
+
+ - name: Call Wails v2 wrapper
+ if: steps.sel.outputs.SELECTED_STACK == 'wails2'
+ uses: ./actions/build/wails2
+ with:
+ build: ${{ inputs.build }}
+ sign: ${{ inputs.sign }}
+ package: ${{ inputs.package }}
+ build-name: ${{ inputs.build-name }}
+ build-platform: ${{ inputs.build-platform }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+outputs:
+ SELECTED_STACK:
+ description: "Stack selected by the orchestrator"
+ value: ${{ steps.sel.outputs.SELECTED_STACK }}
diff --git a/actions/build/README.md b/actions/build/README.md
new file mode 100644
index 0000000..6dd4b8c
--- /dev/null
+++ b/actions/build/README.md
@@ -0,0 +1,29 @@
+# actions/build/ index
+
+Stack wrappers live here. Each wrapper owns its stack-specific inputs and wiring and can be called directly or via the root orchestrator.
+
+Available stacks
+- wails2 — Full Wails v2 pipeline wrapper at `actions/build/wails2`.
+
+Coming soon
+- wails3 — Alpha once upstream stabilizes.
+- cpp — C++ toolchain via `setup/conan` plus dedicated build/sign/package steps.
+
+Usage examples
+- Wrapper (local in this repo):
+ ```yaml
+ - uses: ./actions/build/wails2
+ with:
+ build-name: myApp
+ build-platform: linux/amd64
+ ```
+- From another repo:
+ ```yaml
+ - uses: snider/build/actions/build/wails2@v3
+ with:
+ build-name: myApp
+ build-platform: linux/amd64
+ ```
+
+Notes
+- The root orchestrator decides which stack to call using `actions/discovery` and env flags. Set `STACK` to force a stack or disable auto-selection with `AUTO_STACK: 'false'`.
diff --git a/actions/build/wails2/README.md b/actions/build/wails2/README.md
new file mode 100644
index 0000000..973f923
--- /dev/null
+++ b/actions/build/wails2/README.md
@@ -0,0 +1,134 @@
+# Wails v2 (full pipeline)
+
+Important
+- 20/02/2025 — Wails v2.10.0 is reported problematic. Prefer `wails-version: "v2.9.0"` until upstream fixes land.
+
+Purpose
+- Run the entire Wails v2 build pipeline in one step: discovery → options → setup (Go/npm/Deno/Wails) → build → optional signing → packaging.
+
+Usage (recommended)
+```yaml
+- name: Build Wails v2
+ uses: snider/build/actions/build/wails2@v3
+ with:
+ build-name: wailsApp
+ build-platform: linux/amd64 # or windows/amd64, darwin/universal
+ # Optional features
+ build-obfuscate: 'false'
+ nsis: 'false'
+ sign: 'false'
+ package: 'true'
+ wails-version: 'latest'
+```
+
+Matrix example
+```yaml
+name: Wails v2 build (matrix)
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ build: [
+ {name: wailsTest, platform: linux/amd64, os: ubuntu-latest},
+ {name: wailsTest, platform: windows/amd64, os: windows-latest},
+ {name: wailsTest, platform: darwin/universal, os: macos-latest}
+ ]
+ runs-on: ${{ matrix.build.os }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - uses: snider/build@v3
+ with:
+ build-name: ${{ matrix.build.name }}
+ build-platform: ${{ matrix.build.platform }}
+ build-obfuscate: 'true'
+```
+
+macOS Code Signing (Wails v2)
+
+You need two `gon` configuration files to sign and notarize the `.app` before building the installer pkg.
+
+Workflow snippet
+```yaml
+- uses: snider/build@v3
+ with:
+ build-name: wailsApp
+ sign: true
+ build-platform: darwin/universal
+ sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
+ sign-macos-app-id: ${{ secrets.MACOS_DEVELOPER_CERT_ID }}
+ sign-macos-app-cert: ${{ secrets.MACOS_DEVELOPER_CERT }}
+ sign-macos-app-cert-password: ${{ secrets.MACOS_DEVELOPER_CERT_PASSWORD }}
+ sign-macos-installer-id: ${{ secrets.MACOS_INSTALLER_CERT_ID }}
+ sign-macos-installer-cert: ${{ secrets.MACOS_INSTALLER_CERT }}
+ sign-macos-installer-cert-password: ${{ secrets.MACOS_INSTALLER_CERT_PASSWORD }}
+```
+
+`build/darwin/gon-sign.json`
+```json
+{
+ "source" : ["./build/bin/wailsApp.app"],
+ "bundle_id" : "com.wails.app",
+ "apple_id": {
+ "username": "username",
+ "password": "@env:APPLE_PASSWORD"
+ },
+ "sign" :{
+ "application_identity" : "Developer ID Application: XXXXXXXX (XXXXXX)",
+ "entitlements_file": "./build/darwin/entitlements.plist"
+ },
+ "dmg" :{
+ "output_path": "./build/bin/wailsApp.dmg",
+ "volume_name": "Lethean"
+ }
+}
+```
+
+`build/darwin/gon-notarize.json`
+```json
+{
+ "notarize": [{
+ "path": "./build/bin/wailsApp.pkg",
+ "bundle_id": "com.wails.app",
+ "staple": true
+ },{
+ "path": "./build/bin/wailsApp.app.zip",
+ "bundle_id": "com.wails.app",
+ "staple": false
+ }],
+ "apple_id": {
+ "username": "USER name",
+ "password": "@env:APPLE_PASSWORD"
+ }
+}
+```
+
+`build/darwin/entitlements.plist`
+```xml
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+ com.apple.security.network.server
+
+ com.apple.security.files.user-selected.read-write
+
+
+
+```
+
+Notes
+- Deno is optional and can be configured via environment variables (ENV-first): `DENO_ENABLE`, `DENO_BUILD`, `DENO_VERSION`, `DENO_WORKDIR`.
+- NPM extras (Wails-only): set `NPM_ENABLE` to `true`/`1`/`yes`/`on` and provide `NPM_PACKAGES` (space-separated) to install extra global packages needed by CI before the build. If `NPM_PACKAGES` is empty, the step is skipped.
+- On Linux, the action detects Ubuntu 20.04/22.04/24.04 and installs matching WebKitGTK packages; Ubuntu 24.04 implies `-tags webkit2_41` when appropriate.
+- macOS signing and notarization only occur on tag builds when certs/passwords are provided.
+- This sub-action is a convenience wrapper that delegates to the underlying sub-actions in this repository.
diff --git a/actions/build/wails2/action.yml b/actions/build/wails2/action.yml
new file mode 100644
index 0000000..1bae14f
--- /dev/null
+++ b/actions/build/wails2/action.yml
@@ -0,0 +1,292 @@
+name: "Wails v2 (full pipeline)"
+description: "Runs the full Wails v2 build pipeline: discovery, options, setup (Go/Deno/Wails), build, sign, and package"
+inputs:
+ build:
+ description: "Whether to run the build"
+ required: false
+ default: "true"
+ sign:
+ description: "Sign the build (macOS/Windows)"
+ required: false
+ default: "false"
+ package:
+ description: "Upload artifacts and release on tag"
+ required: false
+ default: "true"
+ nsis:
+ description: "Build a Windows Installer"
+ required: false
+ default: "false"
+ build-name:
+ description: "The name of the binary/app bundle"
+ required: true
+ build-cache:
+ description: "Enable Go cache"
+ required: false
+ default: "true"
+ build-platform:
+ description: "Platform to build for (e.g., linux/amd64, windows/amd64, darwin/universal)"
+ required: false
+ default: "darwin/universal"
+ build-tags:
+ description: "Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"
+ required: false
+ default: "false"
+ build-obfuscate:
+ description: "Obfuscate (garble) the build"
+ required: false
+ default: "false"
+ wails-version:
+ description: "Wails version to use"
+ required: false
+ default: "latest"
+ wails-build-webview2:
+ description: "WebView2 installer method [download,embed,browser,error]"
+ required: false
+ default: "download"
+ go-version:
+ description: "Go version"
+ required: false
+ default: "1.23"
+ node-version:
+ description: "Node js version (unused by pipeline but kept for compatibility)"
+ required: false
+ default: "18.x"
+ deno-build:
+ description: "Deno command to run (optional, ENV-first)"
+ required: false
+ default: ""
+ app-working-directory:
+ description: "App working directory"
+ required: false
+ default: "."
+ deno-working-directory:
+ description: "Deno working directory"
+ required: false
+ default: "."
+ deno-version:
+ description: "Deno version"
+ required: false
+ default: "v1.20.x"
+ sign-macos-app-id:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-apple-password:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-app-cert:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-app-cert-password:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-installer-id:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-installer-cert:
+ required: false
+ default: ''
+ description: ""
+ sign-macos-installer-cert-password:
+ required: false
+ default: ''
+ description: ""
+ sign-windows-cert:
+ required: false
+ default: ''
+ description: ""
+ sign-windows-cert-password:
+ required: false
+ default: ''
+ description: ""
+ wails-dev-build:
+ description: "Use provided wails binary instead of installing"
+ required: false
+ default: "false"
+runs:
+ using: "composite"
+ steps:
+ - name: Resolve Wails configuration (inputs > env > defaults)
+ id: wcfg
+ shell: bash
+ env:
+ IN_BUILD_TAGS: ${{ inputs.build-tags }}
+ IN_OBFUSCATE: ${{ inputs.build-obfuscate }}
+ IN_NSIS: ${{ inputs.nsis }}
+ IN_WV2: ${{ inputs.wails-build-webview2 }}
+ IN_WAILS_VER: ${{ inputs.wails-version }}
+ IN_GO_VER: ${{ inputs.go-version }}
+ IN_NODE_VER: ${{ inputs.node-version }}
+ IN_DENO_VER: ${{ inputs.deno-version }}
+ # Env alternatives
+ EV_BUILD_TAGS: ${{ env.WAILS_BUILD_TAGS }}
+ EV_OBFUSCATE: ${{ env.WAILS_OBFUSCATE }}
+ EV_NSIS: ${{ env.WAILS_NSIS }}
+ EV_WV2: ${{ env.WAILS_WEBVIEW2 }}
+ EV_WAILS_VER: ${{ env.WAILS_VERSION }}
+ EV_GO_VER: ${{ env.WAILS_GO_VERSION }}
+ EV_NODE_VER: ${{ env.WAILS_NODE_VERSION }}
+ EV_DENO_VER: ${{ env.WAILS_DENO_VERSION }}
+ # Signing envs
+ EV_MAC_PW: ${{ env.SIGN_MACOS_APPLE_PASSWORD }}
+ EV_MAC_ID: ${{ env.SIGN_MACOS_APP_ID }}
+ EV_MAC_CERT: ${{ env.SIGN_MACOS_APP_CERT }}
+ EV_MAC_CERT_PW: ${{ env.SIGN_MACOS_APP_CERT_PASSWORD }}
+ EV_MAC_INS_ID: ${{ env.SIGN_MACOS_INSTALLER_ID }}
+ EV_MAC_INS_CERT: ${{ env.SIGN_MACOS_INSTALLER_CERT }}
+ EV_MAC_INS_CERT_PW: ${{ env.SIGN_MACOS_INSTALLER_CERT_PASSWORD }}
+ EV_WIN_CERT: ${{ env.SIGN_WINDOWS_CERT }}
+ EV_WIN_CERT_PW: ${{ env.SIGN_WINDOWS_CERT_PASSWORD }}
+ run: |
+ set -euo pipefail
+ # helper to pick first non-empty
+ pick() { if [ -n "$1" ] && [ "$1" != "false" ]; then echo "$1"; else echo "$2"; fi }
+ # booleans normalize
+ tobool() { case "${1,,}" in true|1|yes|on) echo "true";; *) echo "false";; esac }
+ BUILD_TAGS=$(pick "${IN_BUILD_TAGS:-}" "${EV_BUILD_TAGS:-}")
+ OBFUSCATE=$(tobool "$(pick "${IN_OBFUSCATE:-}" "${EV_OBFUSCATE:-}")")
+ NSIS=$(tobool "$(pick "${IN_NSIS:-}" "${EV_NSIS:-}")")
+ WV2=$(pick "${IN_WV2:-}" "${EV_WV2:-}")
+ WAILS_VER=$(pick "${IN_WAILS_VER:-}" "${EV_WAILS_VER:-}")
+ GO_VER=$(pick "${IN_GO_VER:-}" "${EV_GO_VER:-}")
+ NODE_VER=$(pick "${IN_NODE_VER:-}" "${EV_NODE_VER:-}")
+ DENO_VER=$(pick "${IN_DENO_VER:-}" "${EV_DENO_VER:-}")
+ # Signing
+ MAC_PW="${EV_MAC_PW:-}"; MAC_ID="${EV_MAC_ID:-}"; MAC_CERT="${EV_MAC_CERT:-}"; MAC_CERT_PW="${EV_MAC_CERT_PW:-}"
+ MAC_INS_ID="${EV_MAC_INS_ID:-}"; MAC_INS_CERT="${EV_MAC_INS_CERT:-}"; MAC_INS_CERT_PW="${EV_MAC_INS_CERT_PW:-}"
+ WIN_CERT="${EV_WIN_CERT:-}"; WIN_CERT_PW="${EV_WIN_CERT_PW:-}"
+ echo "BUILD_TAGS=$BUILD_TAGS" >> "$GITHUB_OUTPUT"
+ echo "OBFUSCATE=$OBFUSCATE" >> "$GITHUB_OUTPUT"
+ echo "NSIS=$NSIS" >> "$GITHUB_OUTPUT"
+ echo "WV2=$WV2" >> "$GITHUB_OUTPUT"
+ echo "WAILS_VER=$WAILS_VER" >> "$GITHUB_OUTPUT"
+ echo "GO_VER=$GO_VER" >> "$GITHUB_OUTPUT"
+ echo "NODE_VER=$NODE_VER" >> "$GITHUB_OUTPUT"
+ echo "DENO_VER=$DENO_VER" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_APPLE_PASSWORD=$MAC_PW" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_APP_ID=$MAC_ID" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_APP_CERT=$MAC_CERT" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_APP_CERT_PASSWORD=$MAC_CERT_PW" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_INSTALLER_ID=$MAC_INS_ID" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_INSTALLER_CERT=$MAC_INS_CERT" >> "$GITHUB_OUTPUT"
+ echo "SIGN_MACOS_INSTALLER_CERT_PASSWORD=$MAC_INS_CERT_PW" >> "$GITHUB_OUTPUT"
+ echo "SIGN_WINDOWS_CERT=$WIN_CERT" >> "$GITHUB_OUTPUT"
+ echo "SIGN_WINDOWS_CERT_PASSWORD=$WIN_CERT_PW" >> "$GITHUB_OUTPUT"
+ echo "[DEBUG_LOG] Wails2 resolve: tags='${BUILD_TAGS:-}' obf=${OBFUSCATE} nsis=${NSIS} wv2='${WV2:-default}' wails='${WAILS_VER:-default}' go='${GO_VER:-default}' node='${NODE_VER:-default}' deno='${DENO_VER:-default}'"
+
+ - name: Discovery
+ id: discovery
+ uses: ./actions/discovery
+ with:
+ working-directory: ${{ inputs.app-working-directory }}
+
+ - name: Compute Build Options
+ id: build_options
+ uses: ./actions/options
+ with:
+ build-obfuscate: ${{ steps.wcfg.outputs.OBFUSCATE }}
+ build-tags: ${{ steps.wcfg.outputs.BUILD_TAGS }}
+ nsis: ${{ steps.wcfg.outputs.NSIS }}
+ distro: ${{ steps.discovery.outputs.DISTRO }}
+
+ - name: Setup toolchains (Go/Node/Deno/Wails)
+ uses: ./actions/setup
+ with:
+ go-version: ${{ steps.wcfg.outputs.GO_VER }}
+ build-cache: ${{ inputs.build-cache }}
+ build-obfuscate: ${{ steps.wcfg.outputs.OBFUSCATE }}
+ wails-version: ${{ steps.wcfg.outputs.WAILS_VER }}
+ wails-dev-build: ${{ inputs.wails-dev-build }}
+ node-version: ${{ steps.wcfg.outputs.NODE_VER }}
+ npm-working-directory: ${{ inputs.app-working-directory }}
+ npm-install: 'true'
+ deno-build: ${{ inputs.deno-build }}
+ deno-version: ${{ steps.wcfg.outputs.DENO_VER }}
+ deno-working-directory: ${{ inputs.deno-working-directory }}
+
+ - name: Wails2 npm extras (env-first)
+ shell: bash
+ run: |
+ set -euo pipefail
+ npm_enable="${NPM_ENABLE:-}"
+ npm_packages="${NPM_PACKAGES:-}"
+ case "${npm_enable,,}" in
+ true|1|yes|on) enable=1;;
+ *) enable=0;;
+ esac
+ echo "[DEBUG_LOG] Wails2 npm extras: NPM_ENABLE=${npm_enable:-} NPM_PACKAGES=${npm_packages:-}"
+ if [ -z "$npm_packages" ] && [ "$enable" -ne 1 ]; then
+ echo "[DEBUG_LOG] NPM extras not enabled (set NPM_ENABLE or NPM_PACKAGES). Skipping."
+ exit 0
+ fi
+ if [ -z "$npm_packages" ]; then
+ echo "[DEBUG_LOG] NPM_ENABLE set but NPM_PACKAGES is empty; nothing to install. Skipping."
+ exit 0
+ fi
+ echo "[DEBUG_LOG] Installing extra npm packages globally: $npm_packages"
+ npm install -g $npm_packages
+
+ - name: Build Wails app
+ uses: ./actions/build/wails2/build
+ with:
+ build: ${{ inputs.build }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+ build-platform: ${{ inputs.build-platform }}
+ build-name: ${{ inputs.build-name }}
+ wails-build-webview2: ${{ steps.wcfg.outputs.WV2 }}
+ build-options: ${{ steps.build_options.outputs.BUILD_OPTIONS }}
+
+ - name: Sign artifacts (OS-conditional)
+ uses: ./actions/sign
+ with:
+ sign: ${{ inputs.sign }}
+ app-working-directory: ${{ inputs.app-working-directory }}
+ build-name: ${{ inputs.build-name }}
+ sign-macos-apple-password: ${{ steps.wcfg.outputs.SIGN_MACOS_APPLE_PASSWORD }}
+ sign-macos-app-id: ${{ steps.wcfg.outputs.SIGN_MACOS_APP_ID }}
+ sign-macos-app-cert: ${{ steps.wcfg.outputs.SIGN_MACOS_APP_CERT }}
+ sign-macos-app-cert-password: ${{ steps.wcfg.outputs.SIGN_MACOS_APP_CERT_PASSWORD }}
+ sign-macos-installer-id: ${{ steps.wcfg.outputs.SIGN_MACOS_INSTALLER_ID }}
+ sign-macos-installer-cert: ${{ steps.wcfg.outputs.SIGN_MACOS_INSTALLER_CERT }}
+ sign-macos-installer-cert-password: ${{ steps.wcfg.outputs.SIGN_MACOS_INSTALLER_CERT_PASSWORD }}
+ sign-windows-cert: ${{ steps.wcfg.outputs.SIGN_WINDOWS_CERT }}
+ sign-windows-cert-password: ${{ steps.wcfg.outputs.SIGN_WINDOWS_CERT_PASSWORD }}
+
+ - name: Package & release
+ uses: ./actions/package
+ with:
+ package: ${{ inputs.package }}
+ build-name: ${{ inputs.build-name }}
+ os: ${{ steps.discovery.outputs.OS }}
+ arch: ${{ steps.discovery.outputs.ARCH }}
+ tag: ${{ steps.discovery.outputs.TAG }}
+ is-tag: ${{ steps.discovery.outputs.IS_TAG }}
+ short-sha: ${{ steps.discovery.outputs.SHORT_SHA }}
+ ref: ${{ steps.discovery.outputs.REF }}
+outputs:
+ BUILD_TAGS:
+ description: "Resolved build tags (after env/input precedence)"
+ value: ${{ steps.wcfg.outputs.BUILD_TAGS }}
+ OBFUSCATE:
+ description: "Resolved obfuscate flag"
+ value: ${{ steps.wcfg.outputs.OBFUSCATE }}
+ NSIS:
+ description: "Resolved nsis flag"
+ value: ${{ steps.wcfg.outputs.NSIS }}
+ WAILS_VERSION:
+ description: "Resolved Wails version"
+ value: ${{ steps.wcfg.outputs.WAILS_VER }}
+ GO_VERSION:
+ description: "Resolved Go version"
+ value: ${{ steps.wcfg.outputs.GO_VER }}
+ NODE_VERSION:
+ description: "Resolved Node version"
+ value: ${{ steps.wcfg.outputs.NODE_VER }}
+ DENO_VERSION:
+ description: "Resolved Deno version"
+ value: ${{ steps.wcfg.outputs.DENO_VER }}
diff --git a/actions/build/wails2/build/README.md b/actions/build/wails2/build/README.md
new file mode 100644
index 0000000..dfe3f38
--- /dev/null
+++ b/actions/build/wails2/build/README.md
@@ -0,0 +1,29 @@
+# Build Wails v2 App (sub-action)
+
+Purpose
+- Runs `wails build` for the specified platform/name and fixes executable permissions by OS.
+
+Inputs
+- `build` (default `true`) — set to `false` to skip.
+- `app-working-directory` (default `.`) — where your Wails project resides.
+- `build-platform` (required) — e.g., `linux/amd64`, `windows/amd64`, `darwin/universal`.
+- `build-name` (required) — output name (binary or .app bundle name).
+- `wails-build-webview2` (default `download`) — WebView2 mode on Windows.
+- `build-options` (default `''`) — precomputed flags (from `actions/options`), e.g., `-obfuscated -tags webkit2_41`.
+
+Usage
+```yaml
+- name: Build Wails app
+ uses: snider/build/actions/build/wails2/build@v3
+ with:
+ build: 'true'
+ app-working-directory: 'build/wails2' # or your project dir
+ build-platform: 'linux/amd64'
+ build-name: 'wailsApp'
+ wails-build-webview2: 'download'
+ build-options: ${{ steps.opts.outputs.BUILD_OPTIONS }}
+```
+
+Notes
+- Ensure Wails CLI is installed beforehand (use the `actions/setup` orchestrator which calls `actions/setup/go`).
+- On macOS and Linux, this action will `chmod +x` the built files to ensure they are executable.
diff --git a/actions/build/wails2/build/action.yml b/actions/build/wails2/build/action.yml
new file mode 100644
index 0000000..1dae36f
--- /dev/null
+++ b/actions/build/wails2/build/action.yml
@@ -0,0 +1,41 @@
+name: "Build Wails App"
+description: "Runs wails build and fixes executable permissions per OS"
+inputs:
+ build:
+ description: "Whether to run the build"
+ required: false
+ default: "true"
+ app-working-directory:
+ required: false
+ default: "."
+ build-platform:
+ required: true
+ build-name:
+ required: true
+ wails-build-webview2:
+ required: false
+ default: "download"
+ build-options:
+ description: "Precomputed BUILD_OPTIONS string"
+ required: false
+ default: ""
+runs:
+ using: "composite"
+ steps:
+ - name: Build App
+ if: inputs.build == 'true'
+ env:
+ BUILD_OPTIONS: ${{ inputs.build-options }}
+ working-directory: ${{ inputs.app-working-directory }}
+ run: wails build --platform ${{ inputs.build-platform }} -webview2 ${{ inputs.wails-build-webview2 }} -o ${{ inputs.build-name }} $BUILD_OPTIONS
+ shell: bash
+ - name: Add macOS perms
+ if: inputs.build == 'true' && runner.os == 'macOS'
+ working-directory: ${{ inputs.app-working-directory }}
+ run: chmod +x build/bin/*/Contents/MacOS/*
+ shell: bash
+ - name: Add Linux perms
+ if: inputs.build == 'true' && runner.os == 'Linux'
+ working-directory: ${{ inputs.app-working-directory }}
+ run: chmod +x build/bin/*
+ shell: bash
diff --git a/actions/discovery/README.md b/actions/discovery/README.md
new file mode 100644
index 0000000..8cc06e8
--- /dev/null
+++ b/actions/discovery/README.md
@@ -0,0 +1,57 @@
+# Discovery (sub-action)
+
+Purpose
+- Detects OS and CPU architecture across all runners.
+- On Linux, detects Ubuntu distro and installs required GTK/WebKit packages for Wails builds.
+- Exposes useful repository/ref metadata (REF, BRANCH, TAG, IS_TAG, SHA, SHORT_SHA, REPO, OWNER) for later steps such as packaging.
+- Scans your app working directory for common project markers to suggest a primary stack automatically.
+
+Inputs
+- `working-directory` (default `.`) — directory to scan for project markers.
+
+Outputs
+- Runner info:
+ - `OS` — `Linux`, `macOS`, or `Windows`.
+ - `ARCH` — CPU architecture normalized where possible (`amd64`, `arm64`, etc.).
+ - `DISTRO` — Ubuntu version string like `20.04`, `22.04`, or `24.04` (Linux only, empty otherwise).
+- Repo/ref metadata:
+ - `REF` — Full Git ref (e.g., `refs/heads/main`, `refs/tags/v1.2.3`).
+ - `BRANCH` — Branch name when applicable; empty on tag refs.
+ - `TAG` — Tag name when on a tag ref; empty otherwise.
+ - `IS_TAG` — `1` if ref is a tag, else `0`.
+ - `SHA` — Full commit SHA.
+ - `SHORT_SHA` — First 7 characters of the commit SHA.
+ - `REPO` — `owner/repo`.
+ - `OWNER` — Repository owner.
+- Project markers (scanned under `working-directory`):
+ - `HAS_ROOT_PACKAGE_JSON` — `1` if `package.json` at root.
+ - `HAS_FRONTEND_PACKAGE_JSON` — `1` if `frontend/package.json` exists.
+ - `HAS_ROOT_GO_MOD` — `1` if `go.mod` at root.
+ - `HAS_ROOT_MAIN_GO` — `1` if `main.go` at root.
+ - `HAS_ROOT_CMAKELISTS` — `1` if `CMakeLists.txt` at root.
+ - `HAS_ROOT_MKDOCS` — `1` if `mkdocs.yml` at root.
+ - `HAS_SUB_NPM` — `1` if any `package.json` found within depth 2 (excluding node_modules).
+ - `HAS_SUB_MKDOCS` — `1` if any `mkdocs.yml` found within depth 2.
+ - `FOUND_FILES` — comma-separated summary of notable files found.
+- Stack suggestion:
+ - `PRIMARY_STACK_SUGGESTION` — `wails2`, `cpp`, or `unknown`.
+
+Usage
+```yaml
+- name: Discovery
+ id: disc
+ uses: snider/build/actions/discovery@v3
+ with:
+ working-directory: build/wails2 # or your app dir
+# Later examples:
+# ${{ steps.disc.outputs.OS }}
+# ${{ steps.disc.outputs.ARCH }}
+# ${{ steps.disc.outputs.DISTRO }}
+# ${{ steps.disc.outputs.TAG }}
+# ${{ steps.disc.outputs.PRIMARY_STACK_SUGGESTION }}
+```
+
+Notes
+- Runs on all OSes; only installs packages on Linux.
+- Linux support currently covers Ubuntu 20.04, 22.04, and 24.04 (24.04 uses `libwebkit2gtk-4.1-dev`).
+- Look for `[DEBUG_LOG]` lines in logs to see which markers were detected and which stack was suggested.
\ No newline at end of file
diff --git a/actions/discovery/action.yml b/actions/discovery/action.yml
new file mode 100644
index 0000000..50a78b8
--- /dev/null
+++ b/actions/discovery/action.yml
@@ -0,0 +1,267 @@
+name: "Discovery"
+description: "Detect OS/ARCH and (on Linux) distro; install required packages; expose repo/ref metadata; detect project markers (root and subtree)"
+inputs:
+ working-directory:
+ description: "Directory to scan for project markers (root of the app)"
+ required: false
+ default: "."
+runs:
+ using: "composite"
+ steps:
+ - name: Linux discovery and deps
+ if: runner.os == 'Linux'
+ id: linux
+ shell: bash
+ run: |
+ set -euo pipefail
+ export DEBIAN_FRONTEND=noninteractive
+ tries=0
+ until sudo apt-get -yq update; do
+ tries=$((tries+1))
+ if [ $tries -ge 3 ]; then echo "apt-get update failed after $tries attempts"; exit 1; fi
+ echo "Retrying apt-get update ($tries)"; sleep 2
+ done
+ DISTRO=$(lsb_release -rs)
+ if [[ "$DISTRO" == "20.04" ]]; then
+ sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.0-dev gcc-aarch64-linux-gnu
+ elif [[ "$DISTRO" == "22.04" ]]; then
+ sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.0-dev gcc-aarch64-linux-gnu
+ elif [[ "$DISTRO" == "24.04" ]]; then
+ sudo apt-get -yq install libgtk-3-0 libwebkit2gtk-4.1-dev gcc-aarch64-linux-gnu
+ else
+ echo "Unsupported Linux distribution: $DISTRO"
+ exit 1
+ fi
+ echo "DISTRO=$DISTRO" >> "$GITHUB_OUTPUT"
+ - name: Common metadata (bash)
+ if: runner.os != 'Windows'
+ id: meta_bash
+ shell: bash
+ run: |
+ set -euo pipefail
+ OS="${{ runner.os }}"
+ M="$(uname -m)"
+ case "$M" in
+ x86_64|amd64) ARCH="amd64";;
+ aarch64|arm64) ARCH="arm64";;
+ armv7l) ARCH="armv7";;
+ *) ARCH="$M";;
+ esac
+ REF="${{ github.ref }}"
+ BRANCH=""
+ if [[ "$REF" == refs/heads/* ]]; then
+ BRANCH="${REF#refs/heads/}"
+ fi
+ SHA="${{ github.sha }}"
+ SHORT_SHA="${SHA::7}"
+ REPO="${{ github.repository }}"
+ OWNER="${{ github.repository_owner }}"
+ TAG=""
+ IS_TAG=0
+ if [[ "$REF" == refs/tags/* ]]; then
+ IS_TAG=1
+ TAG="${REF#refs/tags/}"
+ fi
+ echo "OS=$OS" >> "$GITHUB_OUTPUT"
+ echo "ARCH=$ARCH" >> "$GITHUB_OUTPUT"
+ echo "REF=$REF" >> "$GITHUB_OUTPUT"
+ echo "BRANCH=$BRANCH" >> "$GITHUB_OUTPUT"
+ echo "SHA=$SHA" >> "$GITHUB_OUTPUT"
+ echo "SHORT_SHA=$SHORT_SHA" >> "$GITHUB_OUTPUT"
+ echo "REPO=$REPO" >> "$GITHUB_OUTPUT"
+ echo "OWNER=$OWNER" >> "$GITHUB_OUTPUT"
+ echo "TAG=$TAG" >> "$GITHUB_OUTPUT"
+ echo "IS_TAG=$IS_TAG" >> "$GITHUB_OUTPUT"
+ - name: Common metadata (powershell)
+ if: runner.os == 'Windows'
+ id: meta_pwsh
+ shell: powershell
+ run: |
+ $OS = "${{ runner.os }}"
+ $archEnv = $Env:PROCESSOR_ARCHITECTURE
+ if ($archEnv -eq "AMD64") { $ARCH = "amd64" }
+ elseif ($archEnv -eq "ARM64") { $ARCH = "arm64" }
+ else { $ARCH = $archEnv }
+ $REF = "${{ github.ref }}"
+ $BRANCH = ""
+ if ($REF.StartsWith("refs/heads/")) { $BRANCH = $REF.Substring(11) }
+ $SHA = "${{ github.sha }}"
+ $SHORT_SHA = $SHA.Substring(0,7)
+ $REPO = "${{ github.repository }}"
+ $OWNER = "${{ github.repository_owner }}"
+ $TAG = ""
+ $IS_TAG = 0
+ if ($REF.StartsWith("refs/tags/")) {
+ $IS_TAG = 1
+ $TAG = $REF.Substring(10)
+ }
+ "OS=$OS" >> $Env:GITHUB_OUTPUT
+ "ARCH=$ARCH" >> $Env:GITHUB_OUTPUT
+ "REF=$REF" >> $Env:GITHUB_OUTPUT
+ "BRANCH=$BRANCH" >> $Env:GITHUB_OUTPUT
+ "SHA=$SHA" >> $Env:GITHUB_OUTPUT
+ "SHORT_SHA=$SHORT_SHA" >> $Env:GITHUB_OUTPUT
+ "REPO=$REPO" >> $Env:GITHUB_OUTPUT
+ "OWNER=$OWNER" >> $Env:GITHUB_OUTPUT
+ "TAG=$TAG" >> $Env:GITHUB_OUTPUT
+ "IS_TAG=$IS_TAG" >> $Env:GITHUB_OUTPUT
+ - name: Project markers (bash)
+ if: runner.os != 'Windows'
+ id: proj_bash
+ shell: bash
+ working-directory: ${{ inputs.working-directory }}
+ run: |
+ set -euo pipefail
+ echo "[DEBUG_LOG] Discovery scanning working dir: $PWD"
+ has_root_pkg=0; has_frontend_pkg=0; has_go_mod=0; has_main_go=0; has_cmake=0; has_mkdocs=0
+ has_sub_npm=0; has_sub_mkdocs=0
+ found_files=()
+ # Root markers
+ if [ -f "package.json" ]; then has_root_pkg=1; found_files+=("package.json"); fi
+ if [ -f "go.mod" ]; then has_go_mod=1; found_files+=("go.mod"); fi
+ if [ -f "main.go" ]; then has_main_go=1; found_files+=("main.go"); fi
+ if [ -f "CMakeLists.txt" ]; then has_cmake=1; found_files+=("CMakeLists.txt"); fi
+ if [ -f "mkdocs.yml" ]; then has_mkdocs=1; found_files+=("mkdocs.yml"); fi
+ # Common subtree markers
+ if [ -f "frontend/package.json" ]; then has_frontend_pkg=1; found_files+=("frontend/package.json"); fi
+ # Light subtree search up to depth 2 for package.json and mkdocs.yml
+ if command -v find >/dev/null 2>&1; then
+ if find . -maxdepth 2 -mindepth 2 -name package.json | grep -q .; then has_sub_npm=1; fi
+ if find . -maxdepth 2 -mindepth 1 -name mkdocs.yml | grep -q .; then has_sub_mkdocs=1; fi
+ fi
+ # Suggest stack
+ suggestion="unknown"
+ if { [ "$has_go_mod" -eq 1 ] || [ "$has_main_go" -eq 1 ]; } && { [ "$has_root_pkg" -eq 1 ] || [ "$has_frontend_pkg" -eq 1 ] || [ "$has_sub_npm" -eq 1 ]; }; then
+ suggestion="wails2"
+ elif [ "$has_cmake" -eq 1 ]; then
+ suggestion="cpp"
+ fi
+ # Output and logs
+ echo "[DEBUG_LOG] HAS_ROOT_PACKAGE_JSON=$has_root_pkg"
+ echo "[DEBUG_LOG] HAS_FRONTEND_PACKAGE_JSON=$has_frontend_pkg"
+ echo "[DEBUG_LOG] HAS_ROOT_GO_MOD=$has_go_mod"
+ echo "[DEBUG_LOG] HAS_ROOT_MAIN_GO=$has_main_go"
+ echo "[DEBUG_LOG] HAS_ROOT_CMAKELISTS=$has_cmake"
+ echo "[DEBUG_LOG] HAS_ROOT_MKDOCS=$has_mkdocs"
+ echo "[DEBUG_LOG] HAS_SUB_NPM=$has_sub_npm"
+ echo "[DEBUG_LOG] HAS_SUB_MKDOCS=$has_sub_mkdocs"
+ echo "[DEBUG_LOG] PRIMARY_STACK_SUGGESTION=$suggestion"
+ echo "HAS_ROOT_PACKAGE_JSON=$has_root_pkg" >> "$GITHUB_OUTPUT"
+ echo "HAS_FRONTEND_PACKAGE_JSON=$has_frontend_pkg" >> "$GITHUB_OUTPUT"
+ echo "HAS_ROOT_GO_MOD=$has_go_mod" >> "$GITHUB_OUTPUT"
+ echo "HAS_ROOT_MAIN_GO=$has_main_go" >> "$GITHUB_OUTPUT"
+ echo "HAS_ROOT_CMAKELISTS=$has_cmake" >> "$GITHUB_OUTPUT"
+ echo "HAS_ROOT_MKDOCS=$has_mkdocs" >> "$GITHUB_OUTPUT"
+ echo "HAS_SUB_NPM=$has_sub_npm" >> "$GITHUB_OUTPUT"
+ echo "HAS_SUB_MKDOCS=$has_sub_mkdocs" >> "$GITHUB_OUTPUT"
+ echo "PRIMARY_STACK_SUGGESTION=$suggestion" >> "$GITHUB_OUTPUT"
+ if [ ${#found_files[@]} -gt 0 ]; then
+ IFS=,; echo "FOUND_FILES=${found_files[*]}" >> "$GITHUB_OUTPUT"; unset IFS
+ else
+ echo "FOUND_FILES=" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Project markers (powershell)
+ if: runner.os == 'Windows'
+ id: proj_pwsh
+ shell: powershell
+ working-directory: ${{ inputs.working-directory }}
+ run: |
+ Write-Host "[DEBUG_LOG] Discovery scanning working dir: $PWD"
+ $has_root_pkg=0; $has_frontend_pkg=0; $has_go_mod=0; $has_main_go=0; $has_cmake=0; $has_mkdocs=0
+ $has_sub_npm=0; $has_sub_mkdocs=0
+ $found = @()
+ if (Test-Path package.json) { $has_root_pkg=1; $found+="package.json" }
+ if (Test-Path go.mod) { $has_go_mod=1; $found+="go.mod" }
+ if (Test-Path main.go) { $has_main_go=1; $found+="main.go" }
+ if (Test-Path CMakeLists.txt) { $has_cmake=1; $found+="CMakeLists.txt" }
+ if (Test-Path mkdocs.yml) { $has_mkdocs=1; $found+="mkdocs.yml" }
+ if (Test-Path frontend/package.json) { $has_frontend_pkg=1; $found+="frontend/package.json" }
+ # Limited subtree search (depth ~2)
+ Get-ChildItem -Recurse -Depth 2 -Filter package.json -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notlike "*node_modules*" } | ForEach-Object { $has_sub_npm=1 }
+ Get-ChildItem -Recurse -Depth 2 -Filter mkdocs.yml -ErrorAction SilentlyContinue | ForEach-Object { $has_sub_mkdocs=1 }
+ $suggestion = "unknown"
+ if ((($has_go_mod -eq 1) -or ($has_main_go -eq 1)) -and (($has_root_pkg -eq 1) -or ($has_frontend_pkg -eq 1) -or ($has_sub_npm -eq 1))) { $suggestion = "wails2" }
+ elseif ($has_cmake -eq 1) { $suggestion = "cpp" }
+ Write-Host "[DEBUG_LOG] HAS_ROOT_PACKAGE_JSON=$has_root_pkg"
+ Write-Host "[DEBUG_LOG] HAS_FRONTEND_PACKAGE_JSON=$has_frontend_pkg"
+ Write-Host "[DEBUG_LOG] HAS_ROOT_GO_MOD=$has_go_mod"
+ Write-Host "[DEBUG_LOG] HAS_ROOT_MAIN_GO=$has_main_go"
+ Write-Host "[DEBUG_LOG] HAS_ROOT_CMAKELISTS=$has_cmake"
+ Write-Host "[DEBUG_LOG] HAS_ROOT_MKDOCS=$has_mkdocs"
+ Write-Host "[DEBUG_LOG] HAS_SUB_NPM=$has_sub_npm"
+ Write-Host "[DEBUG_LOG] HAS_SUB_MKDOCS=$has_sub_mkdocs"
+ Write-Host "[DEBUG_LOG] PRIMARY_STACK_SUGGESTION=$suggestion"
+ "HAS_ROOT_PACKAGE_JSON=$has_root_pkg" >> $Env:GITHUB_OUTPUT
+ "HAS_FRONTEND_PACKAGE_JSON=$has_frontend_pkg" >> $Env:GITHUB_OUTPUT
+ "HAS_ROOT_GO_MOD=$has_go_mod" >> $Env:GITHUB_OUTPUT
+ "HAS_ROOT_MAIN_GO=$has_main_go" >> $Env:GITHUB_OUTPUT
+ "HAS_ROOT_CMAKELISTS=$has_cmake" >> $Env:GITHUB_OUTPUT
+ "HAS_ROOT_MKDOCS=$has_mkdocs" >> $Env:GITHUB_OUTPUT
+ "HAS_SUB_NPM=$has_sub_npm" >> $Env:GITHUB_OUTPUT
+ "HAS_SUB_MKDOCS=$has_sub_mkdocs" >> $Env:GITHUB_OUTPUT
+ "PRIMARY_STACK_SUGGESTION=$suggestion" >> $Env:GITHUB_OUTPUT
+ if ($found.Count -gt 0) { "FOUND_FILES=$($found -join ',')" >> $Env:GITHUB_OUTPUT } else { "FOUND_FILES=" >> $Env:GITHUB_OUTPUT }
+outputs:
+ OS:
+ description: "Runner OS (Linux/macOS/Windows)"
+ value: ${{ steps.meta_bash.outputs.OS || steps.meta_pwsh.outputs.OS }}
+ ARCH:
+ description: "CPU architecture (e.g., x86_64/amd64/arm64)"
+ value: ${{ steps.meta_bash.outputs.ARCH || steps.meta_pwsh.outputs.ARCH }}
+ DISTRO:
+ description: "Linux distro version when on Linux (e.g., 20.04/22.04/24.04); empty otherwise"
+ value: ${{ steps.linux.outputs.DISTRO }}
+ REF:
+ description: "The full Git ref"
+ value: ${{ steps.meta_bash.outputs.REF || steps.meta_pwsh.outputs.REF }}
+ BRANCH:
+ description: "Branch name when on branch refs; empty on tags"
+ value: ${{ steps.meta_bash.outputs.BRANCH || steps.meta_pwsh.outputs.BRANCH }}
+ TAG:
+ description: "Tag name when running on a tag ref; empty otherwise"
+ value: ${{ steps.meta_bash.outputs.TAG || steps.meta_pwsh.outputs.TAG }}
+ IS_TAG:
+ description: "1 if running on tag ref; 0 otherwise"
+ value: ${{ steps.meta_bash.outputs.IS_TAG || steps.meta_pwsh.outputs.IS_TAG }}
+ SHA:
+ description: "Full commit SHA"
+ value: ${{ steps.meta_bash.outputs.SHA || steps.meta_pwsh.outputs.SHA }}
+ SHORT_SHA:
+ description: "Short commit SHA (7 chars)"
+ value: ${{ steps.meta_bash.outputs.SHORT_SHA || steps.meta_pwsh.outputs.SHORT_SHA }}
+ REPO:
+ description: "owner/repo"
+ value: ${{ steps.meta_bash.outputs.REPO || steps.meta_pwsh.outputs.REPO }}
+ OWNER:
+ description: "Repository owner"
+ value: ${{ steps.meta_bash.outputs.OWNER || steps.meta_pwsh.outputs.OWNER }}
+ HAS_ROOT_PACKAGE_JSON:
+ description: "1 if package.json at root of working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_ROOT_PACKAGE_JSON || steps.proj_pwsh.outputs.HAS_ROOT_PACKAGE_JSON }}
+ HAS_FRONTEND_PACKAGE_JSON:
+ description: "1 if frontend/package.json under working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_FRONTEND_PACKAGE_JSON || steps.proj_pwsh.outputs.HAS_FRONTEND_PACKAGE_JSON }}
+ HAS_ROOT_GO_MOD:
+ description: "1 if go.mod at root of working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_ROOT_GO_MOD || steps.proj_pwsh.outputs.HAS_ROOT_GO_MOD }}
+ HAS_ROOT_MAIN_GO:
+ description: "1 if main.go at root of working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_ROOT_MAIN_GO || steps.proj_pwsh.outputs.HAS_ROOT_MAIN_GO }}
+ HAS_ROOT_CMAKELISTS:
+ description: "1 if CMakeLists.txt at root of working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_ROOT_CMAKELISTS || steps.proj_pwsh.outputs.HAS_ROOT_CMAKELISTS }}
+ HAS_ROOT_MKDOCS:
+ description: "1 if mkdocs.yml at root of working-directory"
+ value: ${{ steps.proj_bash.outputs.HAS_ROOT_MKDOCS || steps.proj_pwsh.outputs.HAS_ROOT_MKDOCS }}
+ HAS_SUB_NPM:
+ description: "1 if a nested package.json was found within depth 2"
+ value: ${{ steps.proj_bash.outputs.HAS_SUB_NPM || steps.proj_pwsh.outputs.HAS_SUB_NPM }}
+ HAS_SUB_MKDOCS:
+ description: "1 if a nested mkdocs.yml was found within depth 2"
+ value: ${{ steps.proj_bash.outputs.HAS_SUB_MKDOCS || steps.proj_pwsh.outputs.HAS_SUB_MKDOCS }}
+ PRIMARY_STACK_SUGGESTION:
+ description: "Computed primary stack suggestion (wails2/cpp/unknown)"
+ value: ${{ steps.proj_bash.outputs.PRIMARY_STACK_SUGGESTION || steps.proj_pwsh.outputs.PRIMARY_STACK_SUGGESTION }}
+ FOUND_FILES:
+ description: "Comma-separated summary of notable files found"
+ value: ${{ steps.proj_bash.outputs.FOUND_FILES || steps.proj_pwsh.outputs.FOUND_FILES }}
diff --git a/actions/options/README.md b/actions/options/README.md
new file mode 100644
index 0000000..6fb6305
--- /dev/null
+++ b/actions/options/README.md
@@ -0,0 +1,35 @@
+# Options (sub-action)
+
+Purpose
+- Computes a `BUILD_OPTIONS` string for `wails build` based on inputs and discovery outputs.
+- On Ubuntu 24.04, it appends `webkit2_41` tag if not already provided.
+
+Inputs
+- `build-obfuscate` (default `false`) — Adds `-obfuscated`.
+- `build-tags` (default `false`) — Space or comma separated. When not `false`, adds `-tags "..."`.
+- `nsis` (default `false`) — Adds `-nsis` flag for Windows installers.
+- `distro` (optional) — Pass the distro output from discovery; enables Ubuntu 24.04 special case.
+
+Outputs
+- `BUILD_OPTIONS` — The computed string to pass to `wails build`.
+
+Usage
+```yaml
+- name: Discovery
+ id: disc
+ uses: snider/build/actions/discovery@v3
+
+- name: Compute Options
+ id: opts
+ uses: snider/build/actions/options@v3
+ with:
+ build-obfuscate: 'true'
+ build-tags: 'release'
+ nsis: 'false'
+ distro: ${{ steps.disc.outputs.DISTRO }}
+# Later: ${{ steps.opts.outputs.BUILD_OPTIONS }}
+```
+
+Notes
+- If you already specify tags and you are on Ubuntu 24.04, `webkit2_41` will be appended.
+- If `build-tags` is left as `false` and distro is `24.04`, the action injects `-tags webkit2_41` automatically.
diff --git a/actions/options/action.yml b/actions/options/action.yml
new file mode 100644
index 0000000..0e4c358
--- /dev/null
+++ b/actions/options/action.yml
@@ -0,0 +1,73 @@
+name: "Options"
+description: "Compute BUILD_OPTIONS string for builds based on inputs and discovery outputs"
+inputs:
+ build-obfuscate:
+ description: "Obfuscate the build"
+ required: false
+ default: "false"
+ build-tags:
+ description: "Build tags to pass to Go compiler. Space and/or comma separated."
+ required: false
+ default: "false"
+ nsis:
+ description: "Include -nsis flag"
+ required: false
+ default: "false"
+ distro:
+ description: "Linux distro version (e.g., 24.04). Optional; if 24.04 and no explicit webkit tag provided, add webkit2_41"
+ required: false
+ default: ""
+ disable-webkit-auto-tag:
+ description: "When true, do not auto-append webkit2_41 for Ubuntu 24.04"
+ required: false
+ default: "false"
+outputs:
+ BUILD_OPTIONS:
+ description: "Computed build options"
+ value: ${{ steps.compute.outputs.BUILD_OPTIONS }}
+runs:
+ using: "composite"
+ steps:
+ - id: compute
+ name: Compute build options
+ shell: bash
+ env:
+ DISTRO: ${{ inputs.distro }}
+ DISABLE_WEBKIT: ${{ inputs.disable-webkit-auto-tag }}
+ run: |
+ set -euo pipefail
+ build_options=""
+ # Obfuscation
+ if ${{ inputs.build-obfuscate == 'true' }}; then
+ build_options+=" -obfuscated"
+ fi
+ # Normalize tags: accept commas and/or spaces, trim, de-duplicate
+ norm_tags=""
+ if [[ "${{ inputs.build-tags }}" != "false" ]]; then
+ raw="${{ inputs.build-tags }}"
+ raw="${raw//,/ }"
+ # collapse multiple spaces
+ raw=$(echo "$raw" | tr -s ' ')
+ declare -A seen=()
+ for t in $raw; do
+ if [[ -n "$t" && -z "${seen[$t]:-}" ]]; then
+ seen[$t]=1
+ norm_tags+=" $t"
+ fi
+ done
+ fi
+ # Ubuntu 24.04 webkit auto-tag unless disabled or already present
+ if [[ "$DISTRO" == '24.04' && "${DISABLE_WEBKIT}" != 'true' ]]; then
+ case " $norm_tags " in
+ *" webkit2_41 "*) : ;; # already present
+ *) norm_tags+=" webkit2_41" ;;
+ esac
+ fi
+ if [[ -n "$norm_tags" ]]; then
+ build_options+=" -tags${norm_tags}"
+ fi
+ # NSIS
+ if ${{ inputs.nsis == 'true' }}; then
+ build_options+=" -nsis"
+ fi
+ echo "BUILD_OPTIONS=$build_options" >> "$GITHUB_OUTPUT"
diff --git a/actions/package/README.md b/actions/package/README.md
new file mode 100644
index 0000000..6ae4bce
--- /dev/null
+++ b/actions/package/README.md
@@ -0,0 +1,37 @@
+# Package & Release (sub-action)
+
+Purpose
+- Uploads build artifacts from standard Wails output directories and publishes a GitHub Release on tag builds.
+
+What it does
+- Uses `actions/upload-artifact@v4` to upload files from `*/bin/` (Linux/macOS) and `*\\bin\\*` (Windows).
+- If the workflow is running on a tag (`refs/tags/*`) and packaging is enabled, it attaches all `*/bin/*` files to a GitHub Release using `softprops/action-gh-release@v1`.
+- Composes a descriptive artifact name using discovery metadata: `___`.
+- Optionally writes a small `artifact_meta.json` (enabled by default) alongside the uploaded artifacts with discovery info for downstream automation.
+
+Inputs
+- `package` (default `true`) — enable/disable uploads and release publishing.
+- `build-name` (required) — base name used in the artifact label.
+- Optional (auto-filled when using the root action/wrapper): `os`, `arch`, `tag`, `short-sha`, `ref`.
+- `include-meta` (default `true`) — when `true`, writes `artifact_meta.json` containing `{ build_name, os, arch, ref, branch, tag, short_sha }` and includes it in both artifact uploads and releases.
+
+Usage
+```yaml
+- name: Package & Release
+ uses: snider/build/actions/package@v3
+ with:
+ package: 'true'
+ build-name: 'wailsApp'
+ # When calling directly from a workflow, you may pass discovery data (optional)
+ os: ${{ steps.discovery.outputs.OS }}
+ arch: ${{ steps.discovery.outputs.ARCH }}
+ tag: ${{ steps.discovery.outputs.TAG }}
+ short-sha: ${{ steps.discovery.outputs.SHORT_SHA }}
+```
+
+Notes
+- Artifact name examples:
+ - Branch build: `wailsApp_Ubuntu-22.04_amd64_ab12cd3`
+ - Tag build: `wailsApp_macos_arm64_v1.2.3`
+- To publish a release, push a tag (e.g., `v1.2.3`). On non-tag builds, only artifacts are uploaded.
+- Adjust paths in your own workflow if your build layout differs from `*/bin/*`.
diff --git a/actions/package/action.yml b/actions/package/action.yml
new file mode 100644
index 0000000..cbb47de
--- /dev/null
+++ b/actions/package/action.yml
@@ -0,0 +1,157 @@
+name: "Package & Release"
+description: "Uploads build artifacts and publishes GitHub Release on tag if enabled"
+inputs:
+ package:
+ required: false
+ default: "true"
+ build-name:
+ required: true
+ os:
+ description: "OS name (optional, from discovery). Defaults to runner.os"
+ required: false
+ default: ""
+ arch:
+ description: "Architecture (optional, from discovery). Defaults to detected arch"
+ required: false
+ default: ""
+ tag:
+ description: "Git tag (optional, from discovery)."
+ required: false
+ default: ""
+ is-tag:
+ description: "Whether the ref is a tag (optional, from discovery)."
+ required: false
+ default: ""
+ short-sha:
+ description: "Short commit SHA (optional, from discovery)."
+ required: false
+ default: ""
+ ref:
+ description: "Git ref (optional, from discovery)."
+ required: false
+ default: ""
+ include-meta:
+ description: "Include artifact_meta.json with discovery metadata in the uploaded artifact"
+ required: false
+ default: "true"
+runs:
+ using: "composite"
+ steps:
+ - name: Compute artifact name
+ id: meta
+ shell: bash
+ run: |
+ set -euo pipefail
+ OS_IN="${{ inputs.os }}"
+ ARCH_IN="${{ inputs.arch }}"
+ TAG_IN="${{ inputs.tag }}"
+ SHORT_SHA_IN="${{ inputs.short-sha }}"
+ # Resolve OS
+ if [ -z "$OS_IN" ]; then
+ OS_IN="${{ runner.os }}"
+ fi
+ # Resolve ARCH (prefer input)
+ if [ -z "$ARCH_IN" ]; then
+ # Try uname -m; fallback to envs on Windows if needed
+ if command -v uname >/dev/null 2>&1; then
+ M=$(uname -m || echo unknown)
+ else
+ M="$PROCESSOR_ARCHITECTURE"
+ fi
+ case "$M" in
+ x86_64|amd64) ARCH_IN="amd64";;
+ aarch64|arm64) ARCH_IN="arm64";;
+ armv7l) ARCH_IN="armv7";;
+ *) ARCH_IN="$M";;
+ esac
+ fi
+ # Resolve revision label (prefer tag)
+ if [ -n "$TAG_IN" ]; then
+ REV="$TAG_IN"
+ else
+ if [ -z "$SHORT_SHA_IN" ]; then
+ GHS="${GITHUB_SHA:-}"
+ SHORT_SHA_IN="${GHS:0:7}"
+ fi
+ REV="$SHORT_SHA_IN"
+ fi
+ NAME="${{ inputs.build-name }}_${OS_IN}_${ARCH_IN}_${REV}"
+ echo "ARTIFACT_NAME=$NAME" >> "$GITHUB_OUTPUT"
+ - name: Debug echo artifact name
+ shell: bash
+ run: |
+ echo "[DEBUG_LOG] ARTIFACT_NAME=${{ steps.meta.outputs.ARTIFACT_NAME }}"
+ - name: Write artifact_meta.json
+ if: inputs.package == 'true' && inputs.include-meta == 'true'
+ shell: bash
+ env:
+ IN_BUILD_NAME: ${{ inputs.build-name }}
+ IN_OS: ${{ inputs.os }}
+ IN_ARCH: ${{ inputs.arch }}
+ IN_REF: ${{ inputs.ref }}
+ IN_TAG: ${{ inputs.tag }}
+ IN_SHORT_SHA: ${{ inputs.short-sha }}
+ run: |
+ set -euo pipefail
+ build_name="$IN_BUILD_NAME"
+ os_val="$IN_OS"
+ arch_val="$IN_ARCH"
+ ref_val="$IN_REF"
+ tag_val="$IN_TAG"
+ short_sha="$IN_SHORT_SHA"
+ # Resolve OS from runner if not provided
+ if [ -z "$os_val" ]; then os_val="${RUNNER_OS:-}"; fi
+ # Resolve ARCH if not provided (best-effort)
+ if [ -z "$arch_val" ]; then
+ if command -v uname >/dev/null 2>&1; then
+ m=$(uname -m || echo unknown)
+ case "$m" in
+ x86_64|amd64) arch_val="amd64";;
+ aarch64|arm64) arch_val="arm64";;
+ armv7l) arch_val="armv7";;
+ *) arch_val="$m";;
+ esac
+ else
+ arch_val="${PROCESSOR_ARCHITECTURE:-}"
+ fi
+ fi
+ # Resolve REF/BRANCH
+ if [ -z "$ref_val" ]; then ref_val="${GITHUB_REF:-}"; fi
+ branch_val=""
+ case "$ref_val" in
+ refs/heads/*) branch_val="${ref_val#refs/heads/}" ;;
+ esac
+ # Resolve SHORT SHA
+ if [ -z "$short_sha" ]; then
+ ghsha="${GITHUB_SHA:-}"
+ short_sha="${ghsha:0:7}"
+ fi
+ cat > artifact_meta.json < inputs > defaults.
+- Supported envs:
+ - `DENO_ENABLE` — `true`/`1`/`yes`/`on` explicitly enables Deno
+ - `DENO_BUILD` — full command to run (e.g., `deno task build`)
+ - `DENO_VERSION` — e.g., `v1.44.x`
+ - `DENO_WORKDIR` — working directory for Deno (default `.`)
+ - Pass-through: `DENO_AUTH_TOKEN`, `DENO_DIR`, proxies, etc.
+
+Behavior
+- Calls sub-actions in order: `setup/go`, `setup/npm`, `setup/deno`, and optionally `setup/conan`.
+- Resolves Deno configuration and sets up Deno only when enabled; runs the command when provided.
+
+Usage
+```yaml
+- name: Setup toolchains
+ uses: snider/build/actions/setup@v3
+ with:
+ go-version: '1.23'
+ build-cache: 'true'
+ build-obfuscate: 'false'
+ wails-version: 'latest'
+ node-version: '18.x'
+ npm-working-directory: 'build/wails2'
+ npm-install: 'true'
+ env:
+ # Optional Deno via env
+ DENO_ENABLE: 'true'
+ DENO_VERSION: 'v1.44.x'
+ DENO_WORKDIR: 'frontend'
+ DENO_BUILD: 'deno task build'
+```
+
+Notes
+- On macOS, `gon` is installed for later signing steps. No-op on other OSes.
+- If you do not need Deno, leave envs and inputs empty — it will be skipped.
+- If `wails-dev-build` is `true`, ensure `wails` is already available on PATH.
diff --git a/actions/setup/action.yml b/actions/setup/action.yml
new file mode 100644
index 0000000..0da2adf
--- /dev/null
+++ b/actions/setup/action.yml
@@ -0,0 +1,69 @@
+name: "Setup toolchains"
+description: "Orchestrates language/tool setup by delegating to sub-actions: setup/go, setup/npm, setup/deno, setup/conan"
+inputs:
+ # Go/Wails
+ go-version:
+ required: false
+ default: "1.23"
+ build-cache:
+ required: false
+ default: "true"
+ build-obfuscate:
+ required: false
+ default: "false"
+ wails-version:
+ required: false
+ default: "latest"
+ wails-dev-build:
+ required: false
+ default: "false"
+ # Node/npm
+ node-version:
+ required: false
+ default: "18.x"
+ npm-working-directory:
+ required: false
+ default: "."
+ npm-install:
+ required: false
+ default: "true"
+ # Deno
+ deno-build:
+ required: false
+ default: ""
+ deno-version:
+ required: false
+ default: "v1.20.x"
+ deno-working-directory:
+ required: false
+ default: "."
+ # Conan (placeholder)
+ conan-enable:
+ required: false
+ default: "false"
+runs:
+ using: "composite"
+ steps:
+ - name: Setup Go
+ uses: ./actions/setup/go
+ with:
+ go-version: ${{ inputs.go-version }}
+ build-cache: ${{ inputs.build-cache }}
+ build-obfuscate: ${{ inputs.build-obfuscate }}
+ wails-version: ${{ inputs.wails-version }}
+ wails-dev-build: ${{ inputs.wails-dev-build }}
+ - name: Setup Node/npm
+ uses: ./actions/setup/npm
+ with:
+ node-version: ${{ inputs.node-version }}
+ working-directory: ${{ inputs.npm-working-directory }}
+ install: ${{ inputs.npm-install }}
+ - name: Setup Deno (optional)
+ uses: ./actions/setup/deno
+ with:
+ deno-build: ${{ inputs.deno-build }}
+ deno-version: ${{ inputs.deno-version }}
+ deno-working-directory: ${{ inputs.deno-working-directory }}
+ - name: Setup Conan (optional)
+ if: inputs.conan-enable == 'true'
+ uses: ./actions/setup/conan
diff --git a/actions/setup/conan/action.yml b/actions/setup/conan/action.yml
new file mode 100644
index 0000000..fbaf542
--- /dev/null
+++ b/actions/setup/conan/action.yml
@@ -0,0 +1,25 @@
+name: "Setup Conan (placeholder)"
+description: "Installs Conan package manager for C/C++ projects (future use)"
+inputs:
+ version:
+ required: false
+ default: "latest"
+runs:
+ using: "composite"
+ steps:
+ - name: Install Python (for pip)
+ if: runner.os == 'Windows'
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.x'
+ - name: Install Conan via pip
+ shell: bash
+ run: |
+ set -euo pipefail
+ if command -v pip3 >/dev/null 2>&1; then PIP=pip3; elif command -v pip >/dev/null 2>&1; then PIP=pip; else echo "pip not found" && exit 1; fi
+ if [ "${{ inputs.version }}" = "latest" ]; then
+ $PIP install --user conan
+ else
+ $PIP install --user conan=="${{ inputs.version }}"
+ fi
+ echo "Conan installed. Add ~/.local/bin to PATH if needed."
diff --git a/actions/setup/deno/README.md b/actions/setup/deno/README.md
new file mode 100644
index 0000000..ae2685a
--- /dev/null
+++ b/actions/setup/deno/README.md
@@ -0,0 +1,50 @@
+# Setup Deno (sub-action)
+
+Purpose
+- Optional Deno setup and command runner with ENV-first configuration.
+- Can be used independently or via the `actions/setup` orchestrator.
+
+Behavior
+- Resolves configuration from environment variables first, then falls back to inputs.
+- Installs the requested Deno version on Linux/macOS/Windows.
+- Runs the provided Deno command in the specified working directory when present.
+
+ENV-first precedence
+- Environment variables > inputs > defaults.
+- Supported envs:
+ - `DENO_ENABLE` — `true`/`1`/`yes`/`on` to explicitly enable Deno setup.
+ - `DENO_BUILD` — full command to run (e.g., `deno task build`, `deno run -A build.ts`).
+ - `DENO_VERSION` — e.g., `v1.44.x`.
+ - `DENO_WORKDIR` — working directory for Deno (default `.`).
+- Pass-through (if set in your workflow env): `DENO_AUTH_TOKEN`, `DENO_DIR`, `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, etc.
+
+Inputs (fallbacks)
+- `deno-build` (default `''`)
+- `deno-version` (default `v1.20.x`)
+- `deno-working-directory` (default `.`)
+
+Usage (direct)
+```yaml
+- name: Setup Deno (direct)
+ uses: snider/build/actions/setup/deno@v3
+ with:
+ deno-version: 'v1.44.x'
+ deno-working-directory: 'frontend'
+ deno-build: 'deno task build'
+```
+
+Usage (ENV-first; recommended)
+```yaml
+- name: Configure Deno via env
+ run: |
+ echo "DENO_ENABLE=true" >> "$GITHUB_ENV"
+ echo "DENO_VERSION=v1.44.x" >> "$GITHUB_ENV"
+ echo "DENO_WORKDIR=frontend" >> "$GITHUB_ENV"
+ echo "DENO_BUILD=deno task build" >> "$GITHUB_ENV"
+- name: Setup toolchains (orchestrator)
+ uses: snider/build/actions/setup@v3
+```
+
+Notes
+- If you do not set any of the envs or inputs, this action is a no-op.
+- This sub-action is stack-agnostic and can be used for non-Wails projects as well.
diff --git a/actions/setup/deno/action.yml b/actions/setup/deno/action.yml
new file mode 100644
index 0000000..cdfd5d8
--- /dev/null
+++ b/actions/setup/deno/action.yml
@@ -0,0 +1,51 @@
+name: "Setup Deno (ENV-first)"
+description: "Sets up Deno and optionally runs a Deno command; configuration comes from env first, then inputs"
+inputs:
+ deno-build:
+ required: false
+ default: ""
+ deno-version:
+ required: false
+ default: "v1.20.x"
+ deno-working-directory:
+ required: false
+ default: "."
+runs:
+ using: "composite"
+ steps:
+ - name: Resolve Deno configuration
+ id: deno_cfg
+ shell: bash
+ env:
+ INPUT_DENO_BUILD: ${{ inputs.deno-build }}
+ INPUT_DENO_VERSION: ${{ inputs.deno-version }}
+ INPUT_DENO_WORKDIR: ${{ inputs.deno-working-directory }}
+ run: |
+ set -euo pipefail
+ deno_enable="${DENO_ENABLE:-}"
+ deno_build="${DENO_BUILD:-}"
+ deno_version="${DENO_VERSION:-}"
+ deno_workdir="${DENO_WORKDIR:-}"
+ # Fallback to inputs if env not set
+ if [ -z "$deno_build" ]; then deno_build="$INPUT_DENO_BUILD"; fi
+ if [ -z "$deno_version" ]; then deno_version="$INPUT_DENO_VERSION"; fi
+ if [ -z "$deno_workdir" ]; then deno_workdir="$INPUT_DENO_WORKDIR"; fi
+ enabled=0
+ case "${deno_enable,,}" in
+ true|1|yes|on) enabled=1;;
+ esac
+ if [ -n "$deno_build" ]; then enabled=1; fi
+ echo "ENABLED=$enabled" >> "$GITHUB_OUTPUT"
+ echo "BUILD=$deno_build" >> "$GITHUB_OUTPUT"
+ echo "VERSION=$deno_version" >> "$GITHUB_OUTPUT"
+ echo "WORKDIR=${deno_workdir:-.}" >> "$GITHUB_OUTPUT"
+ - name: Setup Deno
+ uses: denoland/setup-deno@v2
+ if: steps.deno_cfg.outputs.ENABLED == '1'
+ with:
+ deno-version: ${{ steps.deno_cfg.outputs.VERSION }}
+ - name: Run Deno Command
+ if: steps.deno_cfg.outputs.BUILD != ''
+ shell: bash
+ working-directory: ${{ steps.deno_cfg.outputs.WORKDIR }}
+ run: ${{ steps.deno_cfg.outputs.BUILD }}
diff --git a/actions/setup/go/action.yml b/actions/setup/go/action.yml
new file mode 100644
index 0000000..dc4c8dc
--- /dev/null
+++ b/actions/setup/go/action.yml
@@ -0,0 +1,43 @@
+name: "Setup Go (and Wails)"
+description: "Sets up Go (with optional cache), installs Garble when obfuscating, installs Wails CLI unless wails-dev-build is true; installs gon on macOS"
+inputs:
+ go-version:
+ required: false
+ default: "1.23"
+ build-cache:
+ required: false
+ default: "true"
+ build-obfuscate:
+ required: false
+ default: "false"
+ wails-version:
+ required: false
+ default: "latest"
+ wails-dev-build:
+ required: false
+ default: "false"
+runs:
+ using: "composite"
+ steps:
+ - name: Setup GoLang
+ uses: actions/setup-go@v5
+ if: inputs.wails-dev-build == 'false'
+ with:
+ check-latest: true
+ cache: ${{ inputs.build-cache }}
+ cache-dependency-path: 'go.sum'
+ go-version: ${{ inputs.go-version }}
+ - name: Install Garble
+ if: inputs.build-obfuscate == 'true'
+ run: go install mvdan.cc/garble@latest
+ shell: bash
+ - run: go version
+ shell: bash
+ - name: Install Wails
+ if: inputs.wails-dev-build == 'false'
+ run: go install github.com/wailsapp/wails/v2/cmd/wails@${{ inputs.wails-version }}
+ shell: bash
+ - name: Install macOS deps (gon)
+ if: runner.os == 'macOS'
+ run: brew install mitchellh/gon/gon
+ shell: bash
diff --git a/actions/setup/npm/README.md b/actions/setup/npm/README.md
new file mode 100644
index 0000000..896748b
--- /dev/null
+++ b/actions/setup/npm/README.md
@@ -0,0 +1,24 @@
+# Setup Node and npm (sub-action)
+
+Purpose
+- Installs Node.js and optionally installs dependencies with npm.
+- Auto-detects where to install: current `working-directory` or `frontend/` fallback.
+
+Inputs
+- `node-version` (default `18.x`) — Node.js version to install.
+- `working-directory` (default `.`) — base path to check for `package.json`.
+- `install` (default `true`) — when `true`, runs `npm ci` (falls back to `npm install`).
+
+Usage
+```yaml
+- name: Setup Node/npm
+ uses: snider/build/actions/setup/npm@v3
+ with:
+ node-version: '20.x'
+ working-directory: 'build/wails2' # or '.'
+ install: 'true'
+```
+
+Notes
+- If no `package.json` exists in the working dir, the action will try `frontend/`.
+- Disable installs with `install: 'false'` if you manage dependencies yourself.
diff --git a/actions/setup/npm/action.yml b/actions/setup/npm/action.yml
new file mode 100644
index 0000000..28cad84
--- /dev/null
+++ b/actions/setup/npm/action.yml
@@ -0,0 +1,42 @@
+name: "Setup Node and npm"
+description: "Installs Node.js and optionally installs dependencies (npm ci) in a detected project directory"
+inputs:
+ node-version:
+ required: false
+ default: "18.x"
+ working-directory:
+ required: false
+ default: "."
+ install:
+ required: false
+ default: "true"
+runs:
+ using: "composite"
+ steps:
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ inputs.node-version }}
+ check-latest: true
+ - name: Show node and npm versions
+ shell: bash
+ run: |
+ node -v
+ npm -v
+ - name: Install npm dependencies (auto-detect path)
+ if: inputs.install == 'true'
+ shell: bash
+ working-directory: ${{ inputs.working-directory }}
+ run: |
+ set -euo pipefail
+ install_dir="${{ inputs.working-directory }}"
+ if [ -f "package.json" ]; then
+ echo "Installing npm deps in $PWD"
+ npm ci || npm install
+ elif [ -f "frontend/package.json" ]; then
+ echo "Installing npm deps in frontend/"
+ cd frontend
+ npm ci || npm install
+ else
+ echo "No package.json found in working-directory or frontend/. Skipping npm install."
+ fi
diff --git a/actions/sign/README.md b/actions/sign/README.md
new file mode 100644
index 0000000..91c8b89
--- /dev/null
+++ b/actions/sign/README.md
@@ -0,0 +1,56 @@
+# Sign (sub-action)
+
+Purpose
+- Unified signing step for macOS and Windows.
+- macOS: imports certificates, signs the `.app` with `gon`, zips the `.app`, builds a `.pkg` (signed or unsigned), and notarizes on tag builds.
+- Windows: signs the `.exe` and the NSIS installer using a provided PFX (base64) and password.
+
+When it runs
+- macOS: meaningful on `macOS` runners. Most operations only run when `sign != 'false'` AND the ref is a tag (`refs/tags/*`).
+- Windows: meaningful on `Windows` runners. Runs when `sign != 'false'` AND `sign-windows-cert` is provided.
+
+Inputs (union of previous per-OS signers)
+- `sign` (default `false`) — enable/disable signing.
+- `app-working-directory` (default `.`)
+- `build-name` (required)
+- macOS:
+ - `sign-macos-apple-password` — app-specific password for Apple ID (`gon` uses this)
+ - `sign-macos-app-id` — Developer ID Application subject
+ - `sign-macos-app-cert` — Base64-encoded `.p12`
+ - `sign-macos-app-cert-password` — Password for the Application certificate
+ - `sign-macos-installer-id` — Developer ID Installer subject (optional)
+ - `sign-macos-installer-cert` — Base64-encoded `.p12` (Installer)
+ - `sign-macos-installer-cert-password` — Password for the Installer certificate
+- Windows:
+ - `sign-windows-cert` — Base64-encoded PFX contents
+ - `sign-windows-cert-password` — Password for the PFX
+
+Required project files (macOS)
+- `build/darwin/gon-sign.json` — config for signing the `.app`
+- `build/darwin/gon-notarize.json` — config for notarizing `.pkg` and `.app.zip`
+
+Usage
+```yaml
+- name: Sign artifacts
+ uses: snider/build/actions/sign@v3
+ with:
+ sign: 'true'
+ app-working-directory: '.'
+ build-name: 'wailsApp'
+ # macOS
+ sign-macos-apple-password: ${{ secrets.APPLE_PASSWORD }}
+ sign-macos-app-id: ${{ secrets.MACOS_DEVELOPER_CERT_ID }}
+ sign-macos-app-cert: ${{ secrets.MACOS_DEVELOPER_CERT }}
+ sign-macos-app-cert-password: ${{ secrets.MACOS_DEVELOPER_CERT_PASSWORD }}
+ sign-macos-installer-id: ${{ secrets.MACOS_INSTALLER_CERT_ID }}
+ sign-macos-installer-cert: ${{ secrets.MACOS_INSTALLER_CERT }}
+ sign-macos-installer-cert-password: ${{ secrets.MACOS_INSTALLER_CERT_PASSWORD }}
+ # Windows
+ sign-windows-cert: ${{ secrets.WIN_CERT_PFX_BASE64 }}
+ sign-windows-cert-password: ${{ secrets.WIN_CERT_PASSWORD }}
+```
+
+Notes
+- On macOS, `gon` must be installed (`actions/setup/go` installs it automatically on macOS).
+- The `.app` zip is produced regardless of signing to ease distribution.
+- Installer `.pkg` is signed when `sign == 'true'` and an installer ID is provided; otherwise an unsigned pkg is built on tags.
diff --git a/actions/sign/action.yml b/actions/sign/action.yml
new file mode 100644
index 0000000..213cf3e
--- /dev/null
+++ b/actions/sign/action.yml
@@ -0,0 +1,109 @@
+name: "Sign"
+description: "Unified signing for macOS and Windows (conditional by OS); notarizes on tags for macOS"
+inputs:
+ sign:
+ required: false
+ default: "false"
+ app-working-directory:
+ required: false
+ default: "."
+ build-name:
+ required: true
+ # macOS
+ sign-macos-apple-password:
+ required: false
+ default: ''
+ sign-macos-app-id:
+ required: false
+ default: ''
+ sign-macos-app-cert:
+ required: false
+ default: ''
+ sign-macos-app-cert-password:
+ required: false
+ default: ''
+ sign-macos-installer-id:
+ required: false
+ default: ''
+ sign-macos-installer-cert:
+ required: false
+ default: ''
+ sign-macos-installer-cert-password:
+ required: false
+ default: ''
+ # Windows
+ sign-windows-cert:
+ required: false
+ default: ''
+ sign-windows-cert-password:
+ required: false
+ default: ''
+runs:
+ using: "composite"
+ steps:
+ # macOS signing flow
+ - name: Import Code-Signing Certificates for macOS
+ if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
+ uses: Apple-Actions/import-codesign-certs@v1
+ with:
+ keychain-password: ${{ inputs.sign-macos-apple-password }}
+ p12-file-base64: ${{ inputs.sign-macos-app-cert }}
+ p12-password: ${{ inputs.sign-macos-app-cert-password }}
+ - name: Import Code-Signing Certificates for macOS Installer
+ if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
+ uses: Apple-Actions/import-codesign-certs@v1
+ with:
+ keychain-password: ${{ inputs.sign-macos-apple-password }}
+ p12-file-base64: ${{ inputs.sign-macos-installer-cert }}
+ p12-password: ${{ inputs.sign-macos-installer-cert-password }}
+ create-keychain: false
+ - name: Sign macOS .app with gon
+ if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
+ shell: bash
+ working-directory: ${{ inputs.app-working-directory }}
+ env:
+ APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }}
+ run: |
+ echo "Signing Package"
+ gon -log-level=info ./build/darwin/gon-sign.json
+ - name: Build .app zip file
+ if: runner.os == 'macOS'
+ working-directory: ${{ inputs.app-working-directory }}
+ shell: bash
+ run: |
+ ditto -c -k --keepParent ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.app ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.app.zip
+ - name: Build Installer (signed)
+ if: runner.os == 'macOS' && inputs.sign != 'false' && inputs.sign-macos-installer-id != '' && startsWith(github.ref, 'refs/tags/')
+ shell: bash
+ working-directory: ${{ inputs.app-working-directory }}
+ run: |
+ productbuild --sign '${{ inputs.sign-macos-installer-id }}' --component ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.pkg
+ - name: Build Installer (unsigned)
+ if: runner.os == 'macOS' && inputs.sign-macos-installer-id == '' && startsWith(github.ref, 'refs/tags/')
+ shell: bash
+ working-directory: ${{ inputs.app-working-directory }}
+ run: |
+ productbuild --component ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.app /Applications ${{ inputs.app-working-directory }}/build/bin/${{ inputs.build-name }}.pkg
+ - name: Notarize Installer and zip with gon
+ if: runner.os == 'macOS' && inputs.sign != 'false' && startsWith(github.ref, 'refs/tags/')
+ shell: bash
+ working-directory: ${{ inputs.app-working-directory }}
+ env:
+ APPLE_PASSWORD: ${{ inputs.sign-macos-apple-password }}
+ run: |
+ gon -log-level=info ${{ inputs.app-working-directory }}/build/darwin/gon-notarize.json
+
+ # Windows signing flow
+ - name: Sign Windows binaries
+ shell: powershell
+ if: runner.os == 'Windows' && inputs.sign != 'false' && inputs.sign-windows-cert != ''
+ working-directory: ${{ inputs.app-working-directory }}
+ run: |
+ echo "Creating certificate file"
+ New-Item -ItemType directory -Path certificate
+ Set-Content -Path certificate\certificate.txt -Value '${{ inputs.sign-windows-cert }}'
+ certutil -decode certificate\certificate.txt certificate\certificate.pfx
+ echo "Signing our binaries"
+ & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ inputs.sign-windows-cert-password }}' .\build\bin\${{ inputs.build-name }}.exe
+ echo "Signing Installer"
+ & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ inputs.sign-windows-cert-password }}' .\build\bin\${{ inputs.build-name }}-amd64-installer.exe
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..e376450
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,15 @@
+# Documentation
+
+Welcome to the documentation for `snider/build@v3`. This repository contains a modular build action for multi-stack projects, primarily focused on Wails v2 but designed to be extensible.
+
+## Contents
+
+- [Getting Started](getting-started.md) - Learn how to use the action in your workflows.
+- [Configuration](configuration.md) - Detailed reference for inputs and environment variables.
+- [Architecture](architecture.md) - Understand the modular design and sub-actions.
+- [Stacks](stacks.md) - Specific information about supported stacks (Wails v2, Deno, etc.).
+
+## Quick Links
+
+- [Root README](../README.md)
+- [Action Definition](../action.yml)
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..373764d
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,32 @@
+# Architecture
+
+`snider/build@v3` is designed as a modular, multi-stack build system.
+
+## Overview
+
+The root action acts as a gateway. It performs discovery and then delegates the heavy lifting to specialized sub-actions.
+
+1. **Discovery**: Scans the repository to identify the OS, architecture, and project type (stack).
+2. **Orchestration**: Decides which specific build pipeline (stack) to run.
+3. **Setup**: Installs dependencies (Go, Node, Deno, etc.) based on the selected stack or configuration.
+4. **Build**: Compiles the application.
+5. **Sign**: Signs the binaries (if configured).
+6. **Package**: Archives the output and uploads it as an artifact (and release asset on tags).
+
+## Directory Structure
+
+- `actions/`
+ - `discovery/`: Detects environment and project metadata.
+ - `options/`: Computes build options (e.g., tags).
+ - `setup/`: Orchestrates dependency installation.
+ - `go/`, `npm/`, `deno/`, `conan/`: Specific setup actions.
+ - `build/`: Contains stack-specific build logic.
+ - `wails2/`: Logic for Wails v2 builds.
+ - `sign/`: Unified signing for macOS and Windows.
+ - `package/`: Handles artifact upload and releases.
+
+## Design Principles
+
+- **Modular**: Each step is a separate composite action. You can use them individually if needed.
+- **Smart Defaults**: The system tries to guess the right thing to do (Auto-Stack, Auto-Setup) but allows full override.
+- **CI Gating**: The repository includes self-tests that run sub-actions to ensure stability.
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..83d1c39
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,58 @@
+# Configuration
+
+This document covers the inputs and configuration options for the `snider/build@v3` action.
+
+## Action Inputs
+
+These inputs are defined in `action.yml` and are passed to the `with` block in your workflow.
+
+| Input | Description | Required | Default |
+| :--- | :--- | :--- | :--- |
+| `build-name` | The name of the binary or app bundle. | **Yes** | - |
+| `build-platform` | Target platform (e.g., `linux/amd64`, `windows/amd64`, `darwin/universal`). | No | `darwin/universal` |
+| `build` | Whether to run the build step. | No | `true` |
+| `package` | Upload artifacts and publish release on tags. | No | `true` |
+| `sign` | Enable platform signing (if configured). | No | `false` |
+| `app-working-directory` | Root directory of the app being built. | No | `.` |
+| `AUTO_STACK` | Allow auto-selection of stack based on discovery. | No | `true` |
+| `AUTO_SETUP` | Allow sub-setup enabling based on env toggles. | No | `true` |
+| `STACK` | Explicitly override the stack (e.g., `wails2`). | No | `""` |
+
+## Environment Variables
+
+The action uses environment variables for certain configurations, particularly for sub-actions like Deno setup.
+
+### Deno Configuration
+
+You can configure Deno usage via environment variables. These take precedence over inputs in the setup phase.
+
+| Variable | Description | Example |
+| :--- | :--- | :--- |
+| `DENO_ENABLE` | Explicitly enable Deno setup. | `true`, `1`, `on` |
+| `DENO_BUILD` | Full command to run for Deno build. | `deno task build` |
+| `DENO_VERSION` | Version of Deno to install. | `v1.44.x` |
+| `DENO_WORKDIR` | Working directory for Deno command. | `frontend` |
+
+### Example with Environment Variables
+
+```yaml
+steps:
+ - uses: actions/checkout@v4
+ - uses: snider/build@v3
+ env:
+ DENO_ENABLE: 'true'
+ DENO_VERSION: 'v1.44.x'
+ DENO_WORKDIR: 'frontend'
+ DENO_BUILD: 'deno task build'
+ with:
+ build-name: wailsApp
+ build-platform: linux/amd64
+```
+
+## Orchestration Control
+
+You can control how the action delegates tasks.
+
+- **Auto-Stack**: By default (`AUTO_STACK: true`), the action tries to detect if you are building a Wails v2 app, etc., and uses the appropriate sub-action.
+- **Auto-Setup**: By default (`AUTO_SETUP: true`), the orchestrator looks at environment variables to decide if it should set up Go, Node, Deno, etc.
+- **Manual Stack**: Set `STACK: wails2` to force the Wails v2 pipeline, ignoring auto-detection.
diff --git a/docs/getting-started.md b/docs/getting-started.md
new file mode 100644
index 0000000..6a02d81
--- /dev/null
+++ b/docs/getting-started.md
@@ -0,0 +1,60 @@
+# Getting Started
+
+This guide will help you get started with the `snider/build@v3` action.
+
+## Basic Usage
+
+The simplest way to use this action is to let it auto-detect your project type.
+
+```yaml
+steps:
+ - uses: actions/checkout@v4
+ - uses: snider/build@v3
+ with:
+ build-name: myApp
+ build-platform: linux/amd64
+```
+
+This configuration will:
+1. Detect your project stack (e.g., Wails v2).
+2. Set up the necessary environment (Go, Node.js, etc.).
+3. Build your application.
+4. Package the artifacts.
+5. Upload artifacts (and publish release on tags).
+
+## Common Examples
+
+### Build Only (No Packaging)
+
+If you only want to verify the build without uploading artifacts:
+
+```yaml
+- uses: snider/build@v3
+ with:
+ build-name: myApp
+ build-platform: linux/amd64
+ package: false
+```
+
+### macOS Build
+
+```yaml
+- uses: snider/build@v3
+ with:
+ build-name: myApp
+ build-platform: darwin/universal
+```
+
+### Windows Build
+
+```yaml
+- uses: snider/build@v3
+ with:
+ build-name: myApp
+ build-platform: windows/amd64
+```
+
+## Next Steps
+
+- Check [Configuration](configuration.md) for more advanced options.
+- Read about [Stacks](stacks.md) for specific stack details.
diff --git a/docs/stacks.md b/docs/stacks.md
new file mode 100644
index 0000000..fc94cff
--- /dev/null
+++ b/docs/stacks.md
@@ -0,0 +1,34 @@
+# Stacks
+
+This action supports multiple stacks, with Wails v2 being the primary one currently.
+
+## Wails v2
+
+This is the default stack. It handles the complete lifecycle of a Wails v2 application.
+
+- **Detection**: Automatically detected if `wails.json` is present (logic in `discovery`).
+- **Setup**: Installs Go, Node.js, and the Wails CLI.
+- **Build**: Runs `wails build` with appropriate flags for the target platform.
+- **Signing**: Supports macOS code signing and notarization (requires secrets) and Windows signing.
+
+For detailed documentation on Wails v2 inputs and usage, refer to:
+- [Wails v2 Wrapper README](../actions/build/wails2/README.md)
+- [Wails Build Sub-action README](../actions/build/wails2/build/README.md)
+
+### macOS Code Signing
+
+Detailed instructions for setting up macOS code signing (Certificate, Notarization) can be found in the [Wails v2 README](../actions/build/wails2/README.md).
+
+## Deno
+
+Deno support is integrated into the setup phase and can be used alongside other stacks or independently.
+
+- **Enable**: Set `DENO_ENABLE: true` or define `DENO_BUILD`.
+- **Usage**: Useful for running frontend build steps before the main application build.
+
+For more details, see [Deno Setup README](../actions/setup/deno/README.md).
+
+## Future Stacks
+
+- **Wails v3**: Planned support once upstream stabilizes.
+- **C++**: Placeholder support exists via `conan` setup action.
diff --git a/tdd/cpp-hello/CMakeLists.txt b/tdd/cpp-hello/CMakeLists.txt
new file mode 100644
index 0000000..81350d6
--- /dev/null
+++ b/tdd/cpp-hello/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.16)
+project(hello VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_executable(hello main.cpp)
diff --git a/tdd/cpp-hello/main.cpp b/tdd/cpp-hello/main.cpp
new file mode 100644
index 0000000..302ae82
--- /dev/null
+++ b/tdd/cpp-hello/main.cpp
@@ -0,0 +1,10 @@
+#include
+
+int main(int argc, char* argv[]) {
+ std::string name = "World";
+ if (argc > 1) {
+ name = argv[1];
+ }
+ std::cout << "Hello, " << name << "!" << std::endl;
+ return 0;
+}
diff --git a/tdd/cpp-root/CMakeLists.txt b/tdd/cpp-root/CMakeLists.txt
new file mode 100644
index 0000000..c91b06a
--- /dev/null
+++ b/tdd/cpp-root/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.16)
+project(CppRootFixture)
+
+add_executable(dummy main.cpp)
diff --git a/tdd/deno-http/deno.json b/tdd/deno-http/deno.json
new file mode 100644
index 0000000..f45c7ee
--- /dev/null
+++ b/tdd/deno-http/deno.json
@@ -0,0 +1,8 @@
+{
+ "name": "hello-http",
+ "version": "1.0.0",
+ "tasks": {
+ "start": "deno run --allow-net --allow-env main.ts",
+ "check": "deno check main.ts"
+ }
+}
diff --git a/tdd/deno-http/main.ts b/tdd/deno-http/main.ts
new file mode 100644
index 0000000..82b47b5
--- /dev/null
+++ b/tdd/deno-http/main.ts
@@ -0,0 +1,10 @@
+const port = parseInt(Deno.env.get("PORT") || "8000");
+
+const handler = (_req: Request): Response => {
+ return new Response("Hello, World!", {
+ headers: { "content-type": "text/plain" },
+ });
+};
+
+console.log(`Server running at http://localhost:${port}`);
+Deno.serve({ port }, handler);
diff --git a/tdd/docs/mkdocs.yml b/tdd/docs/mkdocs.yml
new file mode 100644
index 0000000..55a6dd0
--- /dev/null
+++ b/tdd/docs/mkdocs.yml
@@ -0,0 +1,3 @@
+site_name: Docs Fixture
+nav:
+ - Home: index.md
diff --git a/tdd/go-cli/go.mod b/tdd/go-cli/go.mod
new file mode 100644
index 0000000..4907868
--- /dev/null
+++ b/tdd/go-cli/go.mod
@@ -0,0 +1,3 @@
+module hello
+
+go 1.21
diff --git a/tdd/go-cli/main.go b/tdd/go-cli/main.go
new file mode 100644
index 0000000..b451917
--- /dev/null
+++ b/tdd/go-cli/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ name := "World"
+ if len(os.Args) > 1 {
+ name = os.Args[1]
+ }
+ fmt.Printf("Hello, %s!\n", name)
+}
diff --git a/tdd/go-http/go.mod b/tdd/go-http/go.mod
new file mode 100644
index 0000000..8991ee1
--- /dev/null
+++ b/tdd/go-http/go.mod
@@ -0,0 +1,3 @@
+module hello-http
+
+go 1.21
diff --git a/tdd/go-http/main.go b/tdd/go-http/main.go
new file mode 100644
index 0000000..47d506d
--- /dev/null
+++ b/tdd/go-http/main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+)
+
+func main() {
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Hello, World!")
+ })
+
+ log.Println("Server starting on :8080")
+ log.Fatal(http.ListenAndServe(":8080", nil))
+}
diff --git a/tdd/node-http/index.js b/tdd/node-http/index.js
new file mode 100644
index 0000000..11257a8
--- /dev/null
+++ b/tdd/node-http/index.js
@@ -0,0 +1,11 @@
+const express = require('express');
+const app = express();
+const port = process.env.PORT || 3000;
+
+app.get('/', (req, res) => {
+ res.send('Hello, World!');
+});
+
+app.listen(port, () => {
+ console.log(`Server running at http://localhost:${port}`);
+});
diff --git a/tdd/node-http/package-lock.json b/tdd/node-http/package-lock.json
new file mode 100644
index 0000000..2c6e2c6
--- /dev/null
+++ b/tdd/node-http/package-lock.json
@@ -0,0 +1,827 @@
+{
+ "name": "hello-http",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "hello-http",
+ "version": "1.0.0",
+ "dependencies": {
+ "express": "^4.18.2"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+ "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/tdd/node-http/package.json b/tdd/node-http/package.json
new file mode 100644
index 0000000..26336ad
--- /dev/null
+++ b/tdd/node-http/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "hello-http",
+ "version": "1.0.0",
+ "description": "Node.js HTTP hello world",
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js",
+ "build": "echo 'No build step required'"
+ },
+ "dependencies": {
+ "express": "^4.18.2"
+ }
+}
diff --git a/tdd/node-only/package.json b/tdd/node-only/package.json
new file mode 100644
index 0000000..04770db
--- /dev/null
+++ b/tdd/node-only/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "node-only-fixture",
+ "version": "0.0.1",
+ "private": true
+}
diff --git a/build/README.md b/tdd/wails2-root/README.md
similarity index 100%
rename from build/README.md
rename to tdd/wails2-root/README.md
diff --git a/app.go b/tdd/wails2-root/app.go
similarity index 100%
rename from app.go
rename to tdd/wails2-root/app.go
diff --git a/build/appicon.png b/tdd/wails2-root/appicon.png
similarity index 100%
rename from build/appicon.png
rename to tdd/wails2-root/appicon.png
diff --git a/build/darwin/Info.dev.plist b/tdd/wails2-root/build/darwin/Info.dev.plist
similarity index 100%
rename from build/darwin/Info.dev.plist
rename to tdd/wails2-root/build/darwin/Info.dev.plist
diff --git a/build/darwin/Info.plist b/tdd/wails2-root/build/darwin/Info.plist
similarity index 100%
rename from build/darwin/Info.plist
rename to tdd/wails2-root/build/darwin/Info.plist
diff --git a/build/windows/icon.ico b/tdd/wails2-root/build/windows/icon.ico
similarity index 100%
rename from build/windows/icon.ico
rename to tdd/wails2-root/build/windows/icon.ico
diff --git a/build/windows/info.json b/tdd/wails2-root/build/windows/info.json
similarity index 100%
rename from build/windows/info.json
rename to tdd/wails2-root/build/windows/info.json
diff --git a/build/windows/installer/project.nsi b/tdd/wails2-root/build/windows/installer/project.nsi
similarity index 100%
rename from build/windows/installer/project.nsi
rename to tdd/wails2-root/build/windows/installer/project.nsi
diff --git a/build/windows/installer/wails_tools.nsh b/tdd/wails2-root/build/windows/installer/wails_tools.nsh
similarity index 100%
rename from build/windows/installer/wails_tools.nsh
rename to tdd/wails2-root/build/windows/installer/wails_tools.nsh
diff --git a/build/windows/wails.exe.manifest b/tdd/wails2-root/build/windows/wails.exe.manifest
similarity index 100%
rename from build/windows/wails.exe.manifest
rename to tdd/wails2-root/build/windows/wails.exe.manifest
diff --git a/frontend/index.html b/tdd/wails2-root/frontend/index.html
similarity index 100%
rename from frontend/index.html
rename to tdd/wails2-root/frontend/index.html
diff --git a/frontend/package-lock.json b/tdd/wails2-root/frontend/package-lock.json
similarity index 100%
rename from frontend/package-lock.json
rename to tdd/wails2-root/frontend/package-lock.json
diff --git a/frontend/package.json b/tdd/wails2-root/frontend/package.json
similarity index 100%
rename from frontend/package.json
rename to tdd/wails2-root/frontend/package.json
diff --git a/frontend/package.json.md5 b/tdd/wails2-root/frontend/package.json.md5
similarity index 100%
rename from frontend/package.json.md5
rename to tdd/wails2-root/frontend/package.json.md5
diff --git a/frontend/src/app.css b/tdd/wails2-root/frontend/src/app.css
similarity index 100%
rename from frontend/src/app.css
rename to tdd/wails2-root/frontend/src/app.css
diff --git a/frontend/src/assets/fonts/OFL.txt b/tdd/wails2-root/frontend/src/assets/fonts/OFL.txt
similarity index 100%
rename from frontend/src/assets/fonts/OFL.txt
rename to tdd/wails2-root/frontend/src/assets/fonts/OFL.txt
diff --git a/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 b/tdd/wails2-root/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2
similarity index 100%
rename from frontend/src/assets/fonts/nunito-v16-latin-regular.woff2
rename to tdd/wails2-root/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2
diff --git a/frontend/src/assets/images/logo-universal.png b/tdd/wails2-root/frontend/src/assets/images/logo-universal.png
similarity index 100%
rename from frontend/src/assets/images/logo-universal.png
rename to tdd/wails2-root/frontend/src/assets/images/logo-universal.png
diff --git a/frontend/src/main.js b/tdd/wails2-root/frontend/src/main.js
similarity index 100%
rename from frontend/src/main.js
rename to tdd/wails2-root/frontend/src/main.js
diff --git a/frontend/src/style.css b/tdd/wails2-root/frontend/src/style.css
similarity index 100%
rename from frontend/src/style.css
rename to tdd/wails2-root/frontend/src/style.css
diff --git a/frontend/wailsjs/go/main/App.d.ts b/tdd/wails2-root/frontend/wailsjs/go/main/App.d.ts
similarity index 100%
rename from frontend/wailsjs/go/main/App.d.ts
rename to tdd/wails2-root/frontend/wailsjs/go/main/App.d.ts
diff --git a/frontend/wailsjs/go/main/App.js b/tdd/wails2-root/frontend/wailsjs/go/main/App.js
similarity index 100%
rename from frontend/wailsjs/go/main/App.js
rename to tdd/wails2-root/frontend/wailsjs/go/main/App.js
diff --git a/frontend/wailsjs/runtime/package.json b/tdd/wails2-root/frontend/wailsjs/runtime/package.json
similarity index 100%
rename from frontend/wailsjs/runtime/package.json
rename to tdd/wails2-root/frontend/wailsjs/runtime/package.json
diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/tdd/wails2-root/frontend/wailsjs/runtime/runtime.d.ts
similarity index 100%
rename from frontend/wailsjs/runtime/runtime.d.ts
rename to tdd/wails2-root/frontend/wailsjs/runtime/runtime.d.ts
diff --git a/frontend/wailsjs/runtime/runtime.js b/tdd/wails2-root/frontend/wailsjs/runtime/runtime.js
similarity index 100%
rename from frontend/wailsjs/runtime/runtime.js
rename to tdd/wails2-root/frontend/wailsjs/runtime/runtime.js
diff --git a/go.mod b/tdd/wails2-root/go.mod
similarity index 100%
rename from go.mod
rename to tdd/wails2-root/go.mod
diff --git a/go.sum b/tdd/wails2-root/go.sum
similarity index 100%
rename from go.sum
rename to tdd/wails2-root/go.sum
diff --git a/main.go b/tdd/wails2-root/main.go
similarity index 100%
rename from main.go
rename to tdd/wails2-root/main.go
diff --git a/wails.json b/tdd/wails2-root/wails.json
similarity index 100%
rename from wails.json
rename to tdd/wails2-root/wails.json