From c466e3ec156d7172c40c3f1fc8c3e8c0de571bad Mon Sep 17 00:00:00 2001 From: Robert Crumbaugh Date: Wed, 10 Apr 2024 12:04:37 -0700 Subject: [PATCH] feat: add auto-upgrade capability for speakeasyVersion --- .github/workflows/workflow-executor.yaml | 8 +++- action.yml | 7 ++- internal/actions/runWorkflow.go | 35 ++++++++++++--- internal/environment/environment.go | 4 ++ internal/git/git.go | 56 +++++++++++++++--------- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/.github/workflows/workflow-executor.yaml b/.github/workflows/workflow-executor.yaml index d38c1a00..daacea10 100644 --- a/.github/workflows/workflow-executor.yaml +++ b/.github/workflows/workflow-executor.yaml @@ -8,6 +8,11 @@ on: default: latest required: false type: string + auto_upgrade: + description: "Automatically upgrade Speakeasy version to the latest version" + default: "false" + required: false + type: string mode: description: |- The mode to run the workflow in, valid options are 'direct' or 'pr', defaults to 'direct'. @@ -127,9 +132,10 @@ jobs: uses: smorimoto/tune-github-hosted-runner-network@v1 - id: run-workflow name: Run Generation Workflow - uses: speakeasy-api/sdk-generation-action@v15 + uses: speakeasy-api/sdk-generation-action@v15-test with: speakeasy_version: ${{ inputs.speakeasy_version }} + auto_upgrade: ${{ inputs.auto_upgrade }} github_access_token: ${{ secrets.github_access_token }} mode: ${{ inputs.mode }} force: ${{ inputs.force }} diff --git a/action.yml b/action.yml index 6cb8554b..076771c9 100644 --- a/action.yml +++ b/action.yml @@ -6,6 +6,10 @@ inputs: description: The version of the Speakeasy CLI to use or "latest" default: latest required: false + auto_upgrade: + description: "Automatically upgrade Speakeasy version to the latest version" + default: "false" + required: false github_access_token: description: A GitHub access token with write access to the repo required: true @@ -140,7 +144,7 @@ outputs: description: "The location of the OpenAPI document used for generation" runs: using: "docker" - image: "docker://ghcr.io/speakeasy-api/sdk-generation-action:v15" + image: "docker://ghcr.io/speakeasy-api/sdk-generation-action:v15-test" env: SPEAKEASY_API_KEY: ${{ inputs.speakeasy_api_key }} SPEAKEASY_SERVER_URL: ${{ inputs.speakeasy_server_url }} @@ -161,3 +165,4 @@ runs: - ${{ inputs.gpg_fingerprint }} - ${{ inputs.openapi_doc_auth_token }} - ${{ inputs.target }} + - ${{ inputs.auto_upgrade }} diff --git a/internal/actions/runWorkflow.go b/internal/actions/runWorkflow.go index ab40b992..a4b1aeb7 100644 --- a/internal/actions/runWorkflow.go +++ b/internal/actions/runWorkflow.go @@ -2,15 +2,13 @@ package actions import ( "fmt" - "github.com/hashicorp/go-version" - "github.com/speakeasy-api/sdk-generation-action/internal/configuration" - "github.com/speakeasy-api/sdk-generation-action/internal/git" - "github.com/speakeasy-api/sdk-generation-action/internal/run" - "github.com/speakeasy-api/sdk-generation-action/internal/cli" + "github.com/speakeasy-api/sdk-generation-action/internal/configuration" "github.com/speakeasy-api/sdk-generation-action/internal/environment" + "github.com/speakeasy-api/sdk-generation-action/internal/git" "github.com/speakeasy-api/sdk-generation-action/internal/logging" + "github.com/speakeasy-api/sdk-generation-action/internal/run" "github.com/speakeasy-api/sdk-generation-action/pkg/releases" ) @@ -20,11 +18,36 @@ func RunWorkflow() error { return err } - resolvedVersion, err := cli.Download(environment.GetPinnedSpeakeasyVersion(), g) + pinnedVersion := cli.GetVersion(environment.GetPinnedSpeakeasyVersion()) + firstRunVersion := pinnedVersion + if environment.ShouldAutoUpgradeSpeakeasyVersion() { + firstRunVersion = "latest" + } + + resolvedVersion, err := cli.Download(firstRunVersion, g) if err != nil { return err } + // Only bother doing the second run if it will be with a different version than the first + autoUpgradeAttempted := pinnedVersion != "latest" && pinnedVersion != resolvedVersion + + err = runWorkflow(g, resolvedVersion) + if err != nil && autoUpgradeAttempted { + logging.Info("Error running workflow with version %s: %v", firstRunVersion, err) + logging.Info("Trying again with pinned version %s", pinnedVersion) + + resolvedVersion, err := cli.Download(firstRunVersion, g) + if err != nil { + return err + } + + return runWorkflow(g, resolvedVersion) + } + return err +} + +func runWorkflow(g *git.Git, resolvedVersion string) error { minimumVersionForRun := version.Must(version.NewVersion("1.161.0")) if !cli.IsAtLeastVersion(minimumVersionForRun) { return fmt.Errorf("action requires at least version %s of the speakeasy CLI", minimumVersionForRun) diff --git a/internal/environment/environment.go b/internal/environment/environment.go index c1240cda..da91925f 100644 --- a/internal/environment/environment.go +++ b/internal/environment/environment.go @@ -82,6 +82,10 @@ func GetPinnedSpeakeasyVersion() string { return os.Getenv("INPUT_SPEAKEASY_VERSION") } +func ShouldAutoUpgradeSpeakeasyVersion() bool { + return os.Getenv("INPUT_AUTO_UPGRADE") == "true" +} + func GetMaxSuggestions() string { return os.Getenv("INPUT_MAX_SUGGESTIONS") } diff --git a/internal/git/git.go b/internal/git/git.go index c24cb578..c124bc44 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -715,11 +715,19 @@ func (g *Git) GetLatestTag() (string, error) { } func (g *Git) GetDownloadLink(version string) (string, string, error) { - page := 0 - - // Iterate through pages until we find the release, or we run out of results - for { - releases, response, err := g.client.Repositories.ListReleases(context.Background(), "speakeasy-api", "speakeasy", &github.ListOptions{Page: page}) + if version != "latest" { + release, _, err := g.client.Repositories.GetReleaseByTag(context.Background(), "speakeasy-api", "speakeasy", version) + if err != nil { + return "", "", err + } + link, tag := getDownloadLinkFromRelease(release) + if link == nil || tag == nil { + return "", "", fmt.Errorf("no download link found for version %s", version) + } + return *link, *tag, nil + } else { + // Get first matching release + releases, _, err := g.client.Repositories.ListReleases(context.Background(), "speakeasy-api", "speakeasy", nil) if err != nil { return "", "", fmt.Errorf("failed to get speakeasy cli releases: %w", err) } @@ -729,8 +737,7 @@ func (g *Git) GetDownloadLink(version string) (string, string, error) { } else { link, tag := getDownloadLinkFromReleases(releases, version) if link == nil || tag == nil { - page = response.NextPage - continue + return "", "", fmt.Errorf("no download link found for version %s", version) } return *link, *tag, nil @@ -740,19 +747,10 @@ func (g *Git) GetDownloadLink(version string) (string, string, error) { func getDownloadLinkFromReleases(releases []*github.RepositoryRelease, version string) (*string, *string) { for _, release := range releases { - for _, asset := range release.Assets { - if version == "latest" || version == release.GetTagName() { - curOS := runtime.GOOS - curArch := runtime.GOARCH - downloadUrl := asset.GetBrowserDownloadURL() - - // https://github.com/speakeasy-api/sdk-generation-action/pull/28#discussion_r1213129634 - if curOS == "linux" && (strings.Contains(strings.ToLower(asset.GetName()), "_linux_x86_64") || strings.Contains(strings.ToLower(asset.GetName()), "_linux_amd64")) { - return &downloadUrl, release.TagName - } else if strings.Contains(strings.ToLower(asset.GetName()), curOS) && - strings.Contains(strings.ToLower(asset.GetName()), curArch) { - return &downloadUrl, release.TagName - } + if version == "latest" || version == release.GetTagName() { + url, tag := getDownloadLinkFromRelease(release) + if url != nil { + return url, tag } } } @@ -760,6 +758,24 @@ func getDownloadLinkFromReleases(releases []*github.RepositoryRelease, version s return nil, nil } +func getDownloadLinkFromRelease(release *github.RepositoryRelease) (*string, *string) { + for _, asset := range release.Assets { + curOS := runtime.GOOS + curArch := runtime.GOARCH + downloadUrl := asset.GetBrowserDownloadURL() + + // https://github.com/speakeasy-api/sdk-generation-action/pull/28#discussion_r1213129634 + if curOS == "linux" && (strings.Contains(strings.ToLower(asset.GetName()), "_linux_x86_64") || strings.Contains(strings.ToLower(asset.GetName()), "_linux_amd64")) { + return &downloadUrl, release.TagName + } else if strings.Contains(strings.ToLower(asset.GetName()), curOS) && + strings.Contains(strings.ToLower(asset.GetName()), curArch) { + return &downloadUrl, release.TagName + } + } + + return nil, nil +} + func (g *Git) GetCommitedFiles() ([]string, error) { path := environment.GetWorkflowEventPayloadPath()