Skip to content

Commit 7d97fe6

Browse files
feat: ocm version command (open-component-model#354)
* feat: add ocm version command * feat: setup version command * feat: setup version command * feat: setup version command * feat: setup version command * feat: setup version command * chore: fixup * chore: fixup * chore: fixup
1 parent 00e406a commit 7d97fe6

File tree

8 files changed

+282
-4
lines changed

8 files changed

+282
-4
lines changed

.github/config/wordlist.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,4 +390,10 @@ moldenhauer
390390
tac
391391
coc
392392
kunstlaan
393-
stichting
393+
stichting
394+
gobuildinfo
395+
gobuildinfojson
396+
buildDate
397+
gitCommit
398+
gitTreeState
399+
legacyjson

cli/Taskfile.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ version: '3'
33
vars:
44
CLI_DOCUMENTATION_MODE: "markdown"
55
CLI_DOCUMENTATION_DIRECTORY: tmp/docs
6-
76
tasks:
87
tmp:
98
cmds:
@@ -21,8 +20,13 @@ tasks:
2120
- tmp
2221
build:
2322
desc: "Build the CLI"
23+
vars:
24+
DEFAULT_VERSION:
25+
sh: |
26+
echo "0.0.0-$(date -u +%Y%m%d%H%M%S)-$(git rev-parse --short=12 HEAD)$(git diff --quiet || echo '+dirty')"
27+
VERSION: '{{ .CLI_ARGS | default .DEFAULT_VERSION }}'
2428
cmds:
25-
- go build -o {{ .ROOT_DIR }}/tmp/bin/ocm
29+
- go build -ldflags "-X ocm.software/open-component-model/cli/cmd/version.BuildVersion={{ .VERSION }}" -o {{ .ROOT_DIR }}/tmp/bin/ocm
2630

2731
generate/docs:
2832
desc: "Generate CLI Markdown documentation"

cli/cmd/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"ocm.software/open-component-model/cli/cmd/configuration"
1212
"ocm.software/open-component-model/cli/cmd/generate"
1313
"ocm.software/open-component-model/cli/cmd/get"
14+
"ocm.software/open-component-model/cli/cmd/version"
1415
ocmctx "ocm.software/open-component-model/cli/internal/context"
1516
"ocm.software/open-component-model/cli/internal/flags/log"
1617
)
@@ -44,6 +45,7 @@ func New() *cobra.Command {
4445
cmd.AddCommand(generate.New())
4546
cmd.AddCommand(get.New())
4647
cmd.AddCommand(add.New())
48+
cmd.AddCommand(version.New())
4749
return cmd
4850
}
4951

cli/cmd/cmd_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,26 @@ resources:
428428
})
429429
})
430430
}
431+
432+
func Test_Version(t *testing.T) {
433+
r := require.New(t)
434+
logs := test.NewJSONLogReader()
435+
_, err := test.OCM(t, test.WithArgs("version"), test.WithOutput(logs))
436+
r.NoError(err, "failed to run version command")
437+
438+
entries, err := logs.List()
439+
r.NoError(err, "failed to list log entries")
440+
441+
r.NotEmpty(entries, "expected log entries for version command")
442+
443+
found := false
444+
for _, entry := range entries {
445+
ver, ok := entry.Extras["gitVersion"]
446+
if ok {
447+
found = true
448+
r.Equal(ver, "(devel)")
449+
break
450+
}
451+
}
452+
r.True(found, "expected to find gitVersion in log entries")
453+
}

