diff --git a/docker/Dockerfile.linux.amd64 b/docker/Dockerfile.linux.amd64 index fed781f..0197399 100644 --- a/docker/Dockerfile.linux.amd64 +++ b/docker/Dockerfile.linux.amd64 @@ -3,7 +3,8 @@ FROM docker:dind ENV DOCKER_HOST=unix:///var/run/docker.sock RUN apk add --no-cache ca-certificates curl -RUN curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sh -s v0.2.61 +RUN curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sh -s v0.2.70 ADD release/linux/amd64/plugin /bin/ +VOLUME /tmp/engine ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/plugin"] \ No newline at end of file diff --git a/go.mod b/go.mod index 9205b3f..c815c3b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/drone-plugins/drone-github-actions -go 1.19 +go 1.22.0 + +toolchain go1.23.2 require ( github.com/buildkite/yaml v2.1.0+incompatible @@ -9,12 +11,12 @@ require ( github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 github.com/urfave/cli v1.22.10 + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect golang.org/x/sys v0.3.0 // indirect ) diff --git a/go.sum b/go.sum index fca9eb8..4e3eaf8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -10,41 +9,32 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugin.go b/plugin.go index 69f3a0f..545d8ee 100644 --- a/plugin.go +++ b/plugin.go @@ -2,14 +2,14 @@ package plugin import ( "fmt" + "github.com/drone-plugins/drone-github-actions/daemon" + "github.com/drone-plugins/drone-github-actions/utils" + "github.com/pkg/errors" "io/ioutil" + "log" "os" "os/exec" "strings" - - "github.com/drone-plugins/drone-github-actions/daemon" - "github.com/drone-plugins/drone-github-actions/utils" - "github.com/pkg/errors" ) const ( @@ -17,6 +17,7 @@ const ( secretFile = "/tmp/action.secrets" workflowFile = "/tmp/workflow.yml" eventPayloadFile = "/tmp/event.json" + // outputFile = "/tmp/output.env" ) var ( @@ -45,9 +46,42 @@ func (p Plugin) Exec() error { if err := daemon.StartDaemon(p.Daemon); err != nil { return err } + repo, ref, ok := utils.ParseLookup(p.Action.Uses) + sha := "" + if !ok { + log.Println("failed to get repo and ref") + } + + log.Println(p.Action.Uses, repo, ref, sha) + outputFile := os.Getenv("DRONE_OUTPUT") + if _, err := os.Stat(outputFile); os.IsNotExist(err) { + _, err := os.OpenFile(outputFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("failed to create file: %w", err) + } + } + data := `commit_sha=abc123def456 +build_number=42 +artifact_url=https://example.com/artifacts/42 +status=success` +err1 := os.WriteFile(outputFile, []byte(data), 0644) +if err1 != nil { + log.Fatalf("Failed to write to output file: %v", err1) +} + log.Println(outputFile) + content, err3 := os.ReadFile(outputFile) + if err3 != nil { + log.Fatalf("Failed to read the file: %v", err3) + } + + fmt.Println("File contents:") + fmt.Println(string(content)) - if err := utils.CreateWorkflowFile(workflowFile, p.Action.Uses, - p.Action.With, p.Action.Env); err != nil { + outputVar := utils.GetOutputVars("/harness", p.Action.Uses) + log.Printf("Output Variables: %v\n", outputVar) + name := p.Action.Uses + if err := utils.CreateWorkflowFile(workflowFile, name, + p.Action.With, p.Action.Env, outputFile, outputVar); err != nil { return err } @@ -64,8 +98,16 @@ func (p Plugin) Exec() error { secretFile, "--env-file", envFile, + "-C", + "/tmp/engine", + // "--output-file", + // outputFile, "-b", - "--detect-event", + // "/plugin", + // "--detect-event", + // "--eventpath", + // outputFile, + "-v", } // optional arguments diff --git a/utils/locate.go b/utils/locate.go new file mode 100644 index 0000000..e3826b3 --- /dev/null +++ b/utils/locate.go @@ -0,0 +1,51 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package harness provides support for executing Github plugins. +package utils + +import ( + "fmt" + "os" + "path/filepath" + "regexp" +) + +// Is returns true if the root path is a Harness +// plugin repository. +func Is(root string) bool { + path := filepath.Join(root, "action.yml") + if _, err := os.Stat(path); err == nil { + return true + } + path = filepath.Join(root, "action.yaml") + if _, err := os.Stat(path); err == nil { + return true + } + return false +} + +func getActionYamlFname(root string) string { + if _, err := os.Stat(filepath.Join(root, "action.yml")); err == nil { + return filepath.Join(root, "action.yml") + } + if _, err := os.Stat(filepath.Join(root, "action.yaml")); err == nil { + return filepath.Join(root, "action.yaml") + } + return "" +} + +func parseActionName(action string) (org, repo, path, ref string, err error) { + r := regexp.MustCompile(`^([^/@]+)/([^/@]+)(/([^@]*))?(@(.*))?$`) + matches := r.FindStringSubmatch(action) + if len(matches) < 7 || matches[6] == "" { + err = fmt.Errorf("invalid action name: %s", action) + return + } + org = matches[1] + repo = matches[2] + path = matches[4] + ref = matches[6] + return +} \ No newline at end of file diff --git a/utils/lookup.go b/utils/lookup.go new file mode 100644 index 0000000..5610a9c --- /dev/null +++ b/utils/lookup.go @@ -0,0 +1,35 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +import ( + "fmt" + "net/url" + "strings" + + "golang.org/x/exp/slog" +) + +// ParseLookup parses the step string and returns the +// associated repository and ref. +func ParseLookup(s string) (repo string, ref string, ok bool) { + org, repo, _, ref, err := parseActionName(s) + if err == nil { + url := fmt.Sprintf("https://github.com/%s/%s", org, repo) + slog.Debug(fmt.Sprintf("parsed repo: %s, ref: %s", url, ref)) + return url, ref, true + } + + slog.Warn(fmt.Sprintf("failed to parse action name: %s with err: %v", s, err)) + if !strings.HasPrefix(s, "https://github.com") { + s, _ = url.JoinPath("https://github.com", s) + } + + slog.Debug("parsed repo", s) + if parts := strings.SplitN(s, "@", 2); len(parts) == 2 { + return parts[0], parts[1], true + } + return s, "", true +} diff --git a/utils/output.go b/utils/output.go new file mode 100644 index 0000000..6c9f81f --- /dev/null +++ b/utils/output.go @@ -0,0 +1,47 @@ +package utils + +import ( + "fmt" + "log" + "path/filepath" + + // "github.com/joho/godotenv" + "golang.org/x/exp/slog" +) + +func GetOutputVars(codedir, name string) []string { + outputVars := make([]string, 0) + _, _, relPath, _, err := parseActionName(name) + log.Println(relPath) + if err != nil { + slog.Warn(fmt.Sprintf("failed to parse action name: %s with err: %v", name, err)) + return outputVars + } + actionYmlFilePath := filepath.Join(codedir, relPath) + spec, err := parseFile(getActionYamlFname(actionYmlFilePath)) + log.Println(spec) + if err != nil { + slog.Warn(fmt.Sprintf("failed to parse output vars: %v", err)) + } + + if spec != nil && spec.Outputs != nil { + for k := range spec.Outputs { + outputVars = append(outputVars, k) + } + } + return outputVars + + + + // spec, err := parseFile("/tmp/action.yml") + // if err != nil { + // slog.Warn(fmt.Sprintf("failed to parse output vars: %v", err)) + // } + + // if spec != nil && spec.Outputs != nil { + // for k := range spec.Outputs { + // outputVars = append(outputVars, k) + // } + // } + // return outputVars +} \ No newline at end of file diff --git a/utils/parse.go b/utils/parse.go new file mode 100644 index 0000000..4716686 --- /dev/null +++ b/utils/parse.go @@ -0,0 +1,31 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +import ( + "io/ioutil" + + // "gopkg.in/yaml.v2" + "github.com/buildkite/yaml" +) + +// helper function to parse the bitrise plugin yaml. +func parse(b []byte) (*spec, error) { + out := new(spec) + err := yaml.Unmarshal(b, out) + if err != nil { + return nil, err + } + return out, nil +} + +// helper function to parse the bitrise plugin yaml file. +func parseFile(s string) (*spec, error) { + raw, err := ioutil.ReadFile(s) + if err != nil { + return nil, err + } + return parse(raw) +} \ No newline at end of file diff --git a/utils/t.go b/utils/t.go new file mode 100644 index 0000000..d638497 --- /dev/null +++ b/utils/t.go @@ -0,0 +1,10 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utils + +// spec defines the bitrise plugin. +type spec struct { + Outputs map[string]interface{} `yaml:"outputs"` +} \ No newline at end of file diff --git a/utils/workflow.go b/utils/workflow.go index 4f6be52..874f63c 100644 --- a/utils/workflow.go +++ b/utils/workflow.go @@ -3,7 +3,8 @@ package utils import ( "io/ioutil" "os" - + "fmt" + "runtime" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) @@ -21,9 +22,14 @@ type job struct { } type step struct { - Uses string `yaml:"uses"` - With map[string]string `yaml:"with"` - Env map[string]string `yaml:"env"` + Id string `yaml:"id,omitempty"` + Uses string `yaml:"uses,omitempty"` + Name string `yaml:"name,omitempty"` + With map[string]string `yaml:"with,omitempty"` + Env map[string]string `yaml:"env,omitempty"` + Run string `yaml:"run,omitempty"` + Shell string `yaml:"shell,omitempty"` + If string `yaml:"if,omitempty"` } const ( @@ -31,10 +37,11 @@ const ( workflowName = "drone-github-action" jobName = "action" runsOnImage = "ubuntu-latest" + stepId = "stepId" ) func CreateWorkflowFile(ymlFile string, action string, - with map[string]string, env map[string]string) error { + with map[string]string, env map[string]string, outputFile string, outputVars []string) error { j := job{ Name: jobName, RunsOn: runsOnImage, @@ -44,6 +51,7 @@ func CreateWorkflowFile(ymlFile string, action string, With: with, Env: env, }, + getOutputVariables(stepId, outputFile, outputVars), }, } wf := &workflow{ @@ -73,3 +81,45 @@ func getWorkflowEvent() string { } return "custom" } + +func getOutputVariables(prevStepId, outputFile string, outputVars []string) step { + skip := len(outputFile) == 0 || len(outputVars) == 0 + cmd := "" + for _, outputVar := range outputVars { + cmd += fmt.Sprintf("%s=${{ steps.%s.outputs.%s }}\n", outputVar, prevStepId, outputVar) + } + + if runtime.GOOS == "windows" { + // Windows: Create the file and write the output variables using Python + cmd = fmt.Sprintf("python -c \"%s\"", outputVarWinScript(outputVars, outputFile)) + } else { + // Unix-like: Use `touch` to create the file, then append the output variables + cmd = fmt.Sprintf("echo \"%s\" > %s",cmd, outputFile) + } + + s := step{ + Name: "output variables", + Run: cmd, + If: fmt.Sprintf("%t", !skip), + } + + if runtime.GOOS == "windows" { + s.Shell = "powershell" + } + + return s +} + + +func outputVarWinScript(outputVars []string, outputFile string) string { + script := "" + for idx, outputVar := range outputVars { + prefix := "out = " + if idx > 0 { + prefix += "out + " + } + script += fmt.Sprintf("%s'%s=${{ steps.%s.outputs.%s }}\\n';", prefix, outputVar, stepId, outputVar) + } + script += fmt.Sprintf("f = open('%s', 'wb'); f.write(bytes(out, 'UTF-8')); f.close()", outputFile) + return script +} \ No newline at end of file