From 3c44abe0a22e4edf84a6e333769f902f22d388d6 Mon Sep 17 00:00:00 2001 From: Ignacio del Valle Alles Date: Thu, 11 Apr 2024 23:52:36 +0200 Subject: [PATCH] feat: add GHA token setting (#4008) Adds a new hidden command `earthly github add` that lets the user create a new [GHA-Satellites integration](https://docs.google.com/document/d/1Hl42AT8OjRTw2f9Yv0J757_GewLrOk8RVZsBsoxYjbY/edit#heading=h.5imekt3ni58h) (experimental) --------- Co-authored-by: idodod --- cloud/github.go | 25 +++++++ cmd/earthly/subcmd/account_cmds.go | 1 + cmd/earthly/subcmd/github_cmds.go | 105 +++++++++++++++++++++++++++++ cmd/earthly/subcmd/root_cmds.go | 1 + tests/autocompletion/Earthfile | 2 + 5 files changed, 134 insertions(+) create mode 100644 cloud/github.go create mode 100644 cmd/earthly/subcmd/github_cmds.go diff --git a/cloud/github.go b/cloud/github.go new file mode 100644 index 00000000..ced503b4 --- /dev/null +++ b/cloud/github.go @@ -0,0 +1,25 @@ +package cloud + +import ( + "context" + "fmt" + + pb "github.com/earthly/cloud-api/compute" +) + +func (c *Client) SetGithubToken(ctx context.Context, orgName string, ghOrg string, ghRepo string, token string) error { + orgID, err := c.GetOrgID(ctx, orgName) + if err != nil { + return fmt.Errorf("failed getting org ID: %w", err) + } + _, err = c.compute.SetGithubToken(c.withAuth(ctx), &pb.SetGithubTokenRequest{ + OrgId: orgID, + GithubOrgName: ghOrg, + GithubRepoName: ghRepo, + GithubToken: token, + }) + if err != nil { + return fmt.Errorf("failed setting Github token: %w", err) + } + return nil +} diff --git a/cmd/earthly/subcmd/account_cmds.go b/cmd/earthly/subcmd/account_cmds.go index 25d6eb7b..3170470e 100644 --- a/cmd/earthly/subcmd/account_cmds.go +++ b/cmd/earthly/subcmd/account_cmds.go @@ -358,6 +358,7 @@ func promptHiddenText(hiddenTextPrompt string) (string, error) { fmt.Println("") return string(password), nil } + func promptPassword() (string, error) { return promptHiddenText("password") } diff --git a/cmd/earthly/subcmd/github_cmds.go b/cmd/earthly/subcmd/github_cmds.go new file mode 100644 index 00000000..5de9ea13 --- /dev/null +++ b/cmd/earthly/subcmd/github_cmds.go @@ -0,0 +1,105 @@ +package subcmd + +import ( + "fmt" + "os/signal" + "syscall" + + "github.com/urfave/cli/v2" + + "github.com/earthly/earthly/cmd/earthly/helper" +) + +type Github struct { + cli CLI + + Org string + GHOrg string + GHRepo string + GHToken string +} + +func NewGithub(cli CLI) *Github { + return &Github{ + cli: cli, + } +} + +func (a *Github) Cmds() []*cli.Command { + return []*cli.Command{ + { + Name: "github", + Usage: "*experimental* Manage GitHub integration", + Description: "*experimental* Manage GitHub integration.", + Hidden: true, + Subcommands: []*cli.Command{ + { + Name: "add", + Usage: "Add GHA integration", + Description: `This command sets the configuration to create a new Github-Earthly integration, to trigger satellite builds from GHA (GitHub Actions). +From the Github side, integration can be done at two levels: organization-wide and per repository. +The provided token must have enough permissions to register webhooks and to create Github self hosted runners in those two scenarios.`, + UsageText: "earthly github add --org [--repo ] --token ", + Action: a.actionAdd, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "org", + Usage: "The name of the Earthly organization to set an integration with. Defaults to selected organization", + Destination: &a.Org, + }, + &cli.StringFlag{ + Name: "gh-org", + Usage: "The name of the GitHub organization to set an integration with", + Required: true, + Destination: &a.GHOrg, + }, + &cli.StringFlag{ + Name: "gh-repo", + Usage: "The name of the GitHub repository to set an integration with", + Destination: &a.GHRepo, + }, + &cli.StringFlag{ + Name: "gh-token", + Usage: "The GitHub token used for the integration", + Destination: &a.GHToken, + }, + }, + }, + }, + }, + } +} + +func (a *Github) actionAdd(cliCtx *cli.Context) error { + a.cli.SetCommandName("githubAdd") + if a.Org == "" { + if a.cli.OrgName() == "" { + return fmt.Errorf("coudn't determine Earthly organization") + } + a.Org = a.cli.OrgName() + } + if a.GHToken == "" { + // Our signal handling under main() doesn't cause reading from stdin to cancel + // as there's no way to pass app.ctx to stdin read calls. + signal.Reset(syscall.SIGINT, syscall.SIGTERM) + token, err := promptToken() + if err != nil { + return err + } + a.GHToken = token + } + cloudClient, err := helper.NewCloudClient(a.cli) + if err != nil { + return err + } + err = cloudClient.SetGithubToken(cliCtx.Context, a.cli.OrgName(), a.GHOrg, a.GHRepo, a.GHToken) + if err != nil { + return fmt.Errorf("error found running github add: %w", err) + } + a.cli.Console().Printf("GitHub integration successfully created") + return nil +} + +func promptToken() (string, error) { + return promptHiddenText("Enter GH token") +} diff --git a/cmd/earthly/subcmd/root_cmds.go b/cmd/earthly/subcmd/root_cmds.go index 13240506..752b7f18 100644 --- a/cmd/earthly/subcmd/root_cmds.go +++ b/cmd/earthly/subcmd/root_cmds.go @@ -37,6 +37,7 @@ func (a *Root) Cmds() []*cli.Command { NewSecret(a.cli).Cmds(), NewWeb(a.cli).Cmds(), NewBilling(a.cli).Cmds(), + NewGithub(a.cli).Cmds(), }) return cmds diff --git a/tests/autocompletion/Earthfile b/tests/autocompletion/Earthfile index 4f5debc4..f7075da4 100644 --- a/tests/autocompletion/Earthfile +++ b/tests/autocompletion/Earthfile @@ -24,6 +24,7 @@ web " > expected test-hidden-root-commands: ENV EARTHLY_SHOW_HIDDEN=1 + RUN echo "no-cache" RUN echo "account billing bootstrap @@ -33,6 +34,7 @@ debug doc docker-build docker2earthly +github init ls org