Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changes/version-action-outputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
kind: added
---
Expose the computed version as action outputs: `status` emits `next-version`, `previous-version`, and `bump`; `release`/`prerelease` emit `version` (set even on the no-tag path)

## Release Note
Version available as action outputs
`status` mode now writes machine-readable step outputs (`next-version`, `previous-version`, `bump`) and `release`/`prerelease` expose a `version` output, so consuming workflows can read the version directly instead of parsing logs or re-deriving it from git tags.
16 changes: 16 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ outputs:
tag:
description: The tag that was created (empty when no tag was created)
value: ${{ steps.run.outputs.tag }}
version:
description: >
release/prerelease mode: the full computed version string (the tag without
prefix), set even on the no-tag path where 'tag' is empty.
value: ${{ steps.run.outputs.version }}
next-version:
description: >
status mode: the next base version (MAJOR.MINOR.PATCH, no prerelease label).
Computed on any branch since status has no branch-config dependency.
value: ${{ steps.run.outputs.next-version }}
previous-version:
description: The prior stable base version (empty if there is no prior tag)
value: ${{ steps.run.outputs.previous-version }}
bump:
description: "status mode: the computed bump level (patch, minor, major)"
value: ${{ steps.run.outputs.bump }}
prerelease:
description: Whether the created tag is a prerelease
value: ${{ steps.run.outputs.prerelease }}
Expand Down
4 changes: 2 additions & 2 deletions cmd/prerelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,13 @@ func runPrerelease(opts releaseOptions) error {
}
if opts.noTag {
fmt.Fprintf(os.Stderr, "Version: %s (no tag)\n", tag)
writeGitHubOutputs("", true)
writeGitHubOutputs("", tag, latest, true)
return nil
}
if err := git.CreateTag(root, tag); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Created tag: %s\n", tag)
writeGitHubOutputs(tag, true)
writeGitHubOutputs(tag, tag, latest, true)
return nil
}
54 changes: 36 additions & 18 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ func projectRoot() string {
return root
}

// writeGitHubOutputs writes step outputs to GITHUB_OUTPUT if the env var is set.
func writeGitHubOutputs(tag string, prerelease bool) {
// appendGitHubOutput appends key=value step outputs to GITHUB_OUTPUT if the env
// var is set. Values here are short, newline-free version/tag strings, so the
// plain key=value form is sufficient (no heredoc delimiter needed).
func appendGitHubOutput(outputs map[string]string) {
path := os.Getenv("GITHUB_OUTPUT")
if path == "" {
return
Expand All @@ -75,13 +77,41 @@ func writeGitHubOutputs(tag string, prerelease bool) {
return
}
defer f.Close()
for k, v := range outputs {
if _, err := fmt.Fprintf(f, "%s=%s\n", k, v); err != nil {
fmt.Fprintf(os.Stderr, "warning: could not write GITHUB_OUTPUT: %v\n", err)
return
}
}
}

// writeGitHubOutputs writes the release/prerelease step outputs. version is the
// full computed version string (the tag without prefix), exposed even on the
// no-tag path where tag is empty; previous is the prior stable base version.
func writeGitHubOutputs(tag, version, previous string, prerelease bool) {
pre := "false"
if prerelease {
pre = "true"
}
if _, err := fmt.Fprintf(f, "tag=%s\nprerelease=%s\nskip=false\n", tag, pre); err != nil {
fmt.Fprintf(os.Stderr, "warning: could not write GITHUB_OUTPUT: %v\n", err)
}
appendGitHubOutput(map[string]string{
"tag": tag,
"version": version,
"previous-version": previous,
"prerelease": pre,
"skip": "false",
})
}

// writeStatusOutputs writes the status step outputs: the next base version
// (MAJOR.MINOR.PATCH, no prerelease label), the prior stable base, and the
// computed bump level. Works on any branch since it has no branch-config input.
func writeStatusOutputs(nextVersion, previous, bump string) {
appendGitHubOutput(map[string]string{
"next-version": nextVersion,
"previous-version": previous,
"bump": bump,
"skip": "false",
})
}

// enrichment derives the GitHub-enrichment inputs shared by the stable and rc
Expand All @@ -107,17 +137,5 @@ func enrichment(root string, cfg *changes.Config, latest string, dryRun bool) (p
}

func writeGitHubOutputSkip() {
path := os.Getenv("GITHUB_OUTPUT")
if path == "" {
return
}
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "warning: could not open GITHUB_OUTPUT: %v\n", err)
return
}
defer f.Close()
if _, err := fmt.Fprintln(f, "skip=true"); err != nil {
fmt.Fprintf(os.Stderr, "warning: could not write GITHUB_OUTPUT: %v\n", err)
}
appendGitHubOutput(map[string]string{"skip": "true"})
}
2 changes: 1 addition & 1 deletion cmd/stable.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,6 @@ func runRelease(opts releaseOptions) error {
return rollback(err)
}
fmt.Fprintf(os.Stderr, "Created tag: %s\n", tag)
writeGitHubOutputs(tag, false)
writeGitHubOutputs(tag, tag, latest, false)
return nil
}
17 changes: 9 additions & 8 deletions cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func runStatus(_ *cobra.Command, _ []string) error {
}
if len(ch) == 0 {
fmt.Println("No pending changes.")
writeGitHubOutputSkip()
return nil
}

Expand All @@ -43,16 +44,16 @@ func runStatus(_ *cobra.Command, _ []string) error {
}
fmt.Printf("Latest stable tag: %s\n", orNone(latest))

// Use the same ComputeNext the release path uses, so the next version a
// consumer reads from status outputs matches what release would tag.
highest := release.HighestBump(ch)
if latest != "" {
maj, min, pat, err := release.ParseVersion(latest)
if err != nil {
return err
}
maj, min, pat = release.BumpVersion(maj, min, pat, highest)
fmt.Printf("Bump level: %s\n", highest)
fmt.Printf("Next version: %d.%d.%d\n", maj, min, pat)
next, err := release.ComputeNext(ch, latest)
if err != nil {
return err
}
fmt.Printf("Bump level: %s\n", highest)
fmt.Printf("Next version: %s\n", next)
writeStatusOutputs(next, latest, highest)
fmt.Println()

for _, c := range ch {
Expand Down
Loading