Skip to content

fix(ci): stop nesting pypa/gh-action-pypi-publish in a composite action - #94

Merged
jimisola merged 2 commits into
mainfrom
fix/pypi-publish-not-nested-in-composite
Aug 23, 2026
Merged

fix(ci): stop nesting pypa/gh-action-pypi-publish in a composite action#94
jimisola merged 2 commits into
mainfrom
fix/pypi-publish-not-nested-in-composite

Conversation

@jimisola

@jimisola jimisola commented Aug 23, 2026

Copy link
Copy Markdown
Member

Description

See #95 for the full writeup, including
the corrections in the second comment
— an earlier revision of this PR deleted the
composite action outright on a premise that turned out to be wrong.

Today's real release runs across four repos all failed at the publish step:

docker run ... ghcr.io/reqstool/.github:b10b898cd5a1d552a578dbe4f170f84fb8f98b6c
docker: invalid reference format

pypa/gh-action-pypi-publish is a Docker container action. Nested inside a composite
action, GitHub resolves its image against the wrapping action's repository and pinned
ref rather than its own. Its own maintainer
describes the same breakage:

when we were optimizing the action, it ended up being a composite action wrapping a docker
one. It turned out that GH doesn't work well with nested composite actions and various
unexpected context leak into our env making it defunct.

The composite action itself was never the problem — that was my misreading, and it's why
this PR no longer deletes it. A composite action creates no new workflow context, so the job
keeps the caller's own OIDC identity, which is precisely why it works where a workflow_call
reusable workflow does not. Someone makes the same point for
Ruby
in that thread.

What changes

  • actions/publish-to-pypiactions/download-dists. It keeps download-artifact and
    the optional twine check, and no longer publishes — hence the rename; keeping the old name
    would have been the misleading part.
  • The pypa/gh-action-pypi-publish step moves inline into each caller's release.yml
    (companion PRs), with a comment saying why it can't be hoisted.
  • RELEASING.md's PyPI notes corrected. Two claims in the previous revision were wrong:
    PyPI matches job_workflow_ref (the bottom-most workflow), not the top-level caller; and
    reusable workflows aren't rejected categorically — one in the same repo as its caller
    works by accident of that same rule. What fails is a cross-repo one like this repo's, which
    puts reqstool/.github in the claim where the calling project has to be.

Companion PRs

client#445 ·
decorators#93 ·
hatch-plugin#104 ·
poetry-plugin#141

Merge order: this one first. The companions currently reference download-dists@main
because no commit SHA exists to pin to until this merges; the pin goes in before they do.

Checklist

Test plan

tests/actions/run-tests.sh 40/40, shellcheck, yamllint, zizmor and
check-permissions.py all clean locally. None of that would have caught the Docker nesting
bug — only a real dispatch does, which is what the companion PRs' re-release is for.

Confirmed against four real release runs today: pypa/gh-action-pypi-publish
is a Docker container action, and GitHub resolves its image using the
wrapping action's own repository and ref rather than the Docker action's
when nested inside another `uses:` -- every publish failed with `docker:
invalid reference format`, trying to pull
ghcr.io/reqstool/.github:<this-action's-own-sha>. The action's own
maintainers say this usage is untested and unsupported.

This is exactly what the workaround comment linked from #92's PR
description already said -- upload as a bare step in the caller, not
routed through anything else -- and actions/publish-to-pypi violated it
by wrapping the publish step instead of just inlining it. Deleting the
composite action rather than trimming it to only download-artifact: what
remains is one actions/download-artifact call, not enough indirection to
be worth a shared action, and every layer added here tonight has broken
in a new way.

Companion PRs inline both steps directly into each PyPI-publishing repo's
release.yml.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Reworks the previous commit, which deleted the action outright on the
mistaken premise that composite actions were the problem. They are not:
a composite action creates no new workflow context, so the job keeps the
caller's OIDC identity -- which is exactly why it works where a reusable
workflow does not. Someone makes the same point for Ruby in
pypi/warehouse#11096.

Only pypa/gh-action-pypi-publish has to come out. It is a Docker
container action, and GitHub resolves a nested Docker action's image
against the wrapping action's repository rather than its own -- the
action's own maintainer describes the same breakage in that thread.

Renamed publish-to-pypi -> download-dists, since what remains downloads
the distributions and deliberately does not publish them. Keeping the
old name would have been the misleading part.

RELEASING.md's PyPI notes corrected while here. Two claims in the
previous commit were wrong: PyPI matches `job_workflow_ref`, the
bottom-most workflow, not the top-level caller; and reusable workflows
are not rejected categorically -- one in the *same* repo as its caller
works by accident of that same rule. What fails is a cross-repo one like
this repo's, which puts reqstool/.github in the claim where the calling
project has to be.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-client that referenced this pull request Aug 23, 2026
Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-decorators that referenced this pull request Aug 23, 2026
Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-hatch-plugin that referenced this pull request Aug 23, 2026
Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-poetry-plugin that referenced this pull request Aug 23, 2026
Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
@jimisola
jimisola merged commit ef815ea into main Aug 23, 2026
12 checks passed
@jimisola
jimisola deleted the fix/pypi-publish-not-nested-in-composite branch August 23, 2026 14:31
jimisola added a commit to reqstool/reqstool-python-poetry-plugin that referenced this pull request Aug 23, 2026
…141)

* fix(ci): inline the PyPI publish step instead of a composite action

