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
17 changes: 6 additions & 11 deletions .github/workflows/build-executable-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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-<branch>-* 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 ]

Expand All @@ -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
7 changes: 5 additions & 2 deletions .github/workflows/build-release-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,13 @@ jobs:
with:
ref: ${{ needs.get-version.outputs.GitCommitId }}

- name: Download executable build artifacts step
# Collect every release-asset-<branch>-* 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
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/build-toolversions-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Create Docker README.md task
name: Publish Docker Hub readme task

on:
workflow_call:
Expand All @@ -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' }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<branch>-<target>`. The `github-release` job collects every `release-asset-<branch>-*` 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-<branch>-*`. 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-<branch>-<target>`. The `github-release` job collects every `release-asset-<branch>-*` 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-<branch>-*`. 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.<height>`; `develop` builds a prerelease `X.Y.<height>-g<sha>`. `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.
Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand Down