cli/cmd/get/component-version/cmd.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ Specifying types and schemes:
6363
get cv ctf::github.com/locally-checked-out-repo//ocm.software/ocmcli:0.23.0
6464
get cvs oci::http://localhost:8080//ocm.software/ocmcli
6565
`),
66-
Version: "v1alpha1",
6766
RunE: GetComponentVersion,
6867
DisableAutoGenTag: true,
6968
}

cli/cmd/version/legacy_version.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package version
2+
3+
import (
4+
"fmt"
5+
"runtime"
6+
"runtime/debug"
7+
"strconv"
8+
"strings"
9+
10+
_ "embed"
11+
12+
"github.com/Masterminds/semver/v3"
13+
)
14+
15+
type LegacyVersionInfo struct {
16+
Major string `json:"major"`
17+
Minor string `json:"minor"`
18+
Patch string `json:"patch"`
19+
PreRelease string `json:"prerelease,omitempty"`
20+
Meta string `json:"meta,omitempty"`
21+
GitVersion string `json:"gitVersion"`
22+
GitCommit string `json:"gitCommit,omitempty"`
23+
BuildDate string `json:"buildDate,omitempty"`
24+
GoVersion string `json:"goVersion"`
25+
Compiler string `json:"compiler"`
26+
Platform string `json:"platform"`
27+
}
28+
29+
// GetLegacyFormat returns the build information in a legacy format compatible with OCM v1 CLI specifications.
30+
func GetLegacyFormat(bi *debug.BuildInfo) (LegacyVersionInfo, error) {
31+
base := LegacyVersionInfo{
32+
GoVersion: runtime.Version(),
33+
Compiler: runtime.Compiler,
34+
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
35+
}
36+
37+
v, err := semver.NewVersion(bi.Main.Version)
38+
if err != nil {
39+
// If the version is not a valid semantic version, we still want to return it as a regular string.
40+
base.GitVersion = bi.Main.Version
41+
base.Major, base.Minor, base.Patch = "0", "0", "0"
42+
} else {
43+
// If the version is a valid semantic version, we extract the components.
44+
base.GitVersion = v.String()
45+
if v.Metadata() != "" {
46+
base.Meta = strings.TrimPrefix(v.Metadata(), "+")
47+
}
48+
if v.Prerelease() != "" {
49+
base.PreRelease = v.Prerelease()
50+
if base.PreRelease != "" {
51+
base.BuildDate, base.GitCommit, _ = strings.Cut(base.PreRelease, "-")
52+
}
53+
}
54+
base.Major = strconv.FormatUint(v.Major(), 10)
55+
base.Minor = strconv.FormatUint(v.Minor(), 10)
56+
base.Patch = strconv.FormatUint(v.Patch(), 10)
57+
}
58+
59+
return base, nil
60+
}

cli/cmd/version/version.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package version
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"runtime/debug"
8+
"strings"
9+
10+
_ "embed"
11+
12+
"github.com/spf13/cobra"
13+
)
14+
15+
const (
16+
FlagFormat = "format"
17+
FlagFormatShortHand = "f"
18+
FlagFormatOCMv1 = "legacyjson"
19+
FlagFormatGoBuildInfo = "gobuildinfo"
20+
FlagFormatGoBuildInfoJSON = "gobuildinfojson"
21+
)
22+
23+
// BuildVersion is an external variable that can be set at build time to override the version.
24+
// It is set to "n/a" by default, indicating that no version has been specified.
25+
// The variable can be adjusted at build time with
26+
//
27+
// -ldflags "-X ocm.software/open-component-model/cli/cmd/version.BuildVersion=1.2.3"
28+
//
29+
// The build version accepted is interpreted differently depending on the format:
30+
// - For `ocmv1`, it is expected to be a semantic version (e.g., "1.2.3") and will be split
31+
// for a json like output
32+
// - For `gobuildinfo`, it can be any version string, including a full semantic version.
33+
// If set, it will override the detected module build version from the Go build info.
34+
var BuildVersion = "n/a"
35+
36+
func New() *cobra.Command {
37+
cmd := &cobra.Command{
38+
Use: "version",
39+
Short: "Retrieve the build version of the OCM CLI",
40+
Long: fmt.Sprintf(`The version command retrieves the build version of the OCM CLI.
41+
42+
The build version can be formatted in different ways depending on the specified %[1]s flag.
43+
The default format is %[2]q, which outputs the version in a format compatible with OCM v1 specifications,
44+
with slight modifications:
45+
46+
- "gitTreeState" is removed in favor of "meta" field, which contains the git tree state.
47+
- "buildDate" and "gitCommit" are derived from the input version string, and are parsed according to the go module version specification.
48+
49+
When the format is set to %[3]q, it outputs the Go build information as a string. The format is standardized
50+
and unified across all golang applications.
51+
52+
When the format is set to %[4]q, it outputs the Go build information in JSON format.
53+
This is equivalent to %[3]q, but in a structured JSON format.
54+
55+
The build info by default is drawn from the go module build information, which is set at build time of the CLI.
56+
When officially built, it is possibly overwritten with the released version of the OCM CLI.`, FlagFormat, FlagFormatOCMv1, FlagFormatGoBuildInfo, FlagFormatGoBuildInfoJSON),
57+
Example: fmt.Sprintf(`ocm version --format %s`, FlagFormatOCMv1),
58+
RunE: func(cmd *cobra.Command, args []string) error {
59+
format, err := cmd.Flags().GetString(FlagFormat)
60+
if err != nil {
61+
return err
62+
}
63+
ver, ok := debug.ReadBuildInfo()
64+
if !ok {
65+
return fmt.Errorf("no build info available")
66+
}
67+
if BuildVersion != "n/a" {
68+
// Override the version if specified
69+
ver.Main.Version = BuildVersion
70+
}
71+
switch format {
72+
case FlagFormatOCMv1:
73+
ver, err := GetLegacyFormat(ver)
74+
if err != nil {
75+
return err
76+
}
77+
return json.NewEncoder(cmd.OutOrStdout()).Encode(ver)
78+
case FlagFormatGoBuildInfo:
79+
str := ver.String()
80+
_, err = io.Copy(cmd.OutOrStdout(), strings.NewReader(str))
81+
return err
82+
case FlagFormatGoBuildInfoJSON:
83+
return json.NewEncoder(cmd.OutOrStdout()).Encode(ver)
84+
default:
85+
return cmd.Help()
86+
}
87+
},
88+
DisableAutoGenTag: true,
89+
SilenceUsage: true,
90+
}
91+
92+
cmd.Flags().StringP(FlagFormat, FlagFormatShortHand, FlagFormatOCMv1, "format of the generated documentation")
93+
return cmd
94+
}

docs/reference/cli/ocm_version.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: ocm version
3+
description: Retrieve the build version of the OCM CLI.
4+
suppressTitle: true
5+
toc: true
6+
sidebar:
7+
collapsed: true
8+
---
9+
10+
## ocm version
11+
12+
Retrieve the build version of the OCM CLI
13+
14+
### Synopsis
15+
16+
The version command retrieves the build version of the OCM CLI.
17+
18+
The build version can be formatted in different ways depending on the specified format flag.
19+
The default format is "legacyjson", which outputs the version in a format compatible with OCM v1 specifications,
20+
with slight modifications:
21+
22+
- "gitTreeState" is removed in favor of "meta" field, which contains the git tree state.
23+
- "buildDate" and "gitCommit" are derived from the input version string, and are parsed according to the go module version specification.
24+
25+
When the format is set to "gobuildinfo", it outputs the Go build information as a string. The format is standardized
26+
and unified across all golang applications.
27+
28+
When the format is set to "gobuildinfojson", it outputs the Go build information in JSON format.
29+
This is equivalent to "gobuildinfo", but in a structured JSON format.
30+
31+
The build info by default is drawn from the go module build information, which is set at build time of the CLI.
32+
When officially built, it is possibly overwritten with the released version of the OCM CLI.
33+
34+
```
35+
ocm version [flags]
36+
```
37+
38+
### Examples
39+
40+
```
41+
ocm version --format legacyjson
42+
```
43+
44+
### Options
45+
46+
```
47+
-f, --format string format of the generated documentation (default "legacyjson")
48+
-h, --help help for version
49+
```
50+
51+
### Options inherited from parent commands
52+
53+
```
54+
--config string supply configuration by a given configuration file.
55+
By default (without specifying custom locations with this flag), the file will be read from one of the well known locations:
56+
1. The path specified in the OCM_CONFIG_PATH environment variable
57+
2. The XDG_CONFIG_HOME directory (if set), or the default XDG home ($HOME/.config), or the user's home directory
58+
- $XDG_CONFIG_HOME/ocm/config
59+
- $XDG_CONFIG_HOME/.ocmconfig
60+
- $HOME/.config/ocm/config
61+
- $HOME/.config/.ocmconfig
62+
- $HOME/.ocm/config
63+
- $HOME/.ocmconfig
64+
3. The current working directory:
65+
- $PWD/ocm/config
66+
- $PWD/.ocmconfig
67+
4. The directory of the current executable:
68+
- $EXE_DIR/ocm/config
69+
- $EXE_DIR/.ocmconfig
70+
Using the option, this configuration file be used instead of the lookup above.
71+
--logformat enum set the log output format that is used to print individual logs
72+
json: Output logs in JSON format, suitable for machine processing
73+
text: Output logs in human-readable text format, suitable for console output
74+
(must be one of [json text]) (default text)
75+
--loglevel enum sets the logging level
76+
debug: Show all logs including detailed debugging information
77+
info: Show informational messages and above
78+
warn: Show warnings and errors only (default)
79+
error: Show errors only
80+
(must be one of [debug error info warn]) (default info)
81+
--logoutput enum set the log output destination
82+
stdout: Write logs to standard output (default)
83+
stderr: Write logs to standard error, useful for separating logs from normal output
84+
(must be one of [stderr stdout]) (default stdout)
85+
```
86+
87+
### SEE ALSO
88+
89+
* [ocm](ocm.md) - The official Open Component Model (OCM) CLI
90+

0 commit comments

Comments
 (0)