diff --git a/.github/actions/package/action.yml b/.github/actions/package/action.yml new file mode 100644 index 0000000..bc0a2cd --- /dev/null +++ b/.github/actions/package/action.yml @@ -0,0 +1,62 @@ +name: Upload built package +description: >- + Build and update package. + +inputs: + upload-files: + required: false + default: 'pkg/*.gem' + description: File name pattern to upload. + + package-name: + required: false + default: '${GITHUB_REPOSITORY#*/}-${RUNNER_OS%-*}' + description: Package name to upload. + + build-program: + required: false + default: rake build + description: Command to build package files. + +runs: + using: composite + + steps: + - id: setup + run: | + : Setup + PS4="##[command]"; set -x + : Fetch deeper for changelogs + git fetch --force --no-tags origin 'refs/tags/v*:refs/tags/v*' + set -- "$(git symbolic-ref --short HEAD)" $(git tag --list --no-contains HEAD --sort -version:refname) + branch=$1 prev=$2 + git fetch ${prev:+--shallow-exclude=}${prev:---unshallow} origin ${branch} + : Re-checkout with LF + git config core.autocrlf false + git config core.eol lf + git checkout -f + shell: bash + + - id: build + run: | + : Build + if command -v shasum > /dev/null; then + shasum=(shasum -a 256 -b) # Ubuntu, macOS + elif command -v sha256sum > /dev/null; then + shasum=(sha256sum -b) # Windows + else # fallback + shasum=(ruby -rdigest -e "ARGV.each{|f| print Digest::SHA256.file(f).hexdigest, ' *'; puts f}") + fi + PS4="##[command]"; set -x + ${{ inputs.build-program }} + : Show info + ls -l ${{ inputs.upload-files }} + "${shasum[@]}" ${{ inputs.upload-files }} + echo pkg="${{ inputs.package-name }}" >> $GITHUB_OUTPUT + shell: bash + + - id: upload + uses: actions/upload-artifact@v4 + with: + path: ${{ inputs.upload-files }} + name: ${{ steps.build.outputs.pkg }} diff --git a/.github/workflows/push_gem.yml b/.github/workflows/push_gem.yml new file mode 100644 index 0000000..91c6864 --- /dev/null +++ b/.github/workflows/push_gem.yml @@ -0,0 +1,46 @@ +name: Publish gem to rubygems.org + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + push: + if: github.repository == 'ruby/optparse' + runs-on: ubuntu-latest + + environment: + name: rubygems.org + url: https://rubygems.org/gems/optparse + + permissions: + contents: write + id-token: write + + steps: + - name: Harden Runner + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + with: + egress-policy: audit + + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + + - name: Set up Ruby + uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 + with: + bundler-cache: true + ruby-version: ruby + + - name: Publish to RubyGems + uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1 + + - name: Create GitHub release + run: | + tag_name="$(git describe --tags --abbrev=0)" + gh release create "${tag_name}" --verify-tag --generate-notes + env: + GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d862b11..2736e12 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,17 +21,13 @@ jobs: ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} os: [ ubuntu-latest, macos-latest, windows-latest ] exclude: + - { os: macos-latest, ruby: 2.5 } - { os: windows-latest, ruby: truffleruby-head } - { os: windows-latest, ruby: truffleruby } - { os: windows-latest, ruby: jruby-head } - { os: windows-latest, ruby: jruby } runs-on: ${{ matrix.os }} steps: - - name: git config - run: | - git config --global core.autocrlf false - git config --global core.eol lf - git config --global advice.detachedHead 0 - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 @@ -42,42 +38,5 @@ jobs: - name: Run test run: rake test - - id: fetch - run: | - : Fetch deeper for changelogs - set -x - latest=$(git tag --list --contains HEAD) - case "$latest" in - '') prev=;; - *.0.0) prev=${latest%.0.0}; prev=$((prev-1)).0.0;; - *.0) prev=${latest%.0}; x=${prev##*.}; prev=${prev%.*}.$((x-1)).0;; - *) x=${prev##*.}; prev=${prev%.*}.$((x-1));; - esac - : ${prev:+git fetch --unshallow-exclude=$prev origin} - until git log -1 --oneline $prev; do - git fetch --deepen=100 - done - set +x - shell: bash - if: >- - ${{ - matrix.os != 'windows-latest' && - matrix.ruby == needs.ruby-versions.outputs.latest - }} - - - id: build - run: | - rake build - ls -l pkg/*.gem - shasum -a 256 pkg/*.gem - echo "pkg=${GITHUB_REPOSITORY#*/}-${RUNNING_OS%-*}" >> $GITHUB_OUTPUT - env: - RUNNING_OS: ${{matrix.os}} - if: ${{ steps.fetch.outcome == 'success' }} - - - name: Upload package - uses: actions/upload-artifact@v4 - with: - path: pkg/*.gem - name: ${{steps.build.outputs.pkg}} - if: ${{ steps.build.outcome == 'success' }} + - uses: ./.github/actions/package + if: ${{ matrix.ruby == needs.ruby-versions.outputs.latest }} diff --git a/BSDL b/BSDL new file mode 100644 index 0000000..66d9359 --- /dev/null +++ b/BSDL @@ -0,0 +1,22 @@ +Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/README.md b/README.md index 67d829f..160a4e7 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ p ARGV ## Development -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). diff --git a/lib/optparse.rb b/lib/optparse.rb index 069c3e4..27e0843 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -425,7 +425,7 @@ # class OptionParser # The version string - OptionParser::Version = "0.5.0" + OptionParser::Version = "0.6.0" # :stopdoc: NoArgument = [NO_ARGUMENT = :NONE, nil].freeze @@ -1115,7 +1115,7 @@ def help_exit Switch::OptionalArgument.new do |pkg| if pkg begin - require 'optparse/version' + require_relative 'optparse/version' rescue LoadError else show_version(*pkg.split(/,/)) or @@ -1729,9 +1729,9 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, end end begin - opt, cb, *val = sw.parse(rest, argv) {|*exc| raise(*exc)} - val = callback!(cb, 1, *val) if cb - callback!(setter, 2, sw.switch_name, *val) if setter + opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)} + val = callback!(cb, 1, val) if cb + callback!(setter, 2, sw.switch_name, val) if setter rescue ParseError raise $!.set_option(arg, rest) end @@ -1761,7 +1761,7 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, raise $!.set_option(arg, true) end begin - opt, cb, *val = sw.parse(val, argv) {|*exc| raise(*exc) if eq} + opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq} rescue ParseError raise $!.set_option(arg, arg.length > 2) else @@ -1769,8 +1769,8 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, end begin argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-') - val = callback!(cb, 1, *val) if cb - callback!(setter, 2, sw.switch_name, *val) if setter + val = callback!(cb, 1, val) if cb + callback!(setter, 2, sw.switch_name, val) if setter rescue ParseError raise $!.set_option(arg, arg.length > 2) end @@ -1798,6 +1798,8 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, # Calls callback with _val_. def callback!(cb, max_arity, *args) # :nodoc: + args.compact! + if (size = args.size) < max_arity and cb.to_proc.lambda? (arity = cb.arity) < 0 and arity = (1-arity) arity = max_arity if arity > max_arity diff --git a/test/optparse/test_acceptable.rb b/test/optparse/test_acceptable.rb index c7ea215..8b578ef 100644 --- a/test/optparse/test_acceptable.rb +++ b/test/optparse/test_acceptable.rb @@ -199,5 +199,7 @@ def test_decimal_numeric def test_array assert_equal(%w"", no_error {@opt.parse!(%w"--array a,b,c")}) assert_equal(%w"a b c", @array) + assert_equal(%w"", no_error {@opt.parse!(%w"--array a")}) + assert_equal(%w"a", @array) end end diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb index 8d09e0f..7f35cb4 100644 --- a/test/optparse/test_optparse.rb +++ b/test/optparse/test_optparse.rb @@ -74,6 +74,7 @@ def test_into @opt.def_option "-v", "--verbose" do @verbose = true end @opt.def_option "-q", "--quiet" do @quiet = true end @opt.def_option "-o", "--option [OPT]" do |opt| @option = opt end + @opt.def_option "-a", "--array [VAL]", Array do |val| val end result = {} @opt.parse %w(--host localhost --port 8000 -v), into: result assert_equal({host: "localhost", port: 8000, verbose: true}, result) @@ -84,6 +85,18 @@ def test_into result = {} @opt.parse %w(--option OPTION -v), into: result assert_equal({verbose: true, option: "OPTION"}, result) + result = {} + @opt.parse %w(-a b,c,d), into: result + assert_equal({array: %w(b c d)}, result) + result = {} + @opt.parse %w(--array b,c,d), into: result + assert_equal({array: %w(b c d)}, result) + result = {} + @opt.parse %w(-a b), into: result + assert_equal({array: %w(b)}, result) + result = {} + @opt.parse %w(--array b), into: result + assert_equal({array: %w(b)}, result) end def test_require_exact