ci: auto-publish PyPI + npm on v*.*.* tags#60
Conversation
Signed-off-by: Oliver Le <oliver@gradata.ai>
There was a problem hiding this comment.
Gradata has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
📝 Walkthrough
WalkthroughAdds two new GitHub Actions workflows for automated publishing to npm and PyPI repositories, triggered by semantic version tags. Includes accompanying release documentation detailing the tagging, version verification, and publishing process. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Deploying gradata-dashboard with
|
| Latest commit: |
34264a0
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://fe7826af.gradata-dashboard.pages.dev |
| Branch Preview URL: | https://ci-auto-publish-on-tag.gradata-dashboard.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/publish-npm.yml:
- Around line 26-33: Replace the mutable action tags with pinned commit SHAs:
update the uses fields for actions/checkout and actions/setup-node (the lines
referencing "uses: actions/checkout@v4" and "uses: actions/setup-node@v4") to
the corresponding full commit SHAs for the desired release, and optionally add a
trailing comment with the readable tag (e.g., "# v4.1.1") for clarity; ensure
both entries use immutable SHAs and verify the SHAs match the intended release
before committing.
- Around line 15-18: Add an explicit least-privilege permissions block to the
publish job so it doesn't get a broad default token: in the GitHub Actions job
named "publish" (the jobs.publish block driving matrix.pkg_name) add a
permissions mapping that restricts the workflow token to contents: read only,
ensuring the job still can checkout and verify packages while preventing
unnecessary repo write or admin scopes.
- Around line 50-54: The "Publish to npm" step currently runs npm publish
directly (env NODE_AUTH_TOKEN) which will fail if the same package version is
already published during reruns or concurrent workflows; modify the step to
first check whether the package@version exists (e.g., use npm view
<pkg>@<version> or read package.json version into a variable and query npm) and
only run npm publish --access public when the version is not found, otherwise
skip/passthrough the publish; update the "Publish to npm" step to perform this
pre-check and short-circuit the publish to prevent "version already exists"
failures.
In @.github/workflows/publish-python.yml:
- Around line 18-23: Replace the mutable major-version action references with
immutable full commit SHAs: change actions/checkout@v4 and
actions/setup-python@v5 to their corresponding full SHA pins (e.g.,
actions/checkout@<full-sha> and actions/setup-python@<full-sha>) and add an
inline comment with the original tag (e.g., // actions/checkout@v4) for
readability and Dependabot compatibility; ensure you use the canonical commit
SHAs from the official GitHub Actions repos when updating the workflow.
- Around line 14-17: The publish job currently lacks explicit permissions; add a
permissions block to the publish job (job name "publish") granting only the
least privilege needed, e.g. set contents: read so GITHUB_TOKEN won't inherit
broader repo defaults; update the job definition that uses actions/checkout@v4
to include permissions: { contents: read } directly under the publish job
header.
- Line 46: Update the twine upload invocation to be idempotent by adding the
--skip-existing flag to the existing command `python -m twine upload
--non-interactive dist/*` so it becomes `python -m twine upload
--non-interactive --skip-existing dist/*`; this ensures reruns won't fail when
artifacts are already published.
In `@docs/RELEASING.md`:
- Around line 50-52: Update the token-rotation verification instruction that
currently suggests pushing a throwaway tag like `v0.0.0-rc.verify` because CI
requires tag ↔ manifest version parity; instead instruct the user to either
re-run an existing release workflow for the exact release tag already present in
the manifest or create a temporary test tag that matches the manifest version
(e.g., use the actual release version string rather than `v0.0.0-rc.verify`),
then delete the test tag after verifying the token; update the sentence
referencing `v0.0.0-rc.verify` to reflect this exact-match requirement so
readers know to use a tag that matches the release manifest or re-run the prior
workflow.
- Around line 20-24: Add a blank line immediately before the opening fenced code
block and a blank line immediately after the closing ``` fence around the block
that contains "git checkout main && git pull", "git tag vX.Y.Z", and "git push
origin vX.Y.Z" so the fenced-block satisfies markdownlint MD031; edit the fenced
block in RELEASING.md (the ```bash ... ``` section) to insert one empty line
before the opening ```bash and one empty line after the closing ``` to restore
proper spacing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3e9ed6fd-4599-400c-92dc-b66000e2236e
📒 Files selected for processing (3)
.github/workflows/publish-npm.yml.github/workflows/publish-python.ymldocs/RELEASING.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: Cloudflare Pages
🧰 Additional context used
🪛 markdownlint-cli2 (0.22.0)
docs/RELEASING.md
[warning] 20-20: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 24-24: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
| jobs: | ||
| publish: | ||
| name: Publish ${{ matrix.pkg_name }} | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/publish-npm.ymlRepository: Gradata/gradata
Length of output: 2160
Add explicit least-privilege permissions for this job.
The publish job currently lacks explicit permissions, which means it receives a token with default (broader) permissions. Since the job only reads repository contents (checkout, package verification) and uses a separate NPM_TOKEN secret for authentication to npm, restrict the token scope to contents: read:
🔒 Proposed fix
jobs:
publish:
name: Publish ${{ matrix.pkg_name }}
runs-on: ubuntu-latest
+ permissions:
+ contents: read
strategy:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| jobs: | |
| publish: | |
| name: Publish ${{ matrix.pkg_name }} | |
| runs-on: ubuntu-latest | |
| jobs: | |
| publish: | |
| name: Publish ${{ matrix.pkg_name }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-npm.yml around lines 15 - 18, Add an explicit
least-privilege permissions block to the publish job so it doesn't get a broad
default token: in the GitHub Actions job named "publish" (the jobs.publish block
driving matrix.pkg_name) add a permissions mapping that restricts the workflow
token to contents: read only, ensuring the job still can checkout and verify
packages while preventing unnecessary repo write or admin scopes.
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Node | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
| registry-url: "https://registry.npmjs.org" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/publish-npm.yml | sed -n '26,33p'Repository: Gradata/gradata
Length of output: 330
🌐 Web query:
GitHub Actions best practices pinning versions immutable SHA vs major version tags
💡 Result:
In GitHub Actions, the best practice for pinning versions prioritizes security by using full-length commit SHAs for immutability, while major version tags offer a balance of convenience and relative stability for trusted actions. SHA pinning (e.g., uses: actions/checkout@11bd719 # v4.2.2) provides the highest security: SHAs are cryptographically immutable, preventing supply chain attacks where tags are moved to malicious code. GitHub's official Security Hardening guide explicitly states: "Pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release" and helps mitigate backdoors requiring SHA-1 collisions. This is recommended for all third-party actions, especially high-risk ones handling secrets. Major version tags (e.g., @v4) are mutable but safer than branches like main or latest. Use them only for trusted creators (e.g., GitHub's 'actions/*' with Verified creator badge). Tags allow minor/patch updates for fixes but risk tag movement if compromised. Avoid: @main, @latest, short SHAs. Additional practices: - Add version comments (e.g., # v4.1.1) for readability; Dependabot recognizes them for updates. - Use GitHub policies to enforce SHA pinning at repo/org level (since Aug 2025). - Audit action source code. - Enable Dependabot for SHA-pinned action updates and vulnerability alerts. - Pin Docker images to digests (@sha256:...). Example workflow step: uses: actions/checkout@b4ffde6 # v4.1.1 To find SHA: gh api repos/actions/checkout/git/refs/tags/v4.1.1 --jq .object.sha or check Releases page commit link.
Citations:
- 1: https://docs.github.com/en/actions/reference/security/secure-use
- 2: https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions?learn=getting_started&learnProduct=actions
- 3: https://docs.github.com/enterprise-server@3.0/actions/security-guides/security-hardening-for-github-actions
- 4: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 5: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 6: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 7: https://carlosbecker.com/posts/pinning-github-actions/
- 8: https://cloudnativeengineer.substack.com/p/github-actions-reproducibility-security
- 9: https://medium.com/@adan.alvarez/github-actions-and-the-pinning-problem-what-100-security-projects-reveal-54a3a9dcc902
Pin GitHub Actions to immutable commit SHAs.
Lines 27 and 30 use mutable major tags (@v4). Switch to full commit SHAs for supply-chain security in this publish workflow. Example: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-npm.yml around lines 26 - 33, Replace the mutable
action tags with pinned commit SHAs: update the uses fields for actions/checkout
and actions/setup-node (the lines referencing "uses: actions/checkout@v4" and
"uses: actions/setup-node@v4") to the corresponding full commit SHAs for the
desired release, and optionally add a trailing comment with the readable tag
(e.g., "# v4.1.1") for clarity; ensure both entries use immutable SHAs and
verify the SHAs match the intended release before committing.
| - name: Publish to npm | ||
| working-directory: gradata-install | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| run: npm publish --access public |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat .github/workflows/publish-npm.ymlRepository: Gradata/gradata
Length of output: 1782
Guard npm publish to prevent failures on workflow reruns or concurrent executions.
The workflow uses cancel-in-progress: false in its concurrency configuration, which allows multiple runs to execute concurrently or enables workflow reruns. When this occurs, both the initial run and rerun will attempt to publish the same version. Since npm publish fails with "version already exists" when a version is already published, this causes CI/CD failures.
♻️ Proposed fix
- name: Publish to npm
working-directory: gradata-install
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: npm publish --access public
+ run: |
+ PKG_NAME=$(node -p "require('./package.json').name")
+ PKG_VERSION=$(node -p "require('./package.json').version")
+ if npm view "${PKG_NAME}@${PKG_VERSION}" version >/dev/null 2>&1; then
+ echo "Already published: ${PKG_NAME}@${PKG_VERSION}; skipping."
+ exit 0
+ fi
+ npm publish --access public📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Publish to npm | |
| working-directory: gradata-install | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: npm publish --access public | |
| - name: Publish to npm | |
| working-directory: gradata-install | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| PKG_NAME=$(node -p "require('./package.json').name") | |
| PKG_VERSION=$(node -p "require('./package.json').version") | |
| if npm view "${PKG_NAME}@${PKG_VERSION}" version >/dev/null 2>&1; then | |
| echo "Already published: ${PKG_NAME}@${PKG_VERSION}; skipping." | |
| exit 0 | |
| fi | |
| npm publish --access public |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-npm.yml around lines 50 - 54, The "Publish to npm"
step currently runs npm publish directly (env NODE_AUTH_TOKEN) which will fail
if the same package version is already published during reruns or concurrent
workflows; modify the step to first check whether the package@version exists
(e.g., use npm view <pkg>@<version> or read package.json version into a variable
and query npm) and only run npm publish --access public when the version is not
found, otherwise skip/passthrough the publish; update the "Publish to npm" step
to perform this pre-check and short-circuit the publish to prevent "version
already exists" failures.
| publish: | ||
| name: Build and publish gradata to PyPI | ||
| runs-on: ubuntu-latest | ||
| steps: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and read its content
cat -n .github/workflows/publish-python.ymlRepository: Gradata/gradata
Length of output: 1700
Add explicit least-privilege token permissions.
The publish job lacks a permissions block, causing GITHUB_TOKEN to inherit repository defaults which may grant broader access than necessary. Since the job uses actions/checkout@v4 and reads repository files, add explicit contents: read permission.
🔒 Proposed fix
jobs:
publish:
name: Build and publish gradata to PyPI
runs-on: ubuntu-latest
+ permissions:
+ contents: read
steps:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| publish: | |
| name: Build and publish gradata to PyPI | |
| runs-on: ubuntu-latest | |
| steps: | |
| publish: | |
| name: Build and publish gradata to PyPI | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-python.yml around lines 14 - 17, The publish job
currently lacks explicit permissions; add a permissions block to the publish job
(job name "publish") granting only the least privilege needed, e.g. set
contents: read so GITHUB_TOKEN won't inherit broader repo defaults; update the
job definition that uses actions/checkout@v4 to include permissions: { contents:
read } directly under the publish job header.
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
GitHub Actions pinning versions commit SHA best practices security
💡 Result:
Best practices for pinning GitHub Actions versions using commit SHAs focus on enhancing security by ensuring immutability and preventing supply chain attacks. Official GitHub documentation and recent updates (as of 2025) emphasize pinning to full-length (40-character) commit SHAs as the only way to use actions as immutable releases. Key recommendations: - Always pin actions to full commit SHAs, e.g., uses: actions/checkout@11bd719 # v4.2.2. Include a comment with the corresponding version tag for readability and to enable automated updates via Dependabot. - Avoid using @vX tags or @main for third-party actions unless you fully trust the maintainer, as tags are mutable and can be updated or compromised. - Verify SHAs come from the official repository, not forks. - For first-party GitHub actions (e.g., actions/checkout), tags from verified creators may be acceptable with caution, but SHAs are preferred. - Use GitHub's organization or repository policies to enforce SHA pinning: workflows using unpinned actions will fail. - Automate pinning and updates: Tools like Dependabot, Renovate, Piñata (caarlos0/pinata), or StepSecurity can pin actions and create PRs for updates. - Audit action source code, especially for third-party ones handling secrets. - Regularly update pinned SHAs for security fixes, balancing security with maintenance. Example workflow snippet: - uses: actions/checkout@11bd719 # v4.2.2 - uses: actions/setup-node@1e60f62 # v4.0.3 This practice mitigates risks like malicious code injection via tag overwrites, as seen in real incidents.
Citations:
- 1: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 2: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 3: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 4: https://carlosbecker.com/posts/pinning-github-actions/
- 5: https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions?learn=getting_started&learnProduct=actions
- 6: https://github.com/ihs7/action-sha-pinned-actions
- 7: https://docs.github.com/en/actions/reference/security/secure-use
- 8: https://cloudnativeengineer.substack.com/p/github-actions-reproducibility-security
- 9: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 10: https://stackoverflow.com/questions/78903499/how-do-i-pin-an-action-to-a-specific-sha
Pin actions to full commit SHAs instead of mutable major version tags.
Lines 19 and 22 use major version tags (@v4, @v5), which are mutable and can be compromised. Pin to full commit SHAs (e.g., actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683) to ensure immutability and reduce supply-chain risk. Include a comment with the version tag for readability and to enable automated updates via Dependabot.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-python.yml around lines 18 - 23, Replace the
mutable major-version action references with immutable full commit SHAs: change
actions/checkout@v4 and actions/setup-python@v5 to their corresponding full SHA
pins (e.g., actions/checkout@<full-sha> and actions/setup-python@<full-sha>) and
add an inline comment with the original tag (e.g., // actions/checkout@v4) for
readability and Dependabot compatibility; ensure you use the canonical commit
SHAs from the official GitHub Actions repos when updating the workflow.
| env: | ||
| TWINE_USERNAME: __token__ | ||
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | ||
| run: python -m twine upload --non-interactive dist/* |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the workflow file
find . -name "publish-python.yml" -type fRepository: Gradata/gradata
Length of output: 97
🏁 Script executed:
# If found, read the file to check line 46
if [ -f ".github/workflows/publish-python.yml" ]; then
wc -l ".github/workflows/publish-python.yml"
cat -n ".github/workflows/publish-python.yml"
fiRepository: Gradata/gradata
Length of output: 1740
🌐 Web query:
twine upload --skip-existing flag behavior PyPI
💡 Result:
The --skip-existing flag in twine upload allows the command to continue uploading other distribution files even if one already exists on PyPI (or TestPyPI), instead of failing with an error. PyPI rejects duplicate uploads (typically with a 409 status code), so twine attempts the upload, receives the error response, checks if it indicates the file already exists (via status code and response content), prints a WARNING like "Skipping [filename] because it appears to already exist", and proceeds with remaining files without exiting non-zero. Key behaviors: - Shows an "Uploading" progress bar for every file (including skipped ones), as existence can only be reliably confirmed by attempting upload (no pre-check for all repositories). - Only supported for PyPI and TestPyPI; in twine 6.2.0 (released 2025-09-04), hacks for other indexes were removed. - Exit code is 0 if all uploads succeed or are safely skipped; non-zero only for unskippable errors. - Added in early versions (e.g., twine 1.6.0), with fixes over time (e.g., for 409 codes, first-time uploads). As of 2026-04-15 (post-6.2.0), the flag remains available in docs/help for PyPI use, despite some non-authoritative claims of deprecation.
Citations:
- 1: https://twine.readthedocs.io/en/stable
- 2: https://twine.readthedocs.io/en/stable/internal/twine.commands.upload.html
- 3: https://twine.readthedocs.io/
- 4: https://twine.readthedocs.io/en/stable/changelog.html
- 5: https://twine.readthedocs.io/en/latest/changelog.html
- 6: https://github.com/pypa/twine/blob/master/docs/changelog.rst
- 7: https://pypi.org/project/twine/
- 8: https://github.com/pypa/twine/
🌐 Web query:
twine upload already published version error handling
💡 Result:
When using twine to upload a Python package to PyPI and encountering the "already published version" or "file already exists" error (HTTP 400), PyPI prevents overwriting distribution files for the same project version to ensure immutability. Primary handling options: 1. Use twine upload --skip-existing dist/: Skips files that already exist on PyPI, continues with others. Supported for PyPI/TestPyPI in twine 6.2.0 (2025-09-04). Note: Hacks for other repositories removed in 6.2.0. 2. Bump the package version: Update version in pyproject.toml/setup.py (e.g., 1.0.0 -> 1.0.1 or 1.0.0.post1), rebuild (python -m build), and re-upload. Recommended for actual changes. 3. Delete and re-upload (limited): Project owners can delete releases via PyPI web interface, but filenames remain reserved forever. Not recommended for production. Workflow: rm -rf dist/ build/ python -m build twine upload --skip-existing dist/ Or for new version: # Update version, then: python -m build twine upload dist/* Always run twine check dist/* first. As of 2026-04-15, use twine 6.2.0+ for latest features.
Citations:
- 1: Do something more intelligent when the file already exists on pypi pypa/twine#63
- 2: https://stackoverflow.com/questions/52016336/how-to-upload-new-versions-of-project-to-pypi-with-twine
- 3: https://twine.readthedocs.io/en/stable
- 4: https://twine.readthedocs.io/en/stable/internal/twine.commands.upload.html
- 5: https://twine.readthedocs.io/en/stable/
- 6: https://github.com/pypa/twine/blob/main/docs/changelog.rst
Add --skip-existing flag to make PyPI publish step idempotent for reruns.
Line 46 uses twine upload without --skip-existing. GitHub Actions allows manual reruns of workflows, and without this flag, attempting to republish an already-published version will fail with a non-zero exit code, preventing any ability to retry on transient failures.
♻️ Proposed fix
- run: python -m twine upload --non-interactive dist/*
+ run: python -m twine upload --non-interactive --skip-existing dist/*📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| run: python -m twine upload --non-interactive dist/* | |
| run: python -m twine upload --non-interactive --skip-existing dist/* |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-python.yml at line 46, Update the twine upload
invocation to be idempotent by adding the --skip-existing flag to the existing
command `python -m twine upload --non-interactive dist/*` so it becomes `python
-m twine upload --non-interactive --skip-existing dist/*`; this ensures reruns
won't fail when artifacts are already published.
| ```bash | ||
| git checkout main && git pull | ||
| git tag vX.Y.Z | ||
| git push origin vX.Y.Z | ||
| ``` |
There was a problem hiding this comment.
Fix fenced-code spacing to satisfy markdownlint MD031.
Add blank lines before and after the fenced block around Lines 20–24.
📝 Proposed fix
3. Tag and push:
+
```bash
git checkout main && git pull
git tag vX.Y.Z
git push origin vX.Y.Z
```
+
4. Watch the workflows at **Actions** → *Publish Python to PyPI* and🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 20-20: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 24-24: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/RELEASING.md` around lines 20 - 24, Add a blank line immediately before
the opening fenced code block and a blank line immediately after the closing ```
fence around the block that contains "git checkout main && git pull", "git tag
vX.Y.Z", and "git push origin vX.Y.Z" so the fenced-block satisfies markdownlint
MD031; edit the fenced block in RELEASING.md (the ```bash ... ``` section) to
insert one empty line before the opening ```bash and one empty line after the
closing ``` to restore proper spacing.
| 4. Push a throwaway pre-release tag (e.g. `v0.0.0-rc.verify`) on a fork or | ||
| re-run a prior release workflow to confirm the new token works, then | ||
| delete the test tag. |
There was a problem hiding this comment.
Token-rotation verification step is not executable as written.
Lines 50–52 suggest using v0.0.0-rc.verify, but workflows require exact tag↔manifest version matches, so this usually fails before token validation.
📝 Proposed doc correction
-4. Push a throwaway pre-release tag (e.g. `v0.0.0-rc.verify`) on a fork or
- re-run a prior release workflow to confirm the new token works, then
- delete the test tag.
+4. Re-run a prior release workflow to validate authentication behavior, or
+ validate on a repository/fork where manifests are intentionally set to the
+ exact test tag version. (With this repo’s guards, tag/version mismatch fails
+ before token use.)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/RELEASING.md` around lines 50 - 52, Update the token-rotation
verification instruction that currently suggests pushing a throwaway tag like
`v0.0.0-rc.verify` because CI requires tag ↔ manifest version parity; instead
instruct the user to either re-run an existing release workflow for the exact
release tag already present in the manifest or create a temporary test tag that
matches the manifest version (e.g., use the actual release version string rather
than `v0.0.0-rc.verify`), then delete the test tag after verifying the token;
update the sentence referencing `v0.0.0-rc.verify` to reflect this exact-match
requirement so readers know to use a tag that matches the release manifest or
re-run the prior workflow.
GitHub Actions workflows that auto-publish Python SDK to PyPI and npm wrapper to npm on version tag push. Requires PYPI_API_TOKEN + NPM_TOKEN secrets — setup instructions in docs/RELEASING.md.
What ships
.github/workflows/publish-python.yml— fires onv*.*.*tags, builds sdist+wheel viapython -m build, uploads viatwineusingPYPI_API_TOKEN. Refuses to publish if tag version != pyproject.toml version..github/workflows/publish-npm.yml— fires onv*.*.*tags, matrix publishesgradata-install(unscoped) and@gradata/install(scoped) fromgradata-install/usingNPM_TOKEN. Same version-match guard vs package.json.docs/RELEASING.md— release cut procedure, required secrets, token rotation.Notes
npm ciin gradata-install/ — dropped in simplify pass because the package has no dependencies and no lockfile (npm ciwould fail). Publishing only needs the files listed in package.json'sfilesarray.@v4,@v5), no secrets in YAML (only${{ secrets.X }}references), and are tag-push-only so this PR merge will not trigger a publish.sdk-release.yml(Trusted Publisher onv*) will also fire on the same tag. Both are idempotent on already-published versions, but before the first real release we should decide whether to deletesdk-release.ymlor keep these as the canonical path.Test plan
PYPI_API_TOKENandNPM_TOKENat Settings → Secrets → Actionsgit tag vX.Y.Z && git push origin vX.Y.Z→ verify both workflows go green and packages appear on PyPI / npm