Skip to content

feat: expose version and tag outputs from semantic-release workflow#21

Open
ichoosetoaccept wants to merge 1 commit intomainfrom
02-09-feat_expose_version_and_tag_outputs_from_semantic-release_workflow
Open

feat: expose version and tag outputs from semantic-release workflow#21
ichoosetoaccept wants to merge 1 commit intomainfrom
02-09-feat_expose_version_and_tag_outputs_from_semantic-release_workflow

Conversation

@ichoosetoaccept
Copy link
Member

@ichoosetoaccept ichoosetoaccept commented Feb 9, 2026

Summary

Expose version and tag outputs 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 use git describe --tags --abbrev=0 which is fragile (could match non-release tags) and requires a full clone (fetch-depth: 0).

Solution

Extract the version from pyproject.toml after a successful release and expose it as workflow outputs:

  • version — e.g., 1.2.3
  • tag — e.g., v1.2.3

Downstream jobs can now do:

- uses: actions/checkout@v6
  with:
    ref: v${{ needs.release.outputs.version }}

Implementation Details

  • Version extracted via grep '^version = ' pyproject.toml (anchored pattern avoids matching version_toml/version_variables)
  • Tag assumes default v{version} format (all consuming projects use this convention)

Changes

  • .github/workflows/semantic-release-uv.yml: add version and tag job + workflow outputs

Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

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"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — all projects using this workflow use the default v{version} tag format. Documenting would be nice but not blocking.

@ichoosetoaccept ichoosetoaccept force-pushed the 02-09-feat_expose_version_and_tag_outputs_from_semantic-release_workflow branch from 0383190 to 0aef985 Compare February 9, 2026 00:20
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 new potential issues.

Open in Devin Review

exit $exit_code
else
echo "released=true" >> "$GITHUB_OUTPUT"
version=$(grep '^version = ' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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 version

The 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.

Suggested change
version=$(grep '^version = ' pyproject.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +30 to +35
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 }}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 84 to 90
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 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)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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