pypa/gh-action-pypi-publish is a Docker container action. Nested inside
reqstool/.github's actions/publish-to-pypi composite action, GitHub
resolved its image using the composite action's own repo and pinned
ref instead of the Docker action's, and every real publish failed with
`docker: invalid reference format`. The action's own maintainers say
this usage is untested and unsupported.

Both steps -- download-artifact and the actual publish -- are now
inline, matching the pattern the upstream workaround comment already
showed (see reqstool/.github#95 for the full writeup). There is no
shared action for this step in the org at all now; what remained after
removing the publish call wasn't enough to justify one.

Needs reqstool/.github#94 merged first.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

* refactor(ci): use download-dists for the artifact half

Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

---------

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-hatch-plugin that referenced this pull request Aug 23, 2026
…104)

* fix(ci): inline the PyPI publish step instead of a composite action

pypa/gh-action-pypi-publish is a Docker container action. Nested inside
reqstool/.github's actions/publish-to-pypi composite action, GitHub
resolved its image using the composite action's own repo and pinned
ref instead of the Docker action's, and every real publish failed with
`docker: invalid reference format`. The action's own maintainers say
this usage is untested and unsupported.

Both steps -- download-artifact and the actual publish -- are now
inline, matching the pattern the upstream workaround comment already
showed (see reqstool/.github#95 for the full writeup). There is no
shared action for this step in the org at all now; what remained after
removing the publish call wasn't enough to justify one.

Needs reqstool/.github#94 merged first.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

* refactor(ci): use download-dists for the artifact half

Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

---------

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-decorators that referenced this pull request Aug 23, 2026
)

* fix(ci): inline the PyPI publish step instead of a composite action

pypa/gh-action-pypi-publish is a Docker container action. Nested inside
reqstool/.github's actions/publish-to-pypi composite action, GitHub
resolved its image using the composite action's own repo and pinned
ref instead of the Docker action's, and every real publish failed with
`docker: invalid reference format`. The action's own maintainers say
this usage is untested and unsupported.

Both steps -- download-artifact and the actual publish -- are now
inline, matching the pattern the upstream workaround comment already
showed (see reqstool/.github#95 for the full writeup). There is no
shared action for this step in the org at all now; what remained after
removing the publish call wasn't enough to justify one.

Needs reqstool/.github#94 merged first.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

* refactor(ci): use download-dists for the artifact half

Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

---------

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-client that referenced this pull request Aug 23, 2026
…445)

* fix(ci): inline the PyPI publish step instead of a composite action

pypa/gh-action-pypi-publish is a Docker container action. Nested inside
reqstool/.github's actions/publish-to-pypi composite action, GitHub
resolved its image using the composite action's own repo and pinned
ref instead of the Docker action's, and every real publish failed with
`docker: invalid reference format`. The action's own maintainers say
this usage is untested and unsupported.

Both steps -- download-artifact and the actual publish -- are now
inline, matching the pattern the upstream workaround comment already
showed (see reqstool/.github#95 for the full writeup). There is no
shared action for this step in the org at all now; what remained after
removing the publish call wasn't enough to justify one.

Needs reqstool/.github#94 merged first.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

* refactor(ci): use download-dists for the artifact half

Reworks the previous commit. Composite actions were not the problem --
one creates no new workflow context, so the job keeps this repo's own
OIDC identity, which is why it works where a reusable workflow does not.
Only pypa/gh-action-pypi-publish had to come out of it, because nesting
a Docker container action makes GitHub resolve its image against the
wrapping action's repository.

So the download goes back to a shared action, renamed download-dists
now that it no longer publishes, and only the publish step stays inline.

Still pinned at @main here; reqstool/.github#94 has to merge before a
commit SHA exists to pin to. That pin lands before this PR merges.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>

---------

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-client that referenced this pull request Aug 23, 2026
CodeQL flagged actions/unpinned-tag: @main is mutable, so a compromised
or force-pushed reqstool/.github main would execute through this
reference with no review. Pinned to the commit that added the action,
matching the `@<sha> # main YYYY-MM-DD` convention used elsewhere here.

Went in as @main only because no commit SHA existed to pin to until
reqstool/.github#94 merged.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-poetry-plugin that referenced this pull request Aug 23, 2026
CodeQL flagged actions/unpinned-tag: @main is mutable, so a compromised
or force-pushed reqstool/.github main would execute through this
reference with no review. Pinned to the commit that added the action,
matching the `@<sha> # main YYYY-MM-DD` convention used elsewhere here.

Went in as @main only because no commit SHA existed to pin to until
reqstool/.github#94 merged.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-hatch-plugin that referenced this pull request Aug 23, 2026
CodeQL flagged actions/unpinned-tag: @main is mutable, so a compromised
or force-pushed reqstool/.github main would execute through this
reference with no review. Pinned to the commit that added the action,
matching the `@<sha> # main YYYY-MM-DD` convention used elsewhere here.

Went in as @main only because no commit SHA existed to pin to until
reqstool/.github#94 merged.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
jimisola added a commit to reqstool/reqstool-python-decorators that referenced this pull request Aug 23, 2026
CodeQL flagged actions/unpinned-tag: @main is mutable, so a compromised
or force-pushed reqstool/.github main would execute through this
reference with no review. Pinned to the commit that added the action,
matching the `@<sha> # main YYYY-MM-DD` convention used elsewhere here.

Went in as @main only because no commit SHA existed to pin to until
reqstool/.github#94 merged.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
Sign up for free to 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