-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial implementation of the version command * Updates the VersionString constant (and uses it in the init_command.go module) * Changes the Go version used in the pipeline * The version command can now confirm the latest version
- Loading branch information
1 parent
21ab333
commit 9711b0b
Showing
10 changed files
with
292 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package commands | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/matzefriedrich/cobra-extensions/pkg" | ||
"github.com/matzefriedrich/cobra-extensions/pkg/abstractions" | ||
"github.com/matzefriedrich/parsley/internal/utils" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
type versionCommand struct { | ||
use abstractions.CommandName `flag:"version" short:"Show the current Parsley CLI version"` | ||
CheckForUpdate bool `flag:"check-update" usage:"Checks for available updates and prints the update command"` | ||
} | ||
|
||
func (v *versionCommand) Execute() { | ||
|
||
appVersion, appVersionErr := utils.ApplicationVersion() | ||
if appVersionErr == nil { | ||
fmt.Printf("Parsley CLI v%s\n", appVersion.String()) | ||
} | ||
|
||
if v.CheckForUpdate == false { | ||
return | ||
} | ||
|
||
githubClient := utils.NewGitHubApiClient() | ||
release, err := githubClient.QueryLatestReleaseTag(context.Background()) | ||
if err != nil { | ||
return | ||
} | ||
|
||
releaseVersion, releaseVersionErr := release.TryParseVersionFromTag() | ||
if appVersionErr == nil && releaseVersionErr == nil { | ||
if appVersion.LessThan(*releaseVersion) { | ||
|
||
fmt.Printf("\n"+ | ||
"Your version of Parsley CLI is out of date!\n\n"+ | ||
"The latest version is: v%s.\n"+ | ||
"To update run the following command: "+ | ||
"go install github.com/matzefriedrich/parsley/cmd/parsley-cli@v%s\n\n", releaseVersion.String(), releaseVersion.String()) | ||
|
||
fmt.Printf("More information about the release %s is available at:\n%s\n", release.Name, release.HtmlUrl) | ||
|
||
} else if appVersion.Equal(*releaseVersion) { | ||
|
||
fmt.Printf("\n" + | ||
"You are using the latest version of Parsley CLI.\n\n") | ||
|
||
} | ||
} | ||
} | ||
|
||
var _ pkg.TypedCommand = (*versionCommand)(nil) | ||
|
||
func NewVersionCommand() *cobra.Command { | ||
command := &versionCommand{} | ||
return pkg.CreateTypedCommand(command) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package core | ||
|
||
import ( | ||
"github.com/matzefriedrich/parsley/internal/utils" | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func Test_Version_parse_version_from_github_release(t *testing.T) { | ||
|
||
// Arrange | ||
const version = "1.2.3" | ||
release := utils.GithubRelease{TagName: version} | ||
|
||
const expectedVersionString = "1.2.3" | ||
|
||
// Act | ||
actual, err := release.TryParseVersionFromTag() | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedVersionString, actual.String()) | ||
} | ||
|
||
func Test_Version_parse_prefixed_version_from_github_release(t *testing.T) { | ||
|
||
// Arrange | ||
const version = "v1.2.3" | ||
release := utils.GithubRelease{TagName: version} | ||
|
||
const expectedVersionString = "1.2.3" | ||
|
||
// Act | ||
actual, err := release.TryParseVersionFromTag() | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedVersionString, actual.String()) | ||
} | ||
|
||
func Test_Version_parse_prefixed_prerelease_version_from_github_release(t *testing.T) { | ||
|
||
// Arrange | ||
const version = "v1.2.3-alpha.1" | ||
release := utils.GithubRelease{TagName: version} | ||
|
||
const expectedVersionString = "1.2.3" | ||
|
||
// Act | ||
actual, err := release.TryParseVersionFromTag() | ||
|
||
// Assert | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedVersionString, actual.String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package utils | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
type GithubRelease struct { | ||
Id uint64 `json:"id"` | ||
TagName string `json:"tag_name"` | ||
Name string `json:"name"` | ||
HtmlUrl string `json:"html_url"` | ||
PublishedAt time.Time `json:"published_at"` | ||
} | ||
|
||
func (r GithubRelease) TryParseVersionFromTag() (*VersionInfo, error) { | ||
version := r.TagName | ||
return tryParseVersionInfo(version) | ||
} | ||
|
||
type githubApiClient struct { | ||
options HttpClientOptions | ||
} | ||
|
||
type HttpClientOptions struct { | ||
RequestTimeout time.Duration | ||
} | ||
|
||
type HttpClientOptionsFunc func(*HttpClientOptions) | ||
|
||
func NewGitHubApiClient(config ...HttpClientOptionsFunc) *githubApiClient { | ||
options := HttpClientOptions{ | ||
RequestTimeout: 5 * time.Second, | ||
} | ||
for _, optionsFunc := range config { | ||
optionsFunc(&options) | ||
} | ||
return &githubApiClient{ | ||
options: options, | ||
} | ||
} | ||
|
||
// QueryLatestReleaseTag Queries the latest version from the GitHub releases endpoint and compares it against the current application version. | ||
func (c *githubApiClient) QueryLatestReleaseTag(ctx context.Context) (*GithubRelease, error) { | ||
|
||
const owner = "matzefriedrich" | ||
const repo = "parsley" | ||
|
||
requestCtx, cancel := context.WithTimeout(ctx, c.options.RequestTimeout) | ||
defer cancel() | ||
|
||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases", owner, repo) | ||
request, err := http.NewRequestWithContext(requestCtx, http.MethodGet, url, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client := &http.Client{} | ||
response, requestErr := client.Do(request) | ||
if requestErr != nil { | ||
return nil, requestErr | ||
} | ||
|
||
defer response.Body.Close() | ||
|
||
if response.StatusCode != http.StatusOK { | ||
return nil, fmt.Errorf("failed to fetch latest release: %s", response.Status) | ||
} | ||
|
||
var releases []GithubRelease | ||
if unmarshalErr := json.NewDecoder(response.Body).Decode(&releases); unmarshalErr != nil { | ||
return nil, err | ||
} | ||
|
||
if len(releases) > 0 { | ||
latestRelease := releases[0] | ||
return &latestRelease, nil | ||
} | ||
|
||
return nil, errors.New("failed to retrieve release information") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
|
||
"github.com/hashicorp/go-version" | ||
) | ||
|
||
const ( | ||
VersionString string = "0.9.2" | ||
) | ||
|
||
type VersionInfo struct { | ||
Major int | ||
Minor int | ||
Patch int | ||
} | ||
|
||
func (v VersionInfo) String() string { | ||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) | ||
} | ||
|
||
func (v VersionInfo) LessThan(other VersionInfo) bool { | ||
a, _ := version.NewVersion(v.String()) | ||
b, _ := version.NewVersion(other.String()) | ||
return a.LessThan(b) | ||
} | ||
|
||
func (v VersionInfo) Equal(other VersionInfo) bool { | ||
a, _ := version.NewVersion(v.String()) | ||
b, _ := version.NewVersion(other.String()) | ||
return a.Equal(b) | ||
} | ||
|
||
func ApplicationVersion() (*VersionInfo, error) { | ||
version, err := tryParseVersionInfo(VersionString) | ||
if err != nil { | ||
return nil, errors.New("application version not set") | ||
} | ||
return version, nil | ||
} | ||
|
||
func tryParseVersionInfo(version string) (*VersionInfo, error) { | ||
|
||
re := regexp.MustCompile("(?:[vV])?(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)") | ||
match := re.FindStringSubmatch(version) | ||
if match == nil { | ||
return nil, errors.New("invalid version") | ||
} | ||
|
||
extracted := map[string]string{} | ||
names := re.SubexpNames() | ||
for _, name := range names { | ||
index := re.SubexpIndex(name) | ||
if index != -1 && len(name) > 0 { | ||
extracted[name] = match[index] | ||
} | ||
} | ||
|
||
readInt := func(name string) int { | ||
value, found := extracted[name] | ||
if found { | ||
n, err := strconv.Atoi(value) | ||
if err == nil { | ||
return n | ||
} | ||
} | ||
return 0 | ||
} | ||
|
||
major := readInt("major") | ||
minor := readInt("minor") | ||
patch := readInt("patch") | ||
|
||
return &VersionInfo{Major: major, Minor: minor, Patch: patch}, nil | ||
} |