From ac9561d565641c2a7bfacf16c2b258c59e1a8cf9 Mon Sep 17 00:00:00 2001 From: Nick Gallegos Date: Wed, 14 Oct 2026 14:08:43 -0600 Subject: [PATCH 1/4] Add readme --- README.md | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 279 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index abcd284..7072bb1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,280 @@ # deployment-workflows -A collection of shared workflows for Particular's release deployment process + +Shared workflows and actions for Particular's release deployment process. + +- [Usage](#usage) +- Actions: [create-draft-release-with-assets](#create-draft-release-with-assets) · [push-nuget-packages](#push-nuget-packages) · [determine-image-tags](#determine-image-tags) +- Workflows: [verify-authorized-user](#verify-authorized-user) · [generate-release-notes](#generate-release-notes) · [publish-release-notes](#publish-release-notes) · [push-powershell-packages](#push-powershell-packages) · [push-release-info-to-aws-s3](#push-release-info-to-aws-s3) · [push-docker-images](#push-docker-images) + +## Usage + +```yaml +# Action - a step inside your job +- uses: Particular/deployment-workflows/.github/actions/@ + +# Workflow - a job in your workflow +jobs: + my-job: + uses: Particular/deployment-workflows/.github/workflows/.yml@ + secrets: inherit # workflows declare no secrets, so inherit is required when they need any +``` + +- Every action/workflow with a `dry-run` input logs what it *would* do instead of pushing/publishing. +- Scripts run in `pwsh`. +- A release's `version` is its tag (e.g. `1.2.3`), unless noted. + +--- + +# Actions + +## create-draft-release-with-assets + +Creates a draft GitHub release for the current `github.ref_name` and attaches every file found under the given paths. + +**Assumes:** artifacts are already built and sitting in the given paths. Job has `contents: write`. + +```yaml +# Basic +- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ + with: + asset-paths: nugets/ +``` + +```yaml +# Full +- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ + id: draft + with: + asset-paths: nugets/, powershellModules/, assets/*.zip # comma-separated paths/globs, searched recursively; default nugets,assets + dry-run: false # default false +- run: echo "${{ steps.draft.outputs.assets }}" # comma-separated list of attached files +``` + +## push-nuget-packages + +Downloads the `.nupkg` assets from a release and pushes them to the NuGet feed for the target environment (duplicates are skipped). + +| `deploy-environment` | Assets come from | Pushed to | Auth | +|---|---|---|---| +| `staging` | the **draft** release whose *name* is `version` | Feedz (`particular-software/packages`) | `api-key` | +| `production` | the **published** release tagged `version` | nuget.org | NuGet trusted publishing (OIDC) | + +**Assumes:** +- The release already has `.nupkg` assets. +- `api-key` is always required (only used for `staging`). +- `production`: job has `id-token: write` and `contents: read`. + +```yaml +# Basic +- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ + with: + version: ${{ github.ref_name }} + deploy-environment: production + api-key: ${{ secrets.FEEDZ_API_KEY }} +``` + +```yaml +# Full +- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ + with: + version: 1.2.3 # release tag (production) or release *name* (staging) + deploy-environment: staging # staging | production + api-key: ${{ secrets.FEEDZ_API_KEY }} # required; used for the staging push + exclude-package-pattern: '*.Tests.*' # default: none. Glob of package filenames to skip + dry-run: false # default false +``` + +## determine-image-tags + +Works out which Docker tags a version should get (`1.2.3`, `1.2`, `1`, `latest`) by comparing it with the repo's published releases. + +**Assumes:** `version` is `X.Y.Z` or `X.Y.Z-prerelease`. Prereleases only get their exact tag. Floating tags only move forward (a backport won't take `latest` or the major tag). + +```yaml +- uses: Particular/deployment-workflows/.github/actions/determine-image-tags@ + id: tags + with: + version: 1.2.3 +- run: | + echo "${{ steps.tags.outputs.image-tags }}" # comma-separated, e.g. 1.2.3,latest,1,1.2 + echo "${{ steps.tags.outputs.latest }}" # true/false + echo "${{ steps.tags.outputs.major }} ${{ steps.tags.outputs.minor }} ${{ steps.tags.outputs.patch }}" + echo "${{ steps.tags.outputs.prerelease }}" # label, empty when stable +``` + +--- + +# Workflows + +## verify-authorized-user + +Fails unless the triggering user (`github.actor`) is listed in the `AUTHORIZED_RELEASE_USERS` variable. Use as a gate with `needs:`. + +**Assumes:** repo/org variable `AUTHORIZED_RELEASE_USERS` is a comma-separated list of GitHub usernames. + +```yaml +jobs: + authorize: + uses: Particular/deployment-workflows/.github/workflows/verify-authorized-user.yml@ + release: + needs: authorize + # ... +``` + +The `actor` and `authorized-users` inputs are test-only overrides and fail outside this repository. + +## generate-release-notes + +Fills the draft release's notes from the closed issues of the milestone named after the version (via GitReleaseManager). + +**Assumes:** +- A milestone named exactly `version` exists. +- Repo has `global.json`, `.release/GitReleaseManager.yaml` and `.release/.templates/default/issue-note.sbn`. +- Caller grants `contents: write`. + +```yaml +# Basic +generate-notes: + uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ + with: + version: 1.2.3 +``` + +```yaml +# Full +generate-notes: + uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ + with: + version: 1.2.3 # milestone and release name + dry-run: false # default false +``` + +## publish-release-notes + +Publishes the draft release for `version` (via GitReleaseManager). + +**Assumes:** the draft release exists. Caller grants `contents: write`. + +```yaml +# Basic +publish: + uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ + with: + version: 1.2.3 +``` + +```yaml +# Full +publish: + uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ + with: + version: 1.2.3 # release tag + dry-run: false # default false +``` + +## push-powershell-packages + +Downloads matching release assets and publishes them to the PowerShell gallery for the target environment. + +- `staging`: assets come from the **draft** release named `version`; pushed to the Feedz gallery. +- `production`: assets come from the **published** release tagged `version`; pushed to the PowerShell Gallery. + +**Assumes:** the release already has the assets; the gallery variables/secrets below are available to the caller. The gallery is registered if missing. + +| | `staging` | `production` | +|---|---|---| +| vars | `FEEDZ_GALLERY_NAME`, `FEEDZ_GALLERY_SOURCE_LOCATION` | `POWERSHELL_GALLERY_NAME`, `POWERSHELL_GALLERY_SOURCE_LOCATION` | +| secret | `FEEDZ_GALLERY_KEY` | `POWERSHELL_GALLERY_KEY` | + +```yaml +# Basic +push-ps: + uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ + with: + version: 1.2.3 + deploy-environment: production + package-pattern: '*.nupkg' + secrets: inherit +``` + +```yaml +# Full +push-ps: + uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ + with: + version: 1.2.3 # release tag (production) or release *name* (staging) + deploy-environment: staging # staging | production + package-pattern: '*.nupkg' # asset glob to download + dry-run: false # default false + secrets: inherit +``` + +## push-release-info-to-aws-s3 + +Uploads a release's info (tag, URL, publish date, `.exe` assets) as JSON to `s3:///.txt`, then verifies the upload. + +**Assumes:** the release is published (not draft/prerelease), tagged `X.Y.Z`, and has at least one `.exe` asset. Bucket is in `us-west-2`. + +```yaml +# Basic +push-s3: + uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ + with: + version: 1.2.3 + product: ServiceInsight + bucket: my-bucket + secrets: inherit # needs AWS_ACCESSKEY and AWS_SECRETKEY secrets +``` + +```yaml +# Full +push-s3: + uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ + with: + version: 1.2.3 # release tag + product: ServiceInsight # file name: serviceinsight.txt + bucket: my-bucket + dry-run: false # default false + secrets: inherit # AWS_ACCESSKEY and AWS_SECRETKEY +``` + +## push-docker-images + +Copies images from GitHub Container Registry to Docker Hub with the tags from [determine-image-tags](#determine-image-tags), and updates the Docker Hub descriptions when the version is the new `latest`. + +**Assumes:** +- Images are already in `ghcr.io/particular/:`. +- An image config file exists (see below). +- Caller grants `contents: read` and `packages: write`. + +```yaml +# Basic +push-docker: + uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ + with: + version: 1.2.3 + secrets: inherit # needs DOCKERHUB_USERNAME and DOCKERHUB_TOKEN +``` + +```yaml +# Full +push-docker: + uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ + with: + version: 1.2.3 + image-config-file: .github/docker-images.json # default shown + dry-run: false # default false + secrets: inherit # DOCKERHUB_USERNAME and DOCKERHUB_TOKEN +``` + +Image config file - a non-empty JSON array; `name` is both the GHCR image and the `particular/` Docker Hub repo: + +```json +[ + { + "name": "servicecontrol", + "readme_filepath": "docker/servicecontrol/README.md", + "short_description": "Particular Service Platform monitoring" + } +] +``` From 2fb04adf8b3625e58a85b07c6d6506d8c45724d5 Mon Sep 17 00:00:00 2001 From: Tamara Rivera Date: Wed, 21 Oct 2026 15:47:14 -0700 Subject: [PATCH 2/4] Refactor README section headers Updated section headers in README for consistency and clarity. --- README.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7072bb1..1b82305 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ jobs: - Scripts run in `pwsh`. - A release's `version` is its tag (e.g. `1.2.3`), unless noted. ---- -# Actions -## create-draft-release-with-assets +## Actions + +### create-draft-release-with-assets Creates a draft GitHub release for the current `github.ref_name` and attaches every file found under the given paths. @@ -50,7 +50,7 @@ Creates a draft GitHub release for the current `github.ref_name` and attaches ev - run: echo "${{ steps.draft.outputs.assets }}" # comma-separated list of attached files ``` -## push-nuget-packages +### push-nuget-packages Downloads the `.nupkg` assets from a release and pushes them to the NuGet feed for the target environment (duplicates are skipped). @@ -84,7 +84,7 @@ Downloads the `.nupkg` assets from a release and pushes them to the NuGet feed f dry-run: false # default false ``` -## determine-image-tags +### determine-image-tags Works out which Docker tags a version should get (`1.2.3`, `1.2`, `1`, `latest`) by comparing it with the repo's published releases. @@ -102,11 +102,9 @@ Works out which Docker tags a version should get (`1.2.3`, `1.2`, `1`, `latest`) echo "${{ steps.tags.outputs.prerelease }}" # label, empty when stable ``` ---- - -# Workflows +## Workflows -## verify-authorized-user +### verify-authorized-user Fails unless the triggering user (`github.actor`) is listed in the `AUTHORIZED_RELEASE_USERS` variable. Use as a gate with `needs:`. @@ -123,7 +121,7 @@ jobs: The `actor` and `authorized-users` inputs are test-only overrides and fail outside this repository. -## generate-release-notes +### generate-release-notes Fills the draft release's notes from the closed issues of the milestone named after the version (via GitReleaseManager). @@ -149,7 +147,7 @@ generate-notes: dry-run: false # default false ``` -## publish-release-notes +### publish-release-notes Publishes the draft release for `version` (via GitReleaseManager). @@ -172,7 +170,7 @@ publish: dry-run: false # default false ``` -## push-powershell-packages +### push-powershell-packages Downloads matching release assets and publishes them to the PowerShell gallery for the target environment. @@ -209,7 +207,7 @@ push-ps: secrets: inherit ``` -## push-release-info-to-aws-s3 +### push-release-info-to-aws-s3 Uploads a release's info (tag, URL, publish date, `.exe` assets) as JSON to `s3:///.txt`, then verifies the upload. @@ -238,7 +236,7 @@ push-s3: secrets: inherit # AWS_ACCESSKEY and AWS_SECRETKEY ``` -## push-docker-images +### push-docker-images Copies images from GitHub Container Registry to Docker Hub with the tags from [determine-image-tags](#determine-image-tags), and updates the Docker Hub descriptions when the version is the new `latest`. From 728cf084e235ba4bc66cda1802fc72b437efe00e Mon Sep 17 00:00:00 2001 From: Tamara Rivera Date: Wed, 21 Oct 2026 15:48:01 -0700 Subject: [PATCH 3/4] Clean up blank lines in README Removed unnecessary blank lines in the README. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 1b82305..acd0e03 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ jobs: - Scripts run in `pwsh`. - A release's `version` is its tag (e.g. `1.2.3`), unless noted. - - ## Actions ### create-draft-release-with-assets From f56913f7a892c09ad53207f88fe8ac766a9949b8 Mon Sep 17 00:00:00 2001 From: Nick Gallegos Date: Wed, 21 Oct 2026 16:51:44 -0600 Subject: [PATCH 4/4] Apply suggestions from @tamararivera Co-authored-by: Tamara Rivera --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index acd0e03..422e7d4 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ Shared workflows and actions for Particular's release deployment process. ```yaml # Action - a step inside your job -- uses: Particular/deployment-workflows/.github/actions/@ +- uses: Particular/deployment-workflows/.github/actions/@ # Workflow - a job in your workflow jobs: my-job: - uses: Particular/deployment-workflows/.github/workflows/.yml@ + uses: Particular/deployment-workflows/.github/workflows/.yml@ secrets: inherit # workflows declare no secrets, so inherit is required when they need any ``` @@ -33,14 +33,14 @@ Creates a draft GitHub release for the current `github.ref_name` and attaches ev ```yaml # Basic -- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ +- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ with: asset-paths: nugets/ ``` ```yaml # Full -- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ +- uses: Particular/deployment-workflows/.github/actions/create-draft-release-with-assets@ id: draft with: asset-paths: nugets/, powershellModules/, assets/*.zip # comma-separated paths/globs, searched recursively; default nugets,assets @@ -64,7 +64,7 @@ Downloads the `.nupkg` assets from a release and pushes them to the NuGet feed f ```yaml # Basic -- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ +- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ with: version: ${{ github.ref_name }} deploy-environment: production @@ -73,9 +73,9 @@ Downloads the `.nupkg` assets from a release and pushes them to the NuGet feed f ```yaml # Full -- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ +- uses: Particular/deployment-workflows/.github/actions/push-nuget-packages@ with: - version: 1.2.3 # release tag (production) or release *name* (staging) + version: 1.2.3 # release tag or name deploy-environment: staging # staging | production api-key: ${{ secrets.FEEDZ_API_KEY }} # required; used for the staging push exclude-package-pattern: '*.Tests.*' # default: none. Glob of package filenames to skip @@ -89,7 +89,7 @@ Works out which Docker tags a version should get (`1.2.3`, `1.2`, `1`, `latest`) **Assumes:** `version` is `X.Y.Z` or `X.Y.Z-prerelease`. Prereleases only get their exact tag. Floating tags only move forward (a backport won't take `latest` or the major tag). ```yaml -- uses: Particular/deployment-workflows/.github/actions/determine-image-tags@ +- uses: Particular/deployment-workflows/.github/actions/determine-image-tags@ id: tags with: version: 1.2.3 @@ -111,7 +111,7 @@ Fails unless the triggering user (`github.actor`) is listed in the `AUTHORIZED_R ```yaml jobs: authorize: - uses: Particular/deployment-workflows/.github/workflows/verify-authorized-user.yml@ + uses: Particular/deployment-workflows/.github/workflows/verify-authorized-user.yml@ release: needs: authorize # ... @@ -131,7 +131,7 @@ Fills the draft release's notes from the closed issues of the milestone named af ```yaml # Basic generate-notes: - uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ + uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ with: version: 1.2.3 ``` @@ -139,7 +139,7 @@ generate-notes: ```yaml # Full generate-notes: - uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ + uses: Particular/deployment-workflows/.github/workflows/generate-release-notes.yml@ with: version: 1.2.3 # milestone and release name dry-run: false # default false @@ -154,7 +154,7 @@ Publishes the draft release for `version` (via GitReleaseManager). ```yaml # Basic publish: - uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ + uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ with: version: 1.2.3 ``` @@ -162,7 +162,7 @@ publish: ```yaml # Full publish: - uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ + uses: Particular/deployment-workflows/.github/workflows/publish-release-notes.yml@ with: version: 1.2.3 # release tag dry-run: false # default false @@ -185,7 +185,7 @@ Downloads matching release assets and publishes them to the PowerShell gallery f ```yaml # Basic push-ps: - uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ with: version: 1.2.3 deploy-environment: production @@ -196,7 +196,7 @@ push-ps: ```yaml # Full push-ps: - uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-powershell-packages.yml@ with: version: 1.2.3 # release tag (production) or release *name* (staging) deploy-environment: staging # staging | production @@ -214,7 +214,7 @@ Uploads a release's info (tag, URL, publish date, `.exe` assets) as JSON to `s3: ```yaml # Basic push-s3: - uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ with: version: 1.2.3 product: ServiceInsight @@ -225,7 +225,7 @@ push-s3: ```yaml # Full push-s3: - uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-release-info-to-aws-s3.yml@ with: version: 1.2.3 # release tag product: ServiceInsight # file name: serviceinsight.txt @@ -246,7 +246,7 @@ Copies images from GitHub Container Registry to Docker Hub with the tags from [d ```yaml # Basic push-docker: - uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ with: version: 1.2.3 secrets: inherit # needs DOCKERHUB_USERNAME and DOCKERHUB_TOKEN @@ -255,7 +255,7 @@ push-docker: ```yaml # Full push-docker: - uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ + uses: Particular/deployment-workflows/.github/workflows/push-docker-images.yml@ with: version: 1.2.3 image-config-file: .github/docker-images.json # default shown