commit 60d32ee4aee0a54df8a19dc9ed981968c9ab2cb1 Author: Lauris BH Date: Sun Jan 8 04:53:03 2023 +0200 Initial implementation diff --git a/.woodpecker.yaml b/.woodpecker.yaml new file mode 100644 index 0000000..e745fba --- /dev/null +++ b/.woodpecker.yaml @@ -0,0 +1,15 @@ +variables: + - &golang 'golang:1.18' + +pipeline: + - name: vendor + image: *golang + commands: go mod vendor + + - name: lint + image: golangci/golangci-lint:v1.50-alpine + commands: golangci-lint run + + - name: test + image: *golang + commands: go test --cover ./... diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d965749 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Drone.IO Inc. + Copyright 2020 Woodpecker-CI Team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a372155 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Library for creating Woodpecker CI plugins + +Provides basic structure and helpers to load Woodpecker CI environment variables while also +supporting reading Drone CI environment variables where available. + +Adds logging support based on [zerolog](https://github.com/rs/zerolog) library and allows configurable +HTTP client library. + +## Builtin settings + +| Settings Name | Environment variable | Default | Description | +|---|---|----|---| +| `log_level` | - | `info` | Sets log level (`panic`, `fatal`, `error`, `warn`, `info`, `debug`, `trace`) | +| `skip_verify` | - | `false` | - | Skip verification of TLS certificate | +| | `SOCKS_PROXY` | *none* | SOCKS5 proxy to use for connections | +| | `SOCKS_PROXY_OFF` | *none* | Do not use SOCKS5 proxy | + +## Creating plugin + +```go +import ( + "context" + + "codeberg.org/woodpecker-plugins/go-plugin" + "github.com/urfave/cli/v2" + "github.com/rs/zerolog/log" +) + +type Settings struct { + // TODO: Plugin settings + SampleFlag string +} + +type Plugin struct { + *plugin.Plugin + Settings *Settings +} + +func (p *Plugin) Flags() []cli.Flag { + return []cli.Flag{ + // TODO: Add flags + &cli.StringFlag{ + Name: "sample.flag", + Usage: "sample flag", + EnvVars: []string{"PLUGIN_SAMPLE_FLAG"}, + Destination: &p.Settings.SampleFlag, + }, + } +} + +func (p *Plugin) Execute(ctx context.Context) error { + // TODO: Implement execution + log.Debug().Msg("executed") + return nil +} + +func main() { + p := &Plugin{ + &Settings{} + } + + p.Plugin = plugin.New(Options{ + Name: "sample-plugin", + Description: "Sample plugin", + Flags: p.Flags(), + Execute: p.Execute, + }) + + p.Run() +} +``` diff --git a/commit.go b/commit.go new file mode 100644 index 0000000..df9e694 --- /dev/null +++ b/commit.go @@ -0,0 +1,186 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/urfave/cli/v2" +) + +type ( + // Commit defines runtime metadata for a commit. + Commit struct { + Sha string `json:"sha,omitempty"` + Ref string `json:"ref,omitempty"` + Refspec string `json:"refspec,omitempty"` + PullRequest string `json:"pull_request,omitempty"` + SourceBranch string `json:"source_branch,omitempty"` + TargetBranch string `json:"target_branch,omitempty"` + Branch string `json:"branch,omitempty"` + Tag string `json:"tag,omitempty"` + Message string `json:"message,omitempty"` + Author Author `json:"author,omitempty"` + } + + // Author defines runtime metadata for a commit author. + Author struct { + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + Avatar string `json:"avatar,omitempty"` + } +) + +func currFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "commit.sha", + Usage: "commit SHA", + EnvVars: []string{"CI_COMMIT_SHA", "DRONE_COMMIT", "DRONE_COMMIT_SHA"}, + }, + &cli.StringFlag{ + Name: "commit.ref", + Usage: "commit ref", + EnvVars: []string{"CI_COMMIT_REF", "DRONE_COMMIT_REF"}, + }, + &cli.StringFlag{ + Name: "commit.refspec", + Usage: "commit refspec", + EnvVars: []string{"CI_COMMIT_REFSPEC"}, + }, + &cli.StringFlag{ + Name: "commit.pull-request", + Usage: "commit pull request", + EnvVars: []string{"CI_COMMIT_PULL_REQUEST", "DRONE_PULL_REQUEST"}, + }, + &cli.StringFlag{ + Name: "commit.source-branch", + Usage: "commit source branch", + EnvVars: []string{"CI_COMMIT_SOURCE_BRANCH", "DRONE_SOURCE_BRANCH"}, + }, + &cli.StringFlag{ + Name: "commit.target-branch", + Usage: "commit target branch", + EnvVars: []string{"CI_COMMIT_TARGET_BRANCH", "DRONE_TARGET_BRANCH"}, + }, + &cli.StringFlag{ + Name: "commit.branch", + Usage: "commit branch", + EnvVars: []string{"CI_COMMIT_BRANCH", "DRONE_BRANCH"}, + }, + &cli.StringFlag{ + Name: "commit.tag", + Usage: "commit tag", + EnvVars: []string{"CI_COMMIT_TAG", "DRONE_TAG"}, + }, + &cli.StringFlag{ + Name: "commit.message", + Usage: "commit message", + EnvVars: []string{"CI_COMMIT_MESSAGE", "DRONE_COMMIT_MESSAGE"}, + }, + &cli.StringFlag{ + Name: "commit.author.name", + Usage: "commit author name", + EnvVars: []string{"CI_COMMIT_AUTHOR", "DRONE_COMMIT_AUTHOR", "DRONE_COMMIT_AUTHOR_NAME"}, + }, + &cli.StringFlag{ + Name: "commit.author.email", + Usage: "commit author email", + EnvVars: []string{"CI_COMMIT_AUTHOR_EMAIL", "DRONE_COMMIT_AUTHOR_EMAIL"}, + }, + &cli.StringFlag{ + Name: "commit.author.avatar", + Usage: "commit author avatar", + EnvVars: []string{"CI_COMMIT_AUTHOR_AVATAR", "DRONE_COMMIT_AUTHOR_AVATAR"}, + }, + } +} + +func currFromContext(c *cli.Context) Commit { + return Commit{ + Sha: c.String("commit.sha"), + Ref: c.String("commit.ref"), + Refspec: c.String("commit.refspec"), + PullRequest: c.String("commit.pull-request"), + SourceBranch: c.String("commit.source-branch"), + TargetBranch: c.String("commit.target-branch"), + Branch: c.String("commit.branch"), + Tag: c.String("commit.tag"), + Message: c.String("commit.message"), + Author: Author{ + Name: c.String("commit.author.name"), + Email: c.String("commit.author.email"), + Avatar: c.String("commit.author.avatar"), + }, + } +} + +func prevFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "prev.commit.sha", + Usage: "previous commit SHA", + EnvVars: []string{"CI_PREV_COMMIT_SHA", "DRONE_COMMIT_BEFORE"}, + }, + &cli.StringFlag{ + Name: "prev.commit.ref", + Usage: "previous commit ref", + EnvVars: []string{"CI_PREV_COMMIT_REF"}, + }, + &cli.StringFlag{ + Name: "prev.commit.refspec", + Usage: "previous commit refspec", + EnvVars: []string{"CI_PREV_COMMIT_REFSPEC"}, + }, + &cli.StringFlag{ + Name: "prev.commit.branch", + Usage: "previous commit branch", + EnvVars: []string{"CI_PREV_COMMIT_BRANCH"}, + }, + &cli.StringFlag{ + Name: "prev.commit.message", + Usage: "previous commit message", + EnvVars: []string{"CI_PREV_COMMIT_MESSAGE"}, + }, + &cli.StringFlag{ + Name: "prev.commit.author.name", + Usage: "previous commit author name", + EnvVars: []string{"CI_PREV_COMMIT_AUTHOR"}, + }, + &cli.StringFlag{ + Name: "prev.commit.author.email", + Usage: "previous commit author email", + EnvVars: []string{"CI_PREV_COMMIT_AUTHOR_EMAIL"}, + }, + &cli.StringFlag{ + Name: "prev.commit.author.avatar", + Usage: "previous commit author avatar", + EnvVars: []string{"CI_PREV_COMMIT_AUTHOR_AVATAR"}, + }, + } +} + +func prevFromContext(c *cli.Context) Commit { + return Commit{ + Sha: c.String("prev.commit.sha"), + Ref: c.String("prev.commit.ref"), + Refspec: c.String("prev.commit.refspec"), + Branch: c.String("prev.commit.branch"), + Message: c.String("prev.commit.message"), + Author: Author{ + Name: c.String("prev.commit.author.name"), + Email: c.String("prev.commit.author.email"), + Avatar: c.String("prev.commit.author.avatar"), + }, + } +} diff --git a/flags.go b/flags.go new file mode 100644 index 0000000..e84db8d --- /dev/null +++ b/flags.go @@ -0,0 +1,38 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/urfave/cli/v2" +) + +// Flags has the cli.Flags for the Woodpecker plugin. +func Flags() []cli.Flag { + flags := make([]cli.Flag, 0, 30) + + // Pipeline flags + flags = append(flags, repositoryFlags()...) + flags = append(flags, pipelineFlags()...) + flags = append(flags, currFlags()...) + flags = append(flags, prevFlags()...) + flags = append(flags, stepFlags()...) + flags = append(flags, systemFlags()...) + + // Plugin flags + flags = append(flags, loggingFlags()...) + flags = append(flags, httpClientFlags()...) + + return flags +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1447e6c --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module codeberg.org/woodpecker-plugins/go-plugin + +go 1.19 + +require ( + github.com/joho/godotenv v1.4.0 + github.com/rs/zerolog v1.28.0 + github.com/stretchr/testify v1.8.1 + github.com/urfave/cli/v2 v2.23.7 + golang.org/x/net v0.5.0 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/sys v0.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4616faf --- /dev/null +++ b/go.sum @@ -0,0 +1,43 @@ +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +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/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= +github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= +github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http.go b/http.go new file mode 100644 index 0000000..066f3a9 --- /dev/null +++ b/http.go @@ -0,0 +1,102 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" + "crypto/tls" + "crypto/x509" + "net" + "net/http" + "time" + + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" + "golang.org/x/net/proxy" +) + +func httpClientFlags() []cli.Flag { + return []cli.Flag{ + &cli.BoolFlag{ + Name: "transport.skip-verify", + Usage: "skip ssl verify", + EnvVars: []string{"PLUGIN_SKIP_VERIFY"}, + }, + &cli.StringFlag{ + Name: "transport.socks-proxy", + Usage: "socks proxy address", + EnvVars: []string{"SOCKS_PROXY"}, + Hidden: true, + }, + &cli.BoolFlag{ + Name: "transport.socks-proxy-off", + Usage: "socks proxy ignored", + EnvVars: []string{"SOCKS_PROXY_OFF"}, + Hidden: true, + }, + } +} + +func HTTPClientFromContext(ctx *cli.Context) *http.Client { + var ( + skip = ctx.Bool("transport.skip-verify") + socks = ctx.String("transport.socks-proxy") + socksoff = ctx.Bool("transport.socks-proxy-off") + ) + + certs, err := x509.SystemCertPool() + if err != nil { + log.Error().Err(err).Msg("failed to find system CA certs") + } + tlsConfig := &tls.Config{ + RootCAs: certs, + InsecureSkipVerify: skip, + } + + transport := &http.Transport{ + TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + dialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + } + + if len(socks) != 0 && !socksoff { + proxyDialer, err := proxy.SOCKS5("tcp", socks, nil, dialer) + if err != nil { + log.Error().Err(err).Msg("failed to create socks proxy") + } + if contextDialer, ok := proxyDialer.(proxy.ContextDialer); ok { + transport.DialContext = contextDialer.DialContext + } else { + transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + return proxyDialer.Dial(network, addr) + } + } + } else { + transport.DialContext = dialer.DialContext + } + + return &http.Client{ + Transport: transport, + } +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..19c2bcd --- /dev/null +++ b/logger.go @@ -0,0 +1,47 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" +) + +func loggingFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "log-level", + Usage: "log level", + EnvVars: []string{"PLUGIN_LOG_LEVEL"}, + Value: "info", + }, + } +} + +// SetupConsoleLogger sets up the console logger. +func SetupConsoleLogger(c *cli.Context) error { + level := c.String("log-level") + lvl, err := zerolog.ParseLevel(level) + if err != nil { + log.Fatal().Msgf("unknown logging level: %s", level) + } + zerolog.SetGlobalLevel(lvl) + if zerolog.GlobalLevel() <= zerolog.DebugLevel { + log.Logger = log.With().Caller().Logger() + log.Log().Msgf("LogLevel = %s", zerolog.GlobalLevel().String()) + } + return nil +} diff --git a/metadata.go b/metadata.go new file mode 100644 index 0000000..5c7a818 --- /dev/null +++ b/metadata.go @@ -0,0 +1,41 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/urfave/cli/v2" +) + +// Metadata defines runtime metadata. +type Metadata struct { + Repository Repository `json:"repo,omitempty"` + Pipeline Pipeline `json:"curr,omitempty"` + Curr Commit `json:"commit,omitempty"` + Prev Commit `json:"prev,omitempty"` + Step Step `json:"step,omitempty"` + System System `json:"sys,omitempty"` +} + +// MetadataFromContext creates a Metadata from the cli.Context. +func MetadataFromContext(ctx *cli.Context) Metadata { + return Metadata{ + Repository: repositoryFromContext(ctx), + Pipeline: pipelineFromContext(ctx), + Curr: currFromContext(ctx), + Prev: prevFromContext(ctx), + Step: stepFromContext(ctx), + System: systemFromContext(ctx), + } +} diff --git a/metadata_test.go b/metadata_test.go new file mode 100644 index 0000000..389ae1c --- /dev/null +++ b/metadata_test.go @@ -0,0 +1,117 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testMetadata() map[string]string { + return map[string]string{ + "CI": "woodpecker", + // Repository + "CI_REPO": "woodpecker-ci/woodpecker", + "CI_REPO_NAME": "woodpecker", + "CI_REPO_OWNER": "woodpecker-ci", + "CI_REPO_SCM": "git", + "CI_REPO_LINK": "https://github.com/woodpecker-ci/woodpecker", + "CI_REPO_CLONE_URL": "https://github.com/woodpecker-ci/woodpecker.git", + "CI_REPO_DEFAULT_BRANCH": "main", + "CI_REPO_PRIVATE": "false", + // Commit + "CI_COMMIT_SHA": "a1b2c3d4", + "CI_COMMIT_REF": "refs/heads/main", + "CI_COMMIT_REFSPEC": "refs/heads/main", + "CI_COMMIT_BRANCH": "main", + "CI_COMMIT_MESSAGE": "test commit", + "CI_COMMIT_AUTHOR": "John Doe", + "CI_COMMIT_AUTHOR_EMAIL": "john@example.com", + "CI_COMMIT_AUTHOR_AVATAR": "https://avatars.githubusercontent.com/u/1234567?v=4", + "CI_COMMIT_LINK": "https://github.com/woodpecker-ci/woodpecker/commit/a1b2c3d4", + // Build + "CI_PIPELINE_NUMBER": "1", + "CI_PIPELINE_EVENT": "push", + "CI_PIPELINE_LINK": "https://ci.woodpecker-ci.org/woodpecker-ci/woodpecker/1", + "CI_PIPELINE_STATUS": "running", + "CI_PIPELINE_CREATED": "1611234567", + "CI_PIPELINE_STARTED": "1611234567", + // Step + "CI_STEP_NUMBER": "1", + "CI_STEP_NAME": "test", + "CI_STEP_STATUS": "running", + "CI_STEP_STARTED": "1611234567", + // System + "CI_SYSTEM_NAME": "woodpecker", + "CI_SYSTEM_LINK": "https://ci.woodpecker-ci.org", + "CI_SYSTEM_VERSION": "1.0.0", + "CI_SYSTEM_HOST": "woodpecker-ci.org", + } +} + +func TestMetadata(t *testing.T) { + for k, v := range testMetadata() { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + plugin := New(Options{ + Execute: func(ctx context.Context) error { + return nil + }, + }) + err := plugin.app.Run([]string{"test"}) + require.NoError(t, err) + + // Repository + assert.Equal(t, "woodpecker", plugin.Metadata.Repository.Name) + assert.Equal(t, "woodpecker-ci", plugin.Metadata.Repository.Owner) + assert.Equal(t, "https://github.com/woodpecker-ci/woodpecker", plugin.Metadata.Repository.Link) + assert.Equal(t, "https://github.com/woodpecker-ci/woodpecker.git", plugin.Metadata.Repository.CloneURL) + assert.Equal(t, "main", plugin.Metadata.Repository.Branch) + assert.False(t, plugin.Metadata.Repository.Private) + + // Commit + assert.Equal(t, "a1b2c3d4", plugin.Metadata.Curr.Sha) + assert.Equal(t, "refs/heads/main", plugin.Metadata.Curr.Ref) + assert.Equal(t, "refs/heads/main", plugin.Metadata.Curr.Refspec) + assert.Equal(t, "main", plugin.Metadata.Curr.Branch) + assert.Equal(t, "test commit", plugin.Metadata.Curr.Message) + assert.Equal(t, "John Doe", plugin.Metadata.Curr.Author.Name) + assert.Equal(t, "john@example.com", plugin.Metadata.Curr.Author.Email) + assert.Equal(t, "https://avatars.githubusercontent.com/u/1234567?v=4", plugin.Metadata.Curr.Author.Avatar) + + // Pipeline + assert.Equal(t, int64(1), plugin.Metadata.Pipeline.Number) + assert.Equal(t, "push", plugin.Metadata.Pipeline.Event) + assert.Equal(t, "https://ci.woodpecker-ci.org/woodpecker-ci/woodpecker/1", plugin.Metadata.Pipeline.Link) + assert.Equal(t, "running", plugin.Metadata.Pipeline.Status) + assert.Equal(t, time.Unix(1611234567, 0), plugin.Metadata.Pipeline.Created) + assert.Equal(t, time.Unix(1611234567, 0), plugin.Metadata.Pipeline.Started) + + // Step + assert.Equal(t, 1, plugin.Metadata.Step.Number) + assert.Equal(t, time.Unix(1611234567, 0), plugin.Metadata.Step.Started) + + // System + assert.Equal(t, "woodpecker", plugin.Metadata.System.Name) + assert.Equal(t, "https://ci.woodpecker-ci.org", plugin.Metadata.System.Link) + assert.Equal(t, "1.0.0", plugin.Metadata.System.Version) + assert.Equal(t, "woodpecker-ci.org", plugin.Metadata.System.Host) +} diff --git a/pipeline.go b/pipeline.go new file mode 100644 index 0000000..5fcee9c --- /dev/null +++ b/pipeline.go @@ -0,0 +1,125 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "time" + + "github.com/urfave/cli/v2" +) + +// Pipeline defines runtime metadata for a pipeline. +type Pipeline struct { + Number int64 `json:"number,omitempty"` + Status string `json:"status,omitempty"` + Event string `json:"event,omitempty"` + Link string `json:"link,omitempty"` + DeployTarget string `json:"target,omitempty"` + Created time.Time `json:"created,omitempty"` + Started time.Time `json:"started,omitempty"` + Finished time.Time `json:"finished,omitempty"` + Parent int64 `json:"parent,omitempty"` +} + +func pipelineFlags() []cli.Flag { + return []cli.Flag{ + &cli.Int64Flag{ + Name: "pipeline.number", + Usage: "pipeline number", + EnvVars: []string{ + "CI_PIPELINE_NUMBER", + "DRONE_BUILD_NUMBER", + }, + }, + &cli.StringFlag{ + Name: "pipeline.status", + Usage: "pipeline status", + EnvVars: []string{ + "CI_PIPELINE_STATUS", + "DRONE_BUILD_STATUS", + }, + }, + &cli.StringFlag{ + Name: "pipeline.event", + Usage: "pipeline event", + EnvVars: []string{ + "CI_PIPELINE_EVENT", + "DRONE_BUILD_EVENT", + }, + }, + &cli.StringFlag{ + Name: "pipeline.link", + Usage: "pipeline link", + EnvVars: []string{ + "CI_PIPELINE_LINK", + "DRONE_BUILD_LINK", + }, + }, + &cli.StringFlag{ + Name: "pipeline.deploy-target", + Usage: "pipeline deployment target", + EnvVars: []string{ + "CI_PIPELINE_DEPLOY_TARGET", + "DRONE_DEPLOY_TO", + }, + }, + &cli.Int64Flag{ + Name: "pipeline.created", + Usage: "pipeline creation time", + EnvVars: []string{ + "CI_PIPELINE_CREATED", + "DRONE_BUILD_CREATED", + }, + }, + &cli.Int64Flag{ + Name: "pipeline.started", + Usage: "pipeline start time", + EnvVars: []string{ + "CI_PIPELINE_STARTED", + "DRONE_BUILD_STARTED", + }, + }, + &cli.Int64Flag{ + Name: "pipeline.finished", + Usage: "pipeline finish time", + EnvVars: []string{ + "CI_PIPELINE_FINISHED", + "DRONE_BUILD_FINISHED", + }, + }, + &cli.Int64Flag{ + Name: "pipeline.parent", + Usage: "pipeline parent", + EnvVars: []string{ + "CI_PIPELINE_PARENT", + "DRONE_BUILD_PARENT", + }, + }, + } +} + +func pipelineFromContext(c *cli.Context) Pipeline { + return Pipeline{ + Number: c.Int64("pipeline.number"), + Status: c.String("pipeline.status"), + Event: c.String("pipeline.event"), + Link: c.String("pipeline.link"), + DeployTarget: c.String("pipeline.deploy-target"), + Created: time.Unix(c.Int64("pipeline.created"), 0), + Started: time.Unix(c.Int64("pipeline.started"), 0), + Finished: time.Unix(c.Int64("pipeline.finished"), 0), + Parent: c.Int64("pipeline.parent"), + } +} diff --git a/plugin.go b/plugin.go new file mode 100644 index 0000000..578605b --- /dev/null +++ b/plugin.go @@ -0,0 +1,101 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" + "net/http" + "os" + + "github.com/joho/godotenv" + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" +) + +// Options defines the options for the plugin. +type Options struct { + // Name of the plugin. + Name string + // Description of the plugin. + Description string + // Version of the plugin. + Version string + // Flags of the plugin. + Flags []cli.Flag + // Execute function of the plugin. + Execute ExecuteFunc +} + +// Plugin defines the plugin instance. +type Plugin struct { + app *cli.App + execute ExecuteFunc + client *http.Client + // Metadata of the current pipeline. + Metadata Metadata +} + +// ExecuteFunc defines the function that is executed by the plugin. +type ExecuteFunc func(ctx context.Context) error + +// New plugin instance. +func New(opt Options) *Plugin { + if _, err := os.Stat("/run/woodpecker/env"); err == nil { + _ = godotenv.Overload("/run/woodpecker/env") + } + + app := &cli.App{ + Name: opt.Name, + Description: opt.Description, + Version: opt.Version, + Flags: append(opt.Flags, Flags()...), + } + + plugin := &Plugin{ + app: app, + execute: opt.Execute, + } + plugin.app.Action = plugin.action + + return plugin +} + +func (p *Plugin) action(ctx *cli.Context) error { + if err := SetupConsoleLogger(ctx); err != nil { + return err + } + + p.Metadata = MetadataFromContext(ctx) + p.client = HTTPClientFromContext(ctx) + + if p.execute == nil { + panic("plugin execute function is not set") + } + + return p.execute(ctx.Context) +} + +// HTTPClient returns the http.Client instance. +func (p *Plugin) HTTPClient() *http.Client { + return p.client +} + +// Run the plugin. +func (p *Plugin) Run() { + if err := p.app.Run(os.Args); err != nil { + log.Error().Err(err).Msg("execution failed") + os.Exit(1) + } +} diff --git a/plugin_test.go b/plugin_test.go new file mode 100644 index 0000000..65e66f7 --- /dev/null +++ b/plugin_test.go @@ -0,0 +1,36 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPlugin(t *testing.T) { + var executed bool + p := New(Options{ + Name: "test", + Execute: func(ctx context.Context) error { + executed = true + return nil + }, + }) + err := p.app.Run([]string{"test"}) + assert.NoError(t, err) + assert.True(t, executed) +} diff --git a/repo.go b/repo.go new file mode 100644 index 0000000..2f24d7f --- /dev/null +++ b/repo.go @@ -0,0 +1,93 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/urfave/cli/v2" +) + +// Repository defines runtime metadata for a repository. +type Repository struct { + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Link string `json:"link,omitempty"` + CloneURL string `json:"clone_url,omitempty"` + Private bool `json:"private,omitempty"` + Branch string `json:"default_branch,omitempty"` +} + +func repositoryFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "repo.name", + Usage: "repo name", + EnvVars: []string{ + "CI_REPO_NAME", + "DRONE_REPO_NAME", + }, + }, + &cli.StringFlag{ + Name: "repo.owner", + Usage: "repo owner", + EnvVars: []string{ + "CI_REPO_OWNER", + "DRONE_REPO_OWNER", + }, + }, + &cli.StringFlag{ + Name: "repo.link", + Usage: "repo link", + EnvVars: []string{ + "CI_REPO_LINK", + "DRONE_REPO_LINK", + }, + }, + &cli.StringFlag{ + Name: "repo.clone-url", + Usage: "repo clone url", + EnvVars: []string{ + "CI_REPO_CLONE_URL", + "DRONE_GIT_HTTP_URL", + }, + }, + &cli.BoolFlag{ + Name: "repo.private", + Usage: "repo private", + EnvVars: []string{ + "CI_REPO_PRIVATE", + "DRONE_REPO_PRIVATE", + }, + }, + &cli.StringFlag{ + Name: "repo.default-branch", + Usage: "repo default branch", + EnvVars: []string{ + "CI_REPO_DEFAULT_BRANCH", + "DRONE_REPO_BRANCH", + }, + }, + } +} + +func repositoryFromContext(c *cli.Context) Repository { + return Repository{ + Name: c.String("repo.name"), + Owner: c.String("repo.owner"), + Link: c.String("repo.link"), + CloneURL: c.String("repo.clone-url"), + Private: c.Bool("repo.private"), + Branch: c.String("repo.default-branch"), + } +} diff --git a/step.go b/step.go new file mode 100644 index 0000000..c940fd2 --- /dev/null +++ b/step.go @@ -0,0 +1,63 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "time" + + "github.com/urfave/cli/v2" +) + +// Step defines runtime metadata for a step. +type Step struct { + Number int `json:"number,omitempty"` + Started time.Time `json:"started,omitempty"` + Finished time.Time `json:"finished,omitempty"` +} + +func stepFlags() []cli.Flag { + return []cli.Flag{ + &cli.IntFlag{ + Name: "step.number", + Usage: "step number", + EnvVars: []string{ + "CI_STEP_NUMBER", + "DRONE_STEP_NUMBER", + }, + }, + &cli.Int64Flag{ + Name: "step.started", + Usage: "step start time", + EnvVars: []string{ + "CI_STEP_STARTED", + }, + }, + &cli.Int64Flag{ + Name: "step.finished", + Usage: "step finish time", + EnvVars: []string{ + "CI_STEP_FINISHED", + }, + }, + } +} + +func stepFromContext(c *cli.Context) Step { + return Step{ + Number: c.Int("step.number"), + Started: time.Unix(c.Int64("step.started"), 0), + Finished: time.Unix(c.Int64("step.finished"), 0), + } +} diff --git a/system.go b/system.go new file mode 100644 index 0000000..39615db --- /dev/null +++ b/system.go @@ -0,0 +1,93 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +// System defines runtime metadata for a ci/cd system. +type System struct { + Name string `json:"name,omitempty"` + Host string `json:"host,omitempty"` + Link string `json:"link,omitempty"` + Platform string `json:"arch,omitempty"` + Version string `json:"version,omitempty"` +} + +func systemFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "system.name", + Usage: "system name", + EnvVars: []string{ + "CI_SYSTEM_NAME", + }, + }, + &cli.StringFlag{ + Name: "system.host", + Usage: "system host", + EnvVars: []string{ + "CI_SYSTEM_HOST", + "DRONE_SYSTEM_HOST", + "DRONE_SYSTEM_HOSTNAME", + }, + }, + &cli.StringFlag{ + Name: "system.link", + Usage: "system link", + EnvVars: []string{ + "CI_SYSTEM_LINK", + }, + }, + &cli.StringFlag{ + Name: "system.arch", + Usage: "system arch", + EnvVars: []string{ + "CI_SYSTEM_PLATFORM", + "DRONE_STAGE_ARCH", + }, + }, + &cli.StringFlag{ + Name: "system.version", + Usage: "system version", + EnvVars: []string{ + "CI_SYSTEM_VERSION", + "DRONE_SYSTEM_VERSION", + }, + }, + } +} + +func systemFromContext(ctx *cli.Context) System { + link := ctx.String("system.link") + host := ctx.String("system.host") + if link == "" && host != "" { + // Alternative link format used by Drone. + proto := os.Getenv("DRONE_SYSTEM_PROTO") + if proto != "" { + link = proto + "://" + host + } + } + return System{ + Name: ctx.String("system.name"), + Host: host, + Link: link, + Platform: ctx.String("system.arch"), + Version: ctx.String("system.version"), + } +}