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 +[![CI](https://github.com/snider/wails-build-action/actions/workflows/ci.yml/badge.svg)](https://github.com/snider/wails-build-action/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-EUPL-green.svg)](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