diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..4c69cb10a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +# Hand-written (not auto-generated) — see Build.CI.GitHubActions.cs for the +# rationale (lets us bridge GitHub's NUGET_API_KEY secret to NUKE's +# NuGetApiKey parameter, which would otherwise have to share a name). + +name: release + +on: + push: + branches: + - main + +permissions: + contents: write # for ICreateGitHubRelease (tag + GitHub release) + packages: write # in case we later push to GitHub Packages too + +jobs: + release: + name: release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 # Nerdbank.GitVersioning needs full history + - name: 'Cache: .nuke/temp, ~/.nuget/packages' + uses: actions/cache@v4 + with: + path: | + .nuke/temp + ~/.nuget/packages + key: ${{ runner.os }}-release-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props', 'version.json') }} + - name: 'Run: Test, Pack, Publish' + run: ./build.cmd Test Pack Publish + env: + # Publish target is GitHub Packages on this fork, not nuget.org. + # GITHUB_TOKEN has packages:write per the permissions block above — + # no separate NuGet API key needed until the project rename. + NuGetApiKey: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index 86e9a339f..13cb7831c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,11 +60,11 @@ CI providers in use: **GitHub Actions only** (the other providers were dropped Validation workflows (`ubuntu-latest`, `windows-latest`, `macos-latest`) run on every push to non-main branches and every PR targeting `main`, with `paths-ignore` for `docs/**`, `images/**`, `**/*.md`. -Release pipeline is being introduced in a follow-up PR alongside Nerdbank.GitVersioning. GitVersion is still in use until that lands. +**Versioning:** [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) — configured in `version.json` at the repo root. Major+minor is hand-bumped; patch comes from git-height. `main` is the public-release ref (stable versions); everything else gets prerelease tags. GitVersion is still installed as a transitional helper for `MajorMinorPatchVersion` in `Build.cs`; full removal is a follow-up. -Alpha packages flow to the Feedz feed (`https://f.feedz.io/nuke/alpha/nuget`) from `develop` via the GitHub Actions `AlphaDeployment` workflow. Public NuGet publishes happen from `master`/`release/*` via AppVeyor. +**Release pipeline:** `.github/workflows/release.yml` — triggered on push to `main`, runs `./build.cmd Test Pack Publish`. **Publishes to GitHub Packages** (`https://nuget.pkg.github.com//index.json`) using the auto-provided `GITHUB_TOKEN` — no separate NuGet API key is needed. -Versioning is driven by GitVersion (`GitVersion.yml`). +**Why not nuget.org?** Per Matt's wish, the "Nuke" name cannot be carried over to a successor project. Until the hard fork picks a new name, packages stay on this fork's GitHub Packages feed. Switching the target to nuget.org becomes a follow-up once the rename ceremony happens. ## Conventions worth respecting diff --git a/Directory.Packages.props b/Directory.Packages.props index d36d542fe..57202c851 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,6 +15,7 @@ + diff --git a/build/Build.CI.GitHubActions.cs b/build/Build.CI.GitHubActions.cs index d719f3234..47b7d2b4b 100644 --- a/build/Build.CI.GitHubActions.cs +++ b/build/Build.CI.GitHubActions.cs @@ -5,13 +5,6 @@ using Nuke.Common.CI.GitHubActions; using Nuke.Components; -[GitHubActions( - "windows-latest", - GitHubActionsImage.WindowsLatest, - FetchDepth = 0, - OnPushBranches = new[] { MainBranch }, - InvokedTargets = new[] { nameof(ITest.Test), nameof(IPack.Pack) }, - PublishArtifacts = false)] // macOS and Windows runs are reserved for main-branch validation (post-merge // and release pipelines). PRs and feature-branch pushes get Linux-only for // fast, cheap feedback. @@ -22,6 +15,13 @@ OnPushBranches = new[] { MainBranch }, InvokedTargets = new[] { nameof(ITest.Test), nameof(IPack.Pack) }, PublishArtifacts = false)] +[GitHubActions( + "windows-latest", + GitHubActionsImage.WindowsLatest, + FetchDepth = 0, + OnPushBranches = new[] { MainBranch }, + InvokedTargets = new[] { nameof(ITest.Test), nameof(IPack.Pack) }, + PublishArtifacts = false)] [GitHubActions( "ubuntu-latest", GitHubActionsImage.UbuntuLatest, @@ -33,11 +33,10 @@ PublishArtifacts = false)] partial class Build { - // AlphaDeployment workflow removed in trunk migration. The release pipeline - // is reintroduced in the follow-up Nerdbank.GitVersioning PR with proper - // main-branch publish semantics. Code paths still referencing this constant - // (Test.OnlyWhenStatic, Pack.PackSettings, Publish.Requires, DeletePackages) - // are intentionally preserved — they evaluate to false until a workflow - // with this name exists again. - const string AlphaDeployment = "alpha-deployment"; + // The release workflow is intentionally hand-written at + // .github/workflows/release.yml — that lets us name the GitHub secret + // NUGET_API_KEY (conventional screaming-snake-case) while keeping the + // Build.cs property name NuGetApiKey (idiomatic C#). The NUKE attribute + // generator would force the two to match. + const string ReleaseWorkflow = "release"; } diff --git a/build/Build.SignPackages.cs b/build/Build.SignPackages.cs index 2ff0230bd..7d48c5391 100644 --- a/build/Build.SignPackages.cs +++ b/build/Build.SignPackages.cs @@ -18,6 +18,6 @@ partial class Build : ISignPackages public Target SignPackages => _ => _ .Inherit() - .OnlyWhenStatic(() => IsPublicRelease) + .OnlyWhenStatic(() => GitRepository.IsOnMainBranch()) .OnlyWhenStatic(() => EnvironmentInfo.IsWin); } diff --git a/build/Build.cs b/build/Build.cs index 4e960d311..6d33da50e 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -103,7 +103,6 @@ from framework in project.GetTargetFrameworks() Target ITest.Test => _ => _ .Inherit() - .OnlyWhenStatic(() => Host is not GitHubActions { Workflow: AlphaDeployment }) .Partition(2); bool IReportCoverage.CreateCoverageHtmlReport => true; @@ -120,25 +119,30 @@ from framework in project.GetTargetFrameworks() IEnumerable IReportIssues.InspectCodeFailOnIssues => new string[0]; IEnumerable IReportIssues.InspectCodeFailOnCategories => new string[0]; + // Local Terminal runs use a placeholder version so packed nupkgs don't + // collide with real releases. CI runs let Nerdbank.GitVersioning inject the + // real version via MSBuild ($PackageVersion). Configure IPack.PackSettings => _ => _ - .When(Host is Terminal or GitHubActions { Workflow: AlphaDeployment }, _ => _ + .When(Host is Terminal, _ => _ .SetVersion(DefaultDeploymentVersion)); - string PublicNuGetSource => "https://api.nuget.org/v3/index.json"; - string FeedzNuGetSource => "https://f.feedz.io/nuke/alpha/nuget"; string DefaultDeploymentVersion => "9999.0.0"; - [Parameter] [Secret] readonly string PublicNuGetApiKey; - [Parameter] [Secret] readonly string FeedzNuGetApiKey; + [Parameter] [Secret] readonly string NuGetApiKey; - bool IsPublicRelease => GitRepository.IsOnMainBranch(); - string IPublish.NuGetSource => IsPublicRelease ? PublicNuGetSource : FeedzNuGetSource; - string IPublish.NuGetApiKey => IsPublicRelease ? PublicNuGetApiKey : FeedzNuGetApiKey; + // Publishing to GitHub Packages on this fork until the post-hard-fork + // project rename lands. nuget.org would require the new name and can't + // be done under "Nuke.*" — see project_nuke_strategy memory note. + // Repository owner comes from GITHUB_REPOSITORY_OWNER, automatically set + // by GitHub Actions runners. ChrisonSimtian is the local-dev fallback. + string IPublish.NuGetSource => + $"https://nuget.pkg.github.com/{EnvironmentInfo.GetVariable("GITHUB_REPOSITORY_OWNER") ?? "ChrisonSimtian"}/index.json"; + string IPublish.NuGetApiKey => NuGetApiKey; Target IPublish.Publish => _ => _ .Inherit() .Consumes(From().Pack) - .Requires(() => GitRepository.IsOnMainBranch() && Host is GitHubActions && GitHubActions.Workflow == AlphaDeployment) + .Requires(() => GitRepository.IsOnMainBranch() && Host is GitHubActions && GitHubActions.Workflow == ReleaseWorkflow) .WhenSkipped(DependencyBehavior.Execute); IEnumerable NuGetPackageFiles @@ -147,26 +151,12 @@ IEnumerable NuGetPackageFiles Target DeletePackages => _ => _ .DependentFor() .After() - .OnlyWhenStatic(() => Host is Terminal or GitHubActions { Workflow: AlphaDeployment }) + .OnlyWhenStatic(() => Host is Terminal) .Executes(() => { - if (Host is Terminal) - { - var packagesDirectory = NuGetPackageResolver.GetPackagesDirectory(packagesConfigFile: BuildProjectFile); - var packageDirectories = packagesDirectory.GlobDirectories($"nuke.*/{DefaultDeploymentVersion}"); - packageDirectories.DeleteDirectories(); - } - else if (Host is GitHubActions) - { - void DeletePackage(string id, string version) - => DotNet( - $"nuget delete {id} {version} --source {FeedzNuGetSource} --api-key {FeedzNuGetApiKey} --non-interactive", - logOutput: false); - - var packageIds = NuGetPackageFiles.Select(x => new PackageArchiveReader(x).NuspecReader.GetId()); - foreach (var packageId in packageIds) - SuppressErrors(() => DeletePackage(packageId, DefaultDeploymentVersion), logWarning: false); - } + var packagesDirectory = NuGetPackageResolver.GetPackagesDirectory(packagesConfigFile: BuildProjectFile); + var packageDirectories = packagesDirectory.GlobDirectories($"nuke.*/{DefaultDeploymentVersion}"); + packageDirectories.DeleteDirectories(); }); string ICreateGitHubRelease.Name => MajorMinorPatchVersion; diff --git a/source/Directory.Build.props b/source/Directory.Build.props index d4bbbd9d0..16e2b9c57 100644 --- a/source/Directory.Build.props +++ b/source/Directory.Build.props @@ -36,6 +36,7 @@ + diff --git a/version.json b/version.json new file mode 100644 index 000000000..b1b83ee48 --- /dev/null +++ b/version.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", + "version": "10.2", + "publicReleaseRefSpec": [ + "^refs/heads/main$" + ], + "nuGetPackageVersion": { + "semVer": 2 + }, + "assemblyVersion": { + "precision": "minor" + }, + "release": { + "versionIncrement": "minor", + "firstUnstableTag": "preview", + "branchName": "release/v{version}" + } +}