From eb9fc44c57cfeca7ccb4152911c1d1b25cf22ca9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 13:03:07 -0400 Subject: [PATCH 1/9] ci(publish): dual-publish to legacy and MCD CodeArtifact Swaps the local .github/actions/code-artifact composite action for the shared OvertureMaps/workflows/.github/actions/setup-codeartifact action (format: pypi, from workflows#80), then calls it twice in main-publish's publish job, once per account, so overture-schema packages land in both the legacy Airflow CodeArtifact account (505071440022) and the new MCD account (763944545891) during the ops-team#466 migration. The publish step now attempts both accounts even if one fails, so an outage in one does not block the other. The pull_request smoke test still stays read-only against both. reusable-check-python-package-versions.yaml (the only other caller of the local action) is migrated to the shared action too, so nothing references .github/actions/code-artifact anymore and it can be deleted. Fixes OvertureMaps/schema#725 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/actions/code-artifact/action.yml | 67 ---------------- .github/actions/compute-version/action.yml | 4 +- .github/workflows/main-publish.yaml | 76 +++++++++++++------ ...eusable-check-python-package-versions.yaml | 22 ++---- 4 files changed, 63 insertions(+), 106 deletions(-) delete mode 100644 .github/actions/code-artifact/action.yml diff --git a/.github/actions/code-artifact/action.yml b/.github/actions/code-artifact/action.yml deleted file mode 100644 index d64b607c9..000000000 --- a/.github/actions/code-artifact/action.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: CodeArtifact credentials -description: > - Retrieves an authorization token and constructs index/publish URLs for AWS - CodeArtifact. Assumes AWS credentials are already configured in the job. - -inputs: - aws_account_id: - description: AWS account ID that owns the CodeArtifact domain. - required: false - default: "505071440022" - aws_region: - description: AWS region where the CodeArtifact repository is hosted. - required: false - default: us-west-2 - domain: - description: CodeArtifact domain name. - required: false - default: overture-pypi - repository: - description: CodeArtifact repository name. - required: false - default: overture - -outputs: - token: - description: CodeArtifact authorization token (masked in logs). - value: ${{ steps.creds.outputs.token }} - index_url: - description: > - Full index URL with embedded credentials, suitable for - `--index-url` / `--extra-index-url` in pip/uv. - value: ${{ steps.creds.outputs.index_url }} - publish_url: - description: > - Publish endpoint URL (no credentials embedded — pass token separately). - value: ${{ steps.creds.outputs.publish_url }} - -runs: - using: composite - steps: - - name: Get CodeArtifact credentials - id: creds - shell: bash - env: - AWS_ACCOUNT_ID: ${{ inputs.aws_account_id }} - AWS_REGION: ${{ inputs.aws_region }} - DOMAIN: ${{ inputs.domain }} - REPOSITORY: ${{ inputs.repository }} - run: | - set -euo pipefail - - token=$(aws codeartifact get-authorization-token \ - --region "$AWS_REGION" \ - --domain "$DOMAIN" \ - --domain-owner "$AWS_ACCOUNT_ID" \ - --query authorizationToken \ - --output text) - echo "::add-mask::${token}" - echo "token=${token}" >> "$GITHUB_OUTPUT" - - base_url="https://${DOMAIN}-${AWS_ACCOUNT_ID}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${REPOSITORY}" - - index_url="https://aws:${token}@${DOMAIN}-${AWS_ACCOUNT_ID}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${REPOSITORY}/simple/" - echo "::add-mask::${index_url}" - echo "index_url=${index_url}" >> "$GITHUB_OUTPUT" - - echo "publish_url=${base_url}" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/compute-version/action.yml b/.github/actions/compute-version/action.yml index 7bc288013..747b76627 100644 --- a/.github/actions/compute-version/action.yml +++ b/.github/actions/compute-version/action.yml @@ -35,8 +35,8 @@ inputs: index_url: description: > PyPI simple index URL with embedded credentials for querying - CodeArtifact. Obtain via the `.github/actions/code-artifact` action's - `index_url` output after configuring AWS credentials. + CodeArtifact. Obtain via the `setup-codeartifact` action's + (`OvertureMaps/workflows`) `pypi-index-url` output, `format: pypi`. required: true outputs: diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index dba706ae2..d5af689d2 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -2,12 +2,13 @@ name: Main publish # Runs on every push to main that touches packages/**. For each package whose # directory changed WITHOUT a version bump, computes an internal build version -# (main context; see .github/actions/compute-version) and publishes it to -# CodeArtifact. Bumped packages release via release-trigger + release-publish -# instead (see docs/versioning.md) -- publishing a .postN for them here too -# would be redundant. +# (main context; see .github/actions/compute-version) and dual-publishes it to +# both the legacy and MCD CodeArtifact accounts (ops-team#466). Bumped +# packages release via release-trigger + release-publish instead (see +# docs/versioning.md) -- publishing a .postN for them here too would be +# redundant. # -# Also runs (read-only) on PRs that touch the compute-version/code-artifact/ +# Also runs (read-only) on PRs that touch the compute-version/ # detect-affected-packages composite actions or this workflow itself, as a # smoke test for their wiring: the version is computed but nothing is # published. @@ -20,7 +21,6 @@ on: pull_request: paths: - '.github/actions/compute-version/**' - - '.github/actions/code-artifact/**' - '.github/actions/detect-affected-packages/**' - '.github/workflows/main-publish.yaml' @@ -99,27 +99,45 @@ jobs: # PR smoke test only queries CodeArtifact (via compute-version, below) # and never publishes -- the publish step is skipped for pull_request. # Assume the read-only role there so a wiring mistake can't turn the - # smoke test into a real publish. - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 + # smoke test into a real publish. Dual-published per ops-team#466: the + # legacy Airflow account is being phased out in favor of the MCD + # account, so both get every build until the migration completes. + - name: Get legacy CodeArtifact credentials + id: ca-legacy + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) with: - aws-region: us-west-2 - role-to-assume: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_${{ github.event_name == 'pull_request' && 'ReadOnly' || 'Publish' }} - role-session-name: GitHubActions_${{github.job}}_${{github.run_id}} - - - name: Get CodeArtifact credentials - id: ca - uses: ./.github/actions/code-artifact + aws-role-arn: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_${{ github.event_name == 'pull_request' && 'ReadOnly' || 'Publish' }} + codeartifact-domain: overture-pypi + codeartifact-domain-owner: "505071440022" + codeartifact-repository: overture + format: pypi + + # MCD role (omf-core-data-opentofu#91) has no separate read-only + # variant, so the PR smoke test reuses the publish role here too -- + # it's still never invoked to publish outside a real push (see the + # if: on the publish step below). + - name: Get MCD CodeArtifact credentials + id: ca-mcd + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) + with: + aws-role-arn: arn:aws:iam::763944545891:role/codeartifact-pypi-publish-oidc-overturemaps + codeartifact-domain: overture-pypi + codeartifact-domain-owner: "763944545891" + codeartifact-repository: overture + format: pypi # Delegates to the same composite action the PR smoke test exercises, so # the version formula only has one implementation to keep correct. + # Anchored to the legacy account only: both accounts host the same + # package history, so either would compute the same version, and + # picking one keeps this step from depending on the MCD dual-publish. - name: Compute version id: compute uses: ./.github/actions/compute-version with: package: ${{ matrix.package }} context: main - index_url: ${{ steps.ca.outputs.index_url }} + index_url: ${{ steps.ca-legacy.outputs.pypi-index-url }} # overture-schema-pyspark ships generated validation expressions that # are not committed to git; its packages//scripts/prebuild.sh @@ -150,15 +168,20 @@ jobs: PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] run: uv build --package "${PACKAGE}" + # Publishes to both accounts even if one fails, so a legacy outage + # doesn't hold back the MCD build (or vice versa) -- the step still + # fails the job overall if either uv publish call does. - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to CodeArtifact if: github.event_name != 'pull_request' env: - CA_TOKEN: ${{ steps.ca.outputs.token }} - CA_PUBLISH_URL: ${{ steps.ca.outputs.publish_url }} + LEGACY_TOKEN: ${{ steps.ca-legacy.outputs.token }} + LEGACY_PUBLISH_URL: ${{ steps.ca-legacy.outputs.pypi-publish-url }} + MCD_TOKEN: ${{ steps.ca-mcd.outputs.token }} + MCD_PUBLISH_URL: ${{ steps.ca-mcd.outputs.pypi-publish-url }} PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] VERSION: ${{ steps.compute.outputs.version }} # zizmor: ignore[template-injection] run: | - set -euo pipefail + set -uo pipefail wheel="dist/${PACKAGE//-/_}-${VERSION}-py3-none-any.whl" if [ ! -f "$wheel" ]; then echo " Wheel file [$wheel] not found. Aborting!" @@ -169,6 +192,13 @@ jobs: echo " Source tarball file [$tarball] not found. Aborting!" exit 1 fi - uv publish "$wheel" "$tarball" \ - -t "${CA_TOKEN}" \ - --publish-url "${CA_PUBLISH_URL}" + + status=0 + + echo "Publishing to legacy CodeArtifact account (505071440022)" + uv publish "$wheel" "$tarball" -t "${LEGACY_TOKEN}" --publish-url "${LEGACY_PUBLISH_URL}" || status=1 + + echo "Publishing to MCD CodeArtifact account (763944545891)" + uv publish "$wheel" "$tarball" -t "${MCD_TOKEN}" --publish-url "${MCD_PUBLISH_URL}" || status=1 + + exit "$status" diff --git a/.github/workflows/reusable-check-python-package-versions.yaml b/.github/workflows/reusable-check-python-package-versions.yaml index 9a1be66aa..0e0b32687 100644 --- a/.github/workflows/reusable-check-python-package-versions.yaml +++ b/.github/workflows/reusable-check-python-package-versions.yaml @@ -95,28 +95,22 @@ jobs: echo "num_changed_packages=${COUNT}" } >> "$GITHUB_OUTPUT" - - name: Configure AWS credentials - if: steps.save-changes.outputs.num_changed_packages > 0 - uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 - with: - aws-region: ${{ inputs.aws_region }} - role-to-assume: arn:aws:iam::${{ inputs.aws_account_id }}:role/${{ inputs.aws_iam_role_name }} - role-session-name: GitHubActions_${{github.job}}_${{github.run_id}} - - name: Get CodeArtifact index URL id: get-code-artifact-index-url if: steps.save-changes.outputs.num_changed_packages > 0 - uses: ./.github/actions/code-artifact + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) with: - aws_account_id: ${{ inputs.aws_account_id }} - aws_region: ${{ inputs.aws_region }} - domain: ${{ inputs.domain }} - repository: ${{ inputs.repository }} + aws-role-arn: arn:aws:iam::${{ inputs.aws_account_id }}:role/${{ inputs.aws_iam_role_name }} + aws-region: ${{ inputs.aws_region }} + codeartifact-domain: ${{ inputs.domain }} + codeartifact-domain-owner: ${{ inputs.aws_account_id }} + codeartifact-repository: ${{ inputs.repository }} + format: pypi - name: Fail if any of the new versions already exist in the repo if: steps.save-changes.outputs.num_changed_packages > 0 env: - INDEX_URL: ${{ steps.get-code-artifact-index-url.outputs.index_url }} + INDEX_URL: ${{ steps.get-code-artifact-index-url.outputs.pypi-index-url }} DIFF: ${{ steps.diff.outputs.diff }} # zizmor: ignore[template-injection] run: | From ae3656aa427a13c95a60b542d72e5033fb629b99 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 13:43:41 -0400 Subject: [PATCH 2/9] ci(publish): use uv-publish-to-codeartifact for the dual publish Swaps the hand-rolled `uv publish` invocations for the new shared OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact composite action (workflows#97), which wraps setup-codeartifact + uv publish in one step per account. Drops the now-unused MCD setup-codeartifact auth step, since the new action authenticates for itself. Pinned to workflows#97's branch head SHA for now, since it hasn't merged yet; will re-pin to main once it does. Publishing is now two `uses:` steps (one per account) with continue-on-error plus an explicit failure check after, replicating the previous both-accounts-attempted-even-if-one-fails behavior without a run: step wrapping two external actions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 73 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index d5af689d2..229796e37 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -97,11 +97,9 @@ jobs: run: uv sync --locked --all-packages # PR smoke test only queries CodeArtifact (via compute-version, below) - # and never publishes -- the publish step is skipped for pull_request. - # Assume the read-only role there so a wiring mistake can't turn the - # smoke test into a real publish. Dual-published per ops-team#466: the - # legacy Airflow account is being phased out in favor of the MCD - # account, so both get every build until the migration completes. + # and never publishes -- the publish steps are skipped for + # pull_request. Assume the read-only role there so a wiring mistake + # can't turn the smoke test into a real publish. - name: Get legacy CodeArtifact credentials id: ca-legacy uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) @@ -112,25 +110,12 @@ jobs: codeartifact-repository: overture format: pypi - # MCD role (omf-core-data-opentofu#91) has no separate read-only - # variant, so the PR smoke test reuses the publish role here too -- - # it's still never invoked to publish outside a real push (see the - # if: on the publish step below). - - name: Get MCD CodeArtifact credentials - id: ca-mcd - uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) - with: - aws-role-arn: arn:aws:iam::763944545891:role/codeartifact-pypi-publish-oidc-overturemaps - codeartifact-domain: overture-pypi - codeartifact-domain-owner: "763944545891" - codeartifact-repository: overture - format: pypi - # Delegates to the same composite action the PR smoke test exercises, so # the version formula only has one implementation to keep correct. # Anchored to the legacy account only: both accounts host the same # package history, so either would compute the same version, and - # picking one keeps this step from depending on the MCD dual-publish. + # picking one keeps this step from depending on the MCD dual-publish + # below (which authenticates for itself via uv-publish-to-codeartifact). - name: Compute version id: compute uses: ./.github/actions/compute-version @@ -168,20 +153,14 @@ jobs: PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] run: uv build --package "${PACKAGE}" - # Publishes to both accounts even if one fails, so a legacy outage - # doesn't hold back the MCD build (or vice versa) -- the step still - # fails the job overall if either uv publish call does. - - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to CodeArtifact + - name: Locate built artifacts for ${{ matrix.package }} ${{ steps.compute.outputs.version }} + id: artifacts if: github.event_name != 'pull_request' env: - LEGACY_TOKEN: ${{ steps.ca-legacy.outputs.token }} - LEGACY_PUBLISH_URL: ${{ steps.ca-legacy.outputs.pypi-publish-url }} - MCD_TOKEN: ${{ steps.ca-mcd.outputs.token }} - MCD_PUBLISH_URL: ${{ steps.ca-mcd.outputs.pypi-publish-url }} PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] VERSION: ${{ steps.compute.outputs.version }} # zizmor: ignore[template-injection] run: | - set -uo pipefail + set -euo pipefail wheel="dist/${PACKAGE//-/_}-${VERSION}-py3-none-any.whl" if [ ! -f "$wheel" ]; then echo " Wheel file [$wheel] not found. Aborting!" @@ -192,13 +171,35 @@ jobs: echo " Source tarball file [$tarball] not found. Aborting!" exit 1 fi + echo "files=${wheel} ${tarball}" >> "$GITHUB_OUTPUT" - status=0 - - echo "Publishing to legacy CodeArtifact account (505071440022)" - uv publish "$wheel" "$tarball" -t "${LEGACY_TOKEN}" --publish-url "${LEGACY_PUBLISH_URL}" || status=1 + # continue-on-error on both publish steps below, plus the explicit + # failure check after, so a legacy outage doesn't hold back the MCD + # publish (or vice versa) -- the job still fails overall if either did. + - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to legacy CodeArtifact + id: publish-legacy + if: github.event_name != 'pull_request' + continue-on-error: true + uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge + with: + aws-role-arn: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_Publish + codeartifact-domain: overture-pypi + codeartifact-domain-owner: "505071440022" + codeartifact-repository: overture + files: ${{ steps.artifacts.outputs.files }} - echo "Publishing to MCD CodeArtifact account (763944545891)" - uv publish "$wheel" "$tarball" -t "${MCD_TOKEN}" --publish-url "${MCD_PUBLISH_URL}" || status=1 + - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to MCD CodeArtifact + id: publish-mcd + if: github.event_name != 'pull_request' + continue-on-error: true + uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge + with: + aws-role-arn: arn:aws:iam::763944545891:role/codeartifact-pypi-publish-oidc-overturemaps + codeartifact-domain: overture-pypi + codeartifact-domain-owner: "763944545891" + codeartifact-repository: overture + files: ${{ steps.artifacts.outputs.files }} - exit "$status" + - name: Fail if either CodeArtifact publish failed + if: github.event_name != 'pull_request' && (steps.publish-legacy.outcome == 'failure' || steps.publish-mcd.outcome == 'failure') + run: exit 1 From 7b7e161601ae8c71638ed433fff8edd6e6812c83 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 13:49:13 -0400 Subject: [PATCH 3/9] ci(publish): pin merged setup-codeartifact calls to @main Matches the repo's existing convention for OvertureMaps/workflows actions (sync-issue-type-and-scope.yml, schema-pr-preview.yml): setup-codeartifact is already merged there, so track @main with a zizmor: ignore[unpinned-uses] comment instead of a SHA pin. uv-publish-to-codeartifact stays SHA-pinned since workflows#97 hasn't merged yet. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 2 +- .github/workflows/reusable-check-python-package-versions.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 229796e37..165456e7d 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -102,7 +102,7 @@ jobs: # can't turn the smoke test into a real publish. - name: Get legacy CodeArtifact credentials id: ca-legacy - uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@main # zizmor: ignore[unpinned-uses] intentionally track main with: aws-role-arn: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_${{ github.event_name == 'pull_request' && 'ReadOnly' || 'Publish' }} codeartifact-domain: overture-pypi diff --git a/.github/workflows/reusable-check-python-package-versions.yaml b/.github/workflows/reusable-check-python-package-versions.yaml index 0e0b32687..ba4893219 100644 --- a/.github/workflows/reusable-check-python-package-versions.yaml +++ b/.github/workflows/reusable-check-python-package-versions.yaml @@ -98,7 +98,7 @@ jobs: - name: Get CodeArtifact index URL id: get-code-artifact-index-url if: steps.save-changes.outputs.num_changed_packages > 0 - uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@a926f0a586b3f1e639dade180af9d794547e4303 # main, includes pypi format support (workflows#80) + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@main # zizmor: ignore[unpinned-uses] intentionally track main with: aws-role-arn: arn:aws:iam::${{ inputs.aws_account_id }}:role/${{ inputs.aws_iam_role_name }} aws-region: ${{ inputs.aws_region }} From 21779a10cecbad76a865a0af37983eba3b33aaba Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 13:53:10 -0400 Subject: [PATCH 4/9] ci(publish): use lowlydba/are-we-good to gate on both CodeArtifact publishes Splits the publish matrix job into build/publish-legacy/publish-mcd jobs connected by upload-artifact/download-artifact, so are-we-good can aggregate the two publish jobs' results the way it's designed to (needs + toJSON(needs)) instead of us hand-rolling a step-outcome check. Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 109 ++++++++++++++++++---------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 165456e7d..528a98233 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -70,16 +70,16 @@ jobs: with: before: ${{ github.event.before }} - publish: - name: Publish ${{ matrix.package }}${{ github.event_name == 'pull_request' && ' (test)' || '' }} + build: + name: Build ${{ matrix.package }}${{ github.event_name == 'pull_request' && ' (test)' || '' }} needs: detect if: needs.detect.outputs.count != '0' runs-on: ubuntu-latest permissions: contents: read - id-token: write # Required for OIDC authentication to AWS + id-token: write # Required for OIDC authentication to AWS (read-only version query) strategy: - fail-fast: false # One package's failure must not block sibling publishes + fail-fast: false # One package's failure must not block sibling builds matrix: package: ${{ fromJSON(needs.detect.outputs.packages) }} steps: @@ -97,7 +97,7 @@ jobs: run: uv sync --locked --all-packages # PR smoke test only queries CodeArtifact (via compute-version, below) - # and never publishes -- the publish steps are skipped for + # and never publishes -- the publish jobs are skipped for # pull_request. Assume the read-only role there so a wiring mistake # can't turn the smoke test into a real publish. - name: Get legacy CodeArtifact credentials @@ -115,7 +115,7 @@ jobs: # Anchored to the legacy account only: both accounts host the same # package history, so either would compute the same version, and # picking one keeps this step from depending on the MCD dual-publish - # below (which authenticates for itself via uv-publish-to-codeartifact). + # (which authenticates for itself via uv-publish-to-codeartifact). - name: Compute version id: compute uses: ./.github/actions/compute-version @@ -153,53 +153,82 @@ jobs: PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] run: uv build --package "${PACKAGE}" - - name: Locate built artifacts for ${{ matrix.package }} ${{ steps.compute.outputs.version }} - id: artifacts + # Only the real push path needs the wheel/sdist in a later job -- the PR + # smoke test stops here, its build output is never published. + - name: Upload built artifacts for ${{ matrix.package }} if: github.event_name != 'pull_request' - env: - PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] - VERSION: ${{ steps.compute.outputs.version }} # zizmor: ignore[template-injection] - run: | - set -euo pipefail - wheel="dist/${PACKAGE//-/_}-${VERSION}-py3-none-any.whl" - if [ ! -f "$wheel" ]; then - echo " Wheel file [$wheel] not found. Aborting!" - exit 1 - fi - tarball="dist/${PACKAGE//-/_}-${VERSION}.tar.gz" - if [ ! -f "$tarball" ]; then - echo " Source tarball file [$tarball] not found. Aborting!" - exit 1 - fi - echo "files=${wheel} ${tarball}" >> "$GITHUB_OUTPUT" + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dist-${{ matrix.package }} # zizmor: ignore[template-injection] + path: dist/ + if-no-files-found: error + + publish-legacy: + name: Publish ${{ matrix.package }} to legacy CodeArtifact + needs: [detect, build] + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Required for OIDC authentication to AWS + strategy: + fail-fast: false # One package's failure must not block sibling publishes + matrix: + package: ${{ fromJSON(needs.detect.outputs.packages) }} + steps: + - name: Download built artifacts for ${{ matrix.package }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: dist-${{ matrix.package }} # zizmor: ignore[template-injection] + path: dist/ - # continue-on-error on both publish steps below, plus the explicit - # failure check after, so a legacy outage doesn't hold back the MCD - # publish (or vice versa) -- the job still fails overall if either did. - - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to legacy CodeArtifact - id: publish-legacy - if: github.event_name != 'pull_request' - continue-on-error: true + - name: Publish ${{ matrix.package }} to legacy CodeArtifact uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge with: aws-role-arn: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_Publish codeartifact-domain: overture-pypi codeartifact-domain-owner: "505071440022" codeartifact-repository: overture - files: ${{ steps.artifacts.outputs.files }} - - name: Publish ${{ matrix.package }} ${{ steps.compute.outputs.version }} to MCD CodeArtifact - id: publish-mcd - if: github.event_name != 'pull_request' - continue-on-error: true + publish-mcd: + name: Publish ${{ matrix.package }} to MCD CodeArtifact + needs: [detect, build] + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Required for OIDC authentication to AWS + strategy: + fail-fast: false # One package's failure must not block sibling publishes + matrix: + package: ${{ fromJSON(needs.detect.outputs.packages) }} + steps: + - name: Download built artifacts for ${{ matrix.package }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: dist-${{ matrix.package }} # zizmor: ignore[template-injection] + path: dist/ + + - name: Publish ${{ matrix.package }} to MCD CodeArtifact uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge with: aws-role-arn: arn:aws:iam::763944545891:role/codeartifact-pypi-publish-oidc-overturemaps codeartifact-domain: overture-pypi codeartifact-domain-owner: "763944545891" codeartifact-repository: overture - files: ${{ steps.artifacts.outputs.files }} - - name: Fail if either CodeArtifact publish failed - if: github.event_name != 'pull_request' && (steps.publish-legacy.outcome == 'failure' || steps.publish-mcd.outcome == 'failure') - run: exit 1 + # A single named check that stays stable as the package matrix grows, + # instead of branch protection tracking every "Publish to + # CodeArtifact" leg by name. Skipped is fine by default (both publish jobs + # are skipped entirely for pull_request), and each account's job already + # runs independently of the other's outcome, so one account's outage still + # can't block the other. + are-we-good: + name: Are we good? + needs: [publish-legacy, publish-mcd] + if: always() + runs-on: ubuntu-slim + steps: + - uses: lowlydba/are-we-good@0f90ea9fa5e47188fcb69d9306fb8b3abb656018 # v1.2.0 + with: + jobs: ${{ toJSON(needs) }} From 380271e6e3bc8aa3a56ef2ae67f909b302c5bf93 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 14:02:04 -0400 Subject: [PATCH 5/9] ci: use $/ self-repository uses: syntax for local actions zizmor 1.30.0 (introduced the self-repository audit) is what the org's required Overture Security Checks workflow now runs, and it flags every workspace-relative './...' uses: clause repo-wide, not just anything touched by this PR. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/actions/detect-affected-packages/action.yml | 2 +- .github/actions/detect-version-bumps/action.yml | 2 +- .github/workflows/check-python-package-versions.yaml | 2 +- .github/workflows/main-publish.yaml | 4 ++-- .github/workflows/release-trigger.yaml | 4 ++-- .github/workflows/reusable-check-python-package-versions.yaml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/detect-affected-packages/action.yml b/.github/actions/detect-affected-packages/action.yml index 3ca5e8399..60f1517a4 100644 --- a/.github/actions/detect-affected-packages/action.yml +++ b/.github/actions/detect-affected-packages/action.yml @@ -46,7 +46,7 @@ runs: # after it only so both feed the final filter step back to back. - name: Diff package versions id: diff - uses: ./.github/actions/diff-package-versions + uses: $/.github/actions/diff-package-versions with: before: ${{ inputs.before }} after: ${{ inputs.after }} diff --git a/.github/actions/detect-version-bumps/action.yml b/.github/actions/detect-version-bumps/action.yml index eed3460cc..5911dd0b4 100644 --- a/.github/actions/detect-version-bumps/action.yml +++ b/.github/actions/detect-version-bumps/action.yml @@ -31,7 +31,7 @@ runs: steps: - name: Diff package versions id: diff - uses: ./.github/actions/diff-package-versions + uses: $/.github/actions/diff-package-versions with: before: ${{ inputs.before }} diff --git a/.github/workflows/check-python-package-versions.yaml b/.github/workflows/check-python-package-versions.yaml index 0da6bcce4..e6e72900a 100644 --- a/.github/workflows/check-python-package-versions.yaml +++ b/.github/workflows/check-python-package-versions.yaml @@ -14,7 +14,7 @@ concurrency: jobs: check: - uses: ./.github/workflows/reusable-check-python-package-versions.yaml + uses: $/.github/workflows/reusable-check-python-package-versions.yaml permissions: id-token: write # Required for AWS CodeArtifact OIDC authentication contents: read diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 528a98233..80a4903b3 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -66,7 +66,7 @@ jobs: - name: Detect affected packages id: detect if: github.event_name != 'pull_request' - uses: ./.github/actions/detect-affected-packages + uses: $/.github/actions/detect-affected-packages with: before: ${{ github.event.before }} @@ -118,7 +118,7 @@ jobs: # (which authenticates for itself via uv-publish-to-codeartifact). - name: Compute version id: compute - uses: ./.github/actions/compute-version + uses: $/.github/actions/compute-version with: package: ${{ matrix.package }} context: main diff --git a/.github/workflows/release-trigger.yaml b/.github/workflows/release-trigger.yaml index 4e7534909..1c9175760 100644 --- a/.github/workflows/release-trigger.yaml +++ b/.github/workflows/release-trigger.yaml @@ -56,7 +56,7 @@ jobs: - name: Detect version bumps id: detect - uses: ./.github/actions/detect-version-bumps + uses: $/.github/actions/detect-version-bumps with: before: ${{ github.event.before }} @@ -117,7 +117,7 @@ jobs: uv run overture-schema json-schema --tag overture > overture-schema.json - name: Create release - uses: ./.github/actions/create-package-release + uses: $/.github/actions/create-package-release with: package: ${{ matrix.package }} version: ${{ matrix.version }} diff --git a/.github/workflows/reusable-check-python-package-versions.yaml b/.github/workflows/reusable-check-python-package-versions.yaml index ba4893219..efcb06ace 100644 --- a/.github/workflows/reusable-check-python-package-versions.yaml +++ b/.github/workflows/reusable-check-python-package-versions.yaml @@ -79,7 +79,7 @@ jobs: - name: Diff package versions id: diff - uses: ./.github/actions/diff-package-versions + uses: $/.github/actions/diff-package-versions with: before: ${{ inputs.before_commit }} after: ${{ inputs.after_commit }} From f3d2935a2577a7fd7d56f7911af0d58427834230 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 14:10:38 -0400 Subject: [PATCH 6/9] ci(publish): re-pin uv-publish-to-codeartifact to @main OvertureMaps/workflows#97 merged, so the dual-publish steps can track main instead of the PR's branch head SHA, same as setup-codeartifact. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 80a4903b3..b13225a75 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -183,7 +183,7 @@ jobs: path: dist/ - name: Publish ${{ matrix.package }} to legacy CodeArtifact - uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge + uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@main # zizmor: ignore[unpinned-uses] intentionally track main with: aws-role-arn: arn:aws:iam::505071440022:role/GithubActions_Schema_CodeArtifact_Publish codeartifact-domain: overture-pypi @@ -210,7 +210,7 @@ jobs: path: dist/ - name: Publish ${{ matrix.package }} to MCD CodeArtifact - uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@f75f9fce8a30b4403905b09020aee07076a20a3e # workflows#97, pending merge + uses: OvertureMaps/workflows/.github/actions/uv-publish-to-codeartifact@main # zizmor: ignore[unpinned-uses] intentionally track main with: aws-role-arn: arn:aws:iam::763944545891:role/codeartifact-pypi-publish-oidc-overturemaps codeartifact-domain: overture-pypi From 0c8634b332919a4aef22a0ec1cb67f40f35e63f6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 14:24:47 -0400 Subject: [PATCH 7/9] ci(publish): let publish jobs run despite sibling build failures needs: build alone skips publish-legacy/publish-mcd entirely whenever any package in the build matrix fails, since a matrix job's overall result is failure if any leg failed. (success() || failure()) lets them run anyway; the per-package download-artifact step then fails just the legs whose build didn't produce an artifact, leaving the rest to publish. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index b13225a75..9a3ddd846 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -166,7 +166,12 @@ jobs: publish-legacy: name: Publish ${{ matrix.package }} to legacy CodeArtifact needs: [detect, build] - if: github.event_name != 'pull_request' + # (success() || failure()), not the implicit success(): build is a matrix + # job, so its overall result is failure if any single package failed to + # build. Running anyway (skipping only on cancellation) lets the + # per-package download-artifact step below fail just that package's leg, + # instead of skipping every package's publish. + if: (success() || failure()) && github.event_name != 'pull_request' runs-on: ubuntu-latest permissions: contents: read @@ -193,7 +198,7 @@ jobs: publish-mcd: name: Publish ${{ matrix.package }} to MCD CodeArtifact needs: [detect, build] - if: github.event_name != 'pull_request' + if: (success() || failure()) && github.event_name != 'pull_request' runs-on: ubuntu-latest permissions: contents: read From 2e26325b1afd328853325560cb1b958ea7de054d Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 8 Sep 2026 14:27:58 -0400 Subject: [PATCH 8/9] ci(publish): opt are-we-good into a uniquely-named check run The native per-workflow job check ("Are we good?") would collide with any other workflow using the same job name for the same pattern. create-check-run registers a standalone check named "Main publish / are-we-good" via the Checks API instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 9a3ddd846..03592539e 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -233,7 +233,16 @@ jobs: needs: [publish-legacy, publish-mcd] if: always() runs-on: ubuntu-slim + permissions: + checks: write # Required by create-check-run below steps: + # create-check-run opts into a standalone check named "Main publish / + # are-we-good", instead of the native per-workflow "Are we good?" job + # check: the latter collides across any other workflow using the same + # job name, so branch protection would treat them as one check + # satisfied by whichever runs first. - uses: lowlydba/are-we-good@0f90ea9fa5e47188fcb69d9306fb8b3abb656018 # v1.2.0 with: jobs: ${{ toJSON(needs) }} + create-check-run: true + github-token: ${{ secrets.GITHUB_TOKEN }} From 61ce6be695e21e56808d91d59d6a60c94f6c9a6a Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 9 Sep 2026 10:17:03 -0400 Subject: [PATCH 9/9] Restore wheel/tarball filename check as a build-time step Moved from the old inline publish step to run once in build, right after uv build, instead of being duplicated per publish-legacy/ publish-mcd account. Catches a stamp/build version mismatch before either CodeArtifact publish, not just a generic dist/* glob miss. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: John McCall --- .github/workflows/main-publish.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/main-publish.yaml b/.github/workflows/main-publish.yaml index 03592539e..d06010902 100644 --- a/.github/workflows/main-publish.yaml +++ b/.github/workflows/main-publish.yaml @@ -153,6 +153,27 @@ jobs: PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] run: uv build --package "${PACKAGE}" + # Catches a stamp/build mismatch (e.g. the previous step silently no-op'd + # or uv build picked up a stale version) before it reaches either + # CodeArtifact account, instead of failing only one publish leg's + # generic dist/* glob after the fact. + - name: Verify built artifact matches computed version + env: + PACKAGE: ${{ matrix.package }} # zizmor: ignore[template-injection] + VERSION: ${{ steps.compute.outputs.version }} # zizmor: ignore[template-injection] + run: | + set -euo pipefail + wheel="dist/${PACKAGE//-/_}-${VERSION}-py3-none-any.whl" + if [ ! -f "$wheel" ]; then + echo " Wheel file [$wheel] not found. Aborting!" + exit 1 + fi + tarball="dist/${PACKAGE//-/_}-${VERSION}.tar.gz" + if [ ! -f "$tarball" ]; then + echo " Source tarball file [$tarball] not found. Aborting!" + exit 1 + fi + # Only the real push path needs the wheel/sdist in a later job -- the PR # smoke test stops here, its build output is never published. - name: Upload built artifacts for ${{ matrix.package }}