diff --git a/README.md b/README.md index 2e96fef..0fcd158 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ You can also run this step directly with [Bitrise CLI](https://github.com/bitris | --- | --- | --- | --- | | `gradlew_path` | Using a Gradle Wrapper (gradlew) is required, as the wrapper is what makes sure that the right Gradle version is installed and used for the build. __You can find more information about the Gradle Wrapper (gradlew), and about how you can generate one (if you would not have one already)__ in the official guide at: [https://docs.gradle.org/current/userguide/gradle_wrapper.html](https://docs.gradle.org/current/userguide/gradle_wrapper.html). **The path should be relative** to the repository root, for example: `./gradlew`, or if it's in a sub directory: `./sub/dir/gradlew`. | required | `$GRADLEW_PATH` | | `ndk_version` | NDK version to install, for example `23.0.7599858`. Run `sdkmanager --list` on your machine to see all available versions. Leave this input empty if you are not using the Native Development Kit in your project. | | | +| `gradlew_dependencies_options` | Additional options to be added to the executed `gradlew dependencies` command. The step runs `gradlew dependencies --stacktrace` to list and install the missing project dependencies. Additional options will be appended to the end of this command. Example: `--configuration-cache-problems=warn`. | | |
diff --git a/androidcomponents/androidcomponents.go b/androidcomponents/androidcomponents.go index 7852119..095f0f9 100644 --- a/androidcomponents/androidcomponents.go +++ b/androidcomponents/androidcomponents.go @@ -22,9 +22,10 @@ import ( ) type installer struct { - androidSDK *sdk.Model - sdkManager *sdkmanager.Model - gradlewPath string + androidSDK *sdk.Model + sdkManager *sdkmanager.Model + gradlewPath string + gradlewDependenciesOptions []string } // InstallLicences ... @@ -84,15 +85,16 @@ func InstallLicences(androidSdk *sdk.Model) error { } // Ensure ... -func Ensure(androidSdk *sdk.Model, gradlewPath string) error { +func Ensure(androidSdk *sdk.Model, gradlewPath string, gradlewDependenciesOptions []string) error { sdkManager, err := sdkmanager.New(androidSdk) if err != nil { return err } i := installer{ - androidSdk, - sdkManager, - gradlewPath, + androidSDK: androidSdk, + sdkManager: sdkManager, + gradlewPath: gradlewPath, + gradlewDependenciesOptions: gradlewDependenciesOptions, } return retry.Times(1).Wait(time.Second).Try(func(attempt uint) error { @@ -112,15 +114,18 @@ func (i installer) getDependencyCases() map[string]func(match string) error { } } -func getDependenciesOutput(projectLocation string) (string, error) { - gradleCmd := command.New("./gradlew", "dependencies", "--stacktrace") +func getDependenciesOutput(projectLocation string, options []string) (string, error) { + args := []string{"dependencies", "--stacktrace"} + args = append(args, options...) + + gradleCmd := command.New("./gradlew", args...) gradleCmd.SetStdin(strings.NewReader("y")) gradleCmd.SetDir(projectLocation) return gradleCmd.RunAndReturnTrimmedCombinedOutput() } func (i installer) scanDependencies(foundMatches ...string) error { - out, err := getDependenciesOutput(filepath.Dir(i.gradlewPath)) + out, err := getDependenciesOutput(filepath.Dir(i.gradlewPath), i.gradlewDependenciesOptions) if err == nil { return nil } diff --git a/bitrise.yml b/bitrise.yml index 0f183e5..e488fbc 100755 --- a/bitrise.yml +++ b/bitrise.yml @@ -40,6 +40,8 @@ workflows: - branch: master - path::./: title: Execute step + inputs: + - gradlew_dependencies_options: --configuration-cache-problems=warn - gradle-runner: inputs: - gradle_task: assembleDebug diff --git a/go.mod b/go.mod index c19beed..55cdcb2 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,5 @@ require ( github.com/bitrise-io/go-steputils v1.0.1 github.com/bitrise-io/go-utils v1.0.1 github.com/hashicorp/go-version v1.3.0 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 ) diff --git a/go.sum b/go.sum index a8e11dd..ac1a0b5 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiw github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 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/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= diff --git a/main.go b/main.go index 07807ee..521d015 100644 --- a/main.go +++ b/main.go @@ -15,16 +15,18 @@ import ( "github.com/bitrise-io/go-utils/log" "github.com/bitrise-steplib/steps-install-missing-android-tools/androidcomponents" "github.com/hashicorp/go-version" + "github.com/kballard/go-shellquote" ) const androidNDKHome = "ANDROID_NDK_HOME" // Config ... type Config struct { - GradlewPath string `env:"gradlew_path,file"` - AndroidHome string `env:"ANDROID_HOME"` - AndroidSDKRoot string `env:"ANDROID_SDK_ROOT"` - NDKVersion string `env:"ndk_version"` + GradlewPath string `env:"gradlew_path,file"` + AndroidHome string `env:"ANDROID_HOME"` + AndroidSDKRoot string `env:"ANDROID_SDK_ROOT"` + NDKVersion string `env:"ndk_version"` + GradlewDependenciesOptions string `env:"gradlew_dependencies_options"` } func failf(format string, v ...interface{}) { @@ -129,6 +131,10 @@ func main() { if err := stepconf.Parse(&config); err != nil { failf("Process config: %s", err) } + gradlewDependenciesOptions, err := shellquote.Split(config.GradlewDependenciesOptions) + if err != nil { + failf("provided gradlew_dependencies_options (%s) are not valid CLI parameters: %s", config.GradlewDependenciesOptions, err) + } fmt.Println() stepconf.Print(config) @@ -189,7 +195,7 @@ func main() { fmt.Println() log.Infof("Ensure required Android SDK components") - if err := androidcomponents.Ensure(androidSdk, config.GradlewPath); err != nil { + if err := androidcomponents.Ensure(androidSdk, config.GradlewPath, gradlewDependenciesOptions); err != nil { failf("Run: failed to ensure android components, error: %s", err) } diff --git a/step.yml b/step.yml index 775c873..98626a8 100755 --- a/step.yml +++ b/step.yml @@ -62,3 +62,15 @@ inputs: title: NDK version summary: NDK version to install. Leave this input empty if you are not using the Native Development Kit in your project. description: NDK version to install, for example `23.0.7599858`. Run `sdkmanager --list` on your machine to see all available versions. Leave this input empty if you are not using the Native Development Kit in your project. +- gradlew_dependencies_options: + opts: + title: Additional options for the `gradlew dependencies` command + summary: Additional options to be added to the executed `gradlew dependencies` command. + description: |- + Additional options to be added to the executed `gradlew dependencies` command. + + The step runs `gradlew dependencies --stacktrace` to list and install the missing project dependencies. + + Additional options will be appended to the end of this command. + + Example: `--configuration-cache-problems=warn`. diff --git a/vendor/github.com/kballard/go-shellquote/LICENSE b/vendor/github.com/kballard/go-shellquote/LICENSE new file mode 100644 index 0000000..a6d7731 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2014 Kevin Ballard + +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/kballard/go-shellquote/README b/vendor/github.com/kballard/go-shellquote/README new file mode 100644 index 0000000..4d34e87 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/README @@ -0,0 +1,36 @@ +PACKAGE + +package shellquote + import "github.com/kballard/go-shellquote" + + Shellquote provides utilities for joining/splitting strings using sh's + word-splitting rules. + +VARIABLES + +var ( + UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string") + UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string") + UnterminatedEscapeError = errors.New("Unterminated backslash-escape") +) + + +FUNCTIONS + +func Join(args ...string) string + Join quotes each argument and joins them with a space. If passed to + /bin/sh, the resulting string will be split back into the original + arguments. + +func Split(input string) (words []string, err error) + Split splits a string according to /bin/sh's word-splitting rules. It + supports backslash-escapes, single-quotes, and double-quotes. Notably it + does not support the $'' style of quoting. It also doesn't attempt to + perform any other sort of expansion, including brace expansion, shell + expansion, or pathname expansion. + + If the given input has an unterminated quoted string or ends in a + backslash-escape, one of UnterminatedSingleQuoteError, + UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned. + + diff --git a/vendor/github.com/kballard/go-shellquote/doc.go b/vendor/github.com/kballard/go-shellquote/doc.go new file mode 100644 index 0000000..9445fa4 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/doc.go @@ -0,0 +1,3 @@ +// Shellquote provides utilities for joining/splitting strings using sh's +// word-splitting rules. +package shellquote diff --git a/vendor/github.com/kballard/go-shellquote/quote.go b/vendor/github.com/kballard/go-shellquote/quote.go new file mode 100644 index 0000000..72a8cb3 --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/quote.go @@ -0,0 +1,102 @@ +package shellquote + +import ( + "bytes" + "strings" + "unicode/utf8" +) + +// Join quotes each argument and joins them with a space. +// If passed to /bin/sh, the resulting string will be split back into the +// original arguments. +func Join(args ...string) string { + var buf bytes.Buffer + for i, arg := range args { + if i != 0 { + buf.WriteByte(' ') + } + quote(arg, &buf) + } + return buf.String() +} + +const ( + specialChars = "\\'\"`${[|&;<>()*?!" + extraSpecialChars = " \t\n" + prefixChars = "~" +) + +func quote(word string, buf *bytes.Buffer) { + // We want to try to produce a "nice" output. As such, we will + // backslash-escape most characters, but if we encounter a space, or if we + // encounter an extra-special char (which doesn't work with + // backslash-escaping) we switch over to quoting the whole word. We do this + // with a space because it's typically easier for people to read multi-word + // arguments when quoted with a space rather than with ugly backslashes + // everywhere. + origLen := buf.Len() + + if len(word) == 0 { + // oops, no content + buf.WriteString("''") + return + } + + cur, prev := word, word + atStart := true + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) { + // copy the non-special chars up to this point + if len(cur) < len(prev) { + buf.WriteString(prev[0 : len(prev)-len(cur)-l]) + } + buf.WriteByte('\\') + buf.WriteRune(c) + prev = cur + } else if strings.ContainsRune(extraSpecialChars, c) { + // start over in quote mode + buf.Truncate(origLen) + goto quote + } + atStart = false + } + if len(prev) > 0 { + buf.WriteString(prev) + } + return + +quote: + // quote mode + // Use single-quotes, but if we find a single-quote in the word, we need + // to terminate the string, emit an escaped quote, and start the string up + // again + inQuote := false + for len(word) > 0 { + i := strings.IndexRune(word, '\'') + if i == -1 { + break + } + if i > 0 { + if !inQuote { + buf.WriteByte('\'') + inQuote = true + } + buf.WriteString(word[0:i]) + } + word = word[i+1:] + if inQuote { + buf.WriteByte('\'') + inQuote = false + } + buf.WriteString("\\'") + } + if len(word) > 0 { + if !inQuote { + buf.WriteByte('\'') + } + buf.WriteString(word) + buf.WriteByte('\'') + } +} diff --git a/vendor/github.com/kballard/go-shellquote/unquote.go b/vendor/github.com/kballard/go-shellquote/unquote.go new file mode 100644 index 0000000..b1b13da --- /dev/null +++ b/vendor/github.com/kballard/go-shellquote/unquote.go @@ -0,0 +1,156 @@ +package shellquote + +import ( + "bytes" + "errors" + "strings" + "unicode/utf8" +) + +var ( + UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string") + UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string") + UnterminatedEscapeError = errors.New("Unterminated backslash-escape") +) + +var ( + splitChars = " \n\t" + singleChar = '\'' + doubleChar = '"' + escapeChar = '\\' + doubleEscapeChars = "$`\"\n\\" +) + +// Split splits a string according to /bin/sh's word-splitting rules. It +// supports backslash-escapes, single-quotes, and double-quotes. Notably it does +// not support the $'' style of quoting. It also doesn't attempt to perform any +// other sort of expansion, including brace expansion, shell expansion, or +// pathname expansion. +// +// If the given input has an unterminated quoted string or ends in a +// backslash-escape, one of UnterminatedSingleQuoteError, +// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned. +func Split(input string) (words []string, err error) { + var buf bytes.Buffer + words = make([]string, 0) + + for len(input) > 0 { + // skip any splitChars at the start + c, l := utf8.DecodeRuneInString(input) + if strings.ContainsRune(splitChars, c) { + input = input[l:] + continue + } else if c == escapeChar { + // Look ahead for escaped newline so we can skip over it + next := input[l:] + if len(next) == 0 { + err = UnterminatedEscapeError + return + } + c2, l2 := utf8.DecodeRuneInString(next) + if c2 == '\n' { + input = next[l2:] + continue + } + } + + var word string + word, input, err = splitWord(input, &buf) + if err != nil { + return + } + words = append(words, word) + } + return +} + +func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) { + buf.Reset() + +raw: + { + cur := input + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if c == singleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto single + } else if c == doubleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto double + } else if c == escapeChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto escape + } else if strings.ContainsRune(splitChars, c) { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + return buf.String(), cur, nil + } + } + if len(input) > 0 { + buf.WriteString(input) + input = "" + } + goto done + } + +escape: + { + if len(input) == 0 { + return "", "", UnterminatedEscapeError + } + c, l := utf8.DecodeRuneInString(input) + if c == '\n' { + // a backslash-escaped newline is elided from the output entirely + } else { + buf.WriteString(input[:l]) + } + input = input[l:] + } + goto raw + +single: + { + i := strings.IndexRune(input, singleChar) + if i == -1 { + return "", "", UnterminatedSingleQuoteError + } + buf.WriteString(input[0:i]) + input = input[i+1:] + goto raw + } + +double: + { + cur := input + for len(cur) > 0 { + c, l := utf8.DecodeRuneInString(cur) + cur = cur[l:] + if c == doubleChar { + buf.WriteString(input[0 : len(input)-len(cur)-l]) + input = cur + goto raw + } else if c == escapeChar { + // bash only supports certain escapes in double-quoted strings + c2, l2 := utf8.DecodeRuneInString(cur) + cur = cur[l2:] + if strings.ContainsRune(doubleEscapeChars, c2) { + buf.WriteString(input[0 : len(input)-len(cur)-l-l2]) + if c2 == '\n' { + // newline is special, skip the backslash entirely + } else { + buf.WriteRune(c2) + } + input = cur + } + } + } + return "", "", UnterminatedDoubleQuoteError + } + +done: + return buf.String(), input, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 12940b0..f4f81aa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -25,3 +25,6 @@ github.com/hashicorp/go-retryablehttp # github.com/hashicorp/go-version v1.3.0 ## explicit github.com/hashicorp/go-version +# github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 +## explicit +github.com/kballard/go-shellquote