diff --git a/.github/workflows/build-executable-task.yml b/.github/workflows/build-executable-task.yml index a528e29e..fa4fd42c 100644 --- a/.github/workflows/build-executable-task.yml +++ b/.github/workflows/build-executable-task.yml @@ -21,10 +21,6 @@ on: required: false type: boolean default: false - outputs: - # Output of the uploaded artifact id - artifact-id: - value: ${{ jobs.upload-build-artifacts.outputs.artifact-id }} jobs: @@ -73,16 +69,15 @@ jobs: with: name: publish-${{ inputs.branch }}-${{ matrix.runtime }} path: ${{ runner.temp }}/publish + retention-days: 1 # Smoke builds only need the per-runtime compile to succeed (fast PR - # feedback) — the zipped, downloadable artifact is a release concern, so - # skip the aggregation entirely on smoke. The `artifact-id` output is then - # empty, which is fine because the GitHub release job never runs on smoke. + # feedback) - the zipped, downloadable artifact is a release concern, so + # skip the aggregation entirely on smoke. The release job collects the + # release-asset--* artifacts by pattern, so no artifact-id is needed. upload-build-artifacts: name: Upload matrix build artifacts job if: ${{ !inputs.smoke }} - outputs: - artifact-id: ${{ steps.artifact-upload-step.outputs.artifact-id }} runs-on: ubuntu-latest needs: [ build-executable-matrix ] @@ -99,8 +94,8 @@ jobs: run: 7z a -t7z ${{ runner.temp }}/PlexCleaner.7z ${{ runner.temp }}/publish/* - name: Upload build artifacts step - id: artifact-upload-step uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: executable-build-${{ inputs.branch }} + name: release-asset-${{ inputs.branch }}-executable path: ${{ runner.temp }}/PlexCleaner.7z + retention-days: 1 diff --git a/.github/workflows/build-release-task.yml b/.github/workflows/build-release-task.yml index 3ae6cdec..a7c6b876 100644 --- a/.github/workflows/build-release-task.yml +++ b/.github/workflows/build-release-task.yml @@ -103,10 +103,13 @@ jobs: with: ref: ${{ needs.get-version.outputs.GitCommitId }} - - name: Download executable build artifacts step + # Collect every release-asset--* artifact by pattern, so the + # release job never names a build job and stays reusable as targets change. + - name: Download release asset artifacts step uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - artifact-ids: ${{ needs.build-executable.outputs.artifact-id }} + pattern: release-asset-${{ inputs.branch }}-* + merge-multiple: true path: ./Publish # The weekly publisher re-runs even when a branch has no new commits, so diff --git a/.github/workflows/build-toolversions-task.yml b/.github/workflows/build-toolversions-task.yml index 091039c7..c841b682 100644 --- a/.github/workflows/build-toolversions-task.yml +++ b/.github/workflows/build-toolversions-task.yml @@ -69,6 +69,7 @@ jobs: with: # Branch-suffixed so both matrix legs coexist; the inner filename # stays `latest.ver` for main (the m4 `include({{latest.ver}})` - # token), consumed by build-dockerreadme-task.yml. + # token), consumed by publish-docker-readme-task.yml. name: versions-${{ inputs.branch }} path: ${{ runner.temp }}/versions/${{ inputs.branch == 'main' && 'latest.ver' || 'develop.ver' }} + retention-days: 1 diff --git a/.github/workflows/build-dockerreadme-task.yml b/.github/workflows/publish-docker-readme-task.yml similarity index 96% rename from .github/workflows/build-dockerreadme-task.yml rename to .github/workflows/publish-docker-readme-task.yml index 0061269f..242d5bb9 100644 --- a/.github/workflows/build-dockerreadme-task.yml +++ b/.github/workflows/publish-docker-readme-task.yml @@ -1,4 +1,4 @@ -name: Create Docker README.md task +name: Publish Docker Hub readme task on: workflow_call: @@ -15,7 +15,7 @@ on: jobs: docker-readme: - name: Create Docker README.md job + name: Publish Docker Hub readme job # Render only for main: there is a single Docker Hub README, and the m4 # template includes the `latest` image's tool versions. if: ${{ inputs.branch == 'main' }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 2e559e30..3a0e70e3 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -95,13 +95,13 @@ jobs: branch: ${{ matrix.branch }} docker-readme: - name: Create Docker README.md job + name: Publish Docker Hub readme job needs: [setup, tool-versions] if: ${{ needs.setup.outputs.publish == 'true' }} strategy: matrix: branch: ${{ fromJSON(needs.setup.outputs.branches) }} - uses: ./.github/workflows/build-dockerreadme-task.yml + uses: ./.github/workflows/publish-docker-readme-task.yml secrets: inherit with: # The task self-gates to `main`; the develop leg is a no-op. diff --git a/AGENTS.md b/AGENTS.md index de2875e7..37b39ad7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,7 +34,7 @@ This repo uses a **two-phase model by default**: PRs build fast, publishing is b - **Merges don't publish by default.** [`publish-release.yml`](./.github/workflows/publish-release.yml) is the sole publisher: its **weekly schedule** (Mondays 02:00 UTC) and **manual `workflow_dispatch`** always do the full build/publish of **both** `main` and `develop` (a branch matrix). Its `push` trigger publishes only when the **`PUBLISH_ON_MERGE` repository variable** is `true` (opt-in legacy continuous-release). Unset/`false` = two-phase. - **Required check.** The `changes` job is in the `Check pull request workflow status` aggregator's `needs` and **must succeed** (not just "not fail") - a paths-filter error must never let a target-changing PR merge with its smoke build silently skipped. Skipped smoke jobs (no matching change) pass; `failure`/`cancelled` blocks. - **Reusable-task parameter contract.** [`build-release-task.yml`](./.github/workflows/build-release-task.yml) and the leaf `build-*-task.yml` workflows take `ref` (git ref to check out/version), `branch` (logical branch driving config/tags/prerelease - `main` => Release/`latest`/non-prerelease, else Debug/`develop`/prerelease), and where relevant `smoke`. **Branch-derived config keys off `inputs.branch`, never `github.ref_name`** - the publisher's matrix builds `develop` from a run whose `github.ref_name` is `main`, so `ref_name` would be wrong. Artifact names are branch-suffixed so both matrix legs coexist in one run. [`get-version-task.yml`](./.github/workflows/get-version-task.yml) takes a `ref` so NBGV versions the right branch, and exposes `GitCommitId` so the release tag and built artifacts pin to the exact built commit. -- **The release-asset seam.** A target contributes files to the GitHub release by uploading a workflow artifact named `release-asset--`. The `github-release` job collects every `release-asset--*` artifact by pattern and **never names a build job**, so the tag-the-commit + create-the-release + attach-the-assets logic is reusable **verbatim**. PlexCleaner's executable target ([`build-executable-task.yml`](./.github/workflows/build-executable-task.yml)) uses this seam - it `dotnet publish`es the standalone executables and uploads them as `release-asset--*`. The Docker target ([`build-docker-task.yml`](./.github/workflows/build-docker-task.yml)) pushes multi-arch tags directly to Docker Hub (`latest` for main, `develop` for develop) and contributes **no** `release-asset-*`. The Docker Hub repository overview is pushed separately by [`build-dockerreadme-task.yml`](./.github/workflows/build-dockerreadme-task.yml), gated to `main`. +- **The release-asset seam.** A target contributes files to the GitHub release by uploading a workflow artifact named `release-asset--`. The `github-release` job collects every `release-asset--*` artifact by pattern and **never names a build job**, so the tag-the-commit + create-the-release + attach-the-assets logic is reusable **verbatim**. PlexCleaner's executable target ([`build-executable-task.yml`](./.github/workflows/build-executable-task.yml)) uses this seam - it `dotnet publish`es the standalone executables and uploads them as `release-asset--*`. The Docker target ([`build-docker-task.yml`](./.github/workflows/build-docker-task.yml)) pushes multi-arch tags directly to Docker Hub (`latest` for main, `develop` for develop) and contributes **no** `release-asset-*`. The Docker Hub repository overview is pushed separately by [`publish-docker-readme-task.yml`](./.github/workflows/publish-docker-readme-task.yml), gated to `main`. - **Versioning is semantic and maintainer-controlled.** The `version` (major.minor) in [`version.json`](./version.json) is the version floor; NBGV appends the git height (the SemVer patch position) for the build version. `main` (the public release ref) builds a stable `X.Y.`; `develop` builds a prerelease `X.Y.-g`. `version.json`'s `publicReleaseRefSpec` is `^refs/heads/main$`. The maintainer edits `version.json`; dependency bumps, CI/workflow fixes, doc edits, and template re-syncs leave it untouched. - **Bump `version.json` only for functional changes, by maintainer instruction.** Raise the major/minor when the work being introduced warrants a new semantic version - a new feature, a behavior change, a breaking change - and do it in the PR that introduces that work (typically on `develop`). Do **not** bump on a fixed cadence or mechanically after a release. NBGV advances the patch (git height) on every commit automatically, so a release always gets a fresh build version without any `version.json` edit. - **No post-release bump; no develop-ahead requirement.** NBGV advances the patch (git height) on every commit, so a release always gets a fresh build version with no `version.json` edit and there is no `bump-version-X.Y` PR after a release. A `develop -> main` promotion carries whatever `version.json` is current: a promotion with a functional bump releases that new version on `main`; a maintenance-only promotion (dependency bumps, CI/doc fixes, template re-syncs) carries the unchanged `version.json` and `main` advances only its NBGV height. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 960b3860..083170ea 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -345,7 +345,7 @@ Two-phase model - reusable `*-task.yml` workflows orchestrated by two entry poin - **test-pull-request.yml**: PR validation. `changes` (dorny/paths-filter) -> always-on `unit-test` (Husky) + path-gated `smoke-build` (reduced, no-push) -> `Check pull request workflow status` aggregator (ruleset-bound name; requires `changes` succeeded). - **publish-release.yml**: the **sole publisher** (`push` + weekly `schedule` + `workflow_dispatch`). A `setup` job computes the branch list + publish gate; the `publish` matrix builds both branches via `build-release-task.yml` (executable 7-RID matrix + multi-arch Docker `linux/amd64,linux/arm64` + GitHub release), then `tool-versions`, `docker-readme` (main only), `date-badge` (main only). -- Reusable tasks: `build-release-task.yml`, `build-executable-task.yml`, `build-docker-task.yml`, `build-toolversions-task.yml`, `build-dockerreadme-task.yml`, `build-datebadge-task.yml`, `get-version-task.yml`. All thread a required `branch` input (config keys off it, never `github.ref_name`) plus `ref`/`smoke`. +- Reusable tasks: `build-release-task.yml`, `build-executable-task.yml`, `build-docker-task.yml`, `build-toolversions-task.yml`, `publish-docker-readme-task.yml`, `build-datebadge-task.yml`, `get-version-task.yml`. All thread a required `branch` input (config keys off it, never `github.ref_name`) plus `ref`/`smoke`. - Version info: `version.json` with Nerdbank.GitVersioning format. `get-version-task.yml` surfaces `SemVer2`, the assembly versions, and `GitCommitId` (used to pin the release `target_commitish`). - Branches: `main` (stable releases, `latest`), `develop` (pre-releases, `develop`).