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
76 changes: 64 additions & 12 deletions .github/workflows/csharp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,14 +240,40 @@ jobs:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "::error::NUGET_API_KEY is not set; cannot publish to NuGet."
echo "Configure NUGET_API_KEY as a repository secret. See docs/case-studies/issue-25/README.md."
echo "::error title=NUGET_API_KEY missing::Cannot publish to NuGet without an API key."
echo ""
echo "How to fix:"
echo " 1. Generate a key at https://www.nuget.org/account/apikeys"
echo " (scope: 'Push new packages and package versions', glob pattern: package id)."
echo " 2. Add it to this repo at:"
echo " Settings -> Secrets and variables -> Actions -> New repository secret"
echo " Name: NUGET_API_KEY"
echo " 3. Re-run this workflow."
echo ""
echo "See docs/case-studies/issue-29/README.md (credentials) and"
echo " docs/case-studies/issue-25/README.md (publishing pipeline)."
exit 1
fi
if ! OUT=$(dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate 2>&1); then
echo "$OUT"
if echo "$OUT" | grep -qiE 'unauthorized|forbidden|403|401|api ?key'; then
echo "::error title=NuGet credentials rejected::NuGet rejected NUGET_API_KEY."
echo ""
echo "How to fix:"
echo " 1. The key may have expired or been revoked. Rotate it at"
echo " https://www.nuget.org/account/apikeys"
echo " 2. Update the secret at:"
echo " Settings -> Secrets and variables -> Actions -> NUGET_API_KEY"
echo " 3. Verify the key's package glob includes this package id."
echo ""
echo "See docs/case-studies/issue-29/README.md."
fi
exit 1
fi
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
echo "$OUT"

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
Expand Down Expand Up @@ -313,14 +339,40 @@ jobs:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "::error::NUGET_API_KEY is not set; cannot publish to NuGet."
echo "Configure NUGET_API_KEY as a repository secret. See docs/case-studies/issue-25/README.md."
echo "::error title=NUGET_API_KEY missing::Cannot publish to NuGet without an API key."
echo ""
echo "How to fix:"
echo " 1. Generate a key at https://www.nuget.org/account/apikeys"
echo " (scope: 'Push new packages and package versions', glob pattern: package id)."
echo " 2. Add it to this repo at:"
echo " Settings -> Secrets and variables -> Actions -> New repository secret"
echo " Name: NUGET_API_KEY"
echo " 3. Re-run this workflow."
echo ""
echo "See docs/case-studies/issue-29/README.md (credentials) and"
echo " docs/case-studies/issue-25/README.md (publishing pipeline)."
exit 1
fi
if ! OUT=$(dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate 2>&1); then
echo "$OUT"
if echo "$OUT" | grep -qiE 'unauthorized|forbidden|403|401|api ?key'; then
echo "::error title=NuGet credentials rejected::NuGet rejected NUGET_API_KEY."
echo ""
echo "How to fix:"
echo " 1. The key may have expired or been revoked. Rotate it at"
echo " https://www.nuget.org/account/apikeys"
echo " 2. Update the secret at:"
echo " Settings -> Secrets and variables -> Actions -> NUGET_API_KEY"
echo " 3. Verify the key's package glob includes this package id."
echo ""
echo "See docs/case-studies/issue-29/README.md."
fi
exit 1
fi
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
echo "$OUT"

- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,14 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: ./js
run: node scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}"
run: node scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --tag-prefix "js-v"

- name: Format GitHub release notes
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: ./js
run: node scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}"
run: node scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}" --tag-prefix "js-v"

# Manual Instant Release - triggered via workflow_dispatch with instant mode
instant-release:
Expand Down Expand Up @@ -316,14 +316,14 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: ./js
run: node scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}"
run: node scripts/create-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --tag-prefix "js-v"

- name: Format GitHub release notes
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: ./js
run: node scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}"
run: node scripts/format-github-release.mjs --release-version "${{ steps.publish.outputs.published_version }}" --repository "${{ github.repository }}" --commit-sha "${{ github.sha }}" --tag-prefix "js-v"

# Manual Changeset PR - creates a pull request with the changeset for review
changeset-pr:
Expand Down
97 changes: 97 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ jobs:
path: python/dist/

- name: Publish to PyPI
id: pypi_publish
if: steps.version_check.outputs.should_release == 'true'
# Uses PyPI's OIDC Trusted Publisher flow (id-token: write below). If you see "Trusted
# Publisher … not configured" in the logs, configure it for this repo + workflow on PyPI.
Expand All @@ -243,6 +244,50 @@ jobs:
verbose: true
skip-existing: true

