chore: Layer 3 polish — SHA pinning, modern C# rules, metadata, trim/AOT #66
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
| name: CI and Release | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| prerelease_suffix: | |
| description: 'Pre-release channel suffix (e.g. rc.1, beta.2). Leave empty for standard CI build.' | |
| required: false | |
| default: '' | |
| permissions: | |
| contents: read | |
| checks: write | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| DOTNET_NOLOGO: true | |
| DOTNET_CLI_TELEMETRY_OPTOUT: true | |
| NUKE_TELEMETRY_OPTOUT: true | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| ENABLE_ANDROID: 'false' | |
| ENABLE_IOS: 'false' | |
| jobs: | |
| resolve-version: | |
| name: Resolve Version | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| version: ${{ steps.resolve.outputs.version }} | |
| full_version: ${{ steps.resolve.outputs.full_version }} | |
| version_suffix: ${{ steps.resolve.outputs.version_suffix }} | |
| tag: ${{ steps.resolve.outputs.tag }} | |
| sha: ${{ steps.resolve.outputs.sha }} | |
| is_release: ${{ steps.resolve.outputs.is_release }} | |
| build_matrix: ${{ steps.matrix.outputs.build_matrix }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-tags: true | |
| - name: Resolve version and context | |
| id: resolve | |
| shell: bash | |
| env: | |
| PRERELEASE_SUFFIX: ${{ inputs.prerelease_suffix }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| GIT_REF: ${{ github.ref }} | |
| RUN_NUMBER: ${{ github.run_number }} | |
| run: | | |
| SEMVER_REGEX='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' | |
| VERSION=$(perl -0777 -ne 'if (m{<VersionPrefix>\s*([^<]+?)\s*</VersionPrefix>}s) { print $1 }' Directory.Build.props | head -n1 | xargs) | |
| if [ -z "$VERSION" ]; then | |
| echo "::error::VersionPrefix not found in Directory.Build.props" | |
| exit 1 | |
| fi | |
| if ! [[ "$VERSION" =~ $SEMVER_REGEX ]]; then | |
| echo "::error::Invalid VersionPrefix: $VERSION" | |
| exit 1 | |
| fi | |
| SHA="$(git rev-parse HEAD)" | |
| TAG="v$VERSION" | |
| IS_RELEASE="false" | |
| if [ "$EVENT_NAME" != "pull_request" ] && [ "$GIT_REF" = "refs/heads/main" ]; then | |
| git fetch --tags --force | |
| if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then | |
| echo "::notice::Tag $TAG already exists. Version not bumped — skipping release." | |
| else | |
| IS_RELEASE="true" | |
| fi | |
| fi | |
| VERSION_SUFFIX="" | |
| if [ -n "$PRERELEASE_SUFFIX" ]; then | |
| if ! [[ "$PRERELEASE_SUFFIX" =~ ^[0-9A-Za-z.-]+$ ]]; then | |
| echo "::error::Invalid prerelease_suffix: must match [0-9A-Za-z.-]+" | |
| exit 1 | |
| fi | |
| VERSION_SUFFIX="$PRERELEASE_SUFFIX" | |
| elif [ "$IS_RELEASE" != "true" ]; then | |
| VERSION_SUFFIX="ci.${RUN_NUMBER}" | |
| fi | |
| if [ -n "$VERSION_SUFFIX" ]; then | |
| FULL_VERSION="${VERSION}-${VERSION_SUFFIX}" | |
| else | |
| FULL_VERSION="${VERSION}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "full_version=$FULL_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "version_suffix=$VERSION_SUFFIX" >> "$GITHUB_OUTPUT" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "sha=$SHA" >> "$GITHUB_OUTPUT" | |
| echo "is_release=$IS_RELEASE" >> "$GITHUB_OUTPUT" | |
| echo "Resolved: version=$VERSION full_version=$FULL_VERSION tag=$TAG sha=$SHA is_release=$IS_RELEASE" | |
| - name: Version summary | |
| shell: bash | |
| run: | | |
| VERSION="${{ steps.resolve.outputs.version }}" | |
| FULL_VERSION="${{ steps.resolve.outputs.full_version }}" | |
| VERSION_SUFFIX="${{ steps.resolve.outputs.version_suffix }}" | |
| IS_RELEASE="${{ steps.resolve.outputs.is_release }}" | |
| SHA="${{ steps.resolve.outputs.sha }}" | |
| TAG="${{ steps.resolve.outputs.tag }}" | |
| IFS='.' read -r CUR_MAJOR CUR_MINOR CUR_PATCH <<< "$VERSION" | |
| PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -n "$PREV_TAG" ]; then | |
| PREV_VER="${PREV_TAG#v}" | |
| IFS='.' read -r PRE_MAJOR PRE_MINOR PRE_PATCH <<< "$PREV_VER" | |
| if [ "$CUR_MAJOR" != "$PRE_MAJOR" ]; then | |
| BUMP="MAJOR" | |
| elif [ "$CUR_MINOR" != "$PRE_MINOR" ]; then | |
| BUMP="MINOR" | |
| elif [ "$CUR_PATCH" != "$PRE_PATCH" ]; then | |
| BUMP="PATCH" | |
| else | |
| BUMP="NONE (same as $PREV_TAG)" | |
| fi | |
| else | |
| PREV_TAG="(none)" | |
| BUMP="INITIAL" | |
| fi | |
| MODE=$( [ "$IS_RELEASE" = "true" ] && echo "Release" || echo "CI" ) | |
| CHANNEL="stable" | |
| if [ -n "$VERSION_SUFFIX" ]; then | |
| CHANNEL="$VERSION_SUFFIX" | |
| fi | |
| REPO="${{ github.repository }}" | |
| OWNER="${REPO%%/*}" | |
| REPO_NAME="${REPO##*/}" | |
| DOCS_URL="https://${OWNER,,}.github.io/${REPO_NAME}/" | |
| { | |
| echo "## Version Info" | |
| echo "" | |
| echo "| Property | Value |" | |
| echo "|----------|-------|" | |
| echo "| **Version** | \`$VERSION\` |" | |
| echo "| **Full Version** | \`$FULL_VERSION\` |" | |
| echo "| **Channel** | \`$CHANNEL\` |" | |
| echo "| **Tag** | \`$TAG\` |" | |
| echo "| **Previous Tag** | \`$PREV_TAG\` |" | |
| echo "| **Bump Type** | **$BUMP** |" | |
| echo "| **Mode** | $MODE |" | |
| echo "| **Commit** | \`${SHA::8}\` |" | |
| echo "| **Expected Docs URL** | $DOCS_URL |" | |
| echo "| **Docs Auto-Deploy** | Enable Pages (GitHub Actions) + add \`docs/package.json\` |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Compute build matrix | |
| id: matrix | |
| shell: bash | |
| run: | | |
| IS_RELEASE="${{ steps.resolve.outputs.is_release }}" | |
| if [ "$IS_RELEASE" = "true" ]; then | |
| ENTRIES='{"os":"ubuntu-latest","runtime":"linux-x64","pack":true,"publish":true,"workloads":""}' | |
| ENTRIES+=',{"os":"windows-latest","runtime":"win-x64","pack":false,"publish":true,"workloads":""}' | |
| ENTRIES+=',{"os":"macos-latest","runtime":"osx-arm64","pack":false,"publish":true,"workloads":""}' | |
| if [ "$ENABLE_ANDROID" = "true" ]; then | |
| ENTRIES+=',{"os":"ubuntu-latest","runtime":"android","pack":false,"publish":false,"workloads":"android"}' | |
| fi | |
| if [ "$ENABLE_IOS" = "true" ]; then | |
| ENTRIES+=',{"os":"macos-latest","runtime":"ios","pack":false,"publish":false,"workloads":"ios"}' | |
| fi | |
| else | |
| ENTRIES='{"os":"ubuntu-latest","runtime":"linux-x64","pack":true,"publish":false,"workloads":""}' | |
| ENTRIES+=',{"os":"windows-latest","runtime":"win-x64","pack":false,"publish":false,"workloads":""}' | |
| ENTRIES+=',{"os":"macos-latest","runtime":"osx-arm64","pack":false,"publish":false,"workloads":""}' | |
| if [ "$ENABLE_ANDROID" = "true" ]; then | |
| ENTRIES+=',{"os":"ubuntu-latest","runtime":"android","pack":false,"publish":false,"workloads":"android"}' | |
| fi | |
| if [ "$ENABLE_IOS" = "true" ]; then | |
| ENTRIES+=',{"os":"macos-latest","runtime":"ios","pack":false,"publish":false,"workloads":"ios"}' | |
| fi | |
| fi | |
| MATRIX="{\"include\":[$ENTRIES]}" | |
| echo "build_matrix=$MATRIX" >> "$GITHUB_OUTPUT" | |
| echo "Matrix: $MATRIX" | |
| build-and-test: | |
| name: Build and Test (${{ matrix.runtime }}) | |
| needs: resolve-version | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| env: | |
| BuildNumber: ${{ github.event_name != 'pull_request' && github.run_number || '' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.resolve-version.outputs.build_matrix) }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ needs.resolve-version.outputs.sha }} | |
| - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| global-json-file: global.json | |
| - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json', '.config/dotnet-tools.json') }} | |
| restore-keys: ${{ runner.os }}-nuget- | |
| - name: Install .NET workloads | |
| if: matrix.workloads != '' | |
| run: dotnet workload install ${{ matrix.workloads }} | |
| - name: Mark build script executable | |
| if: runner.os != 'Windows' | |
| run: chmod +x build.sh | |
| - name: Check code format | |
| if: matrix.runtime == 'linux-x64' | |
| shell: bash | |
| run: ./build.sh Format | |
| - name: Build and test | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| env: | |
| VERSION_SUFFIX: ${{ needs.resolve-version.outputs.version_suffix }} | |
| run: | | |
| if [ -n "$VERSION_SUFFIX" ]; then | |
| ./build.sh Test --VersionSuffix "$VERSION_SUFFIX" | |
| else | |
| ./build.sh Test | |
| fi | |
| - name: Build and test (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| env: | |
| VERSION_SUFFIX: ${{ needs.resolve-version.outputs.version_suffix }} | |
| run: | | |
| if ($env:VERSION_SUFFIX) { | |
| ./build.ps1 Test --VersionSuffix "$env:VERSION_SUFFIX" | |
| } else { | |
| ./build.ps1 Test | |
| } | |
| - name: Generate coverage report | |
| if: matrix.runtime == 'linux-x64' | |
| shell: bash | |
| run: ./build.sh CoverageReport --skip Test | |
| - name: Upload coverage report | |
| if: matrix.runtime == 'linux-x64' | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: coverage-report | |
| path: artifacts/test-results/coverage-report/** | |
| if-no-files-found: warn | |
| - name: Pack | |
| if: matrix.pack | |
| shell: bash | |
| run: ./build.sh Pack --skip Build Test | |
| - name: Generate release manifest | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.pack | |
| shell: bash | |
| run: ./build.sh GenerateReleaseManifest --skip Build Test Pack | |
| - name: Validate package versions | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.pack | |
| shell: bash | |
| run: ./build.sh ValidatePackageVersions --skip Build Test Pack | |
| - name: Publish app | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.publish && runner.os != 'Windows' | |
| shell: bash | |
| run: ./build.sh Publish --Runtime "${{ matrix.runtime }}" | |
| - name: Publish app (Windows) | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.publish && runner.os == 'Windows' | |
| shell: pwsh | |
| run: ./build.ps1 Publish --Runtime "${{ matrix.runtime }}" | |
| - name: Package app | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.publish && runner.os != 'Windows' | |
| shell: bash | |
| run: ./build.sh PackageApp --Runtime "${{ matrix.runtime }}" --skip Publish | |
| - name: Package app (Windows) | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.publish && runner.os == 'Windows' | |
| shell: pwsh | |
| run: ./build.ps1 PackageApp --Runtime "${{ matrix.runtime }}" --skip Publish | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: test-results-${{ matrix.runtime }} | |
| path: artifacts/test-results/** | |
| if-no-files-found: warn | |
| - name: Report test results | |
| if: always() && !cancelled() | |
| uses: dorny/test-reporter@3d76b34a4535afbd0600d347b09a6ee5deb3ed7f # v2.6.0 | |
| with: | |
| name: Tests (${{ matrix.runtime }}) | |
| path: artifacts/test-results/**/*.trx | |
| reporter: dotnet-trx | |
| - name: Upload packages | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.pack | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: packages | |
| path: artifacts/packages/** | |
| if-no-files-found: error | |
| - name: Upload installer artifacts | |
| if: needs.resolve-version.outputs.is_release == 'true' && matrix.publish | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: installer-${{ matrix.runtime }} | |
| path: artifacts/installers/** | |
| if-no-files-found: error | |
| build-docs: | |
| name: Build Documentation | |
| needs: resolve-version | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| outputs: | |
| has_docs: ${{ steps.check.outputs.has_docs }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ needs.resolve-version.outputs.sha }} | |
| - name: Check for documentation source | |
| id: check | |
| shell: bash | |
| run: | | |
| if [ -f "docs/package.json" ]; then | |
| if [ ! -f "docs/package-lock.json" ]; then | |
| echo "::error::docs/package.json exists but docs/package-lock.json is missing. Run 'npm install' in docs/ and commit the lock file." | |
| exit 1 | |
| fi | |
| echo "has_docs=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "::notice::No docs/package.json found. Skipping documentation build." | |
| echo "has_docs=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| if: steps.check.outputs.has_docs == 'true' | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: docs/package-lock.json | |
| - name: Install dependencies | |
| if: steps.check.outputs.has_docs == 'true' | |
| run: npm ci | |
| working-directory: docs | |
| - name: Build documentation | |
| if: steps.check.outputs.has_docs == 'true' | |
| run: npx vitepress build | |
| working-directory: docs | |
| - name: Upload docs artifact | |
| if: steps.check.outputs.has_docs == 'true' | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: docs-site | |
| path: docs/.vitepress/dist | |
| if-no-files-found: error | |
| release: | |
| name: Release | |
| needs: [resolve-version, build-and-test] | |
| if: needs.resolve-version.outputs.is_release == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| environment: release | |
| permissions: | |
| contents: write | |
| attestations: write | |
| id-token: write | |
| concurrency: | |
| group: release-tag-${{ needs.resolve-version.outputs.tag }} | |
| cancel-in-progress: false | |
| env: | |
| HAS_NUGET_KEY: ${{ secrets.NUGET_API_KEY != '' }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ needs.resolve-version.outputs.sha }} | |
| fetch-depth: 0 | |
| - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| global-json-file: global.json | |
| - name: Download packages | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: packages | |
| path: artifacts/packages | |
| - name: Download installers | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts/installers | |
| pattern: installer-* | |
| merge-multiple: true | |
| - name: Mark build script executable | |
| run: chmod +x build.sh | |
| - name: Verify release manifest | |
| shell: bash | |
| run: ./build.sh ValidateReleaseManifest | |
| # NuGet Publishing: prefers Trusted Publishing (OIDC, no secrets) over API key. | |
| # To use Trusted Publishing: configure a policy at nuget.org → Manage Packages → Trusted Publishers. | |
| # Falls back to NUGET_API_KEY secret. If neither is configured, push is skipped. | |
| - name: NuGet login (Trusted Publishing) | |
| if: env.HAS_NUGET_KEY != 'true' | |
| id: nuget-login | |
| uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544 # v1.1.0 | |
| with: | |
| user: ${{ vars.NUGET_USER || github.repository_owner }} | |
| continue-on-error: true | |
| - name: Push to NuGet.org | |
| if: env.HAS_NUGET_KEY == 'true' || steps.nuget-login.outcome == 'success' | |
| shell: bash | |
| env: | |
| API_KEY: ${{ env.HAS_NUGET_KEY == 'true' && secrets.NUGET_API_KEY || steps.nuget-login.outputs.NUGET_API_KEY }} | |
| run: | | |
| shopt -s nullglob | |
| PACKAGES=(artifacts/packages/*.nupkg) | |
| if [ ${#PACKAGES[@]} -eq 0 ]; then | |
| echo "::error::No .nupkg files found" | |
| exit 1 | |
| fi | |
| for pkg in "${PACKAGES[@]}"; do | |
| echo "Pushing $pkg" | |
| dotnet nuget push "$pkg" \ | |
| --api-key "$API_KEY" \ | |
| --source https://api.nuget.org/v3/index.json \ | |
| --skip-duplicate | |
| done | |
| echo "All packages pushed to NuGet.org" | |
| - name: NuGet push skipped | |
| if: env.HAS_NUGET_KEY != 'true' && steps.nuget-login.outcome != 'success' | |
| shell: bash | |
| run: echo "::warning::No NUGET_API_KEY secret and Trusted Publishing not configured. Skipping NuGet push." | |
| - name: Generate SBOM | |
| uses: anchore/sbom-action@57aae528053a48a3f6235f2d9461b05fbcb7366d # v0.23.1 | |
| with: | |
| artifact-name: sbom-spdx.json | |
| output-file: sbom-spdx.json | |
| format: spdx-json | |
| - name: Attest build provenance | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 | |
| with: | |
| subject-path: | | |
| artifacts/packages/*.nupkg | |
| artifacts/installers/*.zip | |
| - name: Attest SBOM | |
| if: hashFiles('sbom-spdx.json') != '' | |
| uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 | |
| with: | |
| subject-path: | | |
| artifacts/packages/*.nupkg | |
| artifacts/installers/*.zip | |
| sbom-path: sbom-spdx.json | |
| - name: Create and push tag | |
| shell: bash | |
| run: | | |
| TAG="${{ needs.resolve-version.outputs.tag }}" | |
| SHA="${{ needs.resolve-version.outputs.sha }}" | |
| git fetch --tags --force | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "::error::Tag $TAG already exists locally" | |
| exit 1 | |
| fi | |
| if git ls-remote --tags origin "refs/tags/$TAG" | grep -q "$TAG"; then | |
| echo "::error::Tag $TAG already exists on remote" | |
| exit 1 | |
| fi | |
| git tag "$TAG" "$SHA" | |
| git push origin "$TAG" | |
| echo "Created and pushed tag $TAG at $SHA" | |
| - name: Create GitHub Release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| TAG="${{ needs.resolve-version.outputs.tag }}" | |
| RELEASE_NAME="dotnet.CI.template $TAG" | |
| shopt -s nullglob | |
| RELEASE_FILES=(artifacts/installers/*.zip) | |
| if [ ${#RELEASE_FILES[@]} -eq 0 ]; then | |
| echo "::error::No installer zip files found" | |
| exit 1 | |
| fi | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| echo "::error::Release already exists for tag $TAG" | |
| exit 1 | |
| fi | |
| SBOM_FILE="" | |
| if [ -f "sbom-spdx.json" ]; then | |
| SBOM_FILE="sbom-spdx.json" | |
| fi | |
| gh release create "$TAG" "${RELEASE_FILES[@]}" $SBOM_FILE \ | |
| --title "$RELEASE_NAME" \ | |
| --generate-notes | |
| echo "Created release $TAG with ${#RELEASE_FILES[@]} assets" | |
| deploy-docs: | |
| name: Deploy Documentation | |
| needs: [resolve-version, release, build-docs] | |
| if: needs.resolve-version.outputs.is_release == 'true' && needs.build-docs.outputs.has_docs == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| concurrency: | |
| group: pages | |
| cancel-in-progress: false | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Check if deployment is possible | |
| id: check | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| HAS_PAGES="$(gh api repos/${{ github.repository }} --jq '.has_pages' 2>/dev/null || echo "false")" | |
| if [ "$HAS_PAGES" != "true" ]; then | |
| echo "::notice::GitHub Pages is not enabled. Enable Settings > Pages > Source: GitHub Actions." | |
| echo "should_deploy=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "should_deploy=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download docs artifact | |
| if: steps.check.outputs.should_deploy == 'true' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: docs-site | |
| path: docs-site | |
| - name: Upload pages artifact | |
| if: steps.check.outputs.should_deploy == 'true' | |
| uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 | |
| with: | |
| path: docs-site | |
| - name: Deploy to GitHub Pages | |
| if: steps.check.outputs.should_deploy == 'true' | |
| id: deployment | |
| uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 |