|
| 1 | +package telefonistka |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "os" |
| 7 | + "strings" |
| 8 | + |
| 9 | + lru "github.com/hashicorp/golang-lru/v2" |
| 10 | + "github.com/hexops/gotextdiff" |
| 11 | + "github.com/hexops/gotextdiff/myers" |
| 12 | + "github.com/hexops/gotextdiff/span" |
| 13 | + "github.com/mikefarah/yq/v4/pkg/yqlib" |
| 14 | + log "github.com/sirupsen/logrus" |
| 15 | + "github.com/spf13/cobra" |
| 16 | + "github.com/wayfair-incubator/telefonistka/internal/pkg/githubapi" |
| 17 | +) |
| 18 | + |
| 19 | +// This is still(https://github.com/spf13/cobra/issues/1862) the documented way to use cobra |
| 20 | +func init() { //nolint:gochecknoinits |
| 21 | + var targetRepo string |
| 22 | + var targetFile string |
| 23 | + var address string |
| 24 | + var replacement string |
| 25 | + var githubHost string |
| 26 | + var triggeringRepo string |
| 27 | + var triggeringRepoSHA string |
| 28 | + var triggeringActor string |
| 29 | + var autoMerge bool |
| 30 | + eventCmd := &cobra.Command{ |
| 31 | + Use: "bump-yaml", |
| 32 | + Short: "Bump artifact version in a file using yaml selector", |
| 33 | + Long: `Bump artifact version in a file using yaml selector. |
| 34 | +This will open a pull request in the target repo. |
| 35 | +This command uses yq selector to find the yaml value to replace. |
| 36 | +`, |
| 37 | + Args: cobra.ExactArgs(0), |
| 38 | + Run: func(cmd *cobra.Command, args []string) { |
| 39 | + bumpVersionYaml(targetRepo, targetFile, address, replacement, githubHost, triggeringRepo, triggeringRepoSHA, triggeringActor, autoMerge) |
| 40 | + }, |
| 41 | + } |
| 42 | + eventCmd.Flags().StringVarP(&targetRepo, "target-repo", "t", getEnv("TARGET_REPO", ""), "Target Git repository slug(e.g. org-name/repo-name), defaults to TARGET_REPO env var.") |
| 43 | + eventCmd.Flags().StringVarP(&targetFile, "target-file", "f", getEnv("TARGET_FILE", ""), "Target file path(from repo root), defaults to TARGET_FILE env var.") |
| 44 | + eventCmd.Flags().StringVar(&address, "address", "", "Yaml value address described as a yq selector, e.g. '.db.[] | select(.name == \"postgres\").image.tag'.") |
| 45 | + eventCmd.Flags().StringVarP(&replacement, "replacement-string", "n", "", "Replacement string that includes the version value of new artifact, e.g. 'v2.7.1'.") |
| 46 | + eventCmd.Flags().StringVarP(&githubHost, "github-host", "g", "", "GitHub instance HOSTNAME, defaults to \"github.com\". This is used for GitHub Enterprise Server instances.") |
| 47 | + eventCmd.Flags().StringVarP(&triggeringRepo, "triggering-repo", "p", getEnv("GITHUB_REPOSITORY", ""), "Github repo triggering the version bump(e.g. `octocat/Hello-World`) defaults to GITHUB_REPOSITORY env var.") |
| 48 | + eventCmd.Flags().StringVarP(&triggeringRepoSHA, "triggering-repo-sha", "s", getEnv("GITHUB_SHA", ""), "Git SHA of triggering repo, defaults to GITHUB_SHA env var.") |
| 49 | + eventCmd.Flags().StringVarP(&triggeringActor, "triggering-actor", "a", getEnv("GITHUB_ACTOR", ""), "GitHub user of the person/bot who triggered the bump, defaults to GITHUB_ACTOR env var.") |
| 50 | + eventCmd.Flags().BoolVar(&autoMerge, "auto-merge", false, "Automatically merges the created PR, defaults to false.") |
| 51 | + rootCmd.AddCommand(eventCmd) |
| 52 | +} |
| 53 | + |
| 54 | +func bumpVersionYaml(targetRepo string, targetFile string, address string, value string, githubHost string, triggeringRepo string, triggeringRepoSHA string, triggeringActor string, autoMerge bool) { |
| 55 | + ctx := context.Background() |
| 56 | + var githubRestAltURL string |
| 57 | + |
| 58 | + if githubHost != "" { |
| 59 | + githubRestAltURL = "https://" + githubHost + "/api/v3" |
| 60 | + log.Infof("Github REST API endpoint is configured to %s", githubRestAltURL) |
| 61 | + } |
| 62 | + var mainGithubClientPair githubapi.GhClientPair |
| 63 | + mainGhClientCache, _ := lru.New[string, githubapi.GhClientPair](128) |
| 64 | + |
| 65 | + mainGithubClientPair.GetAndCache(mainGhClientCache, "GITHUB_APP_ID", "GITHUB_APP_PRIVATE_KEY_PATH", "GITHUB_OAUTH_TOKEN", strings.Split(targetRepo, "/")[0], ctx) |
| 66 | + |
| 67 | + var ghPrClientDetails githubapi.GhPrClientDetails |
| 68 | + |
| 69 | + ghPrClientDetails.GhClientPair = &mainGithubClientPair |
| 70 | + ghPrClientDetails.Ctx = ctx |
| 71 | + ghPrClientDetails.Owner = strings.Split(targetRepo, "/")[0] |
| 72 | + ghPrClientDetails.Repo = strings.Split(targetRepo, "/")[1] |
| 73 | + ghPrClientDetails.PrLogger = log.WithFields(log.Fields{}) // TODO what fields should be here? |
| 74 | + |
| 75 | + defaultBranch, _ := ghPrClientDetails.GetDefaultBranch() |
| 76 | + |
| 77 | + initialFileContent, err, _ := githubapi.GetFileContent(ghPrClientDetails, defaultBranch, targetFile) |
| 78 | + if err != nil { |
| 79 | + ghPrClientDetails.PrLogger.Errorf("Fail to fetch file content:%s\n", err) |
| 80 | + os.Exit(1) |
| 81 | + } |
| 82 | + newFileContent, err := updateYaml(initialFileContent, address, value) |
| 83 | + if err != nil { |
| 84 | + ghPrClientDetails.PrLogger.Errorf("Fail to update yaml:%s\n", err) |
| 85 | + os.Exit(1) |
| 86 | + } |
| 87 | + |
| 88 | + edits := myers.ComputeEdits(span.URIFromPath(""), initialFileContent, newFileContent) |
| 89 | + ghPrClientDetails.PrLogger.Infof("Diff:\n%s", gotextdiff.ToUnified("Before", "After", initialFileContent, edits)) |
| 90 | + |
| 91 | + err = githubapi.BumpVersion(ghPrClientDetails, "main", targetFile, newFileContent, triggeringRepo, triggeringRepoSHA, triggeringActor, autoMerge) |
| 92 | + if err != nil { |
| 93 | + log.Errorf("Failed to bump version: %v", err) |
| 94 | + os.Exit(1) |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +func updateYaml(yamlContent string, address string, value string) (string, error) { |
| 99 | + yqExpression := fmt.Sprintf("(%s)=\"%s\"", address, value) |
| 100 | + |
| 101 | + preferences := yqlib.NewDefaultYamlPreferences() |
| 102 | + evaluate, err := yqlib.NewStringEvaluator().Evaluate(yqExpression, yamlContent, yqlib.NewYamlEncoder(preferences), yqlib.NewYamlDecoder(preferences)) |
| 103 | + if err != nil { |
| 104 | + return "", err |
| 105 | + } |
| 106 | + return evaluate, nil |
| 107 | +} |
0 commit comments