From 3de19a69d4d0f65347cb55f71ec10e39003d12d5 Mon Sep 17 00:00:00 2001 From: Tamas Papik Date: Thu, 19 Apr 2018 16:13:17 +0200 Subject: [PATCH] Initial (#2) * initial commit * updates * updated step description * clean-up * clean-up * updated status events * interface check * updated gopkg.toml * added skip status reporting * add env if no ens added already * clean-up * PR changes * updated readme * readme updated * ci fix * PR changes --- .gitignore | 1 + Gopkg.lock | 32 +++ Gopkg.toml | 7 + LICENSE | 21 ++ README.md | 92 +++++- bitrise.yml | 85 ++++++ bitrise/bitrise.go | 185 ++++++++++++ main.go | 89 ++++++ release_config.yml | 12 + step.yml | 52 ++++ vendor/github.com/bitrise-io/go-utils/LICENSE | 22 ++ .../go-utils/colorstring/colorstring.go | 110 ++++++++ .../bitrise-io/go-utils/command/command.go | 263 ++++++++++++++++++ .../bitrise-io/go-utils/command/file.go | 57 ++++ .../bitrise-io/go-utils/command/zip.go | 115 ++++++++ .../go-utils/errorutil/errorutil.go | 36 +++ .../bitrise-io/go-utils/log/json_logger.go | 31 +++ .../github.com/bitrise-io/go-utils/log/log.go | 34 +++ .../bitrise-io/go-utils/log/logger.go | 12 + .../bitrise-io/go-utils/log/print.go | 84 ++++++ .../bitrise-io/go-utils/log/raw_logger.go | 31 +++ .../bitrise-io/go-utils/log/severity.go | 35 +++ .../go-utils/parseutil/parseutil.go | 95 +++++++ .../bitrise-io/go-utils/pathutil/pathutil.go | 181 ++++++++++++ .../bitrise-io/go-utils/pointers/pointers.go | 98 +++++++ .../go-steputils/stepconf/stepconf.go | 199 +++++++++++++ .../bitrise-tools/go-steputils/tools/tools.go | 14 + 27 files changed, 1992 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 LICENSE create mode 100644 bitrise.yml create mode 100644 bitrise/bitrise.go create mode 100644 main.go create mode 100644 release_config.yml create mode 100644 step.yml create mode 100644 vendor/github.com/bitrise-io/go-utils/LICENSE create mode 100644 vendor/github.com/bitrise-io/go-utils/colorstring/colorstring.go create mode 100644 vendor/github.com/bitrise-io/go-utils/command/command.go create mode 100644 vendor/github.com/bitrise-io/go-utils/command/file.go create mode 100644 vendor/github.com/bitrise-io/go-utils/command/zip.go create mode 100644 vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/json_logger.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/log.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/logger.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/print.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/raw_logger.go create mode 100644 vendor/github.com/bitrise-io/go-utils/log/severity.go create mode 100644 vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pointers/pointers.go create mode 100644 vendor/github.com/bitrise-tools/go-steputils/stepconf/stepconf.go create mode 100644 vendor/github.com/bitrise-tools/go-steputils/tools/tools.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6397b46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.bitrise* diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..89fe920 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,32 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/bitrise-io/go-utils" + packages = [ + "colorstring", + "command", + "errorutil", + "log", + "parseutil", + "pathutil", + "pointers" + ] + revision = "b33f6bcef9b50045d7e62364b354584afc3ee329" + +[[projects]] + branch = "master" + name = "github.com/bitrise-tools/go-steputils" + packages = [ + "stepconf", + "tools" + ] + revision = "d4d9e08cc4347e8784bb18419fcdceb932e17019" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "25a74561e9f02d158366ae36ddcc28962699f1695950ce5e1bc39469947f5d2e" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..f552de1 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,7 @@ +[[constraint]] + name = "github.com/bitrise-io/go-utils" + branch = "master" + +[prune] + go-tests = true + unused-packages = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cc0f81d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 bitrise-steplib + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 00ac02d..4fe3410 100644 --- a/README.md +++ b/README.md @@ -1 +1,91 @@ -# bitrise-step-build-router \ No newline at end of file +# Bitrise Start Build + +Starts the given workflows in the same app. + +## How to use this Step + +Can be run directly with the [bitrise CLI](https://github.com/bitrise-io/bitrise), +just `git clone` this repository, `cd` into it's folder in your Terminal/Command Line +and call `bitrise run test`. + +*Check the `bitrise.yml` file for required inputs which have to be +added to your `.bitrise.secrets.yml` file!* + +Step by step: + +1. Open up your Terminal / Command Line +2. `git clone` the repository +3. `cd` into the directory of the step (the one you just `git clone`d) +5. Create a `.bitrise.secrets.yml` file in the same directory of `bitrise.yml` - the `.bitrise.secrets.yml` is a git ignored file, you can store your secrets in +6. Check the `bitrise.yml` file for any secret you should set in `.bitrise.secrets.yml` + * Best practice is to mark these options with something like `# define these in your .bitrise.secrets.yml`, in the `app:envs` section. +7. Once you have all the required secret parameters in your `.bitrise.secrets.yml` you can just run this step with the [bitrise CLI](https://github.com/bitrise-io/bitrise): `bitrise run test` + +An example `.bitrise.secrets.yml` file: + +``` +envs: +- A_SECRET_PARAM_ONE: the value for secret one +- A_SECRET_PARAM_TWO: the value for secret two +``` + +## How to create your own step + +1. Create a new git repository for your step (**don't fork** the *step template*, create a *new* repository) +2. Copy the [step template](https://github.com/bitrise-steplib/step-template) files into your repository +3. Fill the `step.sh` with your functionality +4. Wire out your inputs to `step.yml` (`inputs` section) +5. Fill out the other parts of the `step.yml` too +6. Provide test values for the inputs in the `bitrise.yml` +7. Run your step with `bitrise run test` - if it works, you're ready + +__For Step development guidelines & best practices__ check this documentation: [https://github.com/bitrise-io/bitrise/blob/master/_docs/step-development-guideline.md](https://github.com/bitrise-io/bitrise/blob/master/_docs/step-development-guideline.md). + +**NOTE:** + +If you want to use your step in your project's `bitrise.yml`: + +1. git push the step into it's repository +2. reference it in your `bitrise.yml` with the `git::PUBLIC-GIT-CLONE-URL@BRANCH` step reference style: + +``` +- git::https://github.com/user/my-step.git@branch: + title: My step + inputs: + - my_input_1: "my value 1" + - my_input_2: "my value 2" +``` + +You can find more examples of step reference styles +in the [bitrise CLI repository](https://github.com/bitrise-io/bitrise/blob/master/_examples/tutorials/steps-and-workflows/bitrise.yml#L65). + +## How to contribute to this Step + +1. Fork this repository +2. `git clone` it +3. Create a branch you'll work on +4. To use/test the step just follow the **How to use this Step** section +5. Do the changes you want to +6. Run/test the step before sending your contribution + * You can also test the step in your `bitrise` project, either on your Mac or on [bitrise.io](https://www.bitrise.io) + * You just have to replace the step ID in your project's `bitrise.yml` with either a relative path, or with a git URL format + * (relative) path format: instead of `- original-step-id:` use `- path::./relative/path/of/script/on/your/Mac:` + * direct git URL format: instead of `- original-step-id:` use `- git::https://github.com/user/step.git@branch:` + * You can find more example of alternative step referencing at: https://github.com/bitrise-io/bitrise/blob/master/_examples/tutorials/steps-and-workflows/bitrise.yml +7. Once you're done just commit your changes & create a Pull Request + + +## Share your own Step + +You can share your Step or step version with the [bitrise CLI](https://github.com/bitrise-io/bitrise). If you use the `bitrise.yml` included in this repository, all you have to do is: + +1. In your Terminal / Command Line `cd` into this directory (where the `bitrise.yml` of the step is located) +1. Run: `bitrise run test` to test the step +1. Run: `bitrise run audit-this-step` to audit the `step.yml` +1. Check the `share-this-step` workflow in the `bitrise.yml`, and fill out the + `envs` if you haven't done so already (don't forget to bump the version number if this is an update + of your step!) +1. Then run: `bitrise run share-this-step` to share the step (version) you specified in the `envs` +1. Send the Pull Request, as described in the logs of `bitrise run share-this-step` + +That's all ;) \ No newline at end of file diff --git a/bitrise.yml b/bitrise.yml new file mode 100644 index 0000000..bdba5b4 --- /dev/null +++ b/bitrise.yml @@ -0,0 +1,85 @@ +format_version: 5 +default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git + +app: + envs: + - BITRISE_STEP_ID: bitrise-step-build-router-start + - BITRISE_STEP_VERSION: "0.9.0" + - BITRISE_STEP_GIT_CLONE_URL: https://github.com/bitrise-steplib/bitrise-step-build-router-start.git + - MY_STEPLIB_REPO_FORK_GIT_URL: $MY_STEPLIB_REPO_FORK_GIT_URL + - TEST_WORKFLOWS: $TEST_WORKFLOWS + - ACCESS_TOKEN: $ACCESS_TOKEN + +workflows: + test: + before_run: + - audit-this-step + steps: + - go-list: + - golint: + - errcheck: + - go-test: + - path::./: + inputs: + - workflows: $TEST_WORKFLOWS + - access_token: $ACCESS_TOKEN + - wait_for_builds: "true" + + # ---------------------------------------------------------------- + # --- workflows to Share this step into a Step Library + audit-this-step: + steps: + - script: + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + stepman audit --step-yml ./step.yml + + # ---------------------------------------------------------------- + # --- workflows to create Release + create-release: + steps: + - script: + title: + inputs: + - content: | + #!/usr/bin/env bash + set -e + export CI=true + releaseman create --version $BITRISE_STEP_VERSION + + share-this-step: + envs: + # if you want to share this step into a StepLib + - MY_STEPLIB_REPO_FORK_GIT_URL: $MY_STEPLIB_REPO_FORK_GIT_URL + - BITRISE_STEP_ID: $BITRISE_STEP_ID + - BITRISE_STEP_VERSION: $BITRISE_STEP_VERSION + - BITRISE_STEP_GIT_CLONE_URL: $BITRISE_STEP_GIT_CLONE_URL + description: |- + If this is the first time you try to share a Step you should + first call: $ bitrise share + This will print you a guide, and information about how Step sharing + works. Please read it at least once! + As noted in the Step sharing guide you'll have to fork the + StepLib you want to share this step into. Once you're done with forking + the repository you should set your own fork's git clone URL + in the `.bitrise.secrets.yml` file, or here in the `envs` section, + as the value of the `MY_STEPLIB_REPO_FORK_GIT_URL` environment. + You're now ready to share this Step, just make sure that + the `BITRISE_STEP_ID` and `BITRISE_STEP_VERSION` + environments are set to the desired values! + To share this Step into a StepLib you can just run: $ bitrise run share-this-step + Once it finishes the only thing left is to actually create a Pull Request, + the way described in the guide printed at the end of the process. + before_run: + - audit-this-step + steps: + - script: + inputs: + - content: |- + #!/usr/bin/env bash + set -ex + bitrise share start -c "${MY_STEPLIB_REPO_FORK_GIT_URL}" + bitrise share create --stepid "${BITRISE_STEP_ID}" --tag "${BITRISE_STEP_VERSION}" --git "${BITRISE_STEP_GIT_CLONE_URL}" + bitrise share finish diff --git a/bitrise/bitrise.go b/bitrise/bitrise.go new file mode 100644 index 0000000..8cb7520 --- /dev/null +++ b/bitrise/bitrise.go @@ -0,0 +1,185 @@ +package bitrise + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +// Build ... +type Build struct { + Slug string `json:"slug"` + Status int `json:"status"` + StatusText string `json:"status_text"` + BuildNumber int64 `json:"build_number"` + TriggeredWorkflow string `json:"triggered_workflow"` + OriginalBuildParams json.RawMessage `json:"original_build_params"` +} + +type buildResponse struct { + Data Build `json:"data"` +} + +type hookInfo struct { + Type string `json:"type"` +} + +type startRequest struct { + HookInfo hookInfo `json:"hook_info"` + BuildParams json.RawMessage `json:"build_params"` +} + +// StartResponse ... +type StartResponse struct { + Status string `json:"message"` + Message string `json:"status"` + BuildSlug string `json:"build_slug"` + BuildNumber int `json:"build_number"` + BuildURL string `json:"build_url"` + TriggeredWorkflow string `json:"triggered_workflow"` +} + +// App ... +type App struct { + Slug, AccessToken string +} + +// GetBuild ... +func (app App) GetBuild(buildSlug string) (Build, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://api.bitrise.io/v0.1/apps/%s/builds/%s", app.Slug, buildSlug), nil) + if err != nil { + return Build{}, err + } + + req.Header.Add("Authorization", "token "+app.AccessToken) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return Build{}, err + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return Build{}, err + } + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return Build{}, fmt.Errorf("failed to get response, statuscode: %d, body: %s", resp.StatusCode, respBody) + } + + var build buildResponse + if err := json.Unmarshal(respBody, &build); err != nil { + return Build{}, fmt.Errorf("failed to decode response, body: %s, error: %s", respBody, err) + } + return build.Data, nil +} + +// StartBuild ... +func (app App) StartBuild(workflow string, buildParams json.RawMessage, buildNumber string) (StartResponse, error) { + var params map[string]interface{} + if err := json.Unmarshal(buildParams, ¶ms); err != nil { + return StartResponse{}, err + } + params["workflow_id"] = workflow + params["skip_git_status_report"] = true + + sourceBuildNumber := map[string]interface{}{ + "is_expand": true, + "mapped_to": "SOURCE_BITRISE_BUILD_NUMBER", + "value": buildNumber, + } + + if envs, ok := params["environments"].([]interface{}); ok { + params["environments"] = append(envs, sourceBuildNumber) + } else { + params["environments"] = []interface{}{sourceBuildNumber} + } + + b, err := json.Marshal(params) + if err != nil { + return StartResponse{}, nil + } + + rm := startRequest{HookInfo: hookInfo{Type: "bitrise"}, BuildParams: b} + b, err = json.Marshal(rm) + if err != nil { + return StartResponse{}, nil + } + + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("https://api.bitrise.io/v0.1/apps/%s/builds", app.Slug), bytes.NewReader(b)) + if err != nil { + return StartResponse{}, nil + } + req.Header.Add("Authorization", "token "+app.AccessToken) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return StartResponse{}, nil + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return StartResponse{}, nil + } + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return StartResponse{}, fmt.Errorf("failed to get response, statuscode: %d, body: %s", resp.StatusCode, respBody) + } + + var response StartResponse + if err := json.Unmarshal(respBody, &response); err != nil { + return StartResponse{}, fmt.Errorf("failed to decode response, body: %s, error: %s", respBody, err) + } + return response, nil +} + +// WaitForBuilds ... +func (app App) WaitForBuilds(buildSlugs []string, statusChangeCallback func(build Build)) error { + failed := false + status := map[string]string{} + for { + running := 0 + for _, buildSlug := range buildSlugs { + build, err := app.GetBuild(buildSlug) + if err != nil { + return fmt.Errorf("failed to get build info, error: %s", err) + } + + if status[buildSlug] != build.StatusText { + statusChangeCallback(build) + status[buildSlug] = build.StatusText + } + + if build.Status == 0 { + running++ + continue + } + + if build.Status != 1 { + failed = true + } + + buildSlugs = remove(buildSlugs, buildSlug) + } + if running == 0 { + break + } + time.Sleep(time.Second * 3) + } + if failed { + return fmt.Errorf("at least one build failed or aborted") + } + return nil +} + +func remove(slice []string, what string) (b []string) { + for _, s := range slice { + if s != what { + b = append(b, s) + } + } + return +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..27c50a1 --- /dev/null +++ b/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-steplib/bitrise-step-build-router-start/bitrise" + "github.com/bitrise-tools/go-steputils/stepconf" + "github.com/bitrise-tools/go-steputils/tools" +) + +const envBuildSlugs = "ROUTER_STARTED_BUILD_SLUGS" + +// Config ... +type Config struct { + AppSlug string `env:"BITRISE_APP_SLUG,required"` + BuildSlug string `env:"BITRISE_BUILD_SLUG,required"` + BuildNumber string `env:"BITRISE_BUILD_NUMBER,required"` + AccessToken stepconf.Secret `env:"access_token,required"` + WaitForBuilds string `env:"wait_for_builds"` + Workflows string `env:"workflows,required"` +} + +func failf(s string, a ...interface{}) { + log.Errorf(s, a...) + os.Exit(1) +} + +func main() { + var cfg Config + if err := stepconf.Parse(&cfg); err != nil { + failf("Issue with an input: %s", err) + } + + stepconf.Print(cfg) + fmt.Println() + + app := bitrise.App{ + Slug: cfg.AppSlug, + AccessToken: string(cfg.AccessToken), + } + + build, err := app.GetBuild(cfg.BuildSlug) + if err != nil { + failf("failed to get build, error: %s", err) + } + + log.Infof("Starting builds:") + + var buildSlugs []string + for _, wf := range strings.Split(cfg.Workflows, "\n") { + startedBuild, err := app.StartBuild(wf, build.OriginalBuildParams, cfg.BuildNumber) + if err != nil { + failf("Failed to start build, error: %s", err) + } + buildSlugs = append(buildSlugs, startedBuild.BuildSlug) + log.Printf("- %s started (https://www.bitrise.io/build/%s)", startedBuild.TriggeredWorkflow, startedBuild.BuildSlug) + } + + if err := tools.ExportEnvironmentWithEnvman(envBuildSlugs, strings.Join(buildSlugs, "\n")); err != nil { + failf("Failed to export environment variable, error: %s", err) + } + + if cfg.WaitForBuilds != "true" { + return + } + + fmt.Println() + log.Infof("Waiting for builds:") + + if err := app.WaitForBuilds(buildSlugs, func(build bitrise.Build) { + switch build.Status { + case 0: + log.Printf("- %s %s", build.TriggeredWorkflow, build.StatusText) + case 1: + log.Donef("- %s successful", build.TriggeredWorkflow) + case 2: + log.Errorf("- %s failed", build.TriggeredWorkflow) + case 3: + log.Warnf("- %s aborted", build.TriggeredWorkflow) + case 4: + log.Infof("- %s cancelled", build.TriggeredWorkflow) + } + }); err != nil { + failf("An error occoured: %s", err) + } +} diff --git a/release_config.yml b/release_config.yml new file mode 100644 index 0000000..f4f4ec6 --- /dev/null +++ b/release_config.yml @@ -0,0 +1,12 @@ +release: + development_branch: master + release_branch: master +changelog: + path: CHANGELOG.md + content_template: |- + {{range .ContentItems}}### {{.EndTaggedCommit.Tag}} ({{.EndTaggedCommit.Date.Format "2006 Jan 02"}}) + {{range .Commits}}* [{{firstChars .Hash 7}}] {{.Message}} + {{end}} + {{end}} + header_template: '## Changelog (Current version: {{.Version}})' + footer_template: 'Updated: {{.CurrentDate.Format "2006 Jan 02"}}' \ No newline at end of file diff --git a/step.yml b/step.yml new file mode 100644 index 0000000..f67a1ea --- /dev/null +++ b/step.yml @@ -0,0 +1,52 @@ +title: "Bitrise Start Build" +summary: "Starts the given workflows in the same app" +description: "Starts the given workflows in the same app" +website: https://github.com/bitrise-steplib/bitrise-step-build-router-start +source_code_url: https://github.com/bitrise-steplib/bitrise-step-build-router-start +support_url: https://github.com/bitrise-steplib/bitrise-step-build-router-start/issues +host_os_tags: + - osx-10.10 + - ubuntu-16.04 + +type_tags: + - utility + +is_always_run: false +is_skippable: false + +toolkit: + go: + package_name: github.com/bitrise-steplib/bitrise-step-build-router-start + +inputs: + - access_token: + opts: + title: Bitrise Access Token + summary: Your access token that has access to the app in you want to run workflows. + description: | + Your access token that has access to the app in you want to run workflows. + + To acquire a Personal Access Token for your user, sign in with that user on [bitrise.io](https://bitrise.io), + go to your Account Settings page, and select the [Security tab](https://www.bitrise.io/me/profile#/security) on the left side. + is_required: true + - workflows: + opts: + title: Workflows + summary: The workflow(s) to start. One workflow per line. + description: The workflow(s) to start. One workflow per line. + is_required: true + - wait_for_builds: "false" + opts: + title: Wait for builds + summary: Shall the step wait for the builds to be finished? + description: Shall the step wait for the builds to be finished? + is_required: false + value_options: + - "false" + - "true" +outputs: + - ROUTER_STARTED_BUILD_SLUGS: + opts: + title: "Started Build Slugs" + summary: "Newline separated list of started build slugs." + description: "Newline separated list of started build slugs." diff --git a/vendor/github.com/bitrise-io/go-utils/LICENSE b/vendor/github.com/bitrise-io/go-utils/LICENSE new file mode 100644 index 0000000..a6a5c39 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Bitrise + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/bitrise-io/go-utils/colorstring/colorstring.go b/vendor/github.com/bitrise-io/go-utils/colorstring/colorstring.go new file mode 100644 index 0000000..5f31fa9 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/colorstring/colorstring.go @@ -0,0 +1,110 @@ +package colorstring + +import ( + "fmt" +) + +// Color ... +// ANSI color escape sequences +type Color string + +const ( + blackColor Color = "\x1b[30;1m" + redColor Color = "\x1b[31;1m" + greenColor Color = "\x1b[32;1m" + yellowColor Color = "\x1b[33;1m" + blueColor Color = "\x1b[34;1m" + magentaColor Color = "\x1b[35;1m" + cyanColor Color = "\x1b[36;1m" + resetColor Color = "\x1b[0m" +) + +// ColorFunc ... +type ColorFunc func(a ...interface{}) string + +func addColor(color Color, msg string) string { + return string(color) + msg + string(resetColor) +} + +// NoColor ... +func NoColor(a ...interface{}) string { + return fmt.Sprint(a...) +} + +// Black ... +func Black(a ...interface{}) string { + return addColor(blackColor, fmt.Sprint(a...)) +} + +// Red ... +func Red(a ...interface{}) string { + return addColor(redColor, fmt.Sprint(a...)) +} + +// Green ... +func Green(a ...interface{}) string { + return addColor(greenColor, fmt.Sprint(a...)) +} + +// Yellow ... +func Yellow(a ...interface{}) string { + return addColor(yellowColor, fmt.Sprint(a...)) +} + +// Blue ... +func Blue(a ...interface{}) string { + return addColor(blueColor, fmt.Sprint(a...)) +} + +// Magenta ... +func Magenta(a ...interface{}) string { + return addColor(magentaColor, fmt.Sprint(a...)) +} + +// Cyan ... +func Cyan(a ...interface{}) string { + return addColor(cyanColor, fmt.Sprint(a...)) +} + +// ColorfFunc ... +type ColorfFunc func(format string, a ...interface{}) string + +// NoColorf ... +func NoColorf(format string, a ...interface{}) string { + return NoColor(fmt.Sprintf(format, a...)) +} + +// Blackf ... +func Blackf(format string, a ...interface{}) string { + return Black(fmt.Sprintf(format, a...)) +} + +// Redf ... +func Redf(format string, a ...interface{}) string { + return Red(fmt.Sprintf(format, a...)) +} + +// Greenf ... +func Greenf(format string, a ...interface{}) string { + return Green(fmt.Sprintf(format, a...)) +} + +// Yellowf ... +func Yellowf(format string, a ...interface{}) string { + return Yellow(fmt.Sprintf(format, a...)) +} + +// Bluef ... +func Bluef(format string, a ...interface{}) string { + return Blue(fmt.Sprintf(format, a...)) +} + +// Magentaf ... +func Magentaf(format string, a ...interface{}) string { + return Magenta(fmt.Sprintf(format, a...)) +} + +// Cyanf ... +func Cyanf(format string, a ...interface{}) string { + return Cyan(fmt.Sprintf(format, a...)) +} diff --git a/vendor/github.com/bitrise-io/go-utils/command/command.go b/vendor/github.com/bitrise-io/go-utils/command/command.go new file mode 100644 index 0000000..4cd005a --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/command/command.go @@ -0,0 +1,263 @@ +package command + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/errorutil" +) + +// ---------- + +// Model ... +type Model struct { + cmd *exec.Cmd +} + +// New ... +func New(name string, args ...string) *Model { + return &Model{ + cmd: exec.Command(name, args...), + } +} + +// NewWithStandardOuts - same as NewCommand, but sets the command's +// stdout and stderr to the standard (OS) out (os.Stdout) and err (os.Stderr) +func NewWithStandardOuts(name string, args ...string) *Model { + return New(name, args...).SetStdout(os.Stdout).SetStderr(os.Stderr) +} + +// NewWithParams ... +func NewWithParams(params ...string) (*Model, error) { + if len(params) == 0 { + return nil, errors.New("no command provided") + } else if len(params) == 1 { + return New(params[0]), nil + } + + return New(params[0], params[1:]...), nil +} + +// NewFromSlice ... +func NewFromSlice(slice []string) (*Model, error) { + return NewWithParams(slice...) +} + +// NewWithCmd ... +func NewWithCmd(cmd *exec.Cmd) *Model { + return &Model{ + cmd: cmd, + } +} + +// GetCmd ... +func (m *Model) GetCmd() *exec.Cmd { + return m.cmd +} + +// SetDir ... +func (m *Model) SetDir(dir string) *Model { + m.cmd.Dir = dir + return m +} + +// SetEnvs ... +func (m *Model) SetEnvs(envs ...string) *Model { + m.cmd.Env = envs + return m +} + +// AppendEnvs - appends the envs to the current os.Environ() +// Calling this multiple times will NOT appens the envs one by one, +// only the last "envs" set will be appended to os.Environ()! +func (m *Model) AppendEnvs(envs ...string) *Model { + return m.SetEnvs(append(os.Environ(), envs...)...) +} + +// SetStdin ... +func (m *Model) SetStdin(in io.Reader) *Model { + m.cmd.Stdin = in + return m +} + +// SetStdout ... +func (m *Model) SetStdout(out io.Writer) *Model { + m.cmd.Stdout = out + return m +} + +// SetStderr ... +func (m *Model) SetStderr(err io.Writer) *Model { + m.cmd.Stderr = err + return m +} + +// Run ... +func (m Model) Run() error { + return m.cmd.Run() +} + +// RunAndReturnExitCode ... +func (m Model) RunAndReturnExitCode() (int, error) { + return RunCmdAndReturnExitCode(m.cmd) +} + +// RunAndReturnTrimmedOutput ... +func (m Model) RunAndReturnTrimmedOutput() (string, error) { + return RunCmdAndReturnTrimmedOutput(m.cmd) +} + +// RunAndReturnTrimmedCombinedOutput ... +func (m Model) RunAndReturnTrimmedCombinedOutput() (string, error) { + return RunCmdAndReturnTrimmedCombinedOutput(m.cmd) +} + +// PrintableCommandArgs ... +func (m Model) PrintableCommandArgs() string { + return PrintableCommandArgs(false, m.cmd.Args) +} + +// ---------- + +// PrintableCommandArgs ... +func PrintableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string { + cmdArgsDecorated := []string{} + for idx, anArg := range fullCommandArgs { + quotedArg := strconv.Quote(anArg) + if idx == 0 && !isQuoteFirst { + quotedArg = anArg + } + cmdArgsDecorated = append(cmdArgsDecorated, quotedArg) + } + + return strings.Join(cmdArgsDecorated, " ") +} + +// RunCmdAndReturnExitCode ... +func RunCmdAndReturnExitCode(cmd *exec.Cmd) (int, error) { + err := cmd.Run() + if err != nil { + exitCode, castErr := errorutil.CmdExitCodeFromError(err) + if castErr != nil { + return 1, fmt.Errorf("failed get exit code from error: %s, error: %s", err, castErr) + } + + return exitCode, err + } + + return 0, nil +} + +// RunCmdAndReturnTrimmedOutput ... +func RunCmdAndReturnTrimmedOutput(cmd *exec.Cmd) (string, error) { + outBytes, err := cmd.Output() + outStr := string(outBytes) + return strings.TrimSpace(outStr), err +} + +// RunCmdAndReturnTrimmedCombinedOutput ... +func RunCmdAndReturnTrimmedCombinedOutput(cmd *exec.Cmd) (string, error) { + outBytes, err := cmd.CombinedOutput() + outStr := string(outBytes) + return strings.TrimSpace(outStr), err +} + +// RunCommandWithReaderAndWriters ... +func RunCommandWithReaderAndWriters(inReader io.Reader, outWriter, errWriter io.Writer, name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdin = inReader + cmd.Stdout = outWriter + cmd.Stderr = errWriter + return cmd.Run() +} + +// RunCommandWithWriters ... +func RunCommandWithWriters(outWriter, errWriter io.Writer, name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdout = outWriter + cmd.Stderr = errWriter + return cmd.Run() +} + +// RunCommandInDirWithEnvsAndReturnExitCode ... +func RunCommandInDirWithEnvsAndReturnExitCode(envs []string, dir, name string, args ...string) (int, error) { + cmd := exec.Command(name, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if dir != "" { + cmd.Dir = dir + } + if len(envs) > 0 { + cmd.Env = envs + } + + return RunCmdAndReturnExitCode(cmd) +} + +// RunCommandInDirAndReturnExitCode ... +func RunCommandInDirAndReturnExitCode(dir, name string, args ...string) (int, error) { + return RunCommandInDirWithEnvsAndReturnExitCode([]string{}, dir, name, args...) +} + +// RunCommandWithEnvsAndReturnExitCode ... +func RunCommandWithEnvsAndReturnExitCode(envs []string, name string, args ...string) (int, error) { + return RunCommandInDirWithEnvsAndReturnExitCode(envs, "", name, args...) +} + +// RunCommandInDir ... +func RunCommandInDir(dir, name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if dir != "" { + cmd.Dir = dir + } + return cmd.Run() +} + +// RunCommand ... +func RunCommand(name string, args ...string) error { + return RunCommandInDir("", name, args...) +} + +// RunCommandAndReturnStdout .. +func RunCommandAndReturnStdout(name string, args ...string) (string, error) { + cmd := exec.Command(name, args...) + return RunCmdAndReturnTrimmedOutput(cmd) +} + +// RunCommandInDirAndReturnCombinedStdoutAndStderr ... +func RunCommandInDirAndReturnCombinedStdoutAndStderr(dir, name string, args ...string) (string, error) { + cmd := exec.Command(name, args...) + if dir != "" { + cmd.Dir = dir + } + return RunCmdAndReturnTrimmedCombinedOutput(cmd) +} + +// RunCommandAndReturnCombinedStdoutAndStderr .. +func RunCommandAndReturnCombinedStdoutAndStderr(name string, args ...string) (string, error) { + return RunCommandInDirAndReturnCombinedStdoutAndStderr("", name, args...) +} + +// RunBashCommand ... +func RunBashCommand(cmdStr string) error { + return RunCommand("bash", "-c", cmdStr) +} + +// RunBashCommandLines ... +func RunBashCommandLines(cmdLines []string) error { + for _, aLine := range cmdLines { + if err := RunCommand("bash", "-c", aLine); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/command/file.go b/vendor/github.com/bitrise-io/go-utils/command/file.go new file mode 100644 index 0000000..6887e3f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/command/file.go @@ -0,0 +1,57 @@ +package command + +import ( + "errors" + "os" + "strings" + + "github.com/bitrise-io/go-utils/pathutil" +) + +// CopyFile ... +func CopyFile(src, dst string) error { + // replace with a pure Go implementation? + // Golang proposal was: https://go-review.googlesource.com/#/c/1591/5/src/io/ioutil/ioutil.go + isDir, err := pathutil.IsDirExists(src) + if err != nil { + return err + } + if isDir { + return errors.New("Source is a directory: " + src) + } + args := []string{src, dst} + return RunCommand("rsync", args...) +} + +// CopyDir ... +func CopyDir(src, dst string, isOnlyContent bool) error { + if isOnlyContent && !strings.HasSuffix(src, "/") { + src = src + "/" + } + args := []string{"-ar", src, dst} + return RunCommand("rsync", args...) +} + +// RemoveDir ... +func RemoveDir(dirPth string) error { + if exist, err := pathutil.IsPathExists(dirPth); err != nil { + return err + } else if exist { + if err := os.RemoveAll(dirPth); err != nil { + return err + } + } + return nil +} + +// RemoveFile ... +func RemoveFile(pth string) error { + if exist, err := pathutil.IsPathExists(pth); err != nil { + return err + } else if exist { + if err := os.Remove(pth); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/command/zip.go b/vendor/github.com/bitrise-io/go-utils/command/zip.go new file mode 100644 index 0000000..b3e899c --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/command/zip.go @@ -0,0 +1,115 @@ +package command + +import ( + "archive/zip" + "errors" + "io" + "log" + "net/http" + "os" + "path/filepath" + + "github.com/bitrise-io/go-utils/pathutil" +) + +// UnZIP ... +func UnZIP(src, dest string) error { + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer func() { + if err := r.Close(); err != nil { + log.Fatal(err) + } + }() + + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } + + // Closure to address file descriptors issue with all the deferred .Close() methods + extractAndWriteFile := func(f *zip.File) error { + rc, err := f.Open() + if err != nil { + return err + } + defer func() { + if err := rc.Close(); err != nil { + log.Fatal(err) + } + }() + + path := filepath.Join(dest, f.Name) + + if f.FileInfo().IsDir() { + if err := os.MkdirAll(path, f.Mode()); err != nil { + return err + } + } else { + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + defer func() { + if err := f.Close(); err != nil { + log.Fatal(err) + } + }() + + if _, err = io.Copy(f, rc); err != nil { + return err + } + } + return nil + } + + for _, f := range r.File { + if err := extractAndWriteFile(f); err != nil { + return err + } + } + return nil +} + +// DownloadAndUnZIP ... +func DownloadAndUnZIP(url, pth string) error { + tmpDir, err := pathutil.NormalizedOSTempDirPath("") + if err != nil { + return err + } + srcFilePath := tmpDir + "/target.zip" + srcFile, err := os.Create(srcFilePath) + if err != nil { + return err + } + defer func() { + if err := srcFile.Close(); err != nil { + log.Fatal("Failed to close srcFile:", err) + } + if err := os.Remove(srcFilePath); err != nil { + log.Fatal("Failed to remove srcFile:", err) + } + }() + + response, err := http.Get(url) + if err != nil { + return err + } + defer func() { + if err := response.Body.Close(); err != nil { + log.Fatal("Failed to close response body:", err) + } + }() + + if response.StatusCode != http.StatusOK { + errorMsg := "Failed to download target from: " + url + return errors.New(errorMsg) + } + + if _, err := io.Copy(srcFile, response.Body); err != nil { + return err + } + + return UnZIP(srcFilePath, pth) +} diff --git a/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go b/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go new file mode 100644 index 0000000..1128416 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go @@ -0,0 +1,36 @@ +package errorutil + +import ( + "errors" + "os/exec" + "regexp" + "syscall" +) + +// IsExitStatusError ... +func IsExitStatusError(err error) bool { + return IsExitStatusErrorStr(err.Error()) +} + +// IsExitStatusErrorStr ... +func IsExitStatusErrorStr(errString string) bool { + // example exit status error string: exit status 1 + var rex = regexp.MustCompile(`^exit status [0-9]{1,3}$`) + return rex.MatchString(errString) +} + +// CmdExitCodeFromError ... +func CmdExitCodeFromError(err error) (int, error) { + cmdExitCode := 0 + if err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + waitStatus, ok := exitError.Sys().(syscall.WaitStatus) + if !ok { + return 1, errors.New("Failed to cast exit status") + } + cmdExitCode = waitStatus.ExitStatus() + } + return cmdExitCode, nil + } + return 0, nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/json_logger.go b/vendor/github.com/bitrise-io/go-utils/log/json_logger.go new file mode 100644 index 0000000..43b8bfb --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/json_logger.go @@ -0,0 +1,31 @@ +package log + +import ( + "fmt" + "io" + "os" +) + +// JSONLoger ... +type JSONLoger struct { + writer io.Writer +} + +// NewJSONLoger ... +func NewJSONLoger(writer io.Writer) *JSONLoger { + return &JSONLoger{ + writer: writer, + } +} + +// NewDefaultJSONLoger ... +func NewDefaultJSONLoger() JSONLoger { + return JSONLoger{ + writer: os.Stdout, + } +} + +// Print ... +func (l JSONLoger) Print(f Formatable) { + fmt.Fprint(l.writer, f.JSON()) +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/log.go b/vendor/github.com/bitrise-io/go-utils/log/log.go new file mode 100644 index 0000000..1b69028 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/log.go @@ -0,0 +1,34 @@ +package log + +import ( + "fmt" + "io" + "os" + "time" +) + +var outWriter io.Writer = os.Stdout + +// SetOutWriter ... +func SetOutWriter(writer io.Writer) { + outWriter = writer +} + +var enableDebugLog = false + +// SetEnableDebugLog ... +func SetEnableDebugLog(enable bool) { + enableDebugLog = enable +} + +var timestampLayout = "15:04:05" + +// SetTimestampLayout ... +func SetTimestampLayout(layout string) { + timestampLayout = layout +} + +func timestampField() string { + currentTime := time.Now() + return fmt.Sprintf("[%s]", currentTime.Format(timestampLayout)) +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/logger.go b/vendor/github.com/bitrise-io/go-utils/log/logger.go new file mode 100644 index 0000000..4691122 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/logger.go @@ -0,0 +1,12 @@ +package log + +// Logger ... +type Logger interface { + Print(f Formatable) +} + +// Formatable ... +type Formatable interface { + String() string + JSON() string +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/print.go b/vendor/github.com/bitrise-io/go-utils/log/print.go new file mode 100644 index 0000000..3b7c1fa --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/print.go @@ -0,0 +1,84 @@ +package log + +import ( + "fmt" +) + +func printf(severity Severity, withTime bool, format string, v ...interface{}) { + colorFunc := severityColorFuncMap[severity] + message := colorFunc(format, v...) + if withTime { + message = fmt.Sprintf("%s %s", timestampField(), message) + } + + fmt.Fprintln(outWriter, message) +} + +// Successf ... +func Successf(format string, v ...interface{}) { + printf(successSeverity, false, format, v...) +} + +// Donef ... +func Donef(format string, v ...interface{}) { + Successf(format, v...) +} + +// Infof ... +func Infof(format string, v ...interface{}) { + printf(infoSeverity, false, format, v...) +} + +// Printf ... +func Printf(format string, v ...interface{}) { + printf(normalSeverity, false, format, v...) +} + +// Debugf ... +func Debugf(format string, v ...interface{}) { + if enableDebugLog { + printf(debugSeverity, false, format, v...) + } +} + +// Warnf ... +func Warnf(format string, v ...interface{}) { + printf(warnSeverity, false, format, v...) +} + +// Errorf ... +func Errorf(format string, v ...interface{}) { + printf(errorSeverity, false, format, v...) +} + +// TSuccessf ... +func TSuccessf(format string, v ...interface{}) { + printf(successSeverity, true, format, v...) +} + +// TInfof ... +func TInfof(format string, v ...interface{}) { + printf(infoSeverity, true, format, v...) +} + +// TPrintf ... +func TPrintf(format string, v ...interface{}) { + printf(normalSeverity, true, format, v...) +} + +// TDebugf ... +func TDebugf(format string, v ...interface{}) { + if enableDebugLog { + printf(debugSeverity, true, format, v...) + } +} + +// TWarnf ... +func TWarnf(format string, v ...interface{}) { + printf(warnSeverity, true, format, v...) +} + +// TErrorf ... +func TErrorf(format string, v ...interface{}) { + printf(errorSeverity, true, format, v...) +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/raw_logger.go b/vendor/github.com/bitrise-io/go-utils/log/raw_logger.go new file mode 100644 index 0000000..82dc54e --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/raw_logger.go @@ -0,0 +1,31 @@ +package log + +import ( + "fmt" + "io" + "os" +) + +// RawLogger ... +type RawLogger struct { + writer io.Writer +} + +// NewRawLogger ... +func NewRawLogger(writer io.Writer) *RawLogger { + return &RawLogger{ + writer: writer, + } +} + +// NewDefaultRawLogger ... +func NewDefaultRawLogger() RawLogger { + return RawLogger{ + writer: os.Stdout, + } +} + +// Print ... +func (l RawLogger) Print(f Formatable) { + fmt.Fprintln(l.writer, f.String()) +} diff --git a/vendor/github.com/bitrise-io/go-utils/log/severity.go b/vendor/github.com/bitrise-io/go-utils/log/severity.go new file mode 100644 index 0000000..a1c4631 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/log/severity.go @@ -0,0 +1,35 @@ +package log + +import "github.com/bitrise-io/go-utils/colorstring" + +// Severity ... +type Severity uint8 + +const ( + errorSeverity Severity = iota + warnSeverity + normalSeverity + infoSeverity + successSeverity + debugSeverity +) + +type severityColorFunc colorstring.ColorfFunc + +var ( + successSeverityColorFunc severityColorFunc = colorstring.Greenf + infoSeverityColorFunc severityColorFunc = colorstring.Bluef + normalSeverityColorFunc severityColorFunc = colorstring.NoColorf + debugSeverityColorFunc severityColorFunc = colorstring.NoColorf + warnSeverityColorFunc severityColorFunc = colorstring.Yellowf + errorSeverityColorFunc severityColorFunc = colorstring.Redf +) + +var severityColorFuncMap = map[Severity]severityColorFunc{ + successSeverity: successSeverityColorFunc, + infoSeverity: infoSeverityColorFunc, + normalSeverity: normalSeverityColorFunc, + debugSeverity: debugSeverityColorFunc, + warnSeverity: warnSeverityColorFunc, + errorSeverity: errorSeverityColorFunc, +} diff --git a/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go b/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go new file mode 100644 index 0000000..08cec36 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go @@ -0,0 +1,95 @@ +package parseutil + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/pointers" +) + +// ParseBool ... +func ParseBool(userInputStr string) (bool, error) { + if userInputStr == "" { + return false, errors.New("No string to parse") + } + userInputStr = strings.TrimSpace(userInputStr) + + lowercased := strings.ToLower(userInputStr) + if lowercased == "yes" || lowercased == "y" { + return true, nil + } + if lowercased == "no" || lowercased == "n" { + return false, nil + } + return strconv.ParseBool(lowercased) +} + +// CastToString ... +func CastToString(value interface{}) string { + casted, ok := value.(string) + + if !ok { + castedStr := fmt.Sprintf("%v", value) + casted = castedStr + } + + return casted +} + +// CastToStringPtr ... +func CastToStringPtr(value interface{}) *string { + castedValue := CastToString(value) + return pointers.NewStringPtr(castedValue) +} + +// CastToBool ... +func CastToBool(value interface{}) (bool, bool) { + casted, ok := value.(bool) + + if !ok { + castedStr := CastToString(value) + + castedBool, err := ParseBool(castedStr) + if err != nil { + return false, false + } + + casted = castedBool + } + + return casted, true +} + +// CastToBoolPtr ... +func CastToBoolPtr(value interface{}) (*bool, bool) { + castedValue, ok := CastToBool(value) + if !ok { + return nil, false + } + return pointers.NewBoolPtr(castedValue), true +} + +// CastToMapStringInterface ... +func CastToMapStringInterface(value interface{}) (map[string]interface{}, bool) { + castedValue, ok := value.(map[interface{}]interface{}) + desiredMap := map[string]interface{}{} + for key, value := range castedValue { + keyStr, ok := key.(string) + if !ok { + return map[string]interface{}{}, false + } + desiredMap[keyStr] = value + } + return desiredMap, ok +} + +// CastToMapStringInterfacePtr ... +func CastToMapStringInterfacePtr(value interface{}) (*map[string]interface{}, bool) { + casted, ok := CastToMapStringInterface(value) + if !ok { + return nil, false + } + return pointers.NewMapStringInterfacePtr(casted), true +} diff --git a/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go new file mode 100644 index 0000000..1ef74f9 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go @@ -0,0 +1,181 @@ +package pathutil + +import ( + "errors" + "io/ioutil" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" +) + +// RevokableChangeDir ... +func RevokableChangeDir(dir string) (func() error, error) { + origDir, err := CurrentWorkingDirectoryAbsolutePath() + if err != nil { + return nil, err + } + + revokeFn := func() error { + return os.Chdir(origDir) + } + + return revokeFn, os.Chdir(dir) +} + +// ChangeDirForFunction ... +func ChangeDirForFunction(dir string, fn func()) error { + revokeFn, err := RevokableChangeDir(dir) + if err != nil { + return err + } + + fn() + + return revokeFn() +} + +// IsRelativePath ... +func IsRelativePath(pth string) bool { + if strings.HasPrefix(pth, "./") { + return true + } + + if strings.HasPrefix(pth, "/") { + return false + } + + if strings.HasPrefix(pth, "$") { + return false + } + + return true +} + +// EnsureDirExist ... +func EnsureDirExist(dir string) error { + exist, err := IsDirExists(dir) + if !exist || err != nil { + return os.MkdirAll(dir, 0777) + } + return nil +} + +func genericIsPathExists(pth string) (os.FileInfo, bool, error) { + if pth == "" { + return nil, false, errors.New("No path provided") + } + fileInf, err := os.Lstat(pth) + if err == nil { + return fileInf, true, nil + } + if os.IsNotExist(err) { + return nil, false, nil + } + return fileInf, false, err +} + +// IsPathExists ... +func IsPathExists(pth string) (bool, error) { + _, isExists, err := genericIsPathExists(pth) + return isExists, err +} + +// PathCheckAndInfos ... +// Returns: +// 1. file info or nil +// 2. bool, indicating whether the path exists +// 3. error, if any error happens during the check +func PathCheckAndInfos(pth string) (os.FileInfo, bool, error) { + return genericIsPathExists(pth) +} + +// IsDirExists ... +func IsDirExists(pth string) (bool, error) { + fileInf, isExists, err := genericIsPathExists(pth) + if err != nil { + return false, err + } + if !isExists { + return false, nil + } + if fileInf == nil { + return false, errors.New("No file info available") + } + return fileInf.IsDir(), nil +} + +// AbsPath expands ENV vars and the ~ character +// then call Go's Abs +func AbsPath(pth string) (string, error) { + if pth == "" { + return "", errors.New("No Path provided") + } + + pth, err := ExpandTilde(pth) + if err != nil { + return "", err + } + + return filepath.Abs(os.ExpandEnv(pth)) +} + +// ExpandTilde ... +func ExpandTilde(pth string) (string, error) { + if pth == "" { + return "", errors.New("No Path provided") + } + + if strings.HasPrefix(pth, "~") { + pth = strings.TrimPrefix(pth, "~") + + if len(pth) == 0 || strings.HasPrefix(pth, "/") { + return os.ExpandEnv("$HOME" + pth), nil + } + + splitPth := strings.Split(pth, "/") + username := splitPth[0] + + usr, err := user.Lookup(username) + if err != nil { + return "", err + } + + pathInUsrHome := strings.Join(splitPth[1:], "/") + + return filepath.Join(usr.HomeDir, pathInUsrHome), nil + } + + return pth, nil +} + +// CurrentWorkingDirectoryAbsolutePath ... +func CurrentWorkingDirectoryAbsolutePath() (string, error) { + return filepath.Abs("./") +} + +// UserHomeDir ... +func UserHomeDir() string { + if runtime.GOOS == "windows" { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if home == "" { + home = os.Getenv("USERPROFILE") + } + return home + } + return os.Getenv("HOME") +} + +// NormalizedOSTempDirPath ... +// Creates a temp dir, and returns its path. +// If tmpDirNamePrefix is provided it'll be used +// as the tmp dir's name prefix. +// Normalized: it's guaranteed that the path won't end with '/'. +func NormalizedOSTempDirPath(tmpDirNamePrefix string) (retPth string, err error) { + retPth, err = ioutil.TempDir("", tmpDirNamePrefix) + if strings.HasSuffix(retPth, "/") { + retPth = retPth[:len(retPth)-1] + } + return +} diff --git a/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go b/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go new file mode 100644 index 0000000..e26647d --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go @@ -0,0 +1,98 @@ +package pointers + +import "time" + +// NewBoolPtr ... +func NewBoolPtr(val bool) *bool { + ptrValue := new(bool) + *ptrValue = val + return ptrValue +} + +// NewStringPtr ... +func NewStringPtr(val string) *string { + ptrValue := new(string) + *ptrValue = val + return ptrValue +} + +// NewTimePtr ... +func NewTimePtr(val time.Time) *time.Time { + ptrValue := new(time.Time) + *ptrValue = val + return ptrValue +} + +// NewIntPtr ... +func NewIntPtr(val int) *int { + ptrValue := new(int) + *ptrValue = val + return ptrValue +} + +// NewInt64Ptr ... +func NewInt64Ptr(val int64) *int64 { + ptrValue := new(int64) + *ptrValue = val + return ptrValue +} + +// NewMapStringInterfacePtr ... +func NewMapStringInterfacePtr(val map[string]interface{}) *map[string]interface{} { + ptrValue := new(map[string]interface{}) + *ptrValue = map[string]interface{}{} + for key, value := range val { + (*ptrValue)[key] = value + } + return ptrValue +} + +// ------------------------------------------------------ +// --- Safe Getters + +// Bool ... +func Bool(val *bool) bool { + return BoolWithDefault(val, false) +} + +// BoolWithDefault ... +func BoolWithDefault(val *bool, defaultValue bool) bool { + if val == nil { + return defaultValue + } + return *val +} + +// String ... +func String(val *string) string { + return StringWithDefault(val, "") +} + +// StringWithDefault ... +func StringWithDefault(val *string, defaultValue string) string { + if val == nil { + return defaultValue + } + return *val +} + +// TimeWithDefault ... +func TimeWithDefault(val *time.Time, defaultValue time.Time) time.Time { + if val == nil { + return defaultValue + } + return *val +} + +// Int ... +func Int(val *int) int { + return IntWithDefault(val, 0) +} + +// IntWithDefault ... +func IntWithDefault(val *int, defaultValue int) int { + if val == nil { + return defaultValue + } + return *val +} diff --git a/vendor/github.com/bitrise-tools/go-steputils/stepconf/stepconf.go b/vendor/github.com/bitrise-tools/go-steputils/stepconf/stepconf.go new file mode 100644 index 0000000..169035f --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-steputils/stepconf/stepconf.go @@ -0,0 +1,199 @@ +package stepconf + +import ( + "errors" + "fmt" + "os" + "reflect" + "regexp" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/parseutil" +) + +// ErrNotStructPtr indicates a type is not a pointer to a struct. +var ErrNotStructPtr = errors.New("must be a pointer to a struct") + +// ParseError occurs when a struct field cannot be set. +type ParseError struct { + Field string + Value string + Err error +} + +// Error implements builtin errors.Error. +func (e *ParseError) Error() string { + segments := []string{e.Field} + if e.Value != "" { + segments = append(segments, e.Value) + } + segments = append(segments, e.Err.Error()) + return strings.Join(segments, ": ") +} + +// Secret variables are not shown in the printed output. +type Secret string + +const secret = "*****" + +// String implements fmt.Stringer.String. +// When a Secret is printed, it's masking the underlying string with asterisks. +func (s Secret) String() string { + if s == "" { + return "" + } + return secret +} + +// Print the name of the struct in blue color followed by a newline, +// then print all fields formatted as '- field name: field value` separated by newline. +func Print(config interface{}) { + v := reflect.ValueOf(config) + t := reflect.TypeOf(config) + + log.Infof("%s:", t.Name()) + for i := 0; i < t.NumField(); i++ { + fmt.Printf("- %s: %v\n", t.Field(i).Name, v.Field(i).Interface()) + } +} + +// parseTag splits a struct field's env tag into its name and option. +func parseTag(tag string) (string, string) { + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx], tag[idx+1:] + } + return tag, "" +} + +// Parse populates a struct with the retrieved values from environment variables +// described by struct tags and applies the defined validations. +func Parse(conf interface{}) error { + c := reflect.ValueOf(conf) + if c.Kind() != reflect.Ptr { + return ErrNotStructPtr + } + c = c.Elem() + if c.Kind() != reflect.Struct { + return ErrNotStructPtr + } + t := c.Type() + + var errs []*ParseError + for i := 0; i < c.NumField(); i++ { + tag, ok := t.Field(i).Tag.Lookup("env") + if !ok { + continue + } + key, constraint := parseTag(tag) + value := os.Getenv(key) + + if err := setField(c.Field(i), value, constraint); err != nil { + errs = append(errs, &ParseError{t.Field(i).Name, value, err}) + } + } + if len(errs) > 0 { + errorString := "failed to parse config:" + for _, err := range errs { + errorString += fmt.Sprintf("\n- %s", err) + } + return errors.New(errorString) + } + + return nil +} + +func setField(field reflect.Value, value, constraint string) error { + switch constraint { + case "": + break + case "required": + if value == "" { + return errors.New("required variable is not present") + } + case "file", "dir": + if err := checkPath(value, constraint == "dir"); err != nil { + return err + } + // TODO: use FindStringSubmatch to distinguish no match and match for empty string. + case regexp.MustCompile(`^opt\[.*\]$`).FindString(constraint): + if !contains(value, constraint) { + // TODO: print only the value options, not the whole string. + return fmt.Errorf("value is not in value options (%s)", constraint) + } + default: + return fmt.Errorf("invalid constraint (%s)", constraint) + } + + if value == "" { + return nil + } + + switch field.Kind() { + case reflect.String: + field.SetString(value) + case reflect.Bool: + b, err := parseutil.ParseBool(value) + if err != nil { + return errors.New("can't convert to bool") + } + field.SetBool(b) + case reflect.Int: + n, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return errors.New("can't convert to int") + } + field.SetInt(n) + case reflect.Slice: + field.Set(reflect.ValueOf(strings.Split(value, "|"))) + default: + return fmt.Errorf("type is not supported (%s)", field.Kind()) + } + return nil +} + +func checkPath(path string, dir bool) error { + file, err := os.Stat(path) + if err != nil { + // TODO: check case when file exist but os.Stat fails. + return os.ErrNotExist + } + if dir && !file.IsDir() { + return errors.New("not a directory") + } + return nil +} + +// contains reports whether s is within the value options, where value options +// are parsed from opt, which format's is opt[item1,item2,item3]. If an option +// contains commas, it should be single quoted (eg. opt[item1,'item2,item3']). +func contains(s, opt string) bool { + opt = strings.TrimSuffix(strings.TrimPrefix(opt, "opt["), "]") + var valueOpts []string + if strings.Contains(opt, "'") { + // The single quotes separate the options with comma and without comma + // Eg. "a,b,'c,d',e" will results "a,b," "c,d" and ",e" strings. + for _, s := range strings.Split(opt, "'") { + switch { + case s == "," || s == "": + case !strings.HasPrefix(s, ",") && !strings.HasSuffix(s, ","): + // If a string doesn't starts nor ends with a comma it means it's an option which + // contains comma, so we just append it to valueOpts as it is. Eg. "c,d" from above. + valueOpts = append(valueOpts, s) + default: + // If a string starts or ends with comma it means that it contains options without comma. + // So we split the string at commas to get the options. Eg. "a,b," and ",e" from above. + valueOpts = append(valueOpts, strings.Split(strings.Trim(s, ","), ",")...) + } + } + } else { + valueOpts = strings.Split(opt, ",") + } + for _, valOpt := range valueOpts { + if valOpt == s { + return true + } + } + return false +} diff --git a/vendor/github.com/bitrise-tools/go-steputils/tools/tools.go b/vendor/github.com/bitrise-tools/go-steputils/tools/tools.go new file mode 100644 index 0000000..3372b16 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-steputils/tools/tools.go @@ -0,0 +1,14 @@ +package tools + +import ( + "strings" + + "github.com/bitrise-io/go-utils/command" +) + +// ExportEnvironmentWithEnvman ... +func ExportEnvironmentWithEnvman(key, value string) error { + cmd := command.New("envman", "add", "--key", key) + cmd.SetStdin(strings.NewReader(value)) + return cmd.Run() +}