Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,59 @@ jobs:
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 }}
Expand All @@ -145,7 +198,7 @@ jobs:
artifacts/*.zip \
artifacts/*.txt \
--verify-tag \
--generate-notes
--notes-file release-notes.md

nuget-publish:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

#### Changed

- **Release notes now use explicit dynamic comparison links** — The release workflow resolves the latest non-draft, non-prerelease stable GitHub Release by publication time, combines its tag with the current release tag to build the `Full Changelog` comparison URL, and appends the NuGet install/update commands in a consistent release-note template. Regression coverage keeps the release filtering, publication-time selection, dynamic URL, section order, and `nildiff` commands intact. Affected: `.github/workflows/release.yml`, `doc/DEVELOPER_GUIDE.md`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`.

- **GitHub Actions now use immutable dependencies and least-privilege jobs** — Every third-party action is pinned to a reviewed full commit SHA with an adjacent release-version comment, while DocFX and `dotnet-ildasm` installations use exact versions. Mutation-summary comments, benchmark baseline updates, and CodeQL result uploads now run in separate write-capable jobs restricted to same-repository pull requests, `main` pushes, or other trusted events. Validation and CodeQL analysis remain read-only and continue to run for fork-originated pull requests. Dependabot continues to cover pinned GitHub Actions. Affected: `.github/workflows/dotnet.yml`, `.github/workflows/benchmark-regression.yml`, `.github/workflows/codeql.yml`, `.github/workflows/release.yml`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`.

- **Spinner themes are hidden Easter eggs** — The existing themed and random spinner flags remain parseable for backward compatibility, but normal `--help` output and current user-facing documentation no longer advertise them. Regression tests protect both hidden help output and documentation consistency. Affected: `Runner/ProgramRunner.HelpText.cs`, `USER_GUIDE.md`, `doc/config.sample.jsonc`, `doc/DEVELOPER_GUIDE.md`, `doc/TESTING_GUIDE.md`, `FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`.
Expand Down Expand Up @@ -1698,6 +1700,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

#### 変更

- **リリースノートの比較リンクを明示的かつ動的に生成** — リリース workflow は公開日時を基準に、非 Draft・非 Prerelease の最新安定 GitHub Release を解決し、そのタグを今回のリリースタグと組み合わせて `Full Changelog` の比較 URL を構築し、NuGet の install/update コマンドを統一されたリリースノートテンプレートへ追加するようになりました。Release の絞り込み、公開日時による選択、動的 URL、セクション順序、`nildiff` コマンドを回帰テストで保護します。対象: `.github/workflows/release.yml`, `doc/DEVELOPER_GUIDE.md`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`。

- **GitHub Actions の依存関係を不変化し、ジョブを最小権限化** — すべての third-party Action を、隣接するリリース版コメント付きのレビュー済み完全 commit SHA に固定し、DocFX と `dotnet-ildasm` のインストールも正確なバージョンへ固定しました。mutation summary の PR コメント、benchmark baseline 更新、CodeQL 結果 upload は、同一リポジトリ由来 PR、`main` push、またはその他の信頼済みイベントに限定した write 権限付き専用ジョブへ分離しました。検証と CodeQL 解析は read-only のまま fork 由来 PR でも実行します。Dependabot は固定済み GitHub Actions を引き続き更新対象にします。対象: `.github/workflows/dotnet.yml`, `.github/workflows/benchmark-regression.yml`, `.github/workflows/codeql.yml`, `.github/workflows/release.yml`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`。

- **スピナーテーマを非表示のイースターエッグとして整理** — 既存のテーマ指定およびランダム選択フラグは後方互換のため引き続き解析できますが、通常の `--help` 出力と現行のユーザー向け文書では案内しないようにしました。ヘルプでの非表示と文書整合性を回帰テストで保護します。対象: `Runner/ProgramRunner.HelpText.cs`, `USER_GUIDE.md`, `doc/config.sample.jsonc`, `doc/DEVELOPER_GUIDE.md`, `doc/TESTING_GUIDE.md`, `FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ public void ReleaseWorkflow_CreatesGitHubReleaseFromVersionTags()
Assert.Contains("packages: write", workflow, StringComparison.Ordinal);
Assert.Contains("dotnet publish FolderDiffIL4DotNet.csproj", workflow, StringComparison.Ordinal);
Assert.Contains("gh release create", workflow, StringComparison.Ordinal);
Assert.Contains("gh api --paginate --slurp", workflow, StringComparison.Ordinal);
Assert.Contains("jq -r --arg current \"${TAG_NAME}\"", workflow, StringComparison.Ordinal);
Assert.Contains("/repos/${GITHUB_REPOSITORY}/releases?per_page=100", workflow, StringComparison.Ordinal);
Assert.Contains(".draft == false", workflow, StringComparison.Ordinal);
Assert.Contains(".prerelease == false", workflow, StringComparison.Ordinal);
Assert.Contains(".published_at != null", workflow, StringComparison.Ordinal);
Assert.Contains("max_by(.published_at).tag_name", workflow, StringComparison.Ordinal);
Assert.Contains(".tag_name != $current", workflow, StringComparison.Ordinal);
Assert.Contains(
".tag_name | test(\"^v[0-9]+\\\\.[0-9]+\\\\.[0-9]+$\")",
workflow,
StringComparison.Ordinal);
Assert.Contains("## What's Changed", workflow, StringComparison.Ordinal);
Assert.Contains(
"Full Changelog: https://github.com/${GITHUB_REPOSITORY}/compare/${previous_tag}...${TAG_NAME}",
workflow,
StringComparison.Ordinal);
Assert.Contains("## Install or update", workflow, StringComparison.Ordinal);
Assert.Contains("dotnet tool install -g nildiff", workflow, StringComparison.Ordinal);
Assert.Contains("dotnet tool update -g nildiff", workflow, StringComparison.Ordinal);
Assert.Contains("--notes-file release-notes.md", workflow, StringComparison.Ordinal);
Assert.DoesNotContain("--generate-notes", workflow, StringComparison.Ordinal);
var whatsChangedIndex = workflow.IndexOf("## What's Changed", StringComparison.Ordinal);
var fullChangelogIndex = workflow.IndexOf("Full Changelog:", StringComparison.Ordinal);
var installOrUpdateIndex = workflow.IndexOf("## Install or update", StringComparison.Ordinal);
Assert.True(whatsChangedIndex < fullChangelogIndex);
Assert.True(fullChangelogIndex < installOrUpdateIndex);
Assert.Contains("DocumentationSite", workflow, StringComparison.Ordinal);
Assert.Contains("Pack global tool NuGet package", workflow, StringComparison.Ordinal);
Assert.Contains("Publish Core to GitHub Packages", workflow, StringComparison.Ordinal);
Expand Down
4 changes: 2 additions & 2 deletions doc/DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ Release automation:
- [`.github/workflows/release.yml`](../.github/workflows/release.yml) runs for pushed `v*` tags and manual dispatch with an explicit existing tag input
- Rebuilds, reruns coverage-gated tests, regenerates DocFX output, publishes the app, and removes `*.pdb`
- Creates zipped publish/docs artifacts plus SHA-256 checksum files
- Creates a GitHub Release from the existing tag with generated release notes
- Creates a GitHub Release from the existing tag with a deterministic note that selects the latest published stable GitHub Release by `published_at`, builds the dynamic `Full Changelog` comparison URL, and appends the NuGet install/update commands
- After the primary `nuget.org` publications complete, the `nuget-publish` job registers an authenticated `github` NuGet source as a best-effort step and then mirrors to GitHub Packages with `continue-on-error: true`, so a mirror outage or auth failure does not block restore, pack, or the primary registry
- The package-diff checks resolve the previous `v*` tag on the checked-out tag's first-parent release line, so manual `workflow_dispatch` runs against an older existing tag or maintenance release still compare against the correct previous release
- `nildiff` is mirrored on every tagged release, while `FolderDiffIL4DotNet.Core` and `FolderDiffIL4DotNet.Plugin.Abstractions` mirror only when those package directories changed, matching the nuget.org gate so normal releases do not create GitHub-only library versions
Expand Down Expand Up @@ -1783,7 +1783,7 @@ v* タグ push 時:
- [`.github/workflows/release.yml`](../.github/workflows/release.yml) は `v*` タグ push と、既存タグを明示指定する `workflow_dispatch` で実行します
- 再ビルド、カバレッジゲート付き再テスト、DocFX 再生成、アプリ publish、`*.pdb` 除去まで行います
- publish 出力 ZIP、ドキュメント ZIP、SHA-256 チェックサムを生成します
- 既存タグから GitHub Release を作成し、自動生成リリースノートを付けます
- 既存タグから GitHub Release を作成し、`published_at` が最新の公開済み安定 GitHub Release を選んで動的な `Full Changelog` 比較 URL を構築し、NuGet の install/update コマンドを追加した決定的なノートを付けます
- `nuget-publish` ジョブは本流の `nuget.org` 公開が完了した後で、認証済みの `github` NuGet source を best-effort step として登録し、その後に `continue-on-error: true` 付きで GitHub Packages へ mirror します。これにより、mirror 側の障害や認証失敗で restore / pack / 本命のレジストリ公開を止めません
- パッケージ差分判定は checkout 済みタグの first-parent リリース系列上にある直前の `v*` タグを解決するため、古い既存タグや保守リリースを指定した `workflow_dispatch` でも正しい前回リリースとの差分で判定されます
- `nildiff` はタグごとに mirror し、`FolderDiffIL4DotNet.Core` と `FolderDiffIL4DotNet.Plugin.Abstractions` はそのディレクトリに実変更があるときだけ mirror します。これにより、通常 release で GitHub Packages 側だけのライブラリ版が増えることを避けつつ、nuget.org と同じ公開条件を維持します
Expand Down
Loading