ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ci: publish from release-please outputs and allow manual dispatch - #69

Merged
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please
Aug 28, 2026
Merged

ci: publish from release-please outputs and allow manual dispatch#69
nadeem4 merged 1 commit into
mainfrom
ci/trigger-publish-from-release-please

Conversation

@nadeem4

Copy link
Copy Markdown
Owner

The defect

v0.1.0 is tagged and its GitHub Release is published, PR #67 flipped to
autorelease: tagged — but publish_pypi.yaml never ran, so nothing was
uploaded to PyPI, GHCR or the versioned docs.

GitHub does not trigger workflows from events created with the default
GITHUB_TOKEN. It is a deliberate recursion guard with no opt-out short of
pushing the tag with a PAT. release-please-action tags with that token, and
publish_pypi.yaml triggered only on push: tags: ["v*"], so it could never
fire from a release-please tag.

Evidence:

  • v0.1.0daed7c09e64fc337078daf6b9459b5ce0f9a8021, and the GitHub Release
    is authored by github-actions[bot].
  • gh run list shows Publish Release with no run at all — not a failed
    one, not a skipped one. It was never started.

Fix 1 — make this release publishable (workflow_dispatch)

publish_pypi.yaml gains workflow_dispatch with a required tag input, so
the existing v0.1.0 tag can be published by hand without re-tagging anything.

On a dispatched run github.ref is the branch and github.ref_name is main,
not v0.1.0, so every reference to the tag had to be audited. All four are now
${{ inputs.tag || github.ref_name }} — the input on a dispatched or called run,
the pushed tag otherwise:

  • build checkout ref: (previously defaulted to the branch on dispatch)
  • ghcr checkout ref:and the ghcr.io/nadeem4/nl2sql-api:<tag> image tag
  • docs checkout ref: and mike deploy --push --update-aliases "$TAG" latest
    (was $GITHUB_REF_NAME, which would have deployed a docs version literally
    named main and moved latest onto it)

A dispatch therefore builds, publishes and documents the tagged commit, not
whatever main happens to be — which matters here, since main has already
moved past the tag (#68).

Fix 2 — stop depending on the cross-workflow trigger

release_please.yml now gives the action step an id, re-exports its outputs on
the job, and calls the publish directly:

release-please:
outputs:
releases_created: ${{ steps.release.outputs.releases_created }}tag_name: ${{ steps.release.outputs.tag_name }}publish:
needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}uses: ./.github/workflows/publish_pypi.yamlwith:
tag: ${{ needs.release-please.outputs.tag_name }}permissions:
contents: writepackages: writeid-token: write

Output names confirmed from the action's own README at v4, not assumed:
releases_created — "true if any release was created, false otherwise"; and
under Root component outputs (which apply because this repo's manifest has a
single package at path .), tag_name — "Directly related to Create a
release
API".

workflow_call, not a duplicate pipeline. The build → smoke gate → three
PyPI legs → GHCR → docs chain stays in publish_pypi.yaml unchanged, which is
also the workflow filename registered with PyPI's trusted publishers — the
pypi job's OIDC claim still names it, so no publisher entry changes. The
alternative (moving publish jobs into release_please.yml) would have renamed
the workflow PyPI trusts and broken all three registrations.

id-token: write reaches the pypi legs fine: a called workflow's jobs can
never hold more than the calling job grants, so the caller declares the
ceiling and each inner job keeps its own narrower permissions block.

No PAT and no new secret. Trusted publishing was chosen to avoid stored
credentials; working around the trigger rule with a token would have defeated
that. The push: tags: ["v*"] trigger is kept as well — it costs nothing and
still covers a tag a human pushes by hand.

Unchanged

  • Job order and the smoke gate: build (build 3 dists → smoke-install →
    import nl2sqlnl2sql --help) still needs-precedes pypi, and ghcr
    and docs both needs: pypi. If the wheels do not install and import,
    nothing publishes.
  • The three environment names — pypi-nl2sql-engine, pypi-nl2sql-api,
    pypi-nl2sql-adapter-sdk — are the contract with PyPI's pending publishers
    and are byte-identical.
  • No version, CHANGELOG.md or .release-please-manifest.json change.

Docs

docs/development/releasing.md gains two greppable subsections — Why the
publish is chained to release-please, not to the tag
and Publishing a release
manually
(gh workflow run publish_pypi.yaml -f tag=v0.1.0). mkdocs build --strict is clean.

Next step after merge

v0.1.0 is already tagged, so this change cannot retroactively trigger it.
The first publish will be a manual dispatch against the existing tag; every
release after this one publishes automatically off release-please's outputs.

@nadeem4
nadeem4 merged commit 8e98ca4 into mainAug 28, 2026
8 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@nadeem4