name: Build NativeAOT on: push: branches: - main - staging pull_request: branches: - main - staging workflow_dispatch: permissions: contents: read env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_NOLOGO: true PROJECT_PATH: StarterNG/StarterNG.csproj TEST_PROJECT_PATH: StarterNG.Tests/StarterNG.Tests.csproj jobs: # On both systems the packages are built for: paths, encodings and the file # system differ, and that is where these tests earn their keep. test: name: Tests (${{ matrix.name }}) strategy: fail-fast: false matrix: include: - name: Windows x64 os: windows-2022 - name: Linux x64 os: ubuntu-22.04 runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v7 - name: Setup .NET 9 uses: actions/setup-dotnet@v6 with: dotnet-version: 9.0.x - name: Test run: > dotnet test "${{ env.TEST_PROJECT_PATH }}" -c Release # Major and minor are set by hand in the project file; the patch is the number # of commits, so every build of main is higher than the one before and no bot # has to commit a bump back to the branch. version: name: Version runs-on: ubuntu-latest outputs: number: ${{ steps.number.outputs.number }} steps: - name: Checkout repository uses: actions/checkout@v7 with: fetch-depth: 0 - name: Number this build id: number shell: bash run: | set -euo pipefail prefix=$(sed -n 's:.*\([0-9][0-9]*\.[0-9][0-9]*\).*:\1:p' "$PROJECT_PATH") if [ -z "$prefix" ]; then echo "::error file=$PROJECT_PATH::No major.minor in the project file" exit 1 fi number="$prefix.$(git rev-list --count HEAD)" echo "number=$number" >> "$GITHUB_OUTPUT" echo "Version $number" publish: name: ${{ matrix.name }} # A failing test stops the packages, and with them the release. needs: [test, version] strategy: fail-fast: false matrix: include: - name: Windows x64 os: windows-2022 rid: win-x64 artifact: StarterNG-windows-x64 package: artifacts/StarterNG-windows-x64.zip - name: Linux x64 os: ubuntu-22.04 rid: linux-x64 artifact: StarterNG-linux-x64 package: artifacts/StarterNG-linux-x64.tar.gz runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v7 - name: Setup .NET 9 uses: actions/setup-dotnet@v6 with: dotnet-version: 9.0.x # NativeAOT on Linux needs a native compiler/linker toolchain. - name: Install NativeAOT dependencies if: runner.os == 'Linux' shell: bash run: | sudo apt-get update sudo apt-get install -y clang zlib1g-dev - name: Restore run: > dotnet restore "${{ env.PROJECT_PATH }}" -r "${{ matrix.rid }}" - name: Publish NativeAOT run: > dotnet publish "${{ env.PROJECT_PATH }}" -c Release -r "${{ matrix.rid }}" --self-contained true --no-restore -p:PublishAot=true -p:PublishTrimmed=true -p:Version=${{ needs.version.outputs.number }} -o "publish/${{ matrix.rid }}" - name: Package Windows build if: runner.os == 'Windows' shell: pwsh run: | New-Item -ItemType Directory -Force -Path "artifacts" | Out-Null Compress-Archive ` -Path "publish/${{ matrix.rid }}/*" ` -DestinationPath "${{ matrix.package }}" - name: Package Linux build if: runner.os == 'Linux' shell: bash run: | mkdir -p artifacts tar \ -C "publish/${{ matrix.rid }}" \ -czf "${{ matrix.package }}" \ . - name: Upload artifact uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }} path: ${{ matrix.package }} if-no-files-found: error archive: false # Workflow artifacts are only reachable for signed-in users with repository # access, so every build of a release branch also lands in a release, whose # assets anyone can download. main gets a release of its own per build, tagged # with its version - the newest is what the starter checks itself against: # https://github.com/${{ github.repository }}/releases/latest/download/ # staging keeps one rolling pre-release, offered only to starters set to the # testing channel, which tell its builds apart by commit. release: name: Release needs: [publish, version] if: github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: write steps: # v8, not v7: the packages are uploaded with archive: false, and only v8 # can pull those non-zipped artifacts back down. skip-decompress keeps the # Windows .zip a file - unpacked, its contents would end up in the release # instead of the package. - name: Collect the packages uses: actions/download-artifact@v8 with: path: dist merge-multiple: true skip-decompress: true - name: Publish the release if: github.ref_name == 'main' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: v${{ needs.version.outputs.number }} VERSION: ${{ needs.version.outputs.number }} run: | set -euo pipefail ls -l dist # A re-run of the same commit finds its release already there. if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then gh release upload "$TAG" dist/* --repo "$GITHUB_REPOSITORY" --clobber exit 0 fi gh release create "$TAG" \ dist/StarterNG-windows-x64.zip \ dist/StarterNG-linux-x64.tar.gz \ --repo "$GITHUB_REPOSITORY" \ --target "$GITHUB_SHA" \ --title "StarterNG $VERSION" \ --generate-notes \ --latest # Recreated rather than edited, so the tag follows its branch and no asset # from an older build is left behind. - name: Replace the staging pre-release if: github.ref_name == 'staging' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} VERSION: ${{ needs.version.outputs.number }} run: | set -euo pipefail ls -l dist gh release delete staging --repo "$GITHUB_REPOSITORY" --yes --cleanup-tag || true gh release create staging \ dist/StarterNG-windows-x64.zip \ dist/StarterNG-linux-x64.tar.gz \ --repo "$GITHUB_REPOSITORY" \ --target "$GITHUB_SHA" \ --prerelease \ --title "Staging build $VERSION" \ --notes "Automatic build of $GITHUB_REF_NAME (${GITHUB_SHA::7}), $(date -u '+%Y-%m-%d %H:%M UTC')."