diff options
-rw-r--r-- | .expeditor/config.yml | 14 | ||||
-rw-r--r-- | .github/CODEOWNERS | 5 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE/BUG_TEMPLATE.md (renamed from .github/ISSUE_TEMPLATE.md) | 10 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md | 40 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md | 17 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md | 11 | ||||
-rw-r--r-- | .github/PULL_REQUEST_TEMPLATE.md | 3 | ||||
-rw-r--r-- | .github/lock.yml | 1 | ||||
-rw-r--r-- | .travis.yml | 16 | ||||
-rw-r--r-- | CHANGELOG.md | 10 | ||||
-rw-r--r-- | CONTRIBUTING.md | 1 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | Rakefile | 2 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | lib/mixlib/shellout/version.rb | 2 | ||||
-rw-r--r-- | lib/mixlib/shellout/windows.rb | 44 | ||||
-rw-r--r-- | spec/mixlib/shellout/windows_spec.rb | 84 |
17 files changed, 242 insertions, 23 deletions
diff --git a/.expeditor/config.yml b/.expeditor/config.yml index 2d086d5..75e5e3c 100644 --- a/.expeditor/config.yml +++ b/.expeditor/config.yml @@ -2,7 +2,7 @@ --- # Slack channel in Chef Software slack to send notifications about build failures, etc slack: - notify_channel: chef-notify + notify_channel: chef-found-notify # This publish is triggered by the `built_in:publish_rubygems` artifact_action. rubygems: @@ -16,7 +16,10 @@ github: version_tag_format: "v{{version}}" # allow bumping the minor release via label minor_bump_labels: - - "Expeditor: Bump Minor Version" + - "Expeditor: Bump Version Minor" + # allow bumping the major release via label + major_bump_labels: + - "Expeditor: Bump Version Major" changelog: rollup_header: Changes not yet released to rubygems.org @@ -31,7 +34,7 @@ merge_actions: only_if: built_in:bump_version - built_in:update_changelog: ignore_labels: - - "Expeditor: Exclude From Changelog" + - "Expeditor: Skip Changelog" - "Expeditor: Skip All" - built_in:build_gem: only_if: built_in:bump_version @@ -40,3 +43,8 @@ promote: actions: - built_in:rollover_changelog - built_in:publish_rubygems + +pipelines: + - verify: + description: Pull Request validation tests + public: true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fee8d42..9dbcc7b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,5 @@ # Order is important. The last matching pattern has the most precedence. -* @chef/client-maintainers -.expeditor/** @chef/jex-team +* @chef/chef-infra-reviewers +.expeditor/** @chef/jex-team +*.md @chef/docs-team diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md index f229e13..f28915b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md @@ -1,8 +1,16 @@ +--- +name: � Bug Report +about: If something isn't working as expected �. +labels: "Status: Untriaged" +--- + # Version: [Version of the project installed] -# Environment: [Details about the environment such as the Operating System, cookbook details, etc...] +# Environment: + +[Details about the environment such as the Operating System, cookbook details, etc...] # Scenario: diff --git a/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md new file mode 100644 index 0000000..9f4a958 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md @@ -0,0 +1,40 @@ +--- +name: Design Proposal +about: I have a significant change I would like to propose and discuss before starting +labels: "Status: Untriaged" +--- + +### When a Change Needs a Design Proposal + +A design proposal should be opened any time a change meets one of the following qualifications: + +- Significantly changes the user experience of a project in a way that impacts users. +- Significantly changes the underlying architecture of the project in a way that impacts other developers. +- Changes the development or testing process of the project such as a change of CI systems or test frameworks. + +### Why We Use This Process + +- Allows all interested parties (including any community member) to discuss large impact changes to a project. +- Serves as a durable paper trail for discussions regarding project architecture. +- Forces design discussions to occur before PRs are created. +- Reduces PR refactoring and rejected PRs. + +--- + +<!--- Proposal description and rationale. --> + +## Motivation + +<!--- + As a <<user_profile>>, + I want to <<functionality>>, + so that <<benefit>>. + --> + +## Specification + +<!--- A detailed description of the planned implementation. --> + +## Downstream Impact + +<!--- Which other tools will be impacted by this work? --> diff --git a/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..65bf5a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ +--- +name: 🚀 Enhancement Request +about: I have a suggestion (and may want to implement it 🙂)! +labels: "Status: Untriaged" +--- + +### Describe the Enhancement: +<!--- What you are trying to achieve that you can't? --> + +### Describe the Need: +<!--- What kind of user do you believe would utilize this enhancement, and how many users might want this functionality --> + +### Current Alternative +<!--- Is there a current alternative that you can utilize to workaround the lack of this enhancement --> + +### Can We Help You Implement This?: +<!--- The best way to ensure your enhancement is built is to help implement the enhancement yourself. If you're interested in helping out we'd love to give you a hand to make this possible. Let us know if there's something you need. --> diff --git a/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md new file mode 100644 index 0000000..921a5f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md @@ -0,0 +1,11 @@ +--- +name: 🤗 Support Question +about: If you have a question 💬, please check out our Slack! +--- + +We use GitHub issues to track bugs and feature requests. If you need help please post to our Mailing List or join the Chef Community Slack. + + * Chef Community Slack at http://community-slack.chef.io/. + * Chef Mailing List https://discourse.chef.io/ + + Support issues opened here will be closed and redirected to Slack or Discourse. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 441965f..0df03f8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,10 +5,11 @@ ### Issues Resolved [List any existing issues this PR resolves, or any Discourse or -StackOverflow discussion that's relevant] +StackOverflow discussions that are relevant] ### Check List - [ ] New functionality includes tests - [ ] All tests pass - [ ] All commits have been signed-off for the Developer Certificate of Origin. See <https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco> +- [ ] PR title is a worthy inclusion in the CHANGELOG
\ No newline at end of file diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 0000000..66d5d49 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1 @@ +daysUntilLock: 60 diff --git a/.travis.yml b/.travis.yml index 5f7875d..bd71bb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,18 @@ language: ruby cache: bundler dist: xenial +before_install: + - gem install bundler || true + - bundle --version + - gem update --system + - gem --version + matrix: include: - rvm: 2.3.8 - rvm: 2.4.5 - - rvm: 2.5.3 - - rvm: 2.6 + - rvm: 2.5.5 + - rvm: 2.6.2 - rvm: ruby-head allow_failures: - rvm: ruby-head @@ -16,10 +22,6 @@ branches: only: - master -before_install: - - gem update --system - - gem --version - - gem update bundler - - bundle --version +bundler_args: --jobs 7 --without development script: bundle exec rake diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f27755..994aea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,19 @@ # mixlib-shellout Changelog -<!-- latest_release unreleased --> -## Unreleased +<!-- latest_release 3.0.3 --> +## [v3.0.3](https://github.com/chef/mixlib-shellout/tree/v3.0.3) (2019-06-06) #### Merged Pull Requests -- update travis/appveyor, drop ruby 2.2 support, test on 2.6 [#176](https://github.com/chef/mixlib-shellout/pull/176) ([lamont-granquist](https://github.com/lamont-granquist)) +- Support array args on windows WIP [#182](https://github.com/chef/mixlib-shellout/pull/182) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- latest_release --> <!-- release_rollup since=2.4.4 --> ### Changes not yet released to rubygems.org #### Merged Pull Requests +- Support array args on windows WIP [#182](https://github.com/chef/mixlib-shellout/pull/182) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 3.0.3 --> +- Add BuildKite pipeline [#184](https://github.com/chef/mixlib-shellout/pull/184) ([tas50](https://github.com/tas50)) <!-- 3.0.2 --> +- Add new github templates and codeowners file [#179](https://github.com/chef/mixlib-shellout/pull/179) ([tas50](https://github.com/tas50)) <!-- 3.0.1 --> +- Misnamed parameter in README [#178](https://github.com/chef/mixlib-shellout/pull/178) ([martinisoft](https://github.com/martinisoft)) <!-- 3.0.1 --> - update travis/appveyor, drop ruby 2.2 support, test on 2.6 [#176](https://github.com/chef/mixlib-shellout/pull/176) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 3.0.0 --> <!-- release_rollup --> <!-- latest_stable_release --> diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..03fdbfa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +Please refer to https://github.com/chef/chef/blob/master/CONTRIBUTING.md @@ -39,7 +39,7 @@ In addition to the command to run there are other options that can be set to cha Run a command as the `www` user with no extra ENV settings from `/tmp` with a 1s timeout ```ruby - cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp', :timeout => 1) + cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :environment => nil, :cwd => '/tmp', :timeout => 1) cmd.run_command # etc. ``` @@ -67,6 +67,7 @@ Invoke "whoami.exe" with elevated privileges: whoami = Mixlib::ShellOut.new("whoami.exe", :user => "username", :domain => "DOMAIN", :password => "password", :elevated => true) whoami.run_command ``` + **NOTE:** The user 'admin' must have the 'Log on as a batch job' permission and the user chef is running as must have the 'Replace a process level token' and 'Adjust Memory Quotas for a process' permissions. ## Platform Support @@ -3,7 +3,7 @@ require "rspec/core/rake_task" Bundler::GemHelper.install_tasks name: "mixlib-shellout" -task default: [:style, :spec] +task default: [:spec, :style] desc "Run specs" RSpec::Core::RakeTask.new(:spec) do |spec| @@ -1 +1 @@ -3.0.0 +3.0.3
\ No newline at end of file diff --git a/lib/mixlib/shellout/version.rb b/lib/mixlib/shellout/version.rb index 21b5e5e..d105b77 100644 --- a/lib/mixlib/shellout/version.rb +++ b/lib/mixlib/shellout/version.rb @@ -1,5 +1,5 @@ module Mixlib class ShellOut - VERSION = "3.0.0".freeze + VERSION = "3.0.3".freeze end end diff --git a/lib/mixlib/shellout/windows.rb b/lib/mixlib/shellout/windows.rb index bd99ef5..db4fe32 100644 --- a/lib/mixlib/shellout/windows.rb +++ b/lib/mixlib/shellout/windows.rb @@ -2,7 +2,7 @@ # Author:: Daniel DeLeo (<dan@chef.io>) # Author:: John Keiser (<jkeiser@chef.io>) # Author:: Ho-Sheng Hsiao (<hosh@chef.io>) -# Copyright:: Copyright (c) 2011-2016 Chef Software, Inc. +# Copyright:: Copyright (c) 2011-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,7 +66,7 @@ module Mixlib # # Set cwd, environment, appname, etc. # - app_name, command_line = command_to_run(command) + app_name, command_line = command_to_run(combine_args(*command)) create_process_args = { app_name: app_name, command_line: command_line, @@ -198,6 +198,46 @@ module Mixlib true end + # Use to support array passing semantics on windows + # + # 1. strings with whitespace or quotes in them need quotes around them. + # 2. interior quotes need to get backslash escaped (parser needs to know when it really ends). + # 3. random backlsashes in paths themselves remain untouched. + # 4. if the argument must be quoted by #1 and terminates in a sequence of backslashes then all the backlashes must themselves + # be backslash excaped (double the backslashes). + # 5. if an interior quote that must be escaped by #2 has a sequence of backslashes before it then all the backslashes must + # themselves be backslash excaped along with the backslash ecape of the interior quote (double plus one backslashes). + # + # And to restate. We are constructing a string which will be parsed by the windows parser into arguments, and we want those + # arguments to match the *args array we are passed here. So call the windows parser operation A then we need to apply A^-1 to + # our args to construct the string so that applying A gives windows back our *args. + # + # And when the windows parser sees a series of backslashes followed by a double quote, it has to determine if that double quote + # is terminating or not, and how many backslashes to insert in the args. So what it does is divide it by two (rounding down) to + # get the number of backslashes to insert. Then if it is even the double quotes terminate the argument. If it is even the + # double quotes are interior double quotes (the extra backslash quotes the double quote). + # + # We construct the inverse operation so interior double quotes preceeded by N backslashes get 2N+1 backslashes in front of the quote, + # while trailing N backslashes get 2N backslashes in front of the quote that terminates the argument. + # + # see: https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ + # + # @api private + # @param args [Array<String>] array of command arguments + # @return String + def combine_args(*args) + return args[0] if args.length == 1 + args.map do |arg| + if arg =~ /[ \t\n\v"]/ + arg = arg.gsub(/(\\*)"/, '\1\1\"') # interior quotes with N preceeding backslashes need 2N+1 backslashes + arg = arg.sub(/(\\+)$/, '\1\1') # trailing N backslashes need to become 2N backslashes + "\"#{arg}\"" + else + arg + end + end.join(" ") + end + def command_to_run(command) return run_under_cmd(command) if should_run_under_cmd?(command) diff --git a/spec/mixlib/shellout/windows_spec.rb b/spec/mixlib/shellout/windows_spec.rb index d4edabc..1011560 100644 --- a/spec/mixlib/shellout/windows_spec.rb +++ b/spec/mixlib/shellout/windows_spec.rb @@ -307,4 +307,88 @@ describe "Mixlib::ShellOut::Windows", :windows_only do end end end + + context "#combine_args" do + let(:shell_out) { Mixlib::ShellOut.new } + subject { shell_out.send(:combine_args, *largs) } + + def self.with_args(*args, &example) + context "with command #{args}" do + let(:largs) { args } + it(&example) + end + end + + with_args("echo", "%PATH%") do + is_expected.to eql(%q{echo %PATH%}) + end + + with_args("echo %PATH%") do + is_expected.to eql(%q{echo %PATH%}) + end + + # Note carefully for the following that single quotes in ruby support '\\' as an escape sequence for a single + # literal backslash. It is not mandatory to always use this since '\d' does not escape the 'd' and is literally + # a backlash followed by an 'd'. However, in the following all backslashes are escaped for consistency. Otherwise + # it becomes prohibitively confusing to track when you need and do not need the escape the backslash (particularly + # when the literal string has a trailing backslash such that '\\' must be used instead of '\' which would escape + # the intended terminating single quote or %q{\} which escapes the terminating delimiter). + + with_args("child.exe", "argument1", "argument 2", '\\some\\path with\\spaces') do + is_expected.to eql('child.exe argument1 "argument 2" "\\some\\path with\\spaces"') + end + + with_args("child.exe", "argument1", 'she said, "you had me at hello"', '\\some\\path with\\spaces') do + is_expected.to eql('child.exe argument1 "she said, \\"you had me at hello\\"" "\\some\\path with\\spaces"') + end + + with_args("child.exe", "argument1", 'argument\\\\"2\\\\"', "argument3", "argument4") do + is_expected.to eql('child.exe argument1 "argument\\\\\\\\\\"2\\\\\\\\\\"" argument3 argument4') + end + + with_args("child.exe", '\\some\\directory with\\spaces\\', "argument2") do + is_expected.to eql('child.exe "\\some\\directory with\\spaces\\\\" argument2') + end + + with_args("child.exe", '\\some\\directory with\\\\\\spaces\\\\\\', "argument2") do + is_expected.to eql('child.exe "\\some\\directory with\\\\\\spaces\\\\\\\\\\\\" argument2') + end + end + + context "#run_command" do + let(:shell_out) { Mixlib::ShellOut.new(*largs) } + subject { shell_out.send(:run_command) } + + def self.with_args(*args, &example) + context "with command #{args}" do + let(:largs) { args } + it(&example) + end + end + + with_args("echo", "FOO") do + is_expected.not_to be_error + end + + # Test box is going to have to have c:\program files on it, which is perhaps brittle in principle, but + # I don't know enough windows to come up with a less brittle test. Fix it if you've got a better idea. + # The tests need to fail though if the argument is not quoted correctly so using `echo` would be poor + # because `echo FOO BAR` and `echo "FOO BAR"` aren't any different. + + with_args('dir c:\\program files') do + is_expected.to be_error + end + + with_args('dir "c:\\program files"') do + is_expected.not_to be_error + end + + with_args("dir", 'c:\\program files') do + is_expected.not_to be_error + end + + with_args("dir", 'c:\\program files\\') do + is_expected.not_to be_error + end + end end |