Skip to content

feat(deploy): ingress path-split so /api/me routes to auth-api (#281)… #38

feat(deploy): ingress path-split so /api/me routes to auth-api (#281)…

feat(deploy): ingress path-split so /api/me routes to auth-api (#281)… #38

Workflow file for this run

# 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