github-workflow-controller/plugin/plugin.go

192 lines
5.1 KiB
Go

// 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
}