feat: expose version and tag outputs from semantic-release workflow#21
Conversation
| echo "released=true" >> "$GITHUB_OUTPUT" | ||
| version=$(grep '^version' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') | ||
| echo "version=$version" >> "$GITHUB_OUTPUT" | ||
| echo "tag=v$version" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
🚩 Hardcoded v prefix for tag assumes default semantic-release tag format
Line 89 constructs the tag as v$version, assuming python-semantic-release uses the default v{version} tag format. This is configurable in pyproject.toml via [tool.semantic_release] tag_format = .... If a consumer of this reusable workflow configures a different tag format (e.g., no prefix, or a release/ prefix), the tag output will be incorrect while released=true. This is a design assumption rather than a bug, since v is the default, but it's worth documenting or considering parsing the actual tag from semantic-release output or git tags instead.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Intentional — all projects using this workflow use the default v{version} tag format. Documenting would be nice but not blocking.
0383190 to
0aef985
Compare
| exit $exit_code | ||
| else | ||
| echo "released=true" >> "$GITHUB_OUTPUT" | ||
| version=$(grep '^version = ' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') |
There was a problem hiding this comment.
🔴 Version extraction via grep can match wrong TOML section
The version extraction on line 87 uses grep '^version = ' pyproject.toml | head -1 which matches the first version = line in the file, regardless of which TOML section it belongs to. If any TOML section before [project] contains a version key (e.g., [tool.poetry], [tool.bumpversion], or even a dependency spec), the wrong version will be extracted.
Detailed Explanation
A pyproject.toml can have multiple version = "..." lines across different sections. For example:
[tool.poetry]
version = "0.0.0" # placeholder, not the real version
[project]
name = "my-package"
version = "1.2.3" # the actual release versionThe grep '^version = ' pyproject.toml | head -1 would match the [tool.poetry] version (0.0.0) instead of the [project] version (1.2.3). This would cause the version and tag outputs to contain the wrong values, leading downstream jobs to checkout a non-existent tag.
Additionally, if the project uses dynamic versioning (dynamic = ["version"] in [project]) and has no version = line at all, the grep returns empty, resulting in version= and tag=v being set as outputs with no error, causing silent failures in downstream consumers.
A more robust approach would be to use a TOML-aware tool, e.g.:
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")Impact: Downstream jobs relying on version or tag outputs could checkout the wrong ref or fail entirely.
| version=$(grep '^version = ' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') | |
| version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") | |
Was this helpful? React with 👍 or 👎 to provide feedback.
| version: | ||
| description: "The released version (e.g. 1.2.3)" | ||
| value: ${{ jobs.release.outputs.version }} | ||
| tag: | ||
| description: "The release tag (e.g. v1.2.3)" | ||
| value: ${{ jobs.release.outputs.tag }} |
There was a problem hiding this comment.
📝 Info: Output plumbing is correctly wired through both levels
The outputs are properly wired at two levels: job-level outputs (.github/workflows/semantic-release-uv.yml:49-50) reference the step outputs via ${{ steps.release.outputs.version }} and ${{ steps.release.outputs.tag }}, and the workflow-level outputs (lines 31-35) reference the job outputs via ${{ jobs.release.outputs.version }} and ${{ jobs.release.outputs.tag }}. This two-level plumbing is required for reusable workflows and is correctly implemented here, matching the existing released output pattern on lines 28-29 and 48.
Was this helpful? React with 👍 or 👎 to provide feedback.
| exit $exit_code | ||
| else | ||
| echo "released=true" >> "$GITHUB_OUTPUT" | ||
| version=$(grep '^version = ' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') | ||
| echo "version=$version" >> "$GITHUB_OUTPUT" | ||
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | ||
| fi |
There was a problem hiding this comment.
📝 Info: version/tag outputs are only set on release path — empty string for non-release runs
When no release is made (line 81) or when semantic-release fails (line 84), the version and tag outputs are never written to $GITHUB_OUTPUT. In GitHub Actions, unset outputs resolve to empty strings. This means jobs.release.outputs.version and jobs.release.outputs.tag will be empty when released == 'false'. This is consistent with how a consumer would use these — they'd gate on released == 'true' before accessing version/tag. The PR description's example usage (needs.release.outputs.version) would indeed be used only in a job that depends on released == 'true'. This is acceptable but could benefit from documentation noting that these outputs are only meaningful when released is true.
(Refers to lines 80-90)
Was this helpful? React with 👍 or 👎 to provide feedback.

Summary
Expose
versionandtagoutputs from the semantic-release reusable workflow.Problem
Downstream jobs (e.g., PyPI publish) need to checkout the exact release tag, but the workflow only exposed
released(boolean). This forced consumers to usegit describe --tags --abbrev=0which is fragile (could match non-release tags) and requires a full clone (fetch-depth: 0).Solution
Extract the version from
pyproject.tomlafter a successful release and expose it as workflow outputs:version— e.g.,1.2.3tag— e.g.,v1.2.3Downstream jobs can now do:
Implementation Details
grep '^version = ' pyproject.toml(anchored pattern avoids matchingversion_toml/version_variables)v{version}format (all consuming projects use this convention)Changes
.github/workflows/semantic-release-uv.yml: addversionandtagjob + workflow outputs