// Copyright 2020 the Drone Authors. All rights reserved. // Use of this source code is governed by the Blue Oak Model License // that can be found in the LICENSE file. package plugin import ( "context" "fmt" "strings" "time" "github.com/google/go-github/v45/github" "github.com/sirupsen/logrus" "golang.org/x/oauth2" ) // Args provides plugin execution arguments. type Args struct { Pipeline // Level defines the plugin log level. Level string `envconfig:"PLUGIN_LOG_LEVEL"` GithubToken string `envconfig:"PLUGIN_GITHUB_TOKEN"` GithubRepo string `envconfig:"PLUGIN_GITHUB_REPO"` WorkflowId string `envconfig:"PLUGIN_WORKFLOW_ID"` WorkflowActions string `envconfig:"PLUGIN_WORKFLOW_ACTIONS"` } // Exec executes the plugin. func Exec(ctx context.Context, args Args) error { // write code here ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: args.GithubToken}, ) tc := oauth2.NewClient(ctx, ts) client := github.NewClient(tc) repos, _, err := client.Repositories.List(ctx, "", nil) if err != nil { return err } repoExists := false for _, repo := range repos { if repo.GetFullName() == args.GithubRepo { repoExists = true } } if !repoExists { return fmt.Errorf("repo '%s' does not exist or you don't have permission to use it!\n", args.GithubRepo) } // split the workflow actions into an array actions := strings.Split(args.WorkflowActions, ",") logrus.Debugln("Actions: ", actions) // execute the workflow actions for _, action := range actions { err := executeWorkflowAction(action, ctx, args, client) if err != nil { return err } } return err } func executeWorkflowAction(action string, ctx context.Context, args Args, client *github.Client) error { // check which action to execute if action == "start" { // extract owner and repo from github repo owner, repo := parseGithubRepo(args.GithubRepo) // list all workflow runs runs, _, err := client.Actions.ListWorkflowRunsByFileName(ctx, owner, repo, args.WorkflowId, nil) if err != nil { return err } // check if workflow is already running runExists := false for _, run := range runs.WorkflowRuns { logrus.Debugln("Checking run", run.GetCreatedAt(), run.GetStatus()) if contains([]string{"in_progress", "queued", "requested", "waiting"}, run.GetStatus()) { runExists = true break } } if runExists { logrus.Debugln("Workflow is already running, nothing to do!") return nil } // create a new workflow dispatch event resp, err := client.Actions.CreateWorkflowDispatchEventByFileName(ctx, owner, repo, args.WorkflowId, github.CreateWorkflowDispatchEventRequest{Ref: "main"}) logrus.Debugln("Got response", resp) if err != nil { return err } } else if action == "cancel" { // extract owner and repo from github repo owner, repo := parseGithubRepo(args.GithubRepo) // list workflow runs runs, _, err := client.Actions.ListWorkflowRunsByFileName(ctx, owner, repo, args.WorkflowId, nil) if err != nil { return err } // calcel all running workflow runs for _, run := range runs.WorkflowRuns { logrus.Debugln("Checking run", run.GetCreatedAt(), run.GetStatus()) if contains([]string{"in_progress", "queued", "requested", "waiting"}, run.GetStatus()) { logrus.Infoln("Cancelling run: ", run.GetID()) resp, err := client.Actions.CancelWorkflowRunByID(ctx, owner, repo, run.GetID()) logrus.Debugln("Got response", resp) if err != nil && err.Error() != "job scheduled on GitHub side; try again later" { return err } err = waitForRunStatus(ctx, client, owner, repo, run.GetID(), []string{"completed", "cancelled"}) if err != nil { return err } } } } else if action == "delete" { // extract owner and repo from github repo owner, repo := parseGithubRepo(args.GithubRepo) // list workflow runs runs, _, err := client.Actions.ListWorkflowRunsByFileName(ctx, owner, repo, args.WorkflowId, nil) if err != nil { return err } // delete all completed workflow runs for _, run := range runs.WorkflowRuns { logrus.Debugln("Checking run", run.GetCreatedAt(), run.GetStatus()) if contains([]string{"completed", "cancelled", "failure", "skipped", "success", "timed_out"}, run.GetStatus()) { logrus.Infoln("Deleting run: ", run.GetID()) _, err := client.Actions.DeleteWorkflowRun(ctx, owner, repo, run.GetID()) if err != nil { return err } } } } else { return fmt.Errorf("unknown action '%s'\n", action) } return nil } func parseGithubRepo(repo string) (string, string) { parts := strings.Split(repo, "/") return parts[0], parts[1] } func waitForRunStatus(ctx context.Context, client *github.Client, owner string, repo string, runId int64, status []string) error { for { run, _, err := client.Actions.GetWorkflowRunByID(ctx, owner, repo, runId) if err != nil { return err } logrus.Debugln("Checking run", run.GetCreatedAt(), run.GetStatus()) if contains(status, run.GetStatus()) { return nil } time.Sleep(time.Second) } } func contains(slice []string, item string) bool { for _, s := range slice { if s == item { return true } } return false }