- name: Diagnose PyPI publish failure
if: failure() && steps.pypi_publish.outcome == 'failure'
run: |
echo "::error title=PyPI publish failed::pypa/gh-action-pypi-publish exited with an error."
echo ""
echo "Most common causes & fixes:"
echo " 1. 'Trusted publisher … not configured' / 'invalid-publisher':"
echo " PyPI does not yet trust this repository+workflow. Configure it at:"
echo " https://pypi.org/manage/project/${{ steps.version_check.outputs.package_name }}/settings/publishing/"
echo " Match: owner=${{ github.repository_owner }}, repo=$(basename ${{ github.repository }}),"
echo " workflow=python.yml, environment=(blank unless you set one)."
echo ""
echo " 2. Token-based auth in use but PYPI_API_TOKEN missing/expired:"
echo " Generate at https://pypi.org/manage/account/token/ and store as a repo secret."
echo ""
echo " 3. id-token: write missing on the job (this workflow already has it)."
echo ""
echo " 4. Version already published: skip-existing is true so PyPI conflicts are tolerated;"
echo " a hard failure here means something else is wrong."
echo ""
echo "Docs: https://docs.pypi.org/trusted-publishers/"
echo " docs/case-studies/issue-29/README.md"
exit 1

- name: Verify package on PyPI
if: steps.version_check.outputs.should_release == 'true' && steps.pypi_publish.outcome == 'success'
run: |
PKG="${{ steps.version_check.outputs.package_name }}"
VER="${{ steps.version_check.outputs.current_version }}"
# PyPI's CDN can lag a few seconds behind a successful upload.
for i in 1 2 3 4 5; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${PKG}/${VER}/json")
echo "Attempt $i: PyPI HTTP status for ${PKG}@${VER}: ${STATUS}"
if [ "$STATUS" = "200" ]; then
echo "✅ Verified ${PKG}@${VER} is on PyPI"
exit 0
fi
sleep 5
done
echo "::error title=PyPI verification failed::${PKG}@${VER} is not on PyPI after publish."
echo "The publish step reported success but the registry does not see the version."
echo "See docs/case-studies/issue-29/README.md for the runbook."
exit 1

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
env:
Expand Down Expand Up @@ -315,7 +360,16 @@ jobs:
working-directory: ./python
run: twine check dist/*

- name: Read package name from pyproject.toml
id: pkg
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
working-directory: ./python
run: |
PACKAGE_NAME=$(grep -Po '(?<=^name = ")[^"]*' pyproject.toml | head -1)
echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT

- name: Publish to PyPI
id: pypi_publish
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
# See note above: relies on PyPI Trusted Publisher; see docs/case-studies/issue-25/README.md.
uses: pypa/gh-action-pypi-publish@release/v1
Expand All @@ -324,6 +378,49 @@ jobs:
verbose: true
skip-existing: true

- name: Diagnose PyPI publish failure
if: failure() && steps.pypi_publish.outcome == 'failure'
run: |
echo "::error title=PyPI publish failed::pypa/gh-action-pypi-publish exited with an error."
echo ""
echo "Most common causes & fixes:"
echo " 1. 'Trusted publisher … not configured' / 'invalid-publisher':"
echo " PyPI does not yet trust this repository+workflow. Configure it at:"
echo " https://pypi.org/manage/project/${{ steps.pkg.outputs.package_name }}/settings/publishing/"
echo " Match: owner=${{ github.repository_owner }}, repo=$(basename ${{ github.repository }}),"
echo " workflow=python.yml, environment=(blank unless you set one)."
echo ""
echo " 2. Token-based auth in use but PYPI_API_TOKEN missing/expired:"
echo " Generate at https://pypi.org/manage/account/token/ and store as a repo secret."
echo ""
echo " 3. id-token: write missing on the job (this workflow already has it)."
echo ""
echo " 4. Version already published: skip-existing is true so PyPI conflicts are tolerated;"
echo " a hard failure here means something else is wrong."
echo ""
echo "Docs: https://docs.pypi.org/trusted-publishers/"
echo " docs/case-studies/issue-29/README.md"
exit 1

- name: Verify package on PyPI
if: (steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true') && steps.pypi_publish.outcome == 'success'
run: |
PKG="${{ steps.pkg.outputs.package_name }}"
VER="${{ steps.version.outputs.new_version }}"
for i in 1 2 3 4 5; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${PKG}/${VER}/json")
echo "Attempt $i: PyPI HTTP status for ${PKG}@${VER}: ${STATUS}"
if [ "$STATUS" = "200" ]; then
echo "✅ Verified ${PKG}@${VER} is on PyPI"
exit 0
fi
sleep 5
done
echo "::error title=PyPI verification failed::${PKG}@${VER} is not on PyPI after publish."
echo "The publish step reported success but the registry does not see the version."
echo "See docs/case-studies/issue-29/README.md for the runbook."
exit 1

- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
env:
Expand Down
52 changes: 48 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,18 @@ jobs:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || secrets.CARGO_TOKEN }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "::error::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set; cannot publish to crates.io."
echo "Configure one of these as a repository secret. See docs/case-studies/issue-25/README.md."
echo "::error title=CARGO_REGISTRY_TOKEN missing::Cannot publish to crates.io without an API token."
echo ""
echo "How to fix:"
echo " 1. Generate a token at https://crates.io/me (Account Settings -> API Tokens)."
echo " Scope: 'publish-update' for this crate."
echo " 2. Add it to this repo at:"
echo " Settings -> Secrets and variables -> Actions -> New repository secret"
echo " Name: CARGO_REGISTRY_TOKEN (CARGO_TOKEN is also accepted as a fallback)"
echo " 3. Re-run this workflow."
echo ""
echo "See docs/case-studies/issue-29/README.md (credentials) and"
echo " docs/case-studies/issue-25/README.md (publishing pipeline)."
exit 1
fi
# `cargo publish` exits non-zero on retry if the version already exists; we tolerate that
Expand All @@ -349,6 +359,18 @@ jobs:
echo "::warning::Version was published by another run between probe and publish; treating as success."
exit 0
fi
if echo "$OUT" | grep -qiE 'unauthorized|forbidden|403|401|invalid token|token (expired|rejected)'; then
echo "::error title=crates.io credentials rejected::crates.io rejected CARGO_REGISTRY_TOKEN."
echo ""
echo "How to fix:"
echo " 1. The token may have expired or been revoked. Rotate it at"
echo " https://crates.io/me (Account Settings -> API Tokens)"
echo " 2. Update the secret at:"
echo " Settings -> Secrets and variables -> Actions -> CARGO_REGISTRY_TOKEN"
echo " 3. Verify the token's scope includes 'publish-update' for this crate."
echo ""
echo "See docs/case-studies/issue-29/README.md."
fi
exit 1
fi
echo "$OUT"
Expand Down Expand Up @@ -419,8 +441,18 @@ jobs:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || secrets.CARGO_TOKEN }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "::error::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set; cannot publish to crates.io."
echo "Configure one of these as a repository secret. See docs/case-studies/issue-25/README.md."
echo "::error title=CARGO_REGISTRY_TOKEN missing::Cannot publish to crates.io without an API token."
echo ""
echo "How to fix:"
echo " 1. Generate a token at https://crates.io/me (Account Settings -> API Tokens)."
echo " Scope: 'publish-update' for this crate."
echo " 2. Add it to this repo at:"
echo " Settings -> Secrets and variables -> Actions -> New repository secret"
echo " Name: CARGO_REGISTRY_TOKEN (CARGO_TOKEN is also accepted as a fallback)"
echo " 3. Re-run this workflow."
echo ""
echo "See docs/case-studies/issue-29/README.md (credentials) and"
echo " docs/case-studies/issue-25/README.md (publishing pipeline)."
exit 1
fi
if ! OUT=$(cargo publish 2>&1); then
Expand All @@ -429,6 +461,18 @@ jobs:
echo "::warning::Version is already on crates.io; treating manual re-run as success."
exit 0
fi
if echo "$OUT" | grep -qiE 'unauthorized|forbidden|403|401|invalid token|token (expired|rejected)'; then
echo "::error title=crates.io credentials rejected::crates.io rejected CARGO_REGISTRY_TOKEN."
echo ""
echo "How to fix:"
echo " 1. The token may have expired or been revoked. Rotate it at"
echo " https://crates.io/me (Account Settings -> API Tokens)"
echo " 2. Update the secret at:"
echo " Settings -> Secrets and variables -> Actions -> CARGO_REGISTRY_TOKEN"
echo " 3. Verify the token's scope includes 'publish-update' for this crate."
echo ""
echo "See docs/case-studies/issue-29/README.md."
fi
exit 1
fi
echo "$OUT"
Expand Down
Loading
Loading