name: Build Windows EXE

on:
  workflow_dispatch:
    inputs:
      entrypoint:
        description: Python entry point relative to the repository root
        required: true
        default: main.py
        type: string
      app_name:
        description: Executable name without the .exe extension
        required: true
        default: MyApp
        type: string
      windowed:
        description: Hide the console window for a graphical app
        default: false
        type: boolean

permissions:
  contents: read

jobs:
  build:
    runs-on: windows-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: "3.12"
          architecture: x64
      - name: Bundle application
        shell: pwsh
        env:
          ENTRYPOINT: ${{ inputs.entrypoint }}
          APP_NAME: ${{ inputs.app_name }}
          WINDOWED: ${{ inputs.windowed }}
        run: |
          if ($env:ENTRYPOINT -notmatch '^(?:[a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]+\.py$' -or
              -not (Test-Path -LiteralPath $env:ENTRYPOINT -PathType Leaf)) {
            throw 'Use a repository-relative Python file, such as app/main.py.'
          }
          if ($env:APP_NAME -notmatch '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$' -or
              $env:APP_NAME -match '^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])$') {
            throw 'Use a valid Windows file name containing letters, numbers, hyphens or underscores.'
          }
          if (Test-Path -LiteralPath requirements.txt -PathType Leaf) {
            python -m pip install -r requirements.txt
            if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
          }
          python -m pip install 'pyinstaller==6.22.3'
          if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

          $exeName = "$($env:APP_NAME)-windows-x64"
          $bundleArgs = @('--noconfirm', '--onefile', '--name', $exeName, '--distpath', 'windows-output')
          if ($env:WINDOWED -eq 'true') { $bundleArgs += '--windowed' }
          python -m PyInstaller @bundleArgs $env:ENTRYPOINT
          if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
      - uses: actions/upload-artifact@v4
        with:
          name: windows-x64-executable
          path: windows-output/*-windows-x64.exe
          if-no-files-found: error
