feat(deploy): ingress path-split so /api/me routes to auth-api (#281)… #38
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Builds + pushes runtime container images to GHCR on tag, plus the | |
| # per-RID agent binaries attached to the GitHub Release. | |
| # | |
| # Triggered by tags shaped `v*` (e.g. v0.5.0). Tags become the image | |
| # tag, and the always-moving `:latest` label is updated to match. | |
| # | |
| # Container images (per ADR-0023): | |
| # - Satisfactory Web (src/Presentation/Web/Satisfactory.Presentation.Web/Dockerfile) -> ghcr.io/<owner>/erp-web | |
| # - Planner ApiService (src/Presentation/Api/Satisfactory.Presentation.Api/Dockerfile) -> ghcr.io/<owner>/erp-api | |
| # CoI Web image is deliberately not built here yet — agent milestone solves | |
| # catalogue distribution first. | |
| # | |
| # Agent binaries (per ADR-0024 §7): | |
| # - erp-agent-win-x64.zip (Windows service-capable) | |
| # - erp-agent-linux-x64.tar.gz (systemd --user capable) | |
| # Attached to the GitHub Release created from the tag. | |
| name: Release images | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| jobs: | |
| build-push: | |
| name: Build + push ${{ matrix.image }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - image: erp-web | |
| dockerfile: src/Presentation/Web/Satisfactory.Presentation.Web/Dockerfile | |
| - image: erp-api | |
| dockerfile: src/Presentation/Api/Satisfactory.Presentation.Api/Dockerfile | |
| - image: erp-auth-api | |
| dockerfile: src/Presentation/Api/Erp.Presentation.Api.Auth/Dockerfile | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # NB.GV needs git history for version-height calc | |
| - name: Initialize vendored SatisfactorySaveNet submodule | |
| run: git -c submodule.vendor/SatisfactorySaveNet.update=checkout submodule update --init --recursive | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Resolve tags | |
| id: tags | |
| run: | | |
| ref_name="${GITHUB_REF_NAME}" | |
| image_ref="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/${{ matrix.image }}" | |
| # On tag push: <image>:<tag> + :latest. On workflow_dispatch from main: :main + :latest. | |
| if [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| echo "tags=${image_ref}:${ref_name},${image_ref}:latest" >>"$GITHUB_OUTPUT" | |
| else | |
| echo "tags=${image_ref}:${GITHUB_REF_NAME//\//-},${image_ref}:latest" >>"$GITHUB_OUTPUT" | |
| fi | |
| - name: Build + push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| push: true | |
| tags: ${{ steps.tags.outputs.tags }} | |
| # GitHub Packages NuGet auth — nuget.config reads %GITHUB_TOKEN%. | |
| # Passed as a build secret so it never lands in an image layer. | |
| secrets: | | |
| github_token=${{ secrets.GITHUB_TOKEN }} | |
| # GHA cache backend keeps subsequent builds quick across runs. | |
| cache-from: type=gha,scope=${{ matrix.image }} | |
| cache-to: type=gha,scope=${{ matrix.image }},mode=max | |
| # --------------------------------------------------------------------------- | |
| # Agent — per-RID self-contained single-file publish. | |
| # | |
| # Runs on BOTH tag push and workflow_dispatch: | |
| # - Tag push → attach the archive to that tag's release. | |
| # - Dispatch → attach to the most recent existing release (so the Home | |
| # page's `/releases/latest/download/<stable-filename>` link | |
| # just works without waiting for the next tag). | |
| # | |
| # Archive filenames are STABLE (no version in the name) so the website + | |
| # docs can link to a predictable URL. The release page still surfaces the | |
| # version via the release name + commit metadata. | |
| # --------------------------------------------------------------------------- | |
| publish-agent: | |
| name: Publish agent (${{ matrix.rid }}) | |
| runs-on: ${{ matrix.runner }} | |
| permissions: | |
| contents: write # gh release upload | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - rid: win-x64 | |
| runner: windows-latest | |
| archive_ext: zip | |
| - rid: linux-x64 | |
| runner: ubuntu-latest | |
| archive_ext: tar.gz | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # NB.GV needs git history for version-height calc | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ matrix.runner }}-agent-nuget-${{ hashFiles('**/*.csproj', '**/global.json') }} | |
| restore-keys: ${{ matrix.runner }}-agent-nuget- | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| global-json-file: global.json | |
| - name: Publish | |
| # The csproj already locks in PublishSingleFile / SelfContained / R2R; | |
| # this just pins the RID and output dir for this run. | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # nuget.config reads it | |
| run: dotnet publish src/Presentation/Agent/Satisfactory.Presentation.Agent/Satisfactory.Presentation.Agent.csproj -c Release -r ${{ matrix.rid }} -o publish/${{ matrix.rid }} | |
| - name: Pack zip (Windows) | |
| if: matrix.archive_ext == 'zip' | |
| shell: pwsh | |
| run: | | |
| $name = "erp-agent-${{ matrix.rid }}.zip" | |
| Compress-Archive -Path publish/${{ matrix.rid }}/* -DestinationPath $name -Force | |
| "ARCHIVE_NAME=$name" | Out-File -FilePath $env:GITHUB_ENV -Append | |
| - name: Pack tar.gz (Linux) | |
| if: matrix.archive_ext == 'tar.gz' | |
| run: | | |
| name="erp-agent-${{ matrix.rid }}.tar.gz" | |
| tar -czf "$name" -C publish/${{ matrix.rid }} . | |
| echo "ARCHIVE_NAME=$name" >> "$GITHUB_ENV" | |
| - name: Determine target release tag | |
| id: target | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| if [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| # Tag push: attach to that tag's release. | |
| echo "tag=${GITHUB_REF_NAME}" >>"$GITHUB_OUTPUT" | |
| else | |
| # workflow_dispatch: attach to the most recent release. There has | |
| # always been at least one — the `release` job in ci.yml auto-creates | |
| # one on every successful main push. | |
| latest="$(gh release list --limit 1 --json tagName --jq '.[0].tagName')" | |
| if [[ -z "$latest" ]]; then | |
| echo "::error::No existing release found. Push a tag first, then re-dispatch." | |
| exit 1 | |
| fi | |
| echo "tag=${latest}" >>"$GITHUB_OUTPUT" | |
| fi | |
| - name: Attach to release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| tag="${{ steps.target.outputs.tag }}" | |
| # Tag push: GitHub may not have auto-created the release yet, | |
| # depending on ordering. Create idempotently. | |
| gh release view "$tag" >/dev/null 2>&1 \ | |
| || gh release create "$tag" --title "$tag" --notes "See CHANGELOG / commit history. Container images on ghcr.io/${GITHUB_REPOSITORY_OWNER,,}." | |
| gh release upload "$tag" "${ARCHIVE_NAME}" --clobber | |
| # --------------------------------------------------------------------------- | |
| # winget — bump the ErpForFactoryGames.Agent manifest in winget-pkgs. | |
| # | |
| # Bootstrap (first submission) is manual via `wingetcreate submit` against | |
| # the templates in src/Presentation/Agent/Satisfactory.Presentation.Agent/packaging/winget/. From the second release | |
| # onwards this job picks it up: downloads the new zip, recomputes SHA256, | |
| # opens a PR against microsoft/winget-pkgs with the bumped version. | |
| # | |
| # Only runs on tag push — workflow_dispatch doesn't bump the manifest | |
| # because winget needs a 1:1 mapping between PackageVersion and the | |
| # InstallerUrl on the release page. | |
| # | |
| # Needs secret WINGET_SUBMIT_TOKEN (PAT with public_repo scope) — the | |
| # default GITHUB_TOKEN can't push to a fork of microsoft/winget-pkgs. | |
| # --------------------------------------------------------------------------- | |
| publish-winget: | |
| name: Publish winget manifest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: publish-agent | |
| # wingetcreate is Windows-only since Microsoft retired the dotnet-tool | |
| # distribution. windows-latest runners have winget itself pre-installed, | |
| # so we install wingetcreate the canonical way: through winget. | |
| runs-on: windows-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Resolve package version | |
| id: pkg | |
| shell: bash | |
| run: | | |
| tag="${GITHUB_REF_NAME}" | |
| version="${tag#v}" | |
| echo "version=${version}" >>"$GITHUB_OUTPUT" | |
| echo "url=https://github.com/${GITHUB_REPOSITORY}/releases/download/${tag}/erp-agent-win-x64.zip" >>"$GITHUB_OUTPUT" | |
| - name: Submit manifest update to winget-pkgs | |
| env: | |
| WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_SUBMIT_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| if (-not $env:WINGET_CREATE_GITHUB_TOKEN) { | |
| Write-Host "::warning::WINGET_SUBMIT_TOKEN not set — skipping winget manifest update." | |
| Write-Host "Add a PAT with public_repo scope as repo secret WINGET_SUBMIT_TOKEN to enable." | |
| exit 0 | |
| } | |
| winget install wingetcreate ` | |
| --accept-package-agreements ` | |
| --accept-source-agreements ` | |
| --silent ` | |
| --disable-interactivity | |
| wingetcreate update ErpForFactoryGames.Agent ` | |
| --urls "${{ steps.pkg.outputs.url }}" ` | |
| --version "${{ steps.pkg.outputs.version }}" ` | |
| --submit |