{"id":15984734,"url":"https://github.com/jaredreisinger/drone-plugin-helper","last_synced_at":"2025-04-04T20:46:21.035Z","repository":{"id":146778984,"uuid":"163382309","full_name":"JaredReisinger/drone-plugin-helper","owner":"JaredReisinger","description":"Ideas for simplifying Drone plugins that wrap existing command-line tools","archived":false,"fork":false,"pushed_at":"2019-02-07T02:55:01.000Z","size":1345,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-10T05:26:13.277Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JaredReisinger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-28T07:46:49.000Z","updated_at":"2019-02-07T02:55:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"40c3cf55-5d7f-47f0-8af8-a688916a3392","html_url":"https://github.com/JaredReisinger/drone-plugin-helper","commit_stats":{"total_commits":6,"total_committers":1,"mean_commits":6.0,"dds":0.0,"last_synced_commit":"b4fd24a2a2af62ab1da64b6746f0d31964689fa9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaredReisinger%2Fdrone-plugin-helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaredReisinger%2Fdrone-plugin-helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaredReisinger%2Fdrone-plugin-helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JaredReisinger%2Fdrone-plugin-helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JaredReisinger","download_url":"https://codeload.github.com/JaredReisinger/drone-plugin-helper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247249602,"owners_count":20908211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-08T02:10:19.031Z","updated_at":"2025-04-04T20:46:21.013Z","avatar_url":"https://github.com/JaredReisinger.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# drone-plugin-helper\n\nSimplifies Drone plugins that wrap command-line tools.\n\n\n## Getting started\n\nTo write a Drone plugin to wrap a command-line tool, all you really need to do is create a struct that represents the available command-line options, and then let the library take care of everything else.  For example, to handle the first several options for the `curl` command:\n\n```text\nUsage: curl [options...] \u003curl\u003e\n     --abstract-unix-socket \u003cpath\u003e Connect via abstract Unix domain socket\n     --anyauth       Pick any authentication method\n -a, --append        Append to target file when uploading\n     --basic         Use HTTP Basic Authentication\n     --cacert \u003cfile\u003e CA certificate to verify peer against\n     --capath \u003cdir\u003e  CA directory to verify peer against\n -E, --cert \u003ccertificate[:password]\u003e Client certificate file and password\n     --cert-status   Verify the status of the server certificate\n     --cert-type \u003ctype\u003e Certificate file type (DER/PEM/ENG)\n```\n\nThe code is as simple as:\n\n```Go\npackage main\n\nimport (\n  \"github.com/JaredReisinger/drone-plugin-helper/simple\"\n)\n\ntype Params struct {\n  AbstractUnixSocket string\n  Anyauth            bool\n  Append             bool\n  Basic              bool\n  Cacert             string\n  Capath             string\n  Cert               string\n  CertStatus         bool\n  CertType           string\n}\n\nfunc main() {\n  simple.Exec(\"curl\", \u0026Params{})\n}\n```\n\nAll that’s left is for you to package the tool along with the Go executable into a Docker image.\n\nBecause of the way that Drone maps settings to environment variables, and the way that `drone-plugin-helper` maps these names to Go member fields, your `drone.yml` file could then specify (for example):\n\n```yaml\nsome_curl_step:\n  image: drone-curl\n  anyauth: true\n  cacert: ./some/cert\n  cert_status: true\n  cert_type: PEM\n```\n\n... and `drone-plugin-helper` would generate the final command-line:\n\n```sh\ncurl --anyauth --cacert ./some/cert --cert-status --cert-type PEM\n```\n\nBehind the scenes, Drone maps the settings to the environment variables:\n\n```text\nPLUGIN_ANYAUTH=true\nPLUGIN_CACERT=./some/cert\nPLUGIN_CERT_STATUS=true\nPLUGIN_CERT_TYPE=PEM\n```\n\n... and `drone_plugin_helper` matches these with the `Anyauth`, `Cacert`, `CertStatus`, and `CertType` members.\n\n\n### “Subcommand”-style tools\n\nAnother very common pattern for command-line tools is for the initial command-line argument to be a subcommand, often with its own specific options.  Tools like `git` and `helm` are examples of this.  Since this is such a common pattern, there is a helper for this, as well.  The [`example/`](./example/) subdirectory uses this helper to show how a plugin for `helm` could be written. (See [Example / Case study](#example--case-study) below for further information.)\n\n\n### Overriding the defaults\n\nIf the helpers’ default processing doesn’t meet your needs, you can tag the fields with `env:\"\"` and/or `cmd:\"\"` metadata:\n\n```Go\ntype Params struct {\n  WeirdName string `cmd:\"--surprise\"`   // use \"--surprise\" instead of \"--weird-name\" as the option name\n  Command   string `cmd:\",positional\"`  // use the value directly, with no \"--command\" flag prefix\n  Extra     string `cmd:\",omit\"`\n}\n```\n\nBut please see “Best practices”, below, for ways to avoid needing these overrides.\n\n\n### More-complex handling\n\nWhile the behavior of [`drone-plugin-helper/simple`](./simple/) should handle the vast majority of cases, feel free to use the [`/env`](./env/) or [`/cmd`](./cmd/) packages directly if you need to add your own logic in between the environment variable parsing and the command-line generation.  You may find that [`/env`](./env/) alone is a simpler way to expose your plugin’s parameters even if you’re not wrapping an underlying command-line tool.\n\n\n## Best practices\n\n### Work “bottom-up”\n\nThe ultimate goal is to generate a command-line for the underlying tool, so it makes sense to choose struct member names and types that facilitate this.  Doing so will reduce the need for struct field tag metadata to “fix” the command-line option names.  The `drone-plugin-helper/cmd` methods were designed to generate the “expected” command-line option name based on the Go name: the field `Basic` generates an option named `--basic`, and the field `CertStatus` generates `--cert-status`.\n\nThe rule of thumb in naming a Go member for a command-line parameter is to capitalize the first letter of each hyphen-separated term, and then remove the hyphens: `--cert-status` ⇒ `--Cert-Status` ⇒ `CertStatus`.  The helpers are aware of Go's linting rules about capitalizing certain acronyms and respects them.  For example, the proper Go member name for `--tls-cert` is `TLSCert` (not `TlsCert`).  Similar logic in `drone-plugin-helper/env` will look for environment variables with the equivalent environment name: a `TLSCert` member looks for the `PLUGIN_TLS_CERT` environment variable.\n\n\n### Use pointers if “zero values” are valid options\n\nIn typical usage, you will rarely need to inspect the values in the Go struct at all; they become simple pass-throughs from the Drone environment variables to the underlying tool's command-line.  The `drone-plugin-helper` tools will automatically handle struct fields that are pointers: automatically creating and dereferencing as needed.  If a field is *not* a pointer, the option is only emitted when it's not a \"zero value\" for the type (that is, int options of `0` are not emitted, nor are empty strings).  If a \"zero value\" has meaning for the underlying tool, you can use a pointer to the type instead; it will only be allocated if an environment value is provided, and any non-`nil` value will be emitted as a command-line option.\n\nFor example, if the Go struct is defined as:\n\n```Go\ntype Params struct {\n  Example1 int\n  Example2 *int\n}\n```\n\nthen if the environment variables are `PLUGIN_EXAMPLE1=0` and `PLUGIN_EXAMPLE2=0`, only the command-line option `--example2 0` would be created.  The `--example1` option is not created because the value is the \"zero value\" for the field.\n\n\u003e NOTE: Would it be better to recommend \"use pointers by default\" because it more accurately passes along the intent of the caller?\n\n\n## Example / Case study\n\nAs a case study, see [`example/`](./example/) to see how `drone-plugin-helper`could be used to create a plugin for Helm.\n\n\n----\n\n## Background\n\nThere are a huge number of Drone plugins that are simply wrappers around an existing command-line tool.  This should be no surprise, as the typical Un*x toolchain is a set of several command-line tools!  The plugins all follow the same overall pattern:\n\n  * Drone ensures that the plugin settings from the `.drone.yml` file are exposed as environment variables.\n\n  * A generic CLI library (often urfave/cli) is used to map all manner of CLI+environment to a struct whose members support the underlying tool’s options.\n\n  * A bunch of repetitive code is written to remap the struct into the underlying tool's actual command-line.\n\nRather than every plugin re-creating the env-to-struct-to-commandline processing again and again, there should be a library that does this basic work _once_, with minimal redundancy for plugin authors.  This project attempts to do just that.\n\n\n## Notes\n\n### Environment to config mapping\n\nSince a Drone plugin _only_ gets it input/settings via environment variables, a general-purpose CLI helper is overkill, and adds unneeded complexity.  We should be able to deserialize the environment variables directly into a struct, based solely on the names and types.  (_Maybe_ with tagged members for additional options?)\n\n1-to-1 name mapping, to reduce typing and confusion?  Drone upper-cases the variables and prefixes with `PLUGIN_` so it should be easy to find them, and detect extras...\n\n\n### Config to command-line mapping\n\nSimilarly, the vast majority have a 1-to-1 mapping between a config value and the underlying tool's command line.  Simple struct member tagging should allow the command-line to be almost directly serialzed from the struct.\n\n\n\n## TODO (ideas)\n\n* [X] ~~handle \"well-known\" all-caps abbreviations \"TLS\", \"XML\", etc., so that they are parsed correctly: `TLSXMLInfo` -\u003e `TLS` `XML` `Info` \u003e `--tls-xml-info`.  Similarly, `PLUGIN_TLS_XML_INFO` should become `TLSXMLInfo`, not `TlsXmlInfo`.~~\n\n* [X] ~~put the current `cmd.DronePlugin()` into a separate package for \"all-in-one\" tools.  Perhaps `allInOne` or `simple`?~~ `simple.Exec()`\n\n* [X] ~~add (to `simple` as previous?) a helper for typical \"subcommand\" behavior, a la `helm`.  This would allow an easy \"map the initial command to a param set\" which is another 80/20 case.~~\n\n* [ ] helper that can generate a stub doc showing the supported environment vsriables and the allowed syntax... and the mapping to eventual command-line?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredreisinger%2Fdrone-plugin-helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaredreisinger%2Fdrone-plugin-helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredreisinger%2Fdrone-plugin-helper/lists"}