Bump version to 1.22.0 #75
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
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: Existing v* tag to release | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| env: | |
| DOCFX_VERSION: 2.78.5 | |
| DOTNET_ILDASM_VERSION: 0.12.2 | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| # On workflow_dispatch, force refs/tags/<tag_name> so a branch name | |
| # (e.g. "main") fails checkout instead of falling through to the | |
| # tag-assumed downstream steps. | |
| # workflow_dispatch では refs/tags/<tag_name> に固定し、"main" 等の | |
| # ブランチ名が指定された場合にタグ前提の後続ステップへ進む前に | |
| # checkout 段階で失敗させます。 | |
| ref: ${{ inputs.tag_name && format('refs/tags/{0}', inputs.tag_name) || github.ref }} | |
| - name: Set up .NET | |
| uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| global-json-file: global.json | |
| - name: Cache NuGet packages | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| - name: Restore dependencies | |
| run: dotnet restore FolderDiffIL4DotNet.sln | |
| - name: Build | |
| run: dotnet build FolderDiffIL4DotNet.sln --configuration Release --no-restore | |
| - name: Install DocFX | |
| run: dotnet tool update --global docfx --version "$DOCFX_VERSION" | |
| - name: Generate documentation site | |
| run: | | |
| export PATH="$PATH:$HOME/.dotnet/tools" | |
| docfx metadata docfx.json | |
| docfx build docfx.json | |
| - name: Install real disassembler for E2E tests | |
| if: ${{ hashFiles('FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj') != '' }} | |
| run: | | |
| dotnet tool install --global dotnet-ildasm --version "$DOTNET_ILDASM_VERSION" | |
| echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" | |
| DOTNET_ROLL_FORWARD=Major "$HOME/.dotnet/tools/dotnet-ildasm" --version | |
| - name: Test with coverage | |
| if: ${{ hashFiles('FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj') != '' }} | |
| env: | |
| DOTNET_ROLL_FORWARD: Major | |
| FOLDERDIFF_RUN_E2E: true | |
| run: dotnet test FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj --configuration Release --no-build --nologo --settings coverlet.runsettings --logger "trx;LogFileName=test_results.trx" --collect:"XPlat Code Coverage" --results-directory ./TestResults | |
| - name: Enforce coverage thresholds | |
| if: ${{ hashFiles('FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj') != '' }} | |
| run: | | |
| python3 - <<'PY' | |
| import glob | |
| import sys | |
| import xml.etree.ElementTree as ET | |
| line_threshold = 80.0 | |
| branch_threshold = 75.0 | |
| matches = glob.glob("TestResults/**/coverage.cobertura.xml", recursive=True) | |
| if not matches: | |
| print("coverage.cobertura.xml was not found.", file=sys.stderr) | |
| sys.exit(1) | |
| root = ET.parse(matches[0]).getroot() | |
| line_rate = float(root.attrib["line-rate"]) * 100.0 | |
| branch_rate = float(root.attrib["branch-rate"]) * 100.0 | |
| print(f"Total line coverage : {line_rate:.2f}% (threshold {line_threshold:.2f}%)") | |
| print(f"Total branch coverage: {branch_rate:.2f}% (threshold {branch_threshold:.2f}%)") | |
| failures = [] | |
| if line_rate < line_threshold: | |
| failures.append(f"line coverage {line_rate:.2f}% < {line_threshold:.2f}%") | |
| if branch_rate < branch_threshold: | |
| failures.append(f"branch coverage {branch_rate:.2f}% < {branch_threshold:.2f}%") | |
| if failures: | |
| print("Coverage threshold check failed:", file=sys.stderr) | |
| for failure in failures: | |
| print(f" - {failure}", file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| - name: Publish | |
| run: dotnet publish FolderDiffIL4DotNet.csproj --configuration Release --no-build --output publish | |
| - name: Remove debugging symbols | |
| run: find publish -name '*.pdb' -delete | |
| - name: Archive release artifacts | |
| env: | |
| TAG_NAME: ${{ inputs.tag_name || github.ref_name }} | |
| run: | | |
| mkdir -p artifacts | |
| tag_name="${TAG_NAME}" | |
| zip -rq "artifacts/FolderDiffIL4DotNet-${tag_name}.zip" publish | |
| zip -rq "artifacts/DocumentationSite-${tag_name}.zip" _site | |
| sha256sum artifacts/*.zip > "artifacts/sha256-${tag_name}.txt" | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ReleaseArtifacts | |
| path: artifacts/** | |
| - name: Write release notes | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_NAME: ${{ inputs.tag_name || github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "${TAG_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Release tag must be a v-prefixed SemVer version, got: ${TAG_NAME}" >&2 | |
| exit 1 | |
| fi | |
| previous_tag="$( | |
| gh api --paginate --slurp \ | |
| "/repos/${GITHUB_REPOSITORY}/releases?per_page=100" \ | |
| | jq -r --arg current "${TAG_NAME}" ' | |
| flatten | |
| | map( | |
| select( | |
| .draft == false | |
| and .prerelease == false | |
| and .published_at != null | |
| and .tag_name != $current | |
| and (.tag_name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$")) | |
| ) | |
| ) | |
| | max_by(.published_at).tag_name // empty | |
| ' | |
| )" | |
| if [ -z "${previous_tag}" ]; then | |
| echo "No previous non-draft, non-prerelease GitHub release was found before ${TAG_NAME}." >&2 | |
| exit 1 | |
| fi | |
| if [[ ! "${previous_tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Latest GitHub release tag is not a stable v-prefixed SemVer version: ${previous_tag}" >&2 | |
| exit 1 | |
| fi | |
| cat > release-notes.md <<EOF | |
| ## What's Changed | |
| Full Changelog: https://github.com/${GITHUB_REPOSITORY}/compare/${previous_tag}...${TAG_NAME} | |
| ## Install or update | |
| NuGet: | |
| \`\`\`bash | |
| dotnet tool install -g nildiff | |
| dotnet tool update -g nildiff | |
| \`\`\` | |
| EOF | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_NAME: ${{ inputs.tag_name || github.ref_name }} | |
| run: | | |
| gh release create "${TAG_NAME}" \ | |
| artifacts/*.zip \ | |
| artifacts/*.txt \ | |
| --verify-tag \ | |
| --notes-file release-notes.md | |
| nuget-publish: | |
| runs-on: ubuntu-latest | |
| needs: release | |
| if: ${{ success() }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| GITHUB_PACKAGES_SOURCE: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| # See release job for rationale. | |
| # release ジョブの注記を参照してください。 | |
| ref: ${{ inputs.tag_name && format('refs/tags/{0}', inputs.tag_name) || github.ref }} | |
| - name: Set up .NET | |
| uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| global-json-file: global.json | |
| - name: Cache NuGet packages | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| - name: Check if Core has changes since previous tag | |
| id: core-diff | |
| run: | | |
| # Resolve the previous v* tag on the current first-parent release line. | |
| # 現在の first-parent リリース系列上にある直前の v* タグを解決します。 | |
| CURRENT_TAG=$(git describe --tags --exact-match HEAD --match 'v*') | |
| PREV_TAG=$(git describe --first-parent --tags --abbrev=0 HEAD^ --match 'v*' 2>/dev/null || true) | |
| if [ -z "$PREV_TAG" ]; then | |
| echo "No previous tag found — treating Core as changed" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| elif git diff --quiet "${PREV_TAG}..HEAD" -- FolderDiffIL4DotNet.Core/; then | |
| echo "No changes in FolderDiffIL4DotNet.Core/ since ${PREV_TAG} — skipping NuGet publish" | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Core has changes since ${PREV_TAG}" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Check if Plugin.Abstractions has changes since previous tag | |
| id: plugin-diff | |
| run: | | |
| CURRENT_TAG=$(git describe --tags --exact-match HEAD --match 'v*') | |
| PREV_TAG=$(git describe --first-parent --tags --abbrev=0 HEAD^ --match 'v*' 2>/dev/null || true) | |
| if [ -z "$PREV_TAG" ]; then | |
| echo "No previous tag found — treating Plugin.Abstractions as changed" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| elif git diff --quiet "${PREV_TAG}..HEAD" -- FolderDiffIL4DotNet.Plugin.Abstractions/; then | |
| echo "No changes in FolderDiffIL4DotNet.Plugin.Abstractions/ since ${PREV_TAG} — skipping NuGet publish" | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Plugin.Abstractions has changes since ${PREV_TAG}" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Restore Core dependencies | |
| if: steps.core-diff.outputs.changed == 'true' | |
| run: dotnet restore FolderDiffIL4DotNet.Core/FolderDiffIL4DotNet.Core.csproj | |
| - name: Pack Core NuGet package | |
| if: steps.core-diff.outputs.changed == 'true' | |
| run: dotnet pack FolderDiffIL4DotNet.Core/FolderDiffIL4DotNet.Core.csproj --configuration Release --no-restore --output nupkgs | |
| - name: Publish Core to nuget.org | |
| if: steps.core-diff.outputs.changed == 'true' | |
| run: dotnet nuget push "nupkgs/FolderDiffIL4DotNet.Core.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate | |
| - name: Skip Core nuget.org publish (no changes) | |
| if: steps.core-diff.outputs.changed != 'true' | |
| run: echo "NuGet publish skipped — FolderDiffIL4DotNet.Core/ has no changes since the previous tag" | |
| - name: Restore Plugin.Abstractions dependencies | |
| if: steps.plugin-diff.outputs.changed == 'true' | |
| run: dotnet restore FolderDiffIL4DotNet.Plugin.Abstractions/FolderDiffIL4DotNet.Plugin.Abstractions.csproj | |
| - name: Pack Plugin.Abstractions NuGet package | |
| if: steps.plugin-diff.outputs.changed == 'true' | |
| run: dotnet pack FolderDiffIL4DotNet.Plugin.Abstractions/FolderDiffIL4DotNet.Plugin.Abstractions.csproj --configuration Release --no-restore --output nupkgs | |
| - name: Publish Plugin.Abstractions to nuget.org | |
| if: steps.plugin-diff.outputs.changed == 'true' | |
| run: dotnet nuget push "nupkgs/FolderDiffIL4DotNet.Plugin.Abstractions.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate | |
| - name: Skip Plugin.Abstractions nuget.org publish (no changes) | |
| if: steps.plugin-diff.outputs.changed != 'true' | |
| run: echo "NuGet publish skipped — FolderDiffIL4DotNet.Plugin.Abstractions/ has no changes since the previous tag" | |
| - name: Restore global tool dependencies | |
| run: dotnet restore FolderDiffIL4DotNet.csproj | |
| - name: Pack global tool NuGet package | |
| run: dotnet pack FolderDiffIL4DotNet.csproj --configuration Release --no-restore --output nupkgs | |
| - name: Publish global tool to nuget.org | |
| run: dotnet nuget push "nupkgs/nildiff.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate | |
| - name: Authenticate GitHub Packages source | |
| id: github-auth | |
| continue-on-error: true | |
| run: | | |
| dotnet nuget remove source github >/dev/null 2>&1 || true | |
| dotnet nuget add source "${{ env.GITHUB_PACKAGES_SOURCE }}" \ | |
| --name github \ | |
| --username "${{ github.actor }}" \ | |
| --password "${{ secrets.GITHUB_TOKEN }}" \ | |
| --store-password-in-clear-text | |
| - name: Warn if GitHub Packages auth failed | |
| if: steps.github-auth.outcome == 'failure' | |
| run: | | |
| echo "Warning: GitHub Packages mirror auth failed after nuget.org publication; investigate the mirror channel separately." >> "$GITHUB_STEP_SUMMARY" | |
| - name: Publish Core to GitHub Packages | |
| id: core-gpr-publish | |
| if: steps.core-diff.outputs.changed == 'true' && steps.github-auth.outcome == 'success' | |
| continue-on-error: true | |
| run: dotnet nuget push "nupkgs/FolderDiffIL4DotNet.Core.*.nupkg" --source github --skip-duplicate | |
| - name: Warn if Core GitHub Packages mirror failed | |
| if: steps.core-gpr-publish.outcome == 'failure' | |
| run: | | |
| echo "Warning: Core GitHub Packages mirror failed after nuget.org publication; investigate the mirror channel separately." >> "$GITHUB_STEP_SUMMARY" | |
| - name: Publish Plugin.Abstractions to GitHub Packages | |
| id: plugin-gpr-publish | |
| if: steps.plugin-diff.outputs.changed == 'true' && steps.github-auth.outcome == 'success' | |
| continue-on-error: true | |
| run: dotnet nuget push "nupkgs/FolderDiffIL4DotNet.Plugin.Abstractions.*.nupkg" --source github --skip-duplicate | |
| - name: Warn if Plugin.Abstractions GitHub Packages mirror failed | |
| if: steps.plugin-gpr-publish.outcome == 'failure' | |
| run: | | |
| echo "Warning: Plugin.Abstractions GitHub Packages mirror failed after nuget.org publication; investigate the mirror channel separately." >> "$GITHUB_STEP_SUMMARY" | |
| - name: Publish global tool to GitHub Packages | |
| id: tool-gpr-publish | |
| if: steps.github-auth.outcome == 'success' | |
| continue-on-error: true | |
| run: dotnet nuget push "nupkgs/nildiff.*.nupkg" --source github --skip-duplicate | |
| - name: Warn if global tool GitHub Packages mirror failed | |
| if: steps.tool-gpr-publish.outcome == 'failure' | |
| run: | | |
| echo "Warning: nildiff GitHub Packages mirror failed after nuget.org publication; investigate the mirror channel separately." >> "$GITHUB_STEP_SUMMARY" |