name: Release # Three-tier push model: dev build, beta, release. # Since 2026-11-03 PlatinVault is open source (AGPL-3.0-only) in ONE public repository: source code, customer files and every # release live here (the former private source repo and the separate customer repo were merged). # v2026.11.12.13.557-beta -> beta (replaces the former release candidate; no counter, the five segments are unique per commit): # builds the image (version tag + moving tag "beta"), public GitHub pre-release in this repository # v2026.11.12 -> release: same build, version tag + "latest" + "beta", normal public GitHub Release # Release notes (beta and release): only this version's entry of CHANGELOG.md - its English and its German block, found by the readable # version in the heading ("## v2026.11.12 (13.557 β)" for a beta, "## v2026.11.12 (13.557)" for a release), extracted by # scripts/release-notes.sh. A missing entry stops the run. # Image tags for customers (switch "Beta-Versionen erhalten", user decision 2026-11-02): ":latest" = releases only, ":beta" = the newest of # betas AND releases - so a release moves "beta" too (a release is always a later commit than the betas before it). # Tags and image labels use the technical form (no spaces, parentheses or the beta sign); people see the readable form # "v2026.11.12 (13.557 β)" (release: "v2026.11.12 (13.557)") in the release title and the app - scripts/version-display.sh is the single # definition for this workflow, src/PlatinVault.Versioning/VersionFormat.cs for the programs. # The target is not part of the tag name: the annotated tag message carries a line "platform: unraid" (ids unraid, # windows-server, windows-desktop, comma-separated, or all); no such line means every target. unraid/all/empty build the # Docker image + Unraid template (job "build"); windows-desktop/all/empty build the Windows installer (job "windows"). # # Third kind, "Dev-Build" (user decision 2026-11-02): the real package for the user's own testing on his Unraid / Windows PC, never published. # v2026.11.12.13.557-dev -> dev build (five-segment id, no counter; checked against version.txt line 2 like a beta): # image: version tag + moving tag "dev" (never "beta"/"latest"); the Unraid template for it (PlatinVault-Dev, # own appdata and port) is only a workflow artifact; Windows: the installer is attached to a DRAFT release in # this repository (drafts are only visible to collaborators with write access, and the update check # never sees them). The release notes are not taken from any changelog file (a dev build has no # customer-facing notes). # Note: the GHCR package itself is public (so Unraid can pull it without a login), hence ":dev" is technically pullable by anyone. # # Draft first (2026-11-03): the "build" job always creates the GitHub release as a DRAFT, the "windows" job attaches the installer to that # draft, and only the last job "publish" turns it into a pre-release (beta) or a normal release - after every selected platform job # succeeded and every expected file is attached. So a release is never visible (nor offered by the update check, which only sees # published releases) without its installer. A dev build stays a draft. Every release body ends with a rule and the line # "Code signing policy: #code-signing-policy" (SignPath Foundation terms: the term "Code signing policy" on every # download/release page). # # Code signing of the Windows files (SignPath Foundation, free for open source): two stages, because the installer carries the program as # a ZIP appended to the setup exe - stage 1 signs our own binaries of the program folder and the bridge # (.signpath/artifact-configurations/windows-app.xml), they are packed into the installer, stage 2 signs the installer (windows-setup.xml). # Every signing request waits for a manual approval in SignPath (up to 60 minutes per stage). Signing runs only when the repository # variables SIGNPATH_ORGANIZATION_ID, SIGNPATH_PROJECT_SLUG, SIGNPATH_SIGNING_POLICY_SLUG and the secret SIGNPATH_API_TOKEN exist, and only # for betas and releases (dev builds only with the variable SIGNPATH_SIGN_DEV=true). Without them the result is the unsigned installer # exactly as before ("signing skipped (SignPath not configured)" in the log). Optional variables: SIGNPATH_APP_ARTIFACT_CONFIGURATION_SLUG # (default windows-app), SIGNPATH_SETUP_ARTIFACT_CONFIGURATION_SLUG (default windows-setup). All jobs run on GitHub-hosted runners # (SignPath's origin verification for open-source projects requires that). on: push: tags: - "v*" env: IMAGE_NAME: ghcr.io/sushiflux/platinvault jobs: build: runs-on: ubuntu-latest outputs: tag: ${{ steps.version.outputs.tag }} is_beta: ${{ steps.version.outputs.is_beta }} is_dev: ${{ steps.version.outputs.is_dev }} kind: ${{ steps.version.outputs.kind }} builds_windows: ${{ steps.version.outputs.builds_windows }} bridge_version: ${{ steps.version.outputs.bridge_version }} release_name: ${{ steps.version.outputs.release_name }} display_version: ${{ steps.version.outputs.display_version }} has_release: ${{ steps.version.outputs.has_release }} builds_unraid: ${{ steps.version.outputs.builds_unraid }} permissions: contents: write packages: write steps: - name: Repo auschecken uses: actions/checkout@v7 with: # Full history: the bridge version 1.0. counts the commits of the bridge files fetch-depth: 0 - name: Version und Ziel aus dem Tag ableiten id: version run: | tag="${GITHUB_REF_NAME}" is_beta=false is_dev=false if [[ "$tag" == *-beta ]]; then is_beta=true elif [[ "$tag" == *-dev ]]; then is_dev=true elif [[ "$tag" == *-rc* ]]; then echo "::error::Release candidates were replaced by betas (tag v....-beta); $tag cannot be built any more" exit 1 fi # actions/checkout may have flattened the annotated tag - re-fetch the real tag object for its message git fetch --force origin "refs/tags/${tag}:refs/tags/${tag}" || true platform="$(git tag -l --format='%(contents)' "$tag" | sed -n 's/^platform:[[:space:]]*//p' | head -n1 | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" builds_unraid=false case ",${platform}," in ",,"|*,unraid,*|*,all,*) builds_unraid=true ;; esac builds_windows=false case ",${platform}," in ",,"|*,windows-desktop,*|*,all,*) builds_windows=true ;; esac echo "Build-Ziel: platform='${platform:-alle}' builds_unraid=$builds_unraid builds_windows=$builds_windows" target_label="" if [[ -z "$platform" || ",${platform}," == *,all,* ]]; then target_label="All" else IFS=',' read -ra ids <<< "$platform" for id in "${ids[@]}"; do case "$id" in unraid) name="Unraid" ;; windows-server) name="Windows Server" ;; windows-desktop) name="Windows Desktop" ;; *) name="$id" ;; esac target_label="${target_label:+$target_label, }$name" done fi # version.txt is written into every commit by the pre-commit hook: line 1 = vYear.Month.Commit, line 2 = the # five-segment build id. The app derives its readable version ("v2026.11.12 (13.557 β)") from both lines plus the channel. short_version="$(sed -n '1p' version.txt | tr -d '\r[:space:]')" detail_version="$(sed -n '2p' version.txt | tr -d '\r[:space:]')" if [[ -z "$detail_version" ]]; then detail_version="$short_version"; fi kind=release if [[ "$is_beta" == "true" ]]; then kind=beta; fi if [[ "$is_dev" == "true" ]]; then kind=dev; fi # The tag must be the technical form of this commit's version: a beta/dev tag carries the five-segment build id # (v2026.11.12.13.557-beta / -dev), a release tag the plain version (v2026.11.12) expected_tag="$(sh scripts/version-display.sh tag "$short_version" "$detail_version" "$kind")" if [[ "$tag" != "$expected_tag" ]]; then echo "::error::Tag $tag passt nicht zu $expected_tag (version.txt)" exit 1 fi display_version="$(sh scripts/version-display.sh display "$short_version" "$detail_version" "$kind")" bridge_count="$(git rev-list --count HEAD -- src/PlatinVault.Bridge src/Companion.Api/Services/SteamLocalStatsService.cs)" channel=release # The release template points at the moving tag "latest" (a fixed version tag would never show an update in Unraid) template_tag=latest # Release notes: this version's entry of CHANGELOG.md (English, rule, German). A dev build has none, its draft gets a one-line text. notes_file=dist/release-notes.md mkdir -p dist if [[ "$is_beta" == "true" ]]; then channel=beta # The beta template points at the moving tag "beta": the test container is updated in place by Unraid's own update template_tag=beta elif [[ "$is_dev" == "true" ]]; then channel=dev # Same for the dev template and its moving tag "dev" (the image is never tagged beta/latest) template_tag=dev fi if [[ "$is_dev" == "true" ]]; then printf '%s\n' "Dev build for internal testing only - not a release, not offered as an update." > "$notes_file" elif ! sh scripts/release-notes.sh CHANGELOG.md "$display_version" > "$notes_file"; then echo "::error::CHANGELOG.md hat keinen Eintrag \"## $display_version\" (Englisch und Deutsch) - Changelog beim Commit schreiben (Überschrift ## vNEXT bzw. ## vNEXT β)" exit 1 fi # Footer of every release body (SignPath Foundation terms: "Code signing policy" on every download/release page). The app shows # the release notes too and knows this footer (the last block, after a "---" rule). printf '\n---\n\nCode signing policy: %s/%s#code-signing-policy\n' "$GITHUB_SERVER_URL" "$GITHUB_REPOSITORY" >> "$notes_file" echo "Release-Notizen ($(wc -l < "$notes_file") Zeilen):" cat "$notes_file" # Is there a GitHub release at all? A dev build only gets one when there is a Windows installer to attach to it has_release=true if [[ "$is_dev" == "true" && "$builds_windows" != "true" ]]; then has_release=false; fi { echo "tag=$tag" echo "is_beta=$is_beta" echo "is_dev=$is_dev" echo "kind=$kind" echo "notes_file=$notes_file" echo "has_release=$has_release" echo "display_version=$display_version" echo "builds_unraid=$builds_unraid" echo "builds_windows=$builds_windows" echo "docker_tag=${tag#v}" echo "template_tag=$template_tag" echo "app_version=$short_version" echo "created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "app_version_detail=$detail_version" echo "app_channel=$channel" echo "bridge_version=1.0.$bridge_count" # The release title is the readable version, then the target (same shape as the changelog heading): # "v2026.11.12 (13.557 β) · Unraid, Windows"; the middle dot is written as its UTF-8 bytes echo "release_name=$display_version $(printf '\302\267') $target_label" } >> "$GITHUB_OUTPUT" - name: Docker Buildx einrichten if: steps.version.outputs.builds_unraid == 'true' uses: docker/setup-buildx-action@v4 - name: Bei GHCR anmelden if: steps.version.outputs.builds_unraid == 'true' uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Image bauen und pushen if: steps.version.outputs.builds_unraid == 'true' uses: docker/build-push-action@v7 with: context: . file: Dockerfile push: true build-args: | APP_VERSION=${{ steps.version.outputs.app_version }} APP_VERSION_DETAIL=${{ steps.version.outputs.app_version_detail }} APP_CHANNEL=${{ steps.version.outputs.app_channel }} BRIDGE_VERSION=${{ steps.version.outputs.bridge_version }} # Update tools on the server (e.g. ShipLog, Docker Versions) read these labels: the source points at this (public) repository, # where every beta/release is published with its notes; the version is the tag (technical form, five segments for a beta) labels: | org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.version=${{ steps.version.outputs.tag }} org.opencontainers.image.created=${{ steps.version.outputs.created }} org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.title=PlatinVault # "beta" moves with every beta AND every release (":beta" = newest of both), "latest" only with a release; a dev build moves # only its own tag "dev" (the kind is release / beta / dev) tags: | ${{ env.IMAGE_NAME }}:${{ steps.version.outputs.docker_tag }} ${{ steps.version.outputs.kind == 'dev' && format('{0}:dev', env.IMAGE_NAME) || format('{0}:beta', env.IMAGE_NAME) }} ${{ steps.version.outputs.kind == 'release' && format('{0}:latest', env.IMAGE_NAME) || '' }} - name: Unraid-Template rendern if: steps.version.outputs.builds_unraid == 'true' run: | mkdir -p dist template=dist/platinvault.xml # unraid/platinvault.xml in the repository is the RELEASE form (":latest" plus the "beta" tag choice) - the raw file customers # fetch with wget; the beta and dev templates are derived from it here grep -q 'ghcr.io/sushiflux/platinvault:latest' unraid/platinvault.xml grep -q 'https://raw.githubusercontent.com/sushiflux/PlatinVault/master/unraid/platinvault.xml' unraid/platinvault.xml sed "s#ghcr.io/sushiflux/platinvault:latest#ghcr.io/sushiflux/platinvault:${{ steps.version.outputs.template_tag }}#" \ unraid/platinvault.xml > "$template" if [[ "${{ steps.version.outputs.is_beta }}" == "true" ]]; then # The beta template (hand-copied to the own Unraid for testing) has no tag choice: its default already is ":beta". It has no # TemplateURL either (see the check at the end of this step). sed -i -e '//d' -e '//,/<\/Branch>/d' -e '//d' "$template" fi if [[ "${{ steps.version.outputs.is_dev }}" == "true" ]]; then # The dev template can run next to a real installation: own container name, own appdata folder (its own SQLite database - # a dev build may be ahead of the live schema) and its own host port (5239; the live template uses 5233). No tag choice: # its default already is ":dev". It is only a workflow artifact (the user copies it to his own Unraid) - never published anywhere. template=dist/PlatinVault-Dev.xml mv dist/platinvault.xml "$template" sed -i \ -e '//d' -e '//,/<\/Branch>/d' -e '//d' \ -e 's#PlatinVault#PlatinVault-Dev#' \ -e 's#/mnt/cache/appdata/platinvault#/mnt/cache/appdata/platinvault-dev#g' \ -e '/Name="WebUI Port"/s/5233/5239/g' \ -e 's##DEV BUILD for the developer'"'"'s own testing - not for everyday use. It keeps its own data (database, backups, keys) in its own appdata folder, apart from a real installation. / DEV-BUILD nur zum eigenen Testen des Entwicklers - nicht fuer den Alltag. Er hat seine eigenen Daten (Datenbank, Sicherungen, Schluessel) in einem eigenen Appdata-Ordner, getrennt von einer echten Installation.\n\n#' \ "$template" grep -q 'PlatinVault-Dev' "$template" grep -q 'Default="5239"' "$template" grep -q 'Default="/mnt/cache/appdata/platinvault-dev"' "$template" grep -q '>/mnt/cache/appdata/platinvault-dev<' "$template" # Neither the live appdata folder nor any database setting may be left in the dev template if grep -qE '/mnt/(cache|user)/appdata/platinvault(<|")' "$template"; then echo "::error::The dev template still points at the live appdata folder" exit 1 fi if grep -qiE 'Companion__Database|PostgreSQL' "$template"; then echo "::error::The dev template still contains database settings" exit 1 fi fi # Only the release template (the raw file in the repository) carries the TemplateURL: Community Applications matches an # installed template to its feed entry by that URL, and Unraid's (today disabled) template refresh would copy names, defaults and # descriptions from it - a beta/dev container must never be taken for the release one or get its appdata path and port back. if [[ "${{ steps.version.outputs.kind }}" != "release" ]] && grep -q '' "$template"; then echo "::error::A beta/dev template must not carry the TemplateURL of the release template" exit 1 fi grep -q "ghcr.io/sushiflux/platinvault:${{ steps.version.outputs.template_tag }}" "$template" # The dev template is not attached to any release: it is a workflow artifact of this run (private repository, 30 days) - name: Dev-Template als Workflow-Artefakt ablegen if: steps.version.outputs.builds_unraid == 'true' && steps.version.outputs.is_dev == 'true' uses: actions/upload-artifact@v7 with: name: PlatinVault-Dev-template path: dist/PlatinVault-Dev.xml retention-days: 30 if-no-files-found: error # Always created as a DRAFT (draft first): the "windows" job attaches its files, the "publish" job publishes it at the very end. # A dev build only gets a (draft) release when there is a Windows installer to attach to it, and it stays a draft. - name: GitHub Release als Entwurf erstellen if: steps.version.outputs.has_release == 'true' uses: softprops/action-gh-release@v3 with: tag_name: ${{ steps.version.outputs.tag }} name: ${{ steps.version.outputs.release_name }} draft: true prerelease: ${{ steps.version.outputs.is_beta }} body_path: ${{ steps.version.outputs.notes_file }} files: ${{ steps.version.outputs.builds_unraid == 'true' && steps.version.outputs.is_dev != 'true' && 'dist/platinvault.xml' || '' }} # Windows program: the designed installer PlatinVault-Setup-.exe (scripts/build-windows.ps1, in stages). Built on Windows because # the program and the installer are WinForms exes (the database is SQLite inside the program, nothing else is bundled or compiled). # Runs after "build" (which created the draft release) and attaches the installer, its .sha256 and the bridge exe to it - signed when # SignPath is configured (see the top of this file), otherwise unsigned as before. windows: needs: build if: needs.build.outputs.builds_windows == 'true' runs-on: windows-latest permissions: contents: write # The SignPath action reads this run's job details and downloads the uploaded artifact with the GitHub token actions: read steps: - name: Repo auschecken uses: actions/checkout@v7 with: # Full history: the bridge version 1.0. counts the commits of the bridge files fetch-depth: 0 - name: .NET einrichten uses: actions/setup-dotnet@v6 with: global-json-file: global.json - name: Node einrichten uses: actions/setup-node@v7 with: node-version: 26 # Signing is switched on by the repository settings alone - nothing to change here once SignPath has approved the project - name: Signieren? (SignPath) id: signing shell: bash env: ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }} PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }} POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} HAS_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN != '' }} SIGN_DEV: ${{ vars.SIGNPATH_SIGN_DEV }} KIND: ${{ needs.build.outputs.kind }} run: | sign=false if [[ -z "$ORGANIZATION_ID" || -z "$PROJECT_SLUG" || -z "$POLICY_SLUG" || "$HAS_TOKEN" != "true" ]]; then echo "signing skipped (SignPath not configured): the Windows files stay unsigned" echo "::notice::signing skipped (SignPath not configured) - the Windows files of this build are unsigned" elif [[ "$KIND" == "dev" && "$SIGN_DEV" != "true" ]]; then echo "signing skipped (dev build; the repository variable SIGNPATH_SIGN_DEV=true signs dev builds too)" echo "::notice::signing skipped (dev build) - the Windows files of this build are unsigned" else sign=true echo "Signing with SignPath (project $PROJECT_SLUG, policy $POLICY_SLUG): two signing requests, each waits for an approval in SignPath" fi echo "sign=$sign" >> "$GITHUB_OUTPUT" # Stage 1: UI, bridge and the program folder; with signing also the folder of our own binaries for SignPath (to-sign/app) - name: Windows-Programm bauen (Stufe 1) shell: powershell env: # Readable version (product version in the file properties) - passed through the environment, so the beta sign survives PV_PRODUCT_VERSION: ${{ needs.build.outputs.display_version }} run: | $buildArgs = @{ Stage = 'app'; BridgeVersion = '${{ needs.build.outputs.bridge_version }}'; ProductVersion = $env:PV_PRODUCT_VERSION } if ('${{ needs.build.outputs.is_beta }}' -eq 'true') { $buildArgs['Beta'] = $true } if ('${{ needs.build.outputs.is_dev }}' -eq 'true') { $buildArgs['Dev'] = $true } if ('${{ steps.signing.outputs.sign }}' -eq 'true') { $buildArgs['PrepareSigning'] = $true } .\scripts\build-windows.ps1 @buildArgs # The artifact must be a workflow artifact of this run (SignPath's origin verification); the default ZIP matches the root # of the artifact configuration. Names differ per stage (upload-artifact fails on the same file name twice in one run). - name: Programmdateien fuer die Signatur hochladen id: upload-app if: steps.signing.outputs.sign == 'true' uses: actions/upload-artifact@v7 with: name: windows-app-unsigned path: artifacts/windows-cache/to-sign/app if-no-files-found: error retention-days: 7 - name: Warte auf die Freigabe in SignPath (Stufe 1 von 2) if: steps.signing.outputs.sign == 'true' shell: bash run: | echo "::notice::Signing request 1 of 2 (program files: PlatinVault.exe, server, bridge) waits for an approval in SignPath (up to 60 minutes) - approve it at https://app.signpath.io" - name: Programmdateien signieren (SignPath, Stufe 1) id: sign-app if: steps.signing.outputs.sign == 'true' uses: signpath/github-action-submit-signing-request@v3 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }} signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} artifact-configuration-slug: ${{ vars.SIGNPATH_APP_ARTIFACT_CONFIGURATION_SLUG || 'windows-app' }} github-artifact-id: ${{ steps.upload-app.outputs.artifact-id }} wait-for-completion: true wait-for-completion-timeout-in-seconds: 3600 output-artifact-directory: artifacts/windows-cache/signed/app parameters: | productVersion: ${{ toJSON(needs.build.outputs.display_version) }} # Stage 2: the (signed) program into the installer; the .sha256 is written again after the installer itself is signed - name: Installer bauen (Stufe 2) shell: powershell env: PV_PRODUCT_VERSION: ${{ needs.build.outputs.display_version }} run: | $buildArgs = @{ Stage = 'setup'; ProductVersion = $env:PV_PRODUCT_VERSION } if ('${{ needs.build.outputs.is_beta }}' -eq 'true') { $buildArgs['Beta'] = $true } if ('${{ needs.build.outputs.is_dev }}' -eq 'true') { $buildArgs['Dev'] = $true } if ('${{ steps.signing.outputs.sign }}' -eq 'true') { $buildArgs['SignedAppDir'] = 'artifacts\windows-cache\signed\app' } .\scripts\build-windows.ps1 @buildArgs - name: Installer fuer die Signatur hochladen id: upload-setup if: steps.signing.outputs.sign == 'true' uses: actions/upload-artifact@v7 with: name: windows-setup-unsigned path: artifacts/windows/PlatinVault-Setup-*.exe if-no-files-found: error retention-days: 7 - name: Warte auf die Freigabe in SignPath (Stufe 2 von 2) if: steps.signing.outputs.sign == 'true' shell: bash run: | echo "::notice::Signing request 2 of 2 (the installer) waits for an approval in SignPath (up to 60 minutes) - approve it at https://app.signpath.io" - name: Installer signieren (SignPath, Stufe 2) id: sign-setup if: steps.signing.outputs.sign == 'true' uses: signpath/github-action-submit-signing-request@v3 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }} signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }} artifact-configuration-slug: ${{ vars.SIGNPATH_SETUP_ARTIFACT_CONFIGURATION_SLUG || 'windows-setup' }} github-artifact-id: ${{ steps.upload-setup.outputs.artifact-id }} wait-for-completion: true wait-for-completion-timeout-in-seconds: 3600 output-artifact-directory: artifacts/windows-cache/signed/setup parameters: | productVersion: ${{ toJSON(needs.build.outputs.display_version) }} # Final files: the signed installer replaces the built one, the payload must still be found, the signatures of the installer, # PlatinVault.exe and the bridge must be "Valid" (otherwise the job fails), and the .sha256 comes from the final installer - name: Endgueltige Dateien pruefen (Signatur, SHA-256) shell: powershell run: | $buildArgs = @{ Stage = 'checksum' } if ('${{ needs.build.outputs.is_beta }}' -eq 'true') { $buildArgs['Beta'] = $true } if ('${{ needs.build.outputs.is_dev }}' -eq 'true') { $buildArgs['Dev'] = $true } if ('${{ steps.signing.outputs.sign }}' -eq 'true') { $buildArgs['SignedSetup'] = 'artifacts\windows-cache\signed\setup' $buildArgs['RequireSignature'] = $true } .\scripts\build-windows.ps1 @buildArgs Get-ChildItem artifacts\windows | Format-Table Name, Length # Attached to the DRAFT the "build" job created; the "publish" job publishes it after all platform jobs succeeded - name: Installer an den Release-Entwurf haengen uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.build.outputs.tag }} draft: true prerelease: ${{ needs.build.outputs.is_beta }} fail_on_unmatched_files: true files: | artifacts/windows/PlatinVault-Setup-*.exe artifacts/windows/PlatinVault-Setup-*.exe.sha256 artifacts/windows/PlatinVault.Bridge.exe # Publishes the draft (beta: pre-release, release: normal release and "Latest") only after every selected platform job succeeded and # only when every expected file is attached. A dev build is never published (its draft stays for the user's own download). publish: needs: [build, windows] if: >- always() && needs.build.result == 'success' && needs.build.outputs.is_dev != 'true' && needs.build.outputs.has_release == 'true' && (needs.windows.result == 'success' || needs.windows.result == 'skipped') runs-on: ubuntu-latest permissions: contents: write steps: - name: Release veroeffentlichen env: GH_TOKEN: ${{ github.token }} TAG: ${{ needs.build.outputs.tag }} IS_BETA: ${{ needs.build.outputs.is_beta }} BUILDS_WINDOWS: ${{ needs.build.outputs.builds_windows }} BUILDS_UNRAID: ${{ needs.build.outputs.builds_unraid }} WINDOWS_RESULT: ${{ needs.windows.result }} run: | # A Windows build that was asked for must have run (skipped here would mean it never started) if [[ "$BUILDS_WINDOWS" == "true" && "$WINDOWS_RESULT" != "success" ]]; then echo "::error::The Windows build did not succeed ($WINDOWS_RESULT) - the release stays a draft" exit 1 fi assets="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name')" echo "Dateien am Entwurf:" echo "$assets" missing="" expect() { grep -qxF "$1" <<< "$assets" || missing="$missing $1"; } if [[ "$BUILDS_UNRAID" == "true" ]]; then expect platinvault.xml; fi if [[ "$BUILDS_WINDOWS" == "true" ]]; then expect "PlatinVault-Setup-${TAG}.exe" expect "PlatinVault-Setup-${TAG}.exe.sha256" expect PlatinVault.Bridge.exe fi if [[ -n "$missing" ]]; then echo "::error::Missing on the draft:$missing - the release stays a draft" exit 1 fi if [[ "$IS_BETA" == "true" ]]; then gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false --prerelease else gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false --prerelease=false --latest fi gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json name,isDraft,isPrerelease,url --jq '"\(.name): draft=\(.isDraft) prerelease=\(.isPrerelease) \(.url)"'