diff --git a/.github/workflows/go-release.yml b/.github/workflows/go-release.yml index e5482e2b..91dafbad 100644 --- a/.github/workflows/go-release.yml +++ b/.github/workflows/go-release.yml @@ -335,6 +335,14 @@ on: description: 'Name of a GitHub Environment to scope the manifest publish job to. Empty (default) keeps the job unscoped, exactly as before. Set it to put the AWS_INIT_DATA_ROLE_ARN assume-role behind the environment''s protection rules — deployment tag policies (only protected tags may publish) and, optionally, required reviewers.' type: string default: '' + manifest_go_mod_path: + description: 'Path to the go.mod used by the manifest publish scope gate (direct lib-auth dependency check), relative to the repo root. Default is the root go.mod, exactly as before. A monorepo with per-service modules and no root go.mod points this at one module that has the direct dependency — manifest discovery stays repo-wide either way.' + type: string + default: 'go.mod' + manifest_component_tags: + description: 'Accept per-component release tags (-vX.Y.Z, -beta.N, -rc.N) in the manifest publish environment gate, on top of plain vX.Y.Z tags. Default false keeps the gate plain-semver-only, exactly as before. The semver core stays strict either way — only an optional lowercase component prefix is allowed.' + type: boolean + default: false # ----------------- API Dog E2E (api-dog-e2e-tests.yml) ----------------- enable_apidog_e2e: description: 'Run the ApiDog E2E test job on tag push after a successful gitops-update' @@ -877,6 +885,9 @@ jobs: - name: Determine environment folder from tag id: env + env: + # A workflow_call boolean reaches the shell as the string true/false. + MANIFEST_COMPONENT_TAGS: ${{ inputs.manifest_component_tags }} run: | set -euo pipefail TAG="${GITHUB_REF#refs/tags/}" @@ -889,17 +900,29 @@ jobs: # otherwise let `v01.2.3` slip past the gate as a production tag. NUM='(0|[1-9][0-9]*)' CORE="v${NUM}\.${NUM}\.${NUM}" - if [[ "$TAG" =~ ^${CORE}-beta\.${NUM}$ ]]; then + # manifest_component_tags widens the gate by ONE optional prefix — a + # monorepo cutting `-vX.Y.Z` per rail. It is a prefix on the + # same anchored patterns, never a replacement: the semver core, the + # anchors and the three-way order below are untouched, so `v01.2.3` + # and `spi-v1.2.3-hotfix` keep falling through to the empty folder. + PREFIX='' + if [[ "${MANIFEST_COMPONENT_TAGS}" == 'true' ]]; then + PREFIX='([a-z][a-z0-9-]*-)?' + SHAPES='vX.Y.Z, vX.Y.Z-beta.N, vX.Y.Z-rc.N, optionally prefixed with -' + else + SHAPES='vX.Y.Z, vX.Y.Z-beta.N, vX.Y.Z-rc.N' + fi + if [[ "$TAG" =~ ^${PREFIX}${CORE}-beta\.${NUM}$ ]]; then FOLDER="development" - elif [[ "$TAG" =~ ^${CORE}-rc\.${NUM}$ ]]; then + elif [[ "$TAG" =~ ^${PREFIX}${CORE}-rc\.${NUM}$ ]]; then FOLDER="staging" - elif [[ "$TAG" =~ ^${CORE}$ ]]; then + elif [[ "$TAG" =~ ^${PREFIX}${CORE}$ ]]; then FOLDER="production" else FOLDER="" fi echo "folder=${FOLDER}" >> "$GITHUB_OUTPUT" - [[ -n "$FOLDER" ]] && echo "📁 Environment: ${FOLDER}" || echo "::warning::Tag '${TAG}' is not a release semver tag (vX.Y.Z, vX.Y.Z-beta.N, vX.Y.Z-rc.N) — manifest publish skipped." + [[ -n "$FOLDER" ]] && echo "📁 Environment: ${FOLDER}" || echo "::warning::Tag '${TAG}' is not a release semver tag (${SHAPES}) — manifest publish skipped." # ----------------- AWS credentials (guarded) ----------------- # Skip on a dry-run (the composite previews with no AWS call) and when the @@ -920,6 +943,8 @@ jobs: with: environment: ${{ steps.env.outputs.folder }} github-token: ${{ github.token }} + # Only moves the lib-auth scope gate; manifest discovery stays repo-wide. + go-mod-path: ${{ inputs.manifest_go_mod_path }} dry-run: ${{ inputs.dry_run }} # ----------------- Extra Builds (tag push, opt-in) ----------------- diff --git a/docs/go-release.md b/docs/go-release.md index 7414269a..27695aab 100644 --- a/docs/go-release.md +++ b/docs/go-release.md @@ -94,6 +94,8 @@ A third layout needs `release_single_app: true`: **one semantic-release tag for | `s3_upload_mirrors` | JSON object mapping an `s3_uploads` bucket to a second bucket that receives every file uploaded to it under the same key (see [Mirror bucket](#mirror-bucket)) | string | `{"lerian-migration-files":{"bucket":"lerian-development-migrations","region":"us-east-2"}}` | | `run_manifest_publish` | Publish this service's own `permissions.yaml` to the shared Access-Manager RI catalog in S3 on tag push (see [Permission manifest publish](#permission-manifest-publish-ri)) | boolean | `true` | | `manifest_publish_environment` | GitHub Environment to scope the manifest publish job to, putting the `AWS_INIT_DATA_ROLE_ARN` assume-role behind that environment's protection rules. Empty keeps the job unscoped | string | `''` | +| `manifest_go_mod_path` | Path to the `go.mod` the manifest publish scope gate reads for the direct lib-auth dependency, relative to the repo root. Point it at a service module in a monorepo with no root `go.mod` — discovery stays repo-wide | string | `'go.mod'` | +| `manifest_component_tags` | Also accept per-component release tags (`-vX.Y.Z`, `-beta.N`, `-rc.N`) in the environment gate. The semver core stays strict either way | boolean | `false` | | `enable_apidog_e2e` | Run the ApiDog E2E test job on tag push after a successful gitops-update | boolean | `false` | | `apidog_runner_type` | Runner for the ApiDog E2E test job (needs reach to the deployed environment) | string | `eveo-lxc-runners` | | `apidog_auto_detect_environment` | Auto-detect the ApiDog environment from the tag (beta → dev, rc → stg); when `false`, uses `APIDOG_ENVIRONMENT_ID` | boolean | `true` | @@ -243,6 +245,33 @@ Set `run_manifest_publish: false` to opt out. For an extra gate on **who** may publish, set `manifest_publish_environment` to the name of a GitHub Environment configured in the caller repo. The publish job then runs under that environment, so its protection rules apply to the assume-role — a **deployment tag policy** (e.g. `v*.*.*`) restricts publishing to the tags you protect, and required reviewers can hold a production publish for approval. Left empty (the default) the job stays unscoped, as before. +### Per-component tags and a non-root `go.mod` + +A monorepo that cuts one version line per service — tags like `spi-v1.2.3`, `slc-edge-v1.2.3-beta.4` — matches none of those shapes and publishes nothing. Set `manifest_component_tags: true` to allow **one optional lowercase component prefix** before the version: + +| Tag | `manifest_component_tags: false` | `manifest_component_tags: true` | +|---|---|---| +| `v1.2.3` | `production` | `production` | +| `spi-v1.2.3` | skipped | `production` | +| `slc-edge-v1.2.3-beta.4` | skipped | `development` | +| `cockpit-v2.0.0-rc.3` | skipped | `staging` | +| `v01.2.3` | skipped | skipped | +| `spi-v1.2.3-hotfix` | skipped | skipped | + +The prefix is the **only** thing that widens: the semver core keeps rejecting leading zeroes and arbitrary suffixes, and the prefix itself must start with a lowercase letter and hold only lowercase letters, digits and hyphens. A tag off-shape still publishes nothing. Leave the input at its default and the gate behaves exactly as it always has. + +`manifest_go_mod_path` moves **only** the lib-auth scope gate. A repo with per-service modules and no root `go.mod` points it at one module carrying the direct dependency; manifest discovery stays repo-wide, so the repo still publishes one object per `service:` value found anywhere in the tree: + +```yaml + uses: LerianStudio/github-actions-shared-workflows/.github/workflows/go-release.yml@tier-1 + with: + run_manifest_publish: true + manifest_go_mod_path: services/spi/go.mod + manifest_component_tags: true + secrets: + AWS_INIT_DATA_ROLE_ARN: ${{ secrets.AWS_INIT_DATA_ROLE_ARN }} +``` + ## ApiDog E2E tests Set `enable_apidog_e2e: true` to run [api-dog-e2e-tests](./api-dog-e2e-tests.md) on tag push after a successful `update_gitops`. The job is skipped on branch pushes and when the gitops update did not succeed.