├── .expeditor ├── config.yml ├── run_linux_tests.sh ├── run_windows_tests.ps1 ├── update_version.sh └── verify.pipeline.yml ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── BUG_TEMPLATE.md │ ├── DESIGN_PROPOSAL.md │ ├── ENHANCEMENT_REQUEST_TEMPLATE.md │ └── SUPPORT_QUESTION.md └── dependabot.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── VERSION ├── lib └── mixlib │ ├── versioning.rb │ └── versioning │ ├── exceptions.rb │ ├── format.rb │ ├── format │ ├── git_describe.rb │ ├── opscode_semver.rb │ ├── partial_semver.rb │ ├── rubygems.rb │ └── semver.rb │ └── version.rb ├── mixlib-versioning.gemspec └── spec ├── mixlib └── versioning │ ├── format │ ├── git_describe_spec.rb │ ├── opscode_semver_spec.rb │ ├── partial_semver_spec.rb │ ├── rubygems_spec.rb │ └── semver_spec.rb │ ├── format_spec.rb │ └── versioning_spec.rb ├── spec_helper.rb └── support └── shared_examples ├── basic_semver.rb ├── behaviors ├── comparable.rb ├── comparable_types.rb ├── filterable.rb ├── parses_valid_version_strings.rb ├── rejects_invalid_version_strings.rb ├── serializable.rb └── sortable.rb └── semver.rb /.expeditor/config.yml: -------------------------------------------------------------------------------- 1 | # Documentation available at https://expeditor.chef.io/docs/getting-started/ 2 | --- 3 | 4 | # Slack channel in Chef Software slack to send notifications about build failures, etc 5 | slack: 6 | notify_channel: chef-found-notify 7 | 8 | # This publish is triggered by the `built_in:publish_rubygems` artifact_action. 9 | rubygems: 10 | - mixlib-versioning 11 | 12 | github: 13 | # This deletes the GitHub PR branch after successfully merged into the release branch 14 | delete_branch_on_merge: true 15 | # The tag format to use (e.g. v1.0.0) 16 | version_tag_format: "v{{version}}" 17 | # allow bumping the minor release via label 18 | minor_bump_labels: 19 | - "Expeditor: Bump Version Minor" 20 | # allow bumping the major release via label 21 | major_bump_labels: 22 | - "Expeditor: Bump Version Major" 23 | 24 | changelog: 25 | rollup_header: Changes not yet released to rubygems.org 26 | 27 | subscriptions: 28 | # These actions are taken, in order they are specified, anytime a Pull Request is merged. 29 | - workload: pull_request_merged:{{github_repo}}:{{release_branch}}:* 30 | actions: 31 | - built_in:bump_version: 32 | ignore_labels: 33 | - "Expeditor: Skip Version Bump" 34 | - "Expeditor: Skip All" 35 | - bash:.expeditor/update_version.sh: 36 | only_if: built_in:bump_version 37 | - built_in:update_changelog: 38 | ignore_labels: 39 | - "Expeditor: Skip Changelog" 40 | - "Expeditor: Skip All" 41 | - built_in:build_gem: 42 | only_if: built_in:bump_version 43 | - workload: project_promoted:{{agent_id}}:* 44 | actions: 45 | - built_in:rollover_changelog 46 | - built_in:publish_rubygems 47 | 48 | pipelines: 49 | - verify: 50 | description: Pull Request validation tests 51 | public: true 52 | -------------------------------------------------------------------------------- /.expeditor/run_linux_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script runs a passed in command, but first setups up the bundler caching on the repo 4 | 5 | set -ue 6 | 7 | export USER="root" 8 | 9 | echo "--- dependencies" 10 | export LANG=C.UTF-8 LANGUAGE=C.UTF-8 11 | S3_URL="s3://public-cd-buildkite-cache/${BUILDKITE_PIPELINE_SLUG}/${BUILDKITE_LABEL}" 12 | 13 | pull_s3_file() { 14 | aws s3 cp "${S3_URL}/$1" "$1" || echo "Could not pull $1 from S3" 15 | } 16 | 17 | push_s3_file() { 18 | if [ -f "$1" ]; then 19 | aws s3 cp "$1" "${S3_URL}/$1" || echo "Could not push $1 to S3 for caching." 20 | fi 21 | } 22 | 23 | apt-get update -y 24 | apt-get install awscli -y 25 | 26 | echo "--- bundle install" 27 | pull_s3_file "bundle.tar.gz" 28 | pull_s3_file "bundle.sha256" 29 | 30 | if [ -f bundle.tar.gz ]; then 31 | tar -xzf bundle.tar.gz 32 | fi 33 | 34 | if [ -n "${RESET_BUNDLE_CACHE:-}" ]; then 35 | rm bundle.sha256 36 | fi 37 | 38 | bundle config --local path vendor/bundle 39 | bundle install --jobs=7 --retry=3 40 | 41 | echo "--- bundle cache" 42 | if test -f bundle.sha256 && shasum --check bundle.sha256 --status; then 43 | echo "Bundled gems have not changed. Skipping upload to s3" 44 | else 45 | echo "Bundled gems have changed. Uploading to s3" 46 | shasum -a 256 Gemfile.lock > bundle.sha256 47 | tar -czf bundle.tar.gz vendor/ 48 | push_s3_file bundle.tar.gz 49 | push_s3_file bundle.sha256 50 | fi 51 | 52 | echo "+++ bundle exec task" 53 | bundle exec $@ 54 | -------------------------------------------------------------------------------- /.expeditor/run_windows_tests.ps1: -------------------------------------------------------------------------------- 1 | # Stop script execution when a non-terminating error occurs 2 | $ErrorActionPreference = "Stop" 3 | # This will run ruby test on windows platform 4 | 5 | Write-Output "--- Bundle install" 6 | 7 | bundle config --local path vendor/bundle 8 | If ($lastexitcode -ne 0) { Exit $lastexitcode } 9 | 10 | bundle install --jobs=7 --retry=3 11 | If ($lastexitcode -ne 0) { Exit $lastexitcode } 12 | 13 | Write-Output "--- Bundle Execute" 14 | 15 | bundle exec rake spec 16 | If ($lastexitcode -ne 0) { Exit $lastexitcode } 17 | -------------------------------------------------------------------------------- /.expeditor/update_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # After a PR merge, Chef Expeditor will bump the PATCH version in the VERSION file. 4 | # It then executes this file to update any other files/components with that new version. 5 | # 6 | 7 | set -evx 8 | 9 | sed -i -r "s/VERSION = \".+\"/VERSION = \"$(cat VERSION)\"/" lib/mixlib/versioning/version.rb 10 | 11 | # Once Expeditor finshes executing this script, it will commit the changes and push 12 | # the commit as a new tag corresponding to the value in the VERSION file. 13 | -------------------------------------------------------------------------------- /.expeditor/verify.pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | expeditor: 3 | defaults: 4 | buildkite: 5 | timeout_in_minutes: 30 6 | 7 | steps: 8 | 9 | - label: run-lint-ruby-3.0 10 | command: 11 | - .expeditor/run_linux_tests.sh rake style 12 | expeditor: 13 | executor: 14 | docker: 15 | image: ruby:3.0 16 | 17 | - label: run-specs-ruby-3.0 18 | command: 19 | - .expeditor/run_linux_tests.sh rake spec 20 | expeditor: 21 | executor: 22 | docker: 23 | image: ruby:3.0 24 | 25 | - label: run-specs-ruby-3.1 26 | command: 27 | - .expeditor/run_linux_tests.sh rake spec 28 | expeditor: 29 | executor: 30 | docker: 31 | image: ruby:3.1 32 | 33 | - label: run-specs-ruby-3.0-windows 34 | command: 35 | - .expeditor/run_windows_tests.ps1 36 | expeditor: 37 | executor: 38 | docker: 39 | host_os: windows 40 | shell: ["powershell", "-Command"] 41 | image: rubydistros/windows-2019:3.0 42 | user: 'NT AUTHORITY\SYSTEM' 43 | 44 | - label: run-specs-ruby-3.1-windows 45 | command: 46 | - .expeditor/run_windows_tests.ps1 47 | expeditor: 48 | executor: 49 | docker: 50 | host_os: windows 51 | shell: ["powershell", "-Command"] 52 | image: rubydistros/windows-2019:3.1 53 | user: 'NT AUTHORITY\SYSTEM' 54 | 55 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Order is important. The last matching pattern has the most precedence. 2 | 3 | * @chef/chef-infra-reviewers @chef/chef-infra-approvers @chef/chef-infra-owners @jaymzh 4 | .expeditor/ @chef/chef-infra-packages 5 | *.md @chef/docs-team 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: � Bug Report 3 | about: If something isn't working as expected �. 4 | labels: "Status: Untriaged, Type: Bug" 5 | --- 6 | 7 | # Version: 8 | 9 | [Version of the project installed] 10 | 11 | # Environment: 12 | 13 | [Details about the environment such as the Operating System, cookbook details, etc...] 14 | 15 | # Scenario: 16 | 17 | [What you are trying to achieve and you can't?] 18 | 19 | # Steps to Reproduce: 20 | 21 | [If you are filing an issue what are the things we need to do in order to repro your problem?] 22 | 23 | # Expected Result: 24 | 25 | [What are you expecting to happen as the consequence of above reproduction steps?] 26 | 27 | # Actual Result: 28 | 29 | [What actually happens after the reproduction steps?] 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/DESIGN_PROPOSAL.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Design Proposal 3 | about: I have a significant change I would like to propose and discuss before starting 4 | labels: "Status: Untriaged, Type: Design Proposal" 5 | --- 6 | 7 | ### When a Change Needs a Design Proposal 8 | 9 | A design proposal should be opened any time a change meets one of the following qualifications: 10 | 11 | - Significantly changes the user experience of a project in a way that impacts users. 12 | - Significantly changes the underlying architecture of the project in a way that impacts other developers. 13 | - Changes the development or testing process of the project such as a change of CI systems or test frameworks. 14 | 15 | ### Why We Use This Process 16 | 17 | - Allows all interested parties (including any community member) to discuss large impact changes to a project. 18 | - Serves as a durable paper trail for discussions regarding project architecture. 19 | - Forces design discussions to occur before PRs are created. 20 | - Reduces PR refactoring and rejected PRs. 21 | 22 | --- 23 | 24 | 25 | 26 | ## Motivation 27 | 28 | 33 | 34 | ## Specification 35 | 36 | 37 | 38 | ## Downstream Impact 39 | 40 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ENHANCEMENT_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Enhancement Request 3 | about: I have a suggestion (and may want to implement it 🙂)! 4 | labels: "Status: Untriaged" 5 | --- 6 | 7 | ### Describe the Enhancement: 8 | 9 | 10 | ### Describe the Need: 11 | 12 | 13 | ### Current Alternative 14 | 15 | 16 | ### Can We Help You Implement This?: 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🤗 Support Question 3 | about: If you have a question 💬, please check out our Slack! 4 | --- 5 | 6 | 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. 7 | 8 | * Chef Community Slack at https://community-slack.chef.io/. 9 | * Chef Mailing List https://discourse.chef.io/ 10 | 11 | Support issues opened here will be closed and redirected to Slack or Discourse. 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "06:00" 8 | timezone: America/Los_Angeles 9 | open-pull-requests-limit: 10 10 | ignore: 11 | - dependency-name: chefstyle 12 | versions: 13 | - "> 0.4.0" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _yardoc 2 | .bundle 3 | .config 4 | .DS_Store 5 | .idea 6 | .rake_tasks~ 7 | .rspec 8 | .ruby-version 9 | .rvmrc 10 | .yardoc 11 | .yardopts 12 | *.gem 13 | *.rbc 14 | *.sw? 15 | bin/ 16 | coverage 17 | doc 18 | Gemfile.local 19 | Gemfile.lock 20 | InstalledFiles 21 | lib/bundler/man 22 | pkg 23 | spec/reports 24 | test/tmp 25 | test/version_tmp 26 | tmp 27 | vendor 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Mixlib::Versioning Changes 2 | 3 | 4 | ## [v1.2.20](https://github.com/chef/mixlib-versioning/tree/v1.2.20) (2025-05-12) 5 | 6 | #### Merged Pull Requests 7 | - add myself to codeowners [#58](https://github.com/chef/mixlib-versioning/pull/58) ([jaymzh](https://github.com/jaymzh)) 8 | 9 | 10 | 11 | ### Changes not yet released to rubygems.org 12 | 13 | #### Merged Pull Requests 14 | - add myself to codeowners [#58](https://github.com/chef/mixlib-versioning/pull/58) ([jaymzh](https://github.com/jaymzh)) 15 | - Migrate from Chefstyle to Cookstyle [#56](https://github.com/chef/mixlib-versioning/pull/56) ([dafyddcrosby](https://github.com/dafyddcrosby)) 16 | - [CI] Drop EOL Rubies [#57](https://github.com/chef/mixlib-versioning/pull/57) ([dafyddcrosby](https://github.com/dafyddcrosby)) 17 | - do not run style during windows tests [#54](https://github.com/chef/mixlib-versioning/pull/54) ([marcparadise](https://github.com/marcparadise)) 18 | - Test on Ruby Versions 3.1 /3.0 [#52](https://github.com/chef/mixlib-versioning/pull/52) ([poorndm](https://github.com/poorndm)) 19 | - Upgrade to GitHub-native Dependabot [#48](https://github.com/chef/mixlib-versioning/pull/48) ([dependabot-preview[bot]](https://github.com/dependabot-preview[bot])) 20 | - Remove legacy encoding comment [#46](https://github.com/chef/mixlib-versioning/pull/46) ([tas50](https://github.com/tas50)) 21 | - Fix minor spelling mistakes [#47](https://github.com/chef/mixlib-versioning/pull/47) ([tas50](https://github.com/tas50)) 22 | 23 | 24 | 25 | ## [v1.2.12](https://github.com/chef/mixlib-versioning/tree/v1.2.12) (2019-12-30) 26 | 27 | #### Merged Pull Requests 28 | - Add a gem version badge to the readme [#38](https://github.com/chef/mixlib-versioning/pull/38) ([tas50](https://github.com/tas50)) 29 | - Add Ruby 2.6 testing in Travis [#39](https://github.com/chef/mixlib-versioning/pull/39) ([tas50](https://github.com/tas50)) 30 | - Support Ruby 2+ in the gemspec [#42](https://github.com/chef/mixlib-versioning/pull/42) ([tas50](https://github.com/tas50)) 31 | - Move PR testing to Buildkite [#44](https://github.com/chef/mixlib-versioning/pull/44) ([tas50](https://github.com/tas50)) 32 | - Substitute require for require_relative [#43](https://github.com/chef/mixlib-versioning/pull/43) ([tas50](https://github.com/tas50)) 33 | 34 | 35 | ## [v1.2.7](https://github.com/chef/mixlib-versioning/tree/v1.2.7) (2018-12-18) 36 | 37 | #### Merged Pull Requests 38 | - More testing / release updates [#34](https://github.com/chef/mixlib-versioning/pull/34) ([tas50](https://github.com/tas50)) 39 | - Update codeowners and add github PR template [#36](https://github.com/chef/mixlib-versioning/pull/36) ([tas50](https://github.com/tas50)) 40 | - Don't ship the dev and test deps in the gem artifact [#37](https://github.com/chef/mixlib-versioning/pull/37) ([tas50](https://github.com/tas50)) 41 | 42 | ## [v1.2.2](https://github.com/chef/mixlib-versioning/tree/v1.2.2) (2017-07-27) 43 | 44 | #### Merged Pull Requests 45 | - Support older rubies [#27](https://github.com/chef/mixlib-versioning/pull/27) ([schisamo](https://github.com/schisamo)) 46 | 47 | ## [v1.2.1](https://github.com/chef/mixlib-versioning/tree/v1.2.1) (2017-07-24) 48 | 49 | #### Merged Pull Requests 50 | - Use Expeditor for version bumping and CHANGELOG generation [#23](https://github.com/chef/mixlib-versioning/pull/23) ([schisamo](https://github.com/schisamo)) 51 | 52 | ## [1.1.0](https://github.com/chef/mixlib-versioning/tree/v1.1.0) (2015-06-15) 53 | 54 | #### New features 55 | 56 | * Allow parse to take a list of formats to choose from 57 | * Update to SemVer 2.0.0 58 | 59 | #### Improvements 60 | 61 | * Add ability to compare directly with strings. 62 | * Lock to RuboCop 0.31.0 (also fix style errors) 63 | 64 | 65 | ## [1.0.0](https://github.com/chef/mixlib-versioning/tree/v1.0.0) (2013-03-29) 66 | 67 | * The initial release. Versioning parsing shouldn't suck. -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Please refer to the Chef Community Code of Conduct at https://www.chef.io/code-of-conduct/ 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please refer to https://github.com/chef/chef/blob/master/CONTRIBUTING.md 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :docs do 6 | gem "yard" 7 | gem "redcarpet" 8 | gem "github-markup" 9 | end 10 | 11 | group :test do 12 | gem "cookstyle", ">= 7.32.8" 13 | gem "rspec", "~> 3.0" 14 | gem "rspec-its" 15 | gem "rake" 16 | end 17 | 18 | group :development do 19 | gem "pry" 20 | gem "pry-byebug" 21 | gem "rb-readline" 22 | end 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mixlib::Versioning 2 | [![Build status](https://badge.buildkite.com/7aa0e56c4ab881afc6025119868c6828dc2f11a907465b9c51.svg?branch=master)](https://buildkite.com/chef-oss/chef-mixlib-versioning-master-verify) 3 | [![Gem Version](https://badge.fury.io/rb/mixlib-versioning.svg)](https://badge.fury.io/rb/mixlib-versioning) 4 | 5 | **Umbrella Project**: [Chef Foundation](https://github.com/chef/chef-oss-practices/blob/master/projects/chef-foundation.md) 6 | 7 | **Project State**: [Active](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md#active) 8 | 9 | **Issues [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days 10 | 11 | **Pull Request [Response Time Maximum](https://github.com/chef/chef-oss-practices/blob/master/repo-management/repo-states.md)**: 14 days 12 | 13 | Versioning is hard! `mixlib-versioning` is a general Ruby library that allows you to parse, compare and manipulate version numbers in multiple formats. Currently the following version string formats are supported: 14 | 15 | ## SemVer 2.0.0 16 | 17 | **Specification:** 18 | 19 | 20 | 21 | **Supported Formats:** 22 | 23 | ```text 24 | MAJOR.MINOR.PATCH 25 | MAJOR.MINOR.PATCH-PRERELEASE 26 | MAJOR.MINOR.PATCH-PRERELEASE+BUILD 27 | ``` 28 | 29 | Not much to say here except: _YUNO USE SEMVER!_ The specification is focused and brief, do yourself a favor and go read it. 30 | 31 | ## Opscode SemVer 32 | 33 | **Supported Formats:** 34 | 35 | ```text 36 | MAJOR.MINOR.PATCH 37 | MAJOR.MINOR.PATCH-alpha.INDEX 38 | MAJOR.MINOR.PATCH-beta.INDEX 39 | MAJOR.MINOR.PATCH-rc.INDEX 40 | MAJOR.MINOR.PATCH-alpha.INDEX+YYYYMMDDHHMMSS 41 | MAJOR.MINOR.PATCH-beta.INDEX+YYYYMMDDHHMMSS 42 | MAJOR.MINOR.PATCH-rc.INDEX+YYYYMMDDHHMMSS 43 | MAJOR.MINOR.PATCH-alpha.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 44 | MAJOR.MINOR.PATCH-beta.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 45 | MAJOR.MINOR.PATCH-rc.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 46 | ``` 47 | 48 | All the fun of regular SemVer with some extra limits around what constitutes a valid pre-release or build version string. 49 | 50 | Valid prerelease version strings use the format: `PRERELEASE_STAGE.INDEX`. Valid prerelease stages include: `alpha`, `beta` and `rc`. 51 | 52 | All of the following are acceptable Opscode SemVer pre-release versions: 53 | 54 | ```text 55 | 11.0.8-alpha.0 56 | 11.0.8-alpha.1 57 | 11.0.8-beta.7 58 | 11.0.8-beta.8 59 | 11.0.8-rc.1 60 | 11.0.8-rc.2 61 | ``` 62 | 63 | Build version strings are limited to timestamps (`YYYYMMDDHHMMSS`), git describe strings (`git.COMMITS_SINCE.SHA1`) or a combination of the two (`YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1`). 64 | 65 | All of the following are acceptable Opscode build versions: 66 | 67 | ```text 68 | 11.0.8+20130308110833 69 | 11.0.8+git.2.g94a1dde 70 | 11.0.8+20130308110833.git.2.94a1dde 71 | ``` 72 | 73 | And as is true with regular SemVer you can mix pre-release and build versions: 74 | 75 | ```text 76 | 11.0.8-rc.1+20130308110833 77 | 11.0.8-alpha.2+20130308110833.git.2.94a1dde 78 | ``` 79 | 80 | ## Rubygems 81 | 82 | **specification:** 83 | 84 | 85 | 86 | 87 | 88 | **Supported Formats:** 89 | 90 | ```text 91 | MAJOR.MINOR.PATCH 92 | MAJOR.MINOR.PATCH.PRERELEASE 93 | ``` 94 | 95 | Rubygems is _almost_ SemVer compliant but it separates the main version from the pre-release version using a "dot". It also does not have the notion of a build version like SemVer. 96 | 97 | Examples of valid Rubygems version strings: 98 | 99 | ```text 100 | 10.1.1 101 | 10.1.1 102 | 10.1.1.alpha.1 103 | 10.1.1.beta.1 104 | 10.1.1.rc.0 105 | ``` 106 | 107 | ## Git Describe 108 | 109 | **Specification:** 110 | 111 | 112 | 113 | **Supported Formats:** 114 | 115 | ```text 116 | MAJOR.MINOR.PATCH-COMMITS_SINCE-gGIT_SHA1 117 | MAJOR.MINOR.PATCH-COMMITS_SINCE-gGIT_SHA1-ITERATION 118 | MAJOR.MINOR.PATCH-PRERELEASE-COMMITS_SINCE-gGIT_SHA1 119 | MAJOR.MINOR.PATCH-PRERELEASE-COMMITS_SINCE-gGIT_SHA1-ITERATION 120 | ``` 121 | 122 | Examples of valid Git Describe version strings: 123 | 124 | ```text 125 | 10.16.2-49-g21353f0-1 126 | 10.16.2.rc.1-49-g21353f0-1 127 | 11.0.0-alpha-10-g642ffed 128 | 11.0.0-alpha.1-1-gcea071e 129 | ``` 130 | 131 | 132 | ## SemVer Partial 133 | 134 | 135 | ```text 136 | MAJOR 137 | MAJOR.MINOR 138 | ``` 139 | 140 | Examples of valid SemVer Partial version strings: 141 | 142 | ```text 143 | 1 144 | 0.1 145 | 1.0 146 | ``` 147 | 148 | ## Installation 149 | 150 | Add this line to your application's Gemfile: 151 | 152 | ``` 153 | gem 'mixlib-versioning' 154 | ``` 155 | 156 | And then execute: 157 | 158 | ``` 159 | $ bundle 160 | ``` 161 | 162 | Or install it yourself as: 163 | 164 | ``` 165 | $ gem install mixlib-versioning 166 | ``` 167 | 168 | ## Usage 169 | 170 | ### Basic Version String Parsing 171 | 172 | ```irb 173 | >> require 'mixlib/versioning' 174 | true 175 | >> v1 = Mixlib::Versioning.parse("11.0.3") 176 | # 177 | >> v1.release? 178 | true 179 | >> v1.prerelease? 180 | false 181 | >> v1.build? 182 | false 183 | >> v1.prerelease_build? 184 | false 185 | >> v2 = Mixlib::Versioning.parse("11.0.0-beta.1") 186 | # 187 | >> v2.release? 188 | false 189 | >> v2.prerelease? 190 | true 191 | >> v2.build? 192 | false 193 | >> v2.prerelease_build? 194 | false 195 | >> v3 = Mixlib::Versioning.parse("11.0.6+20130216075209") 196 | # 197 | >> v3.release? 198 | false 199 | >> v3.prerelease? 200 | false 201 | >> v3.build? 202 | true 203 | >> v3.prerelease_build? 204 | false 205 | >> v4 = Mixlib::Versioning.parse("11.0.8-rc.1+20130302083119") 206 | # 207 | >> v4.release? 208 | false 209 | >> v4.prerelease? 210 | false 211 | >> v4.build? 212 | true 213 | >> v4.prerelease_build? 214 | true 215 | >> v5 = Mixlib::Versioning.parse("10.16.8.alpha.0") 216 | # 217 | >> v5.major 218 | 10 219 | >> v5.minor 220 | 16 221 | >> v5.patch 222 | 8 223 | >> v5.prerelease 224 | "alpha.0" 225 | >> v5.release? 226 | false 227 | >> v5.prerelease? 228 | true 229 | ``` 230 | 231 | ### Version Comparison and Sorting 232 | 233 | ```irb 234 | >> require 'mixlib/versioning' 235 | true 236 | >> v1 = Mixlib::Versioning.parse("11.0.0-beta.1") 237 | # 238 | >> v2 = Mixlib::Versioning.parse("11.0.0-rc.1") 239 | # 240 | >> v3 = Mixlib::Versioning.parse("11.0.0") 241 | # 242 | >> v1 < v2 243 | true 244 | >> v3 < v1 245 | false 246 | >> v1 == v2 247 | false 248 | >> [v3, v1, v2].sort 249 | [ 250 | [0] #, 251 | [1] #, 252 | [2] # 253 | ] 254 | >> [v3, v1, v2].map { |v| v.to_s}.sort 255 | [ 256 | [0] "11.0.0", 257 | [1] "11.0.0-beta.1", 258 | [2] "11.0.0-rc.1" 259 | ] 260 | ``` 261 | 262 | ### Target Version Selection 263 | 264 | Basic usage: 265 | 266 | ```ruby 267 | >> require 'mixlib/versioning' 268 | true 269 | >> all_versions = %w{ 270 | 11.0.0-alpha.1 271 | 11.0.0-alpha.1-1-gcea071e 272 | 11.0.0-alpha.3+20130103213604.git.11.3fe70b5 273 | 11.0.0-alpha.3+20130129075201.git.38.3332a80 274 | 11.0.0-alpha.3+20130130075202.git.38.3332a80 275 | 11.0.0-beta.0+20130131044557 276 | 11.0.0-beta.1+20130201023059.git.5.c9d3320 277 | 11.0.0-beta.2+20130201035911 278 | 11.0.0-beta.2+20130201191308.git.4.9aa4cb2 279 | 11.0.0-rc.1 280 | 11.0.0+20130204053034.git.1.1802643 281 | 11.0.4 282 | 11.0.6-alpha.0+20130208045134.git.2.298c401 283 | 11.0.6-alpha.0+20130214075209.git.11.5d72e1c 284 | 11.0.6-alpha.0+20130215075208.git.11.5d72e1c 285 | 11.0.6-rc.0 286 | 11.0.6 287 | 11.0.6+20130216075209 288 | 11.0.6+20130221075213 289 | 11.0.8-rc.1 290 | 11.0.8-rc.1+20130302083119 291 | 11.0.8-rc.1+20130304083118 292 | 11.0.8-rc.1+20130305083118 293 | 11.0.8-rc.1+20130305195925.git.2.94a1dde 294 | 11.0.8-rc.1+20130306083036.git.2.94a1dde 295 | 11.0.8-rc.1+20130319083111.git.6.dc8613e 296 | };'' 297 | "" 298 | >> Mixlib::Versioning.find_target_version(all_versions, "11.0.6", false, false) 299 | # 300 | >> target = Mixlib::Versioning.find_target_version(all_versions, "11.0.6", false, false) 301 | # 302 | >> target.to_s 303 | "11.0.6" 304 | ``` 305 | 306 | Select latest release version: 307 | 308 | ```irb 309 | >> target = Mixlib::Versioning.find_target_version(all_versions, nil, false, false) 310 | # 311 | >> target.to_s 312 | "11.0.6" 313 | ``` 314 | 315 | Select latest pre-release version: 316 | 317 | ```irb 318 | >> target = Mixlib::Versioning.find_target_version(all_versions, nil, true, false) 319 | # 320 | >> target.to_s 321 | "11.0.8-rc.1" 322 | ``` 323 | 324 | Select the latest release build version: 325 | 326 | ```irb 327 | >> target = Mixlib::Versioning.find_target_version(all_versions, nil, false, true) 328 | # 329 | >> target.to_s 330 | "11.0.6+20130221075213" 331 | ``` 332 | 333 | Select the latest pre-release build version: 334 | 335 | ```irb 336 | >> target = Mixlib::Versioning.find_target_version(all_versions, nil, true, true) 337 | # 338 | >> target.to_s 339 | "11.0.8-rc.1+20130319083111.git.6.dc8613e" 340 | ``` 341 | 342 | ## How to Run the Tests 343 | 344 | To run the unit tests, run 345 | 346 | ``` 347 | rake spec 348 | ``` 349 | 350 | ## Documentation 351 | 352 | All documentation is written using YARD. You can generate a by running: 353 | 354 | ``` 355 | rake docs 356 | ``` 357 | 358 | ## Contributing 359 | 360 | For information on contributing to this project please see our [Contributing Documentation](https://github.com/chef/chef/blob/master/CONTRIBUTING.md) 361 | 362 | ## License & Copyright 363 | 364 | - Copyright:: Copyright (c) 2008-2019 Chef Software, Inc. 365 | - License:: Apache License, Version 2.0 366 | 367 | ```text 368 | Licensed under the Apache License, Version 2.0 (the "License"); 369 | you may not use this file except in compliance with the License. 370 | You may obtain a copy of the License at 371 | 372 | http://www.apache.org/licenses/LICENSE-2.0 373 | 374 | Unless required by applicable law or agreed to in writing, software 375 | distributed under the License is distributed on an "AS IS" BASIS, 376 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 377 | See the License for the specific language governing permissions and 378 | limitations under the License. 379 | ``` 380 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | Bundler::GemHelper.install_tasks 4 | 5 | begin 6 | require "rspec/core/rake_task" 7 | RSpec::Core::RakeTask.new do |t| 8 | t.pattern = "spec/**/*_spec.rb" 9 | end 10 | rescue LoadError 11 | desc "rspec is not installed, this task is disabled" 12 | task :spec do 13 | abort "rspec is not installed. bundle install first to make sure all dependencies are installed." 14 | end 15 | end 16 | 17 | begin 18 | require "cookstyle/chefstyle" 19 | require "rubocop/rake_task" 20 | desc "Run Chefstyle tests" 21 | RuboCop::RakeTask.new(:style) do |task| 22 | task.options += ["--display-cop-names", "--no-color"] 23 | end 24 | rescue LoadError 25 | puts "cookstyle gem is not installed. bundle install first to make sure all dependencies are installed." 26 | end 27 | 28 | begin 29 | require "yard" 30 | YARD::Rake::YardocTask.new(:docs) 31 | rescue LoadError 32 | puts "yard is not available. bundle install first to make sure all dependencies are installed." 33 | end 34 | 35 | task :console do 36 | require "irb" 37 | require "irb/completion" 38 | require "mixlib/versioning" 39 | ARGV.clear 40 | IRB.start 41 | end 42 | 43 | task default: %i{spec style} 44 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.20 -------------------------------------------------------------------------------- /lib/mixlib/versioning.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require_relative "versioning/exceptions" 21 | require_relative "versioning/format" 22 | 23 | module Mixlib 24 | # @author Seth Chisamore () 25 | # @author Christopher Maier () 26 | class Versioning 27 | DEFAULT_FORMATS = [ 28 | Mixlib::Versioning::Format::GitDescribe, 29 | Mixlib::Versioning::Format::OpscodeSemVer, 30 | Mixlib::Versioning::Format::SemVer, 31 | Mixlib::Versioning::Format::Rubygems, 32 | Mixlib::Versioning::Format::PartialSemVer, 33 | ].freeze 34 | 35 | # Create a new {Format} instance given a version string to parse, and an 36 | # optional format type. 37 | # 38 | # @example 39 | # Mixlib::Versioning.parse('11.0.0') 40 | # Mixlib::Versioning.parse('11.0.0', :semver) 41 | # Mixlib::Versioning.parse('11.0.0', 'semver') 42 | # Mixlib::Versioning.parse('11.0.0', Mixlib::Versioning::Format::SemVer) 43 | # 44 | # @param version_string [String] String representation of the version to 45 | # parse 46 | # @param format [String, Symbol, Mixlib::Versioning::Format, Array] Optional 47 | # format type to parse the version string as. If this is excluded all 48 | # version types will be attempted from most specific to most specific 49 | # with a preference for SemVer formats. If it is an array, only version 50 | # types in that list will be considered 51 | # @raise [Mixlib::Versioning::ParseError] if the parse fails. 52 | # @raise [Mixlib::Versioning::UnknownFormatError] if the given format type 53 | # doesn't exist. 54 | # 55 | # @return 56 | # 57 | def self.parse(version_string, format = nil) 58 | if version_string.is_a?(Mixlib::Versioning::Format) 59 | version_string 60 | else 61 | formats = if format 62 | [format].flatten.map { |f| Mixlib::Versioning::Format.for(f) } 63 | else 64 | DEFAULT_FORMATS 65 | end 66 | # Attempt to parse from the most specific formats first. 67 | parsed_version = nil 68 | formats.each do |version| 69 | 70 | break parsed_version = version.new(version_string) 71 | rescue Mixlib::Versioning::ParseError 72 | next 73 | 74 | end 75 | parsed_version 76 | end 77 | end 78 | 79 | # Selects the most recent version from `all_versions` that satisfies the 80 | # filtering constraints provided by `filter_version`, 81 | # `use_prerelease_versions`, and `use_build_versions`. 82 | # 83 | # If `filter_version` specifies a release (e.g. 1.0.0), then the target 84 | # version that is returned will be in the same "release line" (it will have 85 | # the same major, minor, and patch versions), subject to filtering by 86 | # `use_prerelease_versions` and `use_build_versions`. 87 | # 88 | # If `filter_version` specifies a pre-release (e.g., 1.0.0-alpha.1), the 89 | # returned target version will be in the same "pre-release line", and will 90 | # only be subject to further filtering by `use_build_versions`; that is, 91 | # `use_prerelease_versions` is completely ignored. 92 | # 93 | # If `filter_version` specifies a build version (whether it is a 94 | # pre-release or not), no filtering is performed at all, and 95 | # `filter_version` *is* the target version; `use_prerelease_versions` and 96 | # `use_build_versions` are both ignored. 97 | # 98 | # If `filter_version` is `nil`, then only `use_prerelease_versions` and 99 | # `use_build_versions` are used for filtering. 100 | # 101 | # In all cases, the returned {Format} is the most recent one in 102 | # `all_versions` that satisfies the given constraints. 103 | # 104 | # @example 105 | # all = %w{ 11.0.0-beta.1 106 | # 11.0.0-rc.1 107 | # 11.0.0 108 | # 11.0.1 } 109 | # 110 | # Mixlib::Versioning.find_target_version(all, 111 | # '11.0.1', 112 | # true, 113 | # true) 114 | # 115 | # 116 | # @param all_versions [Array] An array 117 | # of {Format} objects. This is the "world" of versions we will be 118 | # filtering to produce the final target version. Any strings in the array 119 | # will automatically be converted into instances of {Format} using 120 | # {Versioning.parse}. 121 | # @param filter_version [String, Mixlib::Versioning::Format] A version that 122 | # is used to perform more fine-grained filtering. If a string is passed, 123 | # {Versioning.parse} will be used to instantiate a version. 124 | # @param use_prerelease_versions [Boolean] If true, keep versions with 125 | # pre-release specifiers. When false, versions in `all_versions` that 126 | # have a pre-release specifier will be filtered out. 127 | # @param use_build_versions [Boolean] If true, keep versions with build 128 | # version specifiers. When false, versions in `all_versions` that have a 129 | # build version specifier will be filtered out. 130 | # 131 | def self.find_target_version(all_versions, 132 | filter_version = nil, 133 | use_prerelease_versions = false, 134 | use_build_versions = false) 135 | 136 | # attempt to parse a `Mixlib::Versioning::Format` instance if we were 137 | # passed a string 138 | unless filter_version.nil? || 139 | filter_version.is_a?(Mixlib::Versioning::Format) 140 | filter_version = Mixlib::Versioning.parse(filter_version) 141 | end 142 | 143 | all_versions.map! do |v| 144 | if v.is_a?(Mixlib::Versioning::Format) 145 | v 146 | else 147 | Mixlib::Versioning.parse(v) 148 | end 149 | end 150 | 151 | if filter_version && filter_version.build 152 | # If we've requested a build (whether for a pre-release or release), 153 | # there's no sense doing any other filtering; just return that version 154 | filter_version 155 | elsif filter_version && filter_version.prerelease 156 | # If we've requested a prerelease version, we only need to see if we 157 | # want a build version or not. If so, keep only the build version for 158 | # that prerelease, and then take the most recent. Otherwise, just 159 | # return the specified prerelease version 160 | if use_build_versions 161 | all_versions.select { |v| v.in_same_prerelease_line?(filter_version) }.max 162 | else 163 | filter_version 164 | end 165 | else 166 | # If we've gotten this far, we're either just interested in 167 | # variations on a specific release, or the latest of all versions 168 | # (depending on various combinations of prerelease and build status) 169 | all_versions.select do |v| 170 | # If we're given a version to filter by, then we're only 171 | # interested in other versions that share the same major, minor, 172 | # and patch versions. 173 | # 174 | # If we weren't given a version to filter by, then we don't 175 | # care, and we'll take everything 176 | in_release_line = if filter_version 177 | filter_version.in_same_release_line?(v) 178 | else 179 | true 180 | end 181 | 182 | in_release_line && if use_prerelease_versions && use_build_versions 183 | v.prerelease_build? 184 | elsif !use_prerelease_versions && 185 | use_build_versions 186 | v.release_build? 187 | elsif use_prerelease_versions && 188 | !use_build_versions 189 | v.prerelease? 190 | elsif !use_prerelease_versions && 191 | !use_build_versions 192 | v.release? 193 | end 194 | end.max # select the most recent version 195 | end # if 196 | end # self.find_target_version 197 | end # Versioning 198 | end # Mixlib 199 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/exceptions.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | module Mixlib 20 | class Versioning 21 | # Base class for all Mixlib::Versioning Errors 22 | class Error < RuntimeError; end 23 | # Exception raised if parsing fails 24 | class ParseError < Error; end 25 | # Exception raised if version format cannot be identified 26 | class UnknownFormatError < Error; end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require_relative "format/git_describe" 20 | require_relative "format/opscode_semver" 21 | require_relative "format/rubygems" 22 | require_relative "format/semver" 23 | require_relative "format/partial_semver" 24 | 25 | module Mixlib 26 | class Versioning 27 | # @author Seth Chisamore () 28 | # 29 | # @!attribute [r] major 30 | # @return [Integer] major identifier 31 | # @!attribute [r] minor 32 | # @return [Integer] minor identifier 33 | # @!attribute [r] patch 34 | # @return [Integer, nil] patch identifier 35 | # @!attribute [r] prerelease 36 | # @return [String, nil] pre-release identifier 37 | # @!attribute [r] build 38 | # @return [String, nil] build identifier 39 | # @!attribute [r] iteration 40 | # @return [String, nil] build iteration 41 | # @!attribute [r] input 42 | # @return [String, nil] original input version string that was parsed 43 | class Format 44 | include Comparable 45 | 46 | # Returns the {Mixlib::Versioning::Format} class that maps to the given 47 | # format type. 48 | # 49 | # @example 50 | # Mixlib::Versioning::Format.for(:semver) 51 | # Mixlib::Versioning::Format.for('semver') 52 | # Mixlib::Versioning::Format.for(Mixlib::Versioning::Format::SemVer) 53 | # 54 | # @param format_type [String, Symbol, Mixlib::Versioning::Format] Name of 55 | # a valid +Mixlib::Versioning::Format+ in Class or snake-case form. 56 | # 57 | # @raise [Mixlib::Versioning::UnknownFormatError] if the given format 58 | # type doesn't exist 59 | # 60 | # @return [Class] the {Mixlib::Versioning::Format} class 61 | # 62 | def self.for(format_type) 63 | if format_type.is_a?(Class) && 64 | format_type.ancestors.include?(Mixlib::Versioning::Format) 65 | format_type 66 | else 67 | case format_type.to_s 68 | when "semver" then Mixlib::Versioning::Format::SemVer 69 | when "opscode_semver" then Mixlib::Versioning::Format::OpscodeSemVer 70 | when "git_describe" then Mixlib::Versioning::Format::GitDescribe 71 | when "rubygems" then Mixlib::Versioning::Format::Rubygems 72 | when "partial_semver" then Mixlib::Versioning::Format::PartialSemVer 73 | else 74 | msg = "'#{format_type}' is not a supported Mixlib::Versioning format" 75 | raise Mixlib::Versioning::UnknownFormatError, msg 76 | end 77 | end 78 | end 79 | 80 | attr_reader :major, :minor, :patch, :prerelease, :build, :iteration, :input 81 | 82 | # @param version_string [String] string representation of the version 83 | def initialize(version_string) 84 | parse(version_string) 85 | @input = version_string 86 | end 87 | 88 | # Parses the version string splitting it into it's component version 89 | # identifiers for easy comparison and sorting of versions. This method 90 | # **MUST** be overridden by all descendants of this class. 91 | # 92 | # @param version_string [String] string representation of the version 93 | # @raise [Mixlib::Versioning::ParseError] raised if parsing fails 94 | def parse(_version_string) 95 | raise Error, "You must override the #parse" 96 | end 97 | 98 | # @return [Boolean] Whether or not this is a release version 99 | def release? 100 | @prerelease.nil? && @build.nil? 101 | end 102 | 103 | # @return [Boolean] Whether or not this is a pre-release version 104 | def prerelease? 105 | !@prerelease.nil? && @build.nil? 106 | end 107 | 108 | # @return [Boolean] Whether or not this is a release build version 109 | def release_build? 110 | @prerelease.nil? && !@build.nil? 111 | end 112 | 113 | # @return [Boolean] Whether or not this is a pre-release build version 114 | def prerelease_build? 115 | !@prerelease.nil? && !@build.nil? 116 | end 117 | 118 | # @return [Boolean] Whether or not this is a build version 119 | def build? 120 | !@build.nil? 121 | end 122 | 123 | # Returns `true` if `other` and this {Format} share the same `major`, 124 | # `minor`, and `patch` values. Pre-release and build specifiers are not 125 | # taken into consideration. 126 | # 127 | # @return [Boolean] 128 | def in_same_release_line?(other) 129 | @major == other.major && 130 | @minor == other.minor && 131 | @patch == other.patch 132 | end 133 | 134 | # Returns `true` if `other` an share the same 135 | # `major`, `minor`, and `patch` values. Pre-release and build specifiers 136 | # are not taken into consideration. 137 | # 138 | # @return [Boolean] 139 | def in_same_prerelease_line?(other) 140 | @major == other.major && 141 | @minor == other.minor && 142 | @patch == other.patch && 143 | @prerelease == other.prerelease 144 | end 145 | 146 | # @return [String] String representation of this {Format} instance 147 | def to_s 148 | @input 149 | end 150 | 151 | # Since the default implementation of `Object#inspect` uses `Object#to_s` 152 | # under the covers (which we override) we need to also override `#inspect` 153 | # to ensure useful debug information. 154 | def inspect 155 | vars = instance_variables.map do |n| 156 | "#{n}=#{instance_variable_get(n).inspect}" 157 | end 158 | format("#<%s:0x%x %s>", self.class, object_id, vars.join(", ")) 159 | end 160 | 161 | # Returns SemVer compliant string representation of this {Format} 162 | # instance. The string returned will take on the form: 163 | # 164 | # ```text 165 | # MAJOR.MINOR.PATCH-PRERELEASE+BUILD 166 | # ``` 167 | # 168 | # @return [String] SemVer compliant string representation of this 169 | # {Format} instance 170 | # @todo create a proper serialization abstraction 171 | def to_semver_string 172 | s = [@major, @minor, @patch].map(&:to_i).join(".") 173 | s += "-#{@prerelease}" if @prerelease 174 | s += "+#{@build}" if @build 175 | s 176 | end 177 | 178 | # Returns Rubygems compliant string representation of this {Format} 179 | # instance. The string returned will take on the form: 180 | # 181 | # ```text 182 | # MAJOR.MINOR.PATCH.PRERELEASE 183 | # ``` 184 | # 185 | # @return [String] Rubygems compliant string representation of this 186 | # {Format} instance 187 | # @todo create a proper serialization abstraction 188 | def to_rubygems_string 189 | s = [@major, @minor, @patch].map(&:to_i).join(".") 190 | s += ".#{@prerelease}" if @prerelease 191 | s 192 | end 193 | 194 | # Compare this version number with the given version number, following 195 | # Semantic Versioning 2.0.0-rc.1 semantics. 196 | # 197 | # @param other [Mixlib::Versioning::Format] 198 | # @return [Integer] -1, 0, or 1 depending on whether the this version is 199 | # less than, equal to, or greater than the other version 200 | def <=>(other) 201 | # Check whether the `other' is a String and if so, then get an 202 | # instance of *this* class (e.g., GitDescribe, OpscodeSemVer, 203 | # SemVer, Rubygems, etc.), so we can compare against it. 204 | other = self.class.new(other) if other.is_a?(String) 205 | 206 | # First, perform comparisons based on major, minor, and patch 207 | # versions. These are always present and always non-nil 208 | maj = @major <=> other.major 209 | return maj unless maj == 0 210 | 211 | min = @minor <=> other.minor 212 | return min unless min == 0 213 | 214 | pat = @patch <=> other.patch 215 | return pat unless pat == 0 216 | 217 | # Next compare pre-release specifiers. A pre-release sorts 218 | # before a release (e.g. 1.0.0-alpha.1 comes before 1.0.0), so 219 | # we need to take nil into account in our comparison. 220 | # 221 | # If both have pre-release specifiers, we need to compare both 222 | # on the basis of each component of the specifiers. 223 | if @prerelease && other.prerelease.nil? 224 | return -1 225 | elsif @prerelease.nil? && other.prerelease 226 | return 1 227 | elsif @prerelease && other.prerelease 228 | pre = compare_dot_components(@prerelease, other.prerelease) 229 | return pre unless pre == 0 230 | end 231 | 232 | # Build specifiers are compared like pre-release specifiers, 233 | # except that builds sort *after* everything else 234 | # (e.g. 1.0.0+build.123 comes after 1.0.0, and 235 | # 1.0.0-alpha.1+build.123 comes after 1.0.0-alpha.1) 236 | if @build.nil? && other.build 237 | return -1 238 | elsif @build && other.build.nil? 239 | return 1 240 | elsif @build && other.build 241 | build_ver = compare_dot_components(@build, other.build) 242 | return build_ver unless build_ver == 0 243 | end 244 | 245 | # Some older version formats improperly include a package iteration in 246 | # the version string. This is different than a build specifier and 247 | # valid release versions may include an iteration. We'll transparently 248 | # handle this case and compare iterations if it was parsed by the 249 | # implementation class. 250 | if @iteration.nil? && other.iteration 251 | return -1 252 | elsif @iteration && other.iteration.nil? 253 | return 1 254 | elsif @iteration && other.iteration 255 | return @iteration <=> other.iteration 256 | end 257 | 258 | # If we get down here, they're both equal 259 | 0 260 | end 261 | 262 | # @param other [Mixlib::Versioning::Format] 263 | # @return [Boolean] returns true if the versions are equal, false 264 | # otherwise. 265 | def eql?(other) 266 | @major == other.major && 267 | @minor == other.minor && 268 | @patch == other.patch && 269 | @prerelease == other.prerelease && 270 | @build == other.build 271 | end 272 | 273 | def hash 274 | [@major, @minor, @patch, @prerelease, @build].compact.join(".").hash 275 | end 276 | 277 | ######################################################################### 278 | 279 | private 280 | 281 | # If a String `n` can be parsed as an Integer do so; otherwise, do 282 | # nothing. 283 | # 284 | # @param n [String, nil] 285 | # @return [Integer] the parsed {Integer} 286 | def maybe_int(n) 287 | Integer(n) 288 | rescue 289 | n 290 | end 291 | 292 | # Compares prerelease and build version component strings 293 | # according to SemVer 2.0.0-rc.1 semantics. 294 | # 295 | # Returns -1, 0, or 1, just like the spaceship operator (`<=>`), 296 | # and is used in the implementation of `<=>` for this class. 297 | # 298 | # Pre-release and build specifiers are dot-separated strings. 299 | # Numeric components are sorted numerically; otherwise, sorting is 300 | # standard ASCII order. Numerical components have a lower 301 | # precedence than string components. 302 | # 303 | # See http://www.semver.org for more. 304 | # 305 | # Both `a_item` and `b_item` should be Strings; `nil` is not a 306 | # valid input. 307 | def compare_dot_components(a_item, b_item) 308 | a_components = a_item.split(".") 309 | b_components = b_item.split(".") 310 | 311 | max_length = [a_components.length, b_components.length].max 312 | 313 | (0..(max_length - 1)).each do |i| 314 | # Convert the ith component into a number if possible 315 | a = maybe_int(a_components[i]) 316 | b = maybe_int(b_components[i]) 317 | 318 | # Since the components may be of differing lengths, the 319 | # shorter one will yield +nil+ at some point as we iterate. 320 | if a.nil? && !b.nil? 321 | # a_item was shorter 322 | return -1 323 | elsif !a.nil? && b.nil? 324 | # b_item was shorter 325 | return 1 326 | end 327 | 328 | # Now we need to compare appropriately based on type. 329 | # 330 | # Numbers have lower precedence than strings; therefore, if 331 | # the components are of different types (String vs. Integer), 332 | # we just return -1 for the numeric one and we're done. 333 | # 334 | # If both are the same type (Integer vs. Integer, or String 335 | # vs. String), we can just use the native comparison. 336 | # 337 | if a.is_a?(Integer) && b.is_a?(String) 338 | # a_item was "smaller" 339 | return -1 340 | elsif a.is_a?(String) && b.is_a?(Integer) 341 | # b_item was "smaller" 342 | return 1 343 | else 344 | comp = a <=> b 345 | return comp unless comp == 0 346 | end 347 | end # each 348 | 349 | # We've compared all components of both strings; if we've gotten 350 | # down here, they're totally the same 351 | 0 352 | end 353 | end # Format 354 | end # Versioning 355 | end # Mixlib 356 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format/git_describe.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | module Mixlib 21 | class Versioning 22 | class Format 23 | # Handles version strings based on 24 | # {https://www.kernel.org/pub/software/scm/git/docs/git-describe.html git describe} 25 | # output. 26 | # 27 | # SUPPORTED FORMATS 28 | # ----------------- 29 | # ```text 30 | # MAJOR.MINOR.PATCH-COMMITS_SINCE-gGIT_SHA1 31 | # MAJOR.MINOR.PATCH.PRERELEASE-COMMITS_SINCE-gGIT_SHA1 32 | # MAJOR.MINOR.PATCH-PRERELEASE-COMMITS_SINCE-gGIT_SHA1-ITERATION 33 | # ``` 34 | # 35 | # EXAMPLES 36 | # -------- 37 | # ```text 38 | # 10.16.2-49-g21353f0-1 39 | # 10.16.2.rc.1-49-g21353f0-1 40 | # 11.0.0-alpha-10-g642ffed 41 | # 11.0.0-alpha.1-1-gcea071e 42 | # ``` 43 | # 44 | # @author Seth Chisamore () 45 | # @author Christopher Maier () 46 | class GitDescribe < Format 47 | GIT_DESCRIBE_REGEX = /^(\d+)\.(\d+)\.(\d+)(?:\-|\.)?(.+)?\-(\d+)\-g([a-f0-9]{7,40})(?:\-)?(\d+)?$/.freeze 48 | 49 | attr_reader :commits_since, :commit_sha 50 | 51 | # @see Format#parse 52 | def parse(version_string) 53 | match = version_string.match(GIT_DESCRIBE_REGEX) rescue nil 54 | 55 | unless match 56 | raise Mixlib::Versioning::ParseError, "'#{version_string}' is not a valid #{self.class} version string!" 57 | end 58 | 59 | @major, @minor, @patch, @prerelease, @commits_since, @commit_sha, @iteration = match[1..7] 60 | @major, @minor, @patch, @commits_since, @iteration = [@major, @minor, @patch, @commits_since, @iteration].map(&:to_i) 61 | 62 | # Our comparison logic is built around SemVer semantics, so 63 | # we'll store our internal information in that format 64 | @build = "#{@commits_since}.g#{@commit_sha}.#{@iteration}" 65 | end 66 | end # class GitDescribe 67 | end # class Format 68 | end # module Versioning 69 | end # module Mixlib 70 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format/opscode_semver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require_relative "semver" 21 | 22 | module Mixlib 23 | class Versioning 24 | class Format 25 | # Defines the format of the semantic version scheme used for Opscode 26 | # projects. They are SemVer-2.0.0-rc.1 compliant, but we further 27 | # constrain the allowable strings for prerelease and build 28 | # signifiers for our own internal standards. 29 | # 30 | # SUPPORTED FORMATS 31 | # ----------------- 32 | # ```text 33 | # MAJOR.MINOR.PATCH 34 | # MAJOR.MINOR.PATCH-alpha.INDEX 35 | # MAJOR.MINOR.PATCH-beta.INDEX 36 | # MAJOR.MINOR.PATCH-rc.INDEX 37 | # MAJOR.MINOR.PATCH-alpha.INDEX+YYYYMMDDHHMMSS 38 | # MAJOR.MINOR.PATCH-beta.INDEX+YYYYMMDDHHMMSS 39 | # MAJOR.MINOR.PATCH-rc.INDEX+YYYYMMDDHHMMSS 40 | # MAJOR.MINOR.PATCH-alpha.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 41 | # MAJOR.MINOR.PATCH-beta.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 42 | # MAJOR.MINOR.PATCH-rc.INDEX+YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1 43 | # ``` 44 | # 45 | # EXAMPLES 46 | # -------- 47 | # ```text 48 | # 11.0.0 49 | # 11.0.0-alpha.1 50 | # 11.0.0-alpha1+20121218164140 51 | # 11.0.0-alpha1+20121218164140.git.207.694b062 52 | # ``` 53 | # 54 | # @author Seth Chisamore () 55 | # @author Christopher Maier () 56 | class OpscodeSemVer < SemVer 57 | # The pattern is: `YYYYMMDDHHMMSS.git.COMMITS_SINCE.SHA1` 58 | OPSCODE_BUILD_REGEX = /^\d{14}(\.git\.\d+\.[a-f0-9]{7})?$/.freeze 59 | 60 | # Allows the following: 61 | # 62 | # ```text 63 | # alpha, alpha.0, alpha.1, alpha.2, etc. 64 | # beta, beta.0, beta.1, beta.2, etc. 65 | # rc, rc.0, rc.1, rc.2, etc. 66 | # ``` 67 | # 68 | OPSCODE_PRERELEASE_REGEX = /^(alpha|beta|rc)(\.\d+)?$/.freeze 69 | 70 | # @see SemVer#parse 71 | def parse(version_string) 72 | super(version_string) 73 | 74 | raise Mixlib::Versioning::ParseError, "'#{@prerelease}' is not a valid Opscode pre-release signifier!" unless @prerelease.nil? || @prerelease.match(OPSCODE_PRERELEASE_REGEX) 75 | raise Mixlib::Versioning::ParseError, "'#{@build}' is not a valid Opscode build signifier!" unless @build.nil? || @build.match(OPSCODE_BUILD_REGEX) 76 | end 77 | end # class OpscodeSemVer 78 | end # class Format 79 | end # module Versioning 80 | end # module Mixlib 81 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format/partial_semver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Author:: Ryan Hass () 5 | # Copyright:: Copyright (c) 2017-2018 Chef Software Inc. 6 | # License:: Apache License, Version 2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | module Mixlib 22 | class Versioning 23 | class Format 24 | # Handles partial version strings. 25 | # ----------------- 26 | # ```text 27 | # MAJOR 28 | # MAJOR.MINOR 29 | # ``` 30 | # 31 | # EXAMPLES 32 | # -------- 33 | # ```text 34 | # 11 35 | # 11.0 36 | # ``` 37 | # 38 | # @author Seth Chisamore () 39 | # @author Christopher Maier () 40 | # @author Ryan Hass () 41 | class PartialSemVer < Format 42 | # http://rubular.com/r/NmRSN8vCie 43 | PARTIAL_REGEX = /^(\d+)\.?(?:(\d*))$/.freeze 44 | # @see Format#parse 45 | def parse(version_string) 46 | match = version_string.match(PARTIAL_REGEX) rescue nil 47 | 48 | unless match 49 | raise Mixlib::Versioning::ParseError, "'#{version_string}' is not a valid #{self.class} version string!" 50 | end 51 | 52 | @major, @minor = match[1..2] 53 | @major, @minor, @patch = [@major, @minor, @patch].map(&:to_i) 54 | 55 | # Partial versions do not contain these values, so we just set them to nil. 56 | @prerelease = nil 57 | @build = nil 58 | end 59 | end # class Partial 60 | end # class Format 61 | end # module Versioning 62 | end # module Mixlib 63 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format/rubygems.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | module Mixlib 21 | class Versioning 22 | class Format 23 | # Handles version strings based on {http://guides.rubygems.org/patterns/} 24 | # 25 | # SUPPORTED FORMATS 26 | # ----------------- 27 | # ```text 28 | # MAJOR.MINOR.PATCH.PRERELEASE 29 | # MAJOR.MINOR.PATCH.PRERELEASE-ITERATION 30 | # ``` 31 | # 32 | # EXAMPLES 33 | # -------- 34 | # ```text 35 | # 10.1.1 36 | # 10.1.1.alpha.1 37 | # 10.1.1.beta.1 38 | # 10.1.1.rc.0 39 | # 10.16.2 40 | # ``` 41 | # 42 | # @author Seth Chisamore () 43 | # @author Christopher Maier () 44 | class Rubygems < Format 45 | RUBYGEMS_REGEX = /^(\d+)\.(\d+)\.(\d+)(?:\.([[:alnum:]]+(?:\.[[:alnum:]]+)?))?(?:\-(\d+))?$/.freeze 46 | 47 | # @see Format#parse 48 | def parse(version_string) 49 | match = version_string.match(RUBYGEMS_REGEX) rescue nil 50 | 51 | unless match 52 | raise Mixlib::Versioning::ParseError, "'#{version_string}' is not a valid #{self.class} version string!" 53 | end 54 | 55 | @major, @minor, @patch, @prerelease, @iteration = match[1..5] 56 | @major, @minor, @patch = [@major, @minor, @patch].map(&:to_i) 57 | 58 | # Do not convert @prerelease or @iteration to an integer; 59 | # sorting logic will handle the conversion. 60 | @iteration = if @iteration.nil? || @iteration.empty? 61 | nil 62 | else 63 | @iteration.to_i 64 | end 65 | @prerelease = nil if @prerelease.nil? || @prerelease.empty? 66 | end 67 | end # class Rubygems 68 | end # class Format 69 | end # module Versioning 70 | end # module Mixlib 71 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/format/semver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Christopher Maier () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | module Mixlib 21 | class Versioning 22 | class Format 23 | # Handles version strings based on {http://semver.org/ SemVer 2.0.0-rc.1}. 24 | # 25 | # SUPPORTED FORMATS 26 | # ----------------- 27 | # ```text 28 | # MAJOR.MINOR.PATCH 29 | # MAJOR.MINOR.PATCH-PRERELEASE 30 | # MAJOR.MINOR.PATCH-PRERELEASE+BUILD 31 | # ``` 32 | # 33 | # EXAMPLES 34 | # -------- 35 | # ```text 36 | # 11.0.0 37 | # 11.0.0-alpha.1 38 | # 11.0.0-alpha1+20121218164140 39 | # 11.0.0-alpha1+20121218164140.git.207.694b062 40 | # ``` 41 | # 42 | # @author Seth Chisamore () 43 | # @author Christopher Maier () 44 | class SemVer < Format 45 | SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][a-zA-Z0-9-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][a-zA-Z0-9-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.freeze 46 | 47 | # @see Format#parse 48 | def parse(version_string) 49 | match = version_string.match(SEMVER_REGEX) rescue nil 50 | 51 | unless match 52 | raise Mixlib::Versioning::ParseError, "'#{version_string}' is not a valid #{self.class} version string!" 53 | end 54 | 55 | @major, @minor, @patch, @prerelease, @build = match[1..5] 56 | @major, @minor, @patch = [@major, @minor, @patch].map(&:to_i) 57 | 58 | @prerelease = nil if @prerelease.nil? || @prerelease.empty? 59 | @build = nil if @build.nil? || @build.empty? 60 | end 61 | end # class SemVer 62 | end # class Format 63 | end # module Versioning 64 | end # module Mixlib 65 | -------------------------------------------------------------------------------- /lib/mixlib/versioning/version.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | module Mixlib 20 | class Versioning 21 | VERSION = "1.2.20".freeze 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /mixlib-versioning.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path("lib", __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require "mixlib/versioning/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "mixlib-versioning" 7 | spec.version = Mixlib::Versioning::VERSION 8 | spec.authors = ["Seth Chisamore", "Christopher Maier"] 9 | spec.description = "General purpose Ruby library that allows you to parse, compare and manipulate version strings in multiple formats." 10 | spec.summary = spec.description 11 | spec.email = "info@chef.io" 12 | spec.homepage = "https://github.com/chef/mixlib-versioning" 13 | spec.license = "Apache-2.0" 14 | 15 | spec.files = %w{LICENSE} + Dir.glob("lib/**/*") 16 | spec.require_paths = ["lib"] 17 | 18 | # we support EOL ruby because we use this in chef_client_updater cookbook with very old Chef / Ruby releases 19 | spec.required_ruby_version = ">= 2.0" 20 | end 21 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format/git_describe_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "spec_helper" 20 | 21 | describe Mixlib::Versioning::Format::GitDescribe do 22 | subject { described_class.new(version_string) } 23 | 24 | it_has_behavior "parses valid version strings", { 25 | "0.10.8-231-g59d6185" => { 26 | major: 0, 27 | minor: 10, 28 | patch: 8, 29 | prerelease: nil, 30 | build: "231.g59d6185.0", 31 | release?: false, 32 | prerelease?: false, 33 | build?: true, 34 | release_build?: true, 35 | prerelease_build?: false, 36 | commits_since: 231, 37 | commit_sha: "59d6185", 38 | iteration: 0, 39 | }, 40 | "10.16.2-49-g21353f0-1" => { 41 | major: 10, 42 | minor: 16, 43 | patch: 2, 44 | prerelease: nil, 45 | build: "49.g21353f0.1", 46 | release?: false, 47 | prerelease?: false, 48 | build?: true, 49 | release_build?: true, 50 | prerelease_build?: false, 51 | commits_since: 49, 52 | commit_sha: "21353f0", 53 | iteration: 1, 54 | }, 55 | "10.16.2.rc.1-49-g21353f0-1" => { 56 | major: 10, 57 | minor: 16, 58 | patch: 2, 59 | prerelease: "rc.1", 60 | build: "49.g21353f0.1", 61 | release?: false, 62 | prerelease?: false, 63 | build?: true, 64 | release_build?: false, 65 | prerelease_build?: true, 66 | commits_since: 49, 67 | commit_sha: "21353f0", 68 | iteration: 1, 69 | }, 70 | "10.16.2-alpha-49-g21353f0-1" => { 71 | major: 10, 72 | minor: 16, 73 | patch: 2, 74 | prerelease: "alpha", 75 | build: "49.g21353f0.1", 76 | release?: false, 77 | prerelease?: false, 78 | build?: true, 79 | release_build?: false, 80 | prerelease_build?: true, 81 | commits_since: 49, 82 | commit_sha: "21353f0", 83 | iteration: 1, 84 | }, 85 | "10.16.2-alpha-49-g21353f0" => { 86 | major: 10, 87 | minor: 16, 88 | patch: 2, 89 | prerelease: "alpha", 90 | build: "49.g21353f0.0", 91 | release?: false, 92 | prerelease?: false, 93 | build?: true, 94 | release_build?: false, 95 | prerelease_build?: true, 96 | commits_since: 49, 97 | commit_sha: "21353f0", 98 | iteration: 0, 99 | }, 100 | } 101 | 102 | it_has_behavior "rejects invalid version strings", { 103 | "1.0.0" => "no git describe data", 104 | "1.0.0-alpha.1" => "no git describe data", 105 | "1.0.0-alpha.1+build.deadbeef" => "no git describe data", 106 | "1.0.0-123-gfd0e3a65282cb5f6df3bab6a53f4fcb722340d499-1" => "too many SHA1 characters", 107 | "1.0.0-123-gdeadbe-1" => "too few SHA1 characters", 108 | "1.0.0-123-gNOTHEX1-1" => "illegal SHA1 characters", 109 | "1.0.0-123-g1234567-alpha" => "non-numeric iteration", 110 | "1.0.0-alpha-poop-g1234567-1" => "non-numeric 'commits_since'", 111 | "1.0.0-g1234567-1" => "missing 'commits_since'", 112 | "1.0.0-123-1" => "missing SHA1", 113 | } 114 | 115 | version_strings = %w{ 116 | 9.0.1-1-gdeadbee-1 117 | 9.1.2-2-g1234567-1 118 | 10.0.0-1-gabcdef3-1 119 | 10.5.7-2-g21353f0-1 120 | 10.20.2-2-gbbbbbbb-1 121 | 10.20.2-3-gaaaaaaa-1 122 | 9.0.1-2-gdeadbe1-1 123 | 9.0.1-2-gdeadbe1-2 124 | 9.0.1-2-gdeadbe2-1 125 | 9.1.1-2-g1234567-1 126 | } 127 | 128 | it_has_behavior "serializable", version_strings 129 | 130 | it_has_behavior "sortable" do 131 | let(:unsorted_version_strings) { version_strings } 132 | let(:sorted_version_strings) do 133 | %w{ 134 | 9.0.1-1-gdeadbee-1 135 | 9.0.1-2-gdeadbe1-1 136 | 9.0.1-2-gdeadbe1-2 137 | 9.0.1-2-gdeadbe2-1 138 | 9.1.1-2-g1234567-1 139 | 9.1.2-2-g1234567-1 140 | 10.0.0-1-gabcdef3-1 141 | 10.5.7-2-g21353f0-1 142 | 10.20.2-2-gbbbbbbb-1 143 | 10.20.2-3-gaaaaaaa-1 144 | } 145 | end 146 | let(:min) { "9.0.1-1-gdeadbee-1" } 147 | let(:max) { "10.20.2-3-gaaaaaaa-1" } 148 | end # it_has_behavior 149 | 150 | # The +GitDescribe+ format only produces release build versions. 151 | it_has_behavior "filterable" do 152 | let(:unsorted_version_strings) { version_strings } 153 | let(:build_versions) do 154 | %w{ 155 | 9.0.1-1-gdeadbee-1 156 | 9.1.2-2-g1234567-1 157 | 10.0.0-1-gabcdef3-1 158 | 10.5.7-2-g21353f0-1 159 | 10.20.2-2-gbbbbbbb-1 160 | 10.20.2-3-gaaaaaaa-1 161 | 9.0.1-2-gdeadbe1-1 162 | 9.0.1-2-gdeadbe1-2 163 | 9.0.1-2-gdeadbe2-1 164 | 9.1.1-2-g1234567-1 165 | } 166 | end 167 | let(:release_build_versions) do 168 | %w{ 169 | 9.0.1-1-gdeadbee-1 170 | 9.1.2-2-g1234567-1 171 | 10.0.0-1-gabcdef3-1 172 | 10.5.7-2-g21353f0-1 173 | 10.20.2-2-gbbbbbbb-1 174 | 10.20.2-3-gaaaaaaa-1 175 | 9.0.1-2-gdeadbe1-1 176 | 9.0.1-2-gdeadbe1-2 177 | 9.0.1-2-gdeadbe2-1 178 | 9.1.1-2-g1234567-1 179 | } 180 | end 181 | end # it_has_behavior 182 | 183 | it_has_behavior "comparable", [ 184 | "9.0.1-1-gdeadbee-1", "9.0.1-2-gdeadbe1-1", 185 | "9.0.1-2-gdeadbe1-2", "9.0.1-2-gdeadbe2-1", 186 | "9.1.1-2-g1234567-1", "9.1.2-2-g1234567-1", 187 | "10.0.0-1-gabcdef3-1", "10.5.7-2-g21353f0-1", 188 | "10.20.2-2-gbbbbbbb-1", "10.20.2-3-gaaaaaaa-1" 189 | ] 190 | end # describe 191 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format/opscode_semver_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "spec_helper" 20 | 21 | describe Mixlib::Versioning::Format::OpscodeSemVer do 22 | subject { described_class.new(version_string) } 23 | 24 | it_should_behave_like Mixlib::Versioning::Format::SemVer 25 | 26 | it_has_behavior "rejects invalid version strings", { 27 | "1.0.0-poop.0" => "non-valid pre-release type", 28 | "1.0.0+2010AA08010101" => "a malformed timestamp", 29 | "1.0.0+cvs.33.e0f985a" => "a malformed git describe: no git string", 30 | "1.0.0+git.AA.e0f985a" => "a malformed git describe: non-numeric COMMITS_SINCE", 31 | "1.0.0+git.33.z0f985a" => "a malformed git describe: invalid SHA1", 32 | "11.0.08-rc.1+20130308110833" => "leading zero invalid", 33 | "01.0.8-alpha.2+20130308110833.git.2.94a1dde" => "leading zero invalid", 34 | "11.02.8-rc.1+20130308110833" => "leading zero invalid", 35 | "0008.1.4" => "leading zero invalid", 36 | "11.00000000002.8-rc.1+20130308110833" => "leading zero invalid", 37 | "4.67.00012+build.20131219" => "leading zero invalid", 38 | "3.6.7-rc.007" => "leading zero invalid", 39 | } 40 | 41 | it_has_behavior "serializable", [ 42 | "1.0.0", 43 | "1.0.0-alpha.1", 44 | "1.0.0-alpha.1+20130308110833", 45 | "1.0.0+20130308110833.git.2.94a1dde", 46 | ] 47 | 48 | it_has_behavior "sortable" do 49 | let(:unsorted_version_strings) do 50 | %w{ 51 | 1.0.0-beta.2 52 | 1.0.0-alpha 53 | 1.0.0-rc.1+20130309074433 54 | 1.0.0 55 | 1.0.0-beta.11 56 | 1.0.0+20121009074433 57 | 1.0.0-rc.1 58 | 1.0.0-alpha.1 59 | 1.3.7+20131009104433.git.2.94a1dde 60 | 1.3.7+20131009104433 61 | 1.3.7+20131009074433 62 | } 63 | end 64 | let(:sorted_version_strings) do 65 | %w{ 66 | 1.0.0-alpha 67 | 1.0.0-alpha.1 68 | 1.0.0-beta.2 69 | 1.0.0-beta.11 70 | 1.0.0-rc.1 71 | 1.0.0-rc.1+20130309074433 72 | 1.0.0 73 | 1.0.0+20121009074433 74 | 1.3.7+20131009074433 75 | 1.3.7+20131009104433 76 | 1.3.7+20131009104433.git.2.94a1dde 77 | } 78 | end 79 | let(:min) { "1.0.0-alpha" } 80 | let(:max) { "1.3.7+20131009104433.git.2.94a1dde" } 81 | end # it_has_behavior 82 | 83 | it_has_behavior "filterable" do 84 | let(:unsorted_version_strings) do 85 | %w{ 86 | 1.0.0-beta.2 87 | 1.0.0-alpha 88 | 1.0.0-rc.1+20130309074433 89 | 1.0.0 90 | 1.0.0-beta.11 91 | 1.0.0+20121009074433 92 | 1.0.0-rc.1 93 | 1.0.0-alpha.1 94 | 1.3.7+20131009104433.git.2.94a1dde 95 | 1.3.7+20131009104433 96 | 1.3.7+20131009074433 97 | } 98 | end 99 | let(:release_versions) { %w{1.0.0} } 100 | let(:prerelease_versions) do 101 | %w{ 102 | 1.0.0-beta.2 103 | 1.0.0-alpha 104 | 1.0.0-beta.11 105 | 1.0.0-rc.1 106 | 1.0.0-alpha.1 107 | } 108 | end 109 | let(:build_versions) do 110 | %w{ 111 | 1.0.0-rc.1+20130309074433 112 | 1.0.0+20121009074433 113 | 1.3.7+20131009104433.git.2.94a1dde 114 | 1.3.7+20131009104433 115 | 1.3.7+20131009074433 116 | } 117 | end 118 | let(:release_build_versions) do 119 | %w{ 120 | 1.0.0+20121009074433 121 | 1.3.7+20131009104433.git.2.94a1dde 122 | 1.3.7+20131009104433 123 | 1.3.7+20131009074433 124 | } 125 | end 126 | let(:prerelease_build_versions) do 127 | %w{ 128 | 1.0.0-rc.1+20130309074433 } 129 | end 130 | end # it_has_behavior 131 | 132 | it_has_behavior "comparable", [ 133 | "0.1.0", "0.2.0", 134 | "1.0.0-alpha.1", "1.0.0", 135 | "1.2.3", "1.2.3+20121009074433", 136 | "2.0.0-beta.1", "2.0.0+20131009104433.git.2.94a1dde" 137 | ] 138 | end # describe 139 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format/partial_semver_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Ryan Hass () 4 | # Copyright:: Copyright (c) 2017-2018 Chef Software Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "spec_helper" 21 | 22 | describe Mixlib::Versioning::Format::PartialSemVer do 23 | subject { described_class.new(version_string) } 24 | 25 | it_has_behavior "serializable", [ 26 | "1", 27 | "12", 28 | "1.2", 29 | "1.23", 30 | "12.3", 31 | "12.34", 32 | ] 33 | 34 | it_has_behavior "sortable" do 35 | let(:unsorted_version_strings) do 36 | %w{ 37 | 2 38 | 1.1 39 | 1 40 | 1.0 41 | 2.0 42 | 1.2 43 | } 44 | end 45 | let(:sorted_version_strings) do 46 | %w{ 47 | 1 48 | 1.0 49 | 1.1 50 | 1.2 51 | 2 52 | 2.0 53 | } 54 | end 55 | let(:min) { "1" } 56 | let(:max) { "2.0" } 57 | end # it_has_behavior 58 | 59 | it_has_behavior "filterable" do 60 | let(:unsorted_version_strings) do 61 | %w{ 62 | 2 63 | 1.1 64 | 1 65 | 1.0 66 | 2.0 67 | 1.2 68 | 12.0 69 | 12 70 | } 71 | end 72 | let(:release_versions) do 73 | %w{ 74 | 2.0 75 | 1.1 76 | 1.0 77 | 1.0 78 | 2.0 79 | 1.2 80 | 12.0 81 | 12.0 82 | } 83 | end 84 | end # it_has_behavior 85 | 86 | it_has_behavior "comparable", [ 87 | "1", "2", 88 | "1", "1.1", 89 | "1.1", "2", 90 | "1.1", "1.2" 91 | ] 92 | 93 | it_has_behavior "comparable_types", [ 94 | "0.1", { value: "1.0.0.pre.1", class: Mixlib::Versioning::Format::Rubygems }, 95 | "0.1", { value: "1.0.0-rc.1", class: Mixlib::Versioning::Format::OpscodeSemVer }, 96 | "1", { value: "1.0.1", class: Mixlib::Versioning::Format::Rubygems }, 97 | "1", { value: "1.0.1", class: Mixlib::Versioning::Format::SemVer }, 98 | "1", { value: "1.0.1", class: Mixlib::Versioning::Format::OpscodeSemVer }, 99 | "1", { value: "1.0.0-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe }, 100 | "1", { value: "2.0.0", class: Mixlib::Versioning::Format::Rubygems }, 101 | "1", { value: "2.0.0", class: Mixlib::Versioning::Format::SemVer }, 102 | "1", { value: "2.0.0-rc.1", class: Mixlib::Versioning::Format::OpscodeSemVer }, 103 | "1", { value: "2.0.0-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe }, 104 | "1", { value: "1.1.0", class: Mixlib::Versioning::Format::Rubygems }, 105 | "1", { value: "1.1.0", class: Mixlib::Versioning::Format::SemVer }, 106 | "1", { value: "1.1.0", class: Mixlib::Versioning::Format::OpscodeSemVer }, 107 | "1", { value: "1.1.0-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe }, 108 | "1.1", { value: "1.1.1", class: Mixlib::Versioning::Format::Rubygems }, 109 | "1.1", { value: "1.1.1", class: Mixlib::Versioning::Format::SemVer }, 110 | "1.1", { value: "1.1.1", class: Mixlib::Versioning::Format::OpscodeSemVer }, 111 | "1.1", { value: "1.1.1-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe }, 112 | "1.1", { value: "2.0.0", class: Mixlib::Versioning::Format::Rubygems }, 113 | "1.1", { value: "2.0.0", class: Mixlib::Versioning::Format::SemVer }, 114 | "1.1", { value: "2.0.0", class: Mixlib::Versioning::Format::OpscodeSemVer }, 115 | "1.1", { value: "2.0.0-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe }, 116 | "1.1", { value: "1.2.0", class: Mixlib::Versioning::Format::Rubygems }, 117 | "1.1", { value: "1.2.0", class: Mixlib::Versioning::Format::SemVer }, 118 | "1.1", { value: "1.2.0", class: Mixlib::Versioning::Format::OpscodeSemVer }, 119 | "1.1", { value: "1.2.0-1-gdeadbee-1", class: Mixlib::Versioning::Format::GitDescribe } 120 | ] 121 | end # describe 122 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format/rubygems_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "spec_helper" 20 | 21 | describe Mixlib::Versioning::Format::Rubygems do 22 | subject { described_class.new(version_string) } 23 | 24 | it_should_behave_like "Basic SemVer" 25 | 26 | it_has_behavior "parses valid version strings", { 27 | "10.1.1" => { 28 | major: 10, 29 | minor: 1, 30 | patch: 1, 31 | prerelease: nil, 32 | build: nil, 33 | release?: true, 34 | prerelease?: false, 35 | build?: false, 36 | release_build?: false, 37 | prerelease_build?: false, 38 | iteration: nil, 39 | }, 40 | "10.1.1.alpha.1" => { 41 | major: 10, 42 | minor: 1, 43 | patch: 1, 44 | prerelease: "alpha.1", 45 | build: nil, 46 | release?: false, 47 | prerelease?: true, 48 | build?: false, 49 | release_build?: false, 50 | prerelease_build?: false, 51 | iteration: nil, 52 | }, 53 | "11.0.8.rc.3" => { 54 | major: 11, 55 | minor: 0, 56 | patch: 8, 57 | prerelease: "rc.3", 58 | build: nil, 59 | release?: false, 60 | prerelease?: true, 61 | build?: false, 62 | release_build?: false, 63 | prerelease_build?: false, 64 | iteration: nil, 65 | }, 66 | "11.0.8-33" => { 67 | major: 11, 68 | minor: 0, 69 | patch: 8, 70 | prerelease: nil, 71 | build: nil, 72 | release?: true, 73 | prerelease?: false, 74 | build?: false, 75 | release_build?: false, 76 | prerelease_build?: false, 77 | iteration: 33, 78 | }, 79 | "11.0.8.rc.3-1" => { 80 | major: 11, 81 | minor: 0, 82 | patch: 8, 83 | prerelease: "rc.3", 84 | build: nil, 85 | release?: false, 86 | prerelease?: true, 87 | build?: false, 88 | release_build?: false, 89 | prerelease_build?: false, 90 | iteration: 1, 91 | }, 92 | } 93 | 94 | it_has_behavior "rejects invalid version strings", { 95 | "1.1.1-rutro" => "dash for pre-release delimiter", 96 | } 97 | 98 | it_has_behavior "serializable", [ 99 | "1.0.0", 100 | "1.0.0.alpha.1", 101 | "1.0.0.beta", 102 | ] 103 | 104 | it_has_behavior "sortable" do 105 | let(:unsorted_version_strings) do 106 | %w{ 107 | 1.0.0.beta.2 108 | 1.3.7.alpha.0 109 | 1.0.0.alpha 110 | 1.0.0.rc.1 111 | 1.0.0 112 | } 113 | end 114 | let(:sorted_version_strings) do 115 | %w{ 116 | 1.0.0.alpha 117 | 1.0.0.beta.2 118 | 1.0.0.rc.1 119 | 1.0.0 120 | 1.3.7.alpha.0 121 | } 122 | end 123 | let(:min) { "1.0.0.alpha" } 124 | let(:max) { "1.3.7.alpha.0" } 125 | end 126 | 127 | # The +Rubygems+ format only produces release and prerelease versions. 128 | it_has_behavior "filterable" do 129 | let(:unsorted_version_strings) do 130 | %w{ 131 | 1.0.0.beta.2 132 | 1.3.7.alpha.0 133 | 1.0.0.alpha 134 | 1.0.0.rc.1 135 | 1.0.0 136 | } 137 | end 138 | let(:release_versions) { %w{1.0.0} } 139 | let(:prerelease_versions) do 140 | %w{ 141 | 1.0.0.beta.2 142 | 1.3.7.alpha.0 143 | 1.0.0.alpha 144 | 1.0.0.rc.1 145 | } 146 | end 147 | end 148 | 149 | it_has_behavior "comparable", [ 150 | "0.1.0", "0.2.0", 151 | "1.0.0.alpha.1", "1.0.0", 152 | "1.2.3.alpha", "1.2.3.alpha.1", 153 | "2.4.5.alpha", "2.4.5.beta", 154 | "3.0.0.beta.1", "3.0.0.rc.1", 155 | "3.1.2.rc.42", "3.1.2" 156 | ] 157 | end 158 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format/semver_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "spec_helper" 20 | 21 | describe Mixlib::Versioning::Format::SemVer do 22 | subject { described_class.new(version_string) } 23 | 24 | it_should_behave_like Mixlib::Versioning::Format::SemVer 25 | 26 | it_has_behavior "serializable", [ 27 | "1.0.0", 28 | "1.0.0-alpha.1", 29 | "1.0.0-alpha.1+some.build.version", 30 | "1.0.0+build.build.build", 31 | ] 32 | 33 | it_has_behavior "sortable" do 34 | let(:unsorted_version_strings) do 35 | %w{ 36 | 1.0.0-beta.2 37 | 1.0.0-alpha 38 | 1.0.0-alpha.july 39 | 1.0.0-rc.1+build.1 40 | 1.0.0 41 | 1.0.0-beta.11 42 | 1.0.0+0.3.7 43 | 1.0.0-rc.1 44 | 1.0.0-alpha.1 45 | 1.3.7+build.2.b8f12d7 46 | 1.3.7+build.11.e0f985a 47 | 1.3.7+build 48 | } 49 | end 50 | let(:sorted_version_strings) do 51 | %w{ 52 | 1.0.0-alpha 53 | 1.0.0-alpha.1 54 | 1.0.0-alpha.july 55 | 1.0.0-beta.2 56 | 1.0.0-beta.11 57 | 1.0.0-rc.1 58 | 1.0.0-rc.1+build.1 59 | 1.0.0 60 | 1.0.0+0.3.7 61 | 1.3.7+build 62 | 1.3.7+build.2.b8f12d7 63 | 1.3.7+build.11.e0f985a 64 | } 65 | end 66 | let(:min) { "1.0.0-alpha" } 67 | let(:max) { "1.3.7+build.11.e0f985a" } 68 | end # it_has_behavior 69 | 70 | it_has_behavior "filterable" do 71 | let(:unsorted_version_strings) do 72 | %w{ 73 | 1.0.0-beta.2 74 | 1.0.0-alpha 75 | 1.0.0-rc.1+build.1 76 | 1.0.0 77 | 1.0.0-beta.11 78 | 1.0.0+0.3.7 79 | 1.0.0-rc.1 80 | 1.0.0-alpha.1 81 | 1.3.7+build.2.b8f12d7 82 | 1.3.7+build.11.e0f985a 83 | 1.3.7+build 84 | } 85 | end 86 | let(:release_versions) do 87 | %w{ 88 | 1.0.0 } 89 | end 90 | let(:prerelease_versions) do 91 | %w{ 92 | 1.0.0-beta.2 93 | 1.0.0-alpha 94 | 1.0.0-beta.11 95 | 1.0.0-rc.1 96 | 1.0.0-alpha.1 97 | } 98 | end 99 | let(:build_versions) do 100 | %w{ 101 | 1.0.0-rc.1+build.1 102 | 1.0.0+0.3.7 103 | 1.3.7+build.2.b8f12d7 104 | 1.3.7+build.11.e0f985a 105 | 1.3.7+build 106 | } 107 | end 108 | let(:release_build_versions) do 109 | %w{ 110 | 1.0.0+0.3.7 111 | 1.3.7+build.2.b8f12d7 112 | 1.3.7+build.11.e0f985a 113 | 1.3.7+build 114 | } 115 | end 116 | let(:prerelease_build_versions) do 117 | %w{ 118 | 1.0.0-rc.1+build.1 } 119 | end 120 | end # it_has_behavior 121 | 122 | it_has_behavior "comparable", [ 123 | "0.1.0", "0.2.0", 124 | "1.0.0-alpha.1", "1.0.0", 125 | "1.2.3", "1.2.3+build.123", 126 | "2.0.0-beta.1", "2.0.0-beta.1+build.123" 127 | ] 128 | end # describe 129 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/format_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Author:: Ryan Hass () 4 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | require "spec_helper" 21 | 22 | describe Mixlib::Versioning::Format do 23 | describe "#initialize" do 24 | subject { described_class.new(version_string) } 25 | let(:version_string) { "11.0.0" } 26 | 27 | it "descendants must override #parse" do 28 | expect { subject }.to raise_error(StandardError) 29 | end 30 | end 31 | 32 | describe ".for" do 33 | subject { described_class } 34 | 35 | [ 36 | :rubygems, 37 | "rubygems", 38 | Mixlib::Versioning::Format::Rubygems, 39 | ].each do |format_type| 40 | context 'format_type is a: #{format_type.class}' do 41 | let(:format_type) { format_type } 42 | it "returns the correct format class" do 43 | expect(subject.for(format_type)).to eq Mixlib::Versioning::Format::Rubygems 44 | end # it 45 | end # context 46 | end # each 47 | 48 | describe "unknown format_type" do 49 | [ 50 | :poop, 51 | "poop", 52 | Mixlib::Versioning, 53 | ].each do |invalid_format_type| 54 | context 'format_type is a: #{invalid_format_type.class}' do 55 | it "raises a Mixlib::Versioning::UnknownFormatError" do 56 | expect { subject.for(invalid_format_type) }.to raise_error(Mixlib::Versioning::UnknownFormatError) 57 | end # it 58 | end # context 59 | end # each 60 | end # describe 61 | end # describe ".for" 62 | end # describe Mixlib::Versioning::Format 63 | 64 | describe Mixlib::Versioning do 65 | versions = [ 66 | "1", "1.0.0", 67 | "1.2", "1.2.0" 68 | ] 69 | 70 | describe "#==" do 71 | formats = described_class::DEFAULT_FORMATS.select do |klass| 72 | unless klass.name == "Mixlib::Versioning::Format::PartialSemVer" || klass.name == "Mixlib::Versioning::Format::GitDescribe" 73 | klass 74 | end 75 | end 76 | 77 | formats.each do |format| 78 | context "#{format}" do 79 | versions.each_slice(2) do |a, b| 80 | it "parsed value #{a} is equal to #{format} parsed value #{b}" do 81 | expect(described_class.parse(a) == format.new(b)).to be true 82 | end 83 | end 84 | end # context 85 | end # formats.each 86 | end # describe "#==" 87 | end # describe Mixlib::Version 88 | -------------------------------------------------------------------------------- /spec/mixlib/versioning/versioning_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "spec_helper" 20 | 21 | describe Mixlib::Versioning do 22 | subject { described_class } 23 | 24 | let(:version_string) { "11.0.0" } 25 | 26 | describe ".parse" do 27 | describe "parsing when format type is specified" do 28 | { 29 | "11.0.8" => { 30 | format_type: :semver, 31 | expected_format: Mixlib::Versioning::Format::SemVer, 32 | }, 33 | "11.1.1" => { 34 | format_type: :rubygems, 35 | expected_format: Mixlib::Versioning::Format::Rubygems, 36 | }, 37 | "11.1.1.alpha.1" => { 38 | format_type: :rubygems, 39 | expected_format: Mixlib::Versioning::Format::Rubygems, 40 | }, 41 | "11.1.1-alpha.1" => { 42 | format_type: :opscode_semver, 43 | expected_format: Mixlib::Versioning::Format::OpscodeSemVer, 44 | }, 45 | "11.1.1-rc.2" => { 46 | format_type: :opscode_semver, 47 | expected_format: Mixlib::Versioning::Format::SemVer, 48 | }, 49 | }.each_pair do |version_string, options| 50 | context "#{version_string} as #{options[:expected_format]}" do 51 | let(:version_string) { version_string } 52 | let(:expected_format) { options[:expected_format] } 53 | 54 | [ 55 | options[:format_type], 56 | options[:format_type].to_s, 57 | options[:expected_format], 58 | ].each do |format_type| 59 | context "format type as a: #{format_type.class}" do 60 | it "parses version string as: #{options[:expected_format]}" do 61 | result = subject.parse(version_string, format_type) 62 | expect(result).to be_a(expected_format) 63 | end # it 64 | end # context 65 | end # each 66 | end # context 67 | end # each_pair 68 | 69 | describe "invalid format type specified" do 70 | [ 71 | :poop, 72 | "poop", 73 | Mixlib::Versioning, 74 | ].each do |invalid_format_type| 75 | context "invalid format as a: #{invalid_format_type.class}" do 76 | it "raises a Mixlib::Versioning::UnknownFormatError" do 77 | expect { subject.parse(version_string, invalid_format_type) }.to raise_error(Mixlib::Versioning::UnknownFormatError) 78 | end # it 79 | end # context 80 | end # each 81 | end # describe 82 | end # describe 83 | 84 | describe "parsing with automatic format detection" do 85 | { 86 | "11.0.8" => Mixlib::Versioning::Format::SemVer, 87 | "11.0.8-1" => Mixlib::Versioning::Format::SemVer, 88 | "11.0.8.rc.1" => Mixlib::Versioning::Format::Rubygems, 89 | "11.0.8.rc.1-1" => Mixlib::Versioning::Format::Rubygems, 90 | "11.0.8-rc.1" => Mixlib::Versioning::Format::OpscodeSemVer, 91 | 92 | "10.18.2" => Mixlib::Versioning::Format::SemVer, 93 | "10.18.2-poop" => Mixlib::Versioning::Format::SemVer, 94 | "10.18.2.poop" => Mixlib::Versioning::Format::Rubygems, 95 | "10.18.2.poop-1" => Mixlib::Versioning::Format::Rubygems, 96 | 97 | "12.1.1+20130311134422" => Mixlib::Versioning::Format::OpscodeSemVer, 98 | "12.1.1-rc.3+20130311134422" => Mixlib::Versioning::Format::OpscodeSemVer, 99 | "12.1.1+20130308110833.git.2.94a1dde" => Mixlib::Versioning::Format::OpscodeSemVer, 100 | 101 | "10.16.2-49-g21353f0" => Mixlib::Versioning::Format::GitDescribe, 102 | "10.16.2-49-g21353f0-1" => Mixlib::Versioning::Format::GitDescribe, 103 | "10.16.2.rc.2-49-g21353f0" => Mixlib::Versioning::Format::GitDescribe, 104 | "10.16.2-rc.2-49-g21353f0" => Mixlib::Versioning::Format::GitDescribe, 105 | }.each_pair do |version_string, expected_format| 106 | context version_string do 107 | let(:version_string) { version_string } 108 | it "parses version string as: #{expected_format}" do 109 | expect(subject.parse(version_string)).to be_a(expected_format) 110 | end # it 111 | end # context 112 | end # each_pair 113 | 114 | describe "version_string cannot be parsed" do 115 | let(:version_string) { "A.B.C" } 116 | it "returns nil" do 117 | expect(subject.parse(version_string)).to be_nil 118 | end 119 | end 120 | end # describe "parsing with automatic format detection" 121 | 122 | describe "parsing an Mixlib::Versioning::Format object" do 123 | it "returns the same object" do 124 | v = Mixlib::Versioning.parse(version_string) 125 | result = subject.parse(v) 126 | expect(v).to be result 127 | end 128 | end 129 | 130 | describe "when formats are given" do 131 | context "when the format is not in the list" do 132 | let(:version_string) { "10.16.2-rc.2-49-g21353f0" } 133 | it "returns nil when the array contains a Mixlib::Versioning::Format" do 134 | expect(subject.parse(version_string, [Mixlib::Versioning::Format::Rubygems])).to be_nil 135 | end 136 | 137 | it "returns nil when the array contains a string" do 138 | expect(subject.parse(version_string, ["rubygems"])).to be_nil 139 | end 140 | 141 | it "returns nil when the array contains a symbol" do 142 | expect(subject.parse(version_string, [:rubygems])).to be_nil 143 | end 144 | end 145 | 146 | context "when the format is in the list" do 147 | let(:version_string) { "10.16.2-rc.2-49-g21353f0" } 148 | let(:expected_format) { Mixlib::Versioning::Format::GitDescribe } 149 | it "returns nil when the array contains a Mixlib::Versioning::Format" do 150 | expect(subject.parse(version_string, [expected_format])).to be_a(expected_format) 151 | end 152 | 153 | it "returns nil when the array contains a string" do 154 | expect(subject.parse(version_string, ["git_describe"])).to be_a(expected_format) 155 | end 156 | 157 | it "returns nil when the array contains a symbol" do 158 | expect(subject.parse(version_string, [:git_describe])).to be_a(expected_format) 159 | end 160 | end 161 | end 162 | end # describe .parse 163 | 164 | describe ".find_target_version" do 165 | let(:all_versions) do 166 | %w{ 167 | 11.0.0-beta.1 168 | 11.0.0-rc.1 169 | 11.0.0 170 | 11.0.1 171 | 11.0.1+2013031116332 172 | 11.0.2-alpha.0 173 | 11.0.2-alpha.0+2013041116332 174 | 11.0.2 175 | } 176 | end 177 | let(:filter_version) { nil } 178 | let(:use_prerelease_versions) { false } 179 | let(:use_build_versions) { false } 180 | let(:subject_find) do 181 | subject.find_target_version( 182 | all_versions, 183 | filter_version, 184 | use_prerelease_versions, 185 | use_build_versions 186 | ) 187 | end 188 | 189 | { 190 | nil => { 191 | releases_only: "11.0.2", 192 | prerelease_versions: "11.0.2-alpha.0", 193 | build_versions: "11.0.1+2013031116332", 194 | prerelease_and_build_versions: "11.0.2-alpha.0+2013041116332", 195 | }, 196 | "11.0.0" => { 197 | releases_only: "11.0.0", 198 | prerelease_versions: "11.0.0-rc.1", 199 | build_versions: nil, 200 | prerelease_and_build_versions: nil, 201 | }, 202 | "11.0.2" => { 203 | releases_only: "11.0.2", 204 | prerelease_versions: "11.0.2-alpha.0", 205 | build_versions: nil, 206 | prerelease_and_build_versions: "11.0.2-alpha.0+2013041116332", 207 | }, 208 | "11.0.2" => { # rubocop: disable Lint/DuplicatedKey 209 | releases_only: "11.0.2", 210 | prerelease_versions: "11.0.2-alpha.0", 211 | build_versions: nil, 212 | prerelease_and_build_versions: "11.0.2-alpha.0+2013041116332", 213 | }, 214 | "11.0.2-alpha.0" => { 215 | releases_only: "11.0.2-alpha.0", 216 | prerelease_versions: "11.0.2-alpha.0", 217 | build_versions: "11.0.2-alpha.0+2013041116332", 218 | prerelease_and_build_versions: "11.0.2-alpha.0+2013041116332", 219 | }, 220 | }.each_pair do |filter_version, options| 221 | context "filter version of: #{filter_version}" do 222 | let(:filter_version) { filter_version } 223 | let(:expected_version) { options[:releases_only] } 224 | 225 | it "finds the most recent release version" do 226 | expect(subject_find).to eq Mixlib::Versioning.parse(expected_version) 227 | end 228 | 229 | context "include pre-release versions" do 230 | let(:use_prerelease_versions) { true } 231 | let(:expected_version) { options[:prerelease_versions] } 232 | 233 | it "finds the most recent pre-release version" do 234 | expect(subject_find).to eq Mixlib::Versioning.parse(expected_version) 235 | end # it 236 | end # context 237 | 238 | context "include build versions" do 239 | let(:use_build_versions) { true } 240 | let(:expected_version) { options[:build_versions] } 241 | 242 | it "finds the most recent build version" do 243 | expect(subject_find).to eq Mixlib::Versioning.parse(expected_version) 244 | end # it 245 | end # context 246 | 247 | context "include pre-release and build versions" do 248 | let(:use_prerelease_versions) { true } 249 | let(:use_build_versions) { true } 250 | let(:expected_version) { options[:prerelease_and_build_versions] } 251 | 252 | it "finds the most recent pre-release build version" do 253 | expect(subject_find).to eq Mixlib::Versioning.parse(expected_version) 254 | end # it 255 | end # context 256 | end # context 257 | end # each_pair 258 | 259 | describe "all_versions argument is a mix of String and Mixlib::Versioning::Format instances" do 260 | let(:all_versions) do 261 | [ 262 | "11.0.0-beta.1", 263 | "11.0.0-rc.1", 264 | Mixlib::Versioning.parse("11.0.0"), 265 | ] 266 | end 267 | 268 | it "correctly parses the array" do 269 | expect(subject_find).to eq Mixlib::Versioning.parse("11.0.0") 270 | end 271 | end # describe 272 | 273 | describe "filter_version argument is an instance of Mixlib::Versioning::Format" do 274 | let(:filter_version) { Mixlib::Versioning::Format::SemVer.new("11.0.0") } 275 | 276 | it "finds the correct version" do 277 | expect(subject_find).to eq Mixlib::Versioning.parse("11.0.0") 278 | end 279 | end 280 | end # describe 281 | end # describe Mixlib::Versioning 282 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "mixlib/versioning" 20 | require "rspec/its" 21 | 22 | # load all shared examples and shared contexts 23 | Dir[File.expand_path("support/**/*.rb", __dir__)].each do |file| 24 | require(file) 25 | end 26 | 27 | RSpec.configure do |config| 28 | # a little syntactic sugar 29 | config.alias_it_should_behave_like_to :it_has_behavior, "has behavior:" 30 | 31 | # Use documentation format 32 | config.formatter = "doc" 33 | 34 | # Use color in STDOUT 35 | config.color = true 36 | 37 | # Use color not only in STDOUT but also in pagers and files 38 | config.tty = true 39 | 40 | # run the examples in random order 41 | config.order = :rand 42 | 43 | config.filter_run focus: true 44 | config.run_all_when_everything_filtered = true 45 | config.warnings = true 46 | end 47 | -------------------------------------------------------------------------------- /spec/support/shared_examples/basic_semver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "Basic SemVer" do 20 | it_has_behavior "parses valid version strings", { 21 | "1.2.3" => { 22 | major: 1, 23 | minor: 2, 24 | patch: 3, 25 | prerelease: nil, 26 | build: nil, 27 | release?: true, 28 | prerelease?: false, 29 | build?: false, 30 | release_build?: false, 31 | prerelease_build?: false, 32 | }, 33 | } 34 | 35 | it_has_behavior "rejects invalid version strings", { 36 | "a.1.1" => "non-numeric MAJOR", 37 | "1.a.1" => "non-numeric MINOR", 38 | "1.1.a" => "non-numeric PATCH", 39 | } 40 | end 41 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/comparable.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Krzysztof Wilczynski () 3 | # Copyright:: Copyright (c) 2014-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "comparable" do |version_matrix| 20 | describe "#<" do 21 | version_matrix.each_slice(2) do |a, b| 22 | it "confirms that #{a} is less-than #{b}" do 23 | expect(described_class.new(a) < b).to be true 24 | expect(b < described_class.new(a)).to be false 25 | end 26 | end 27 | end 28 | 29 | describe "#<=" do 30 | version_matrix.each_slice(2) do |a, b| 31 | it "confirms that #{a} less-than or equal to #{b}" do 32 | expect(described_class.new(a) <= b).to be true 33 | expect(described_class.new(a) <= a).to be true 34 | expect(b <= described_class.new(a)).to be false 35 | expect(b < described_class.new(a)).to be false 36 | end 37 | end 38 | end 39 | 40 | describe "#==" do 41 | version_matrix.each do |v| 42 | it "confirms that #{v} is equal to #{v}" do 43 | expect(described_class.new(v) == v).to be true 44 | expect(described_class.new(v) < v).to be false 45 | expect(described_class.new(v) > v).to be false 46 | end 47 | end 48 | end 49 | 50 | describe "#>" do 51 | version_matrix.reverse.each_slice(2) do |a, b| 52 | it "confirms that #{a} is greater-than #{b}" do 53 | expect(described_class.new(a) > b).to be true 54 | expect(b > described_class.new(a)).to be false 55 | end 56 | end 57 | end 58 | 59 | describe "#>=" do 60 | version_matrix.reverse.each_slice(2) do |a, b| 61 | it "confirms that #{a} greater-than or equal to #{b}" do 62 | expect(described_class.new(a) >= b).to be true 63 | expect(described_class.new(a) >= a).to be true 64 | expect(b >= described_class.new(a)).to be false 65 | expect(b > described_class.new(a)).to be false 66 | end 67 | end 68 | end 69 | 70 | describe "#between?" do 71 | let(:versions) { version_matrix.map { |v| described_class.new(v) }.sort } 72 | 73 | it "confirms that a version is between the oldest and latest release" do 74 | min, max = versions.minmax.map(&:to_s) 75 | middle = versions[versions.size / 2].to_s 76 | expect(described_class.new(middle).between?(min, max)).to be true 77 | expect(described_class.new(middle).between?(max, min)).to be false 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/comparable_types.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Krzysztof Wilczynski () 3 | # Author:: Ryan Hass () 4 | # Copyright:: Copyright (c) 2014-2018 Chef Software Inc. 5 | # License:: Apache License, Version 2.0 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | shared_examples "comparable_types" do |version_matrix| 21 | describe "#<" do 22 | version_matrix.each_slice(2) do |a, b| 23 | it "confirms that #{a} is less-than #{b}" do 24 | expect(described_class.new(a) < b[:class].new(b[:value])).to be true 25 | expect(b[:class].new(b[:value]) < described_class.new(a)).to be false 26 | end 27 | end 28 | end 29 | 30 | describe "#<=" do 31 | version_matrix.each_slice(2) do |a, b| 32 | it "confirms that #{a} less-than or equal to #{b}" do 33 | expect(described_class.new(a) <= b[:class].new(b[:value])).to be true 34 | expect(b[:class].new("#{b[:value]}") <= described_class.new(a)).to be false 35 | end 36 | end 37 | end 38 | 39 | describe "#>" do 40 | version_matrix.each_slice(2) do |a, b| 41 | it "confirms that #{a} is greater-than #{b}" do 42 | expect(b[:class].new(b[:value]) > described_class.new(a)).to be true 43 | expect(described_class.new(a) > b[:class].new(b[:value])).to be false 44 | end 45 | end 46 | end 47 | 48 | describe "#>=" do 49 | version_matrix.each_slice(2) do |a, b| 50 | it "confirms that #{a} greater-than or equal to #{b}" do 51 | expect(b[:class].new(b[:value]) >= described_class.new(a)).to be true 52 | expect(described_class.new(a) >= b[:class].new(b[:value])).to be false 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/filterable.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "filterable" do 20 | let(:unsorted_versions) do 21 | unsorted_version_strings.map { |v| described_class.new(v) } 22 | end 23 | 24 | # this should/will be overridden by calling spec 25 | let(:release_versions) { [] } 26 | let(:prerelease_versions) { [] } 27 | let(:build_versions) { [] } 28 | let(:release_build_versions) { [] } 29 | let(:prerelease_build_versions) { [] } 30 | 31 | it "filters by release versions only" do 32 | expect(unsorted_versions.select(&:release?)).to eq(release_versions.map { |v| described_class.new(v) }) 33 | end # it 34 | 35 | it "filters by pre-release versions only" do 36 | filtered = unsorted_versions.select(&:prerelease?) 37 | expect(filtered).to eq(prerelease_versions.map { |v| described_class.new(v) }) 38 | end # it 39 | 40 | it "filters by build versions only" do 41 | filtered = unsorted_versions.select(&:build?) 42 | expect(filtered).to eq(build_versions.map { |v| described_class.new(v) }) 43 | end # it 44 | 45 | it "filters by release build versions only" do 46 | filtered = unsorted_versions.select(&:release_build?) 47 | expect(filtered).to eq(release_build_versions.map { |v| described_class.new(v) }) 48 | end # it 49 | 50 | it "filters by pre-release build versions only" do 51 | filtered = unsorted_versions.select(&:prerelease_build?) 52 | expect(filtered).to eq(prerelease_build_versions.map { |v| described_class.new(v) }) 53 | end # it 54 | end # shared_examples 55 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/parses_valid_version_strings.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "parses valid version strings" do |valid_examples| 20 | valid_examples.each do |version, options| 21 | context version do 22 | let(:version_string) { version } 23 | 24 | options.each_pair do |name, value| 25 | its(name) { should eq value } 26 | end 27 | end # context 28 | end # valid_examples 29 | end # shared_examples 30 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/rejects_invalid_version_strings.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "rejects invalid version strings" do |invalid_examples| 20 | invalid_examples.each_pair do |version, reason| 21 | context version do 22 | let(:version_string) { version } 23 | 24 | it "fails because: #{reason}" do 25 | expect { subject }.to raise_error(Mixlib::Versioning::ParseError) 26 | end 27 | end # context 28 | end # invalid_examples 29 | end # shared_examples 30 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/serializable.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "serializable" do |version_strings| 20 | describe "#to_s" do 21 | version_strings.each do |v| 22 | it "reconstructs the initial input for #{v}" do 23 | expect(described_class.new(v).to_s).to eq(v) 24 | end # it 25 | end # version_strings 26 | end # describe 27 | 28 | describe "#to_semver_string" do 29 | version_strings.each do |v| 30 | it "generates a semver version string for #{v}" do 31 | subject = described_class.new(v) 32 | string = subject.to_semver_string 33 | semver = Mixlib::Versioning::Format::SemVer.new(string) 34 | expect(string).to eq semver.to_s 35 | end # it 36 | end # version_strings 37 | end # describe 38 | 39 | describe "#to_rubygems_string" do 40 | version_strings.each do |v| 41 | it "generates a rubygems version string for #{v}" do 42 | subject = described_class.new(v) 43 | string = subject.to_rubygems_string 44 | rubygems = Mixlib::Versioning::Format::Rubygems.new(string) 45 | expect(string).to eq rubygems.to_s 46 | end # it 47 | end # version_strings 48 | end # describe 49 | end # shared_examples 50 | -------------------------------------------------------------------------------- /spec/support/shared_examples/behaviors/sortable.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | shared_examples "sortable" do 20 | let(:unsorted_versions) do 21 | unsorted_version_strings.map { |v| described_class.new(v) } 22 | end 23 | 24 | let(:sorted_versions) do 25 | sorted_version_strings.map { |v| described_class.new(v) } 26 | end 27 | 28 | it "responds to <=>" do 29 | expect(described_class).to respond_to(:<=>) 30 | end 31 | 32 | it "sorts all properly" do 33 | expect(unsorted_versions.sort).to eq sorted_versions 34 | end 35 | 36 | it "finds the min" do 37 | expect(unsorted_versions.min).to eq described_class.new(min) 38 | end 39 | 40 | it "finds the max" do 41 | expect(unsorted_versions.max).to eq described_class.new(max) 42 | end 43 | end # shared_examples 44 | -------------------------------------------------------------------------------- /spec/support/shared_examples/semver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Seth Chisamore () 3 | # Copyright:: Copyright (c) 2013-2018 Chef Software, Inc. 4 | # License:: Apache License, Version 2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | require "mixlib/versioning" 20 | 21 | shared_examples Mixlib::Versioning::Format::SemVer do 22 | it_should_behave_like "Basic SemVer" 23 | 24 | it_has_behavior "parses valid version strings", { 25 | "1.0.0-alpha.1" => { 26 | major: 1, 27 | minor: 0, 28 | patch: 0, 29 | prerelease: "alpha.1", 30 | build: nil, 31 | release?: false, 32 | prerelease?: true, 33 | build?: false, 34 | release_build?: false, 35 | prerelease_build?: false, 36 | }, 37 | "1.0.0+20130308110833" => { 38 | major: 1, 39 | minor: 0, 40 | patch: 0, 41 | prerelease: nil, 42 | build: "20130308110833", 43 | release?: false, 44 | prerelease?: false, 45 | build?: true, 46 | release_build?: true, 47 | prerelease_build?: false, 48 | }, 49 | "1.0.0-beta.3+20130308110833" => { 50 | major: 1, 51 | minor: 0, 52 | patch: 0, 53 | prerelease: "beta.3", 54 | build: "20130308110833", 55 | release?: false, 56 | prerelease?: false, 57 | build?: true, 58 | release_build?: false, 59 | prerelease_build?: true, 60 | }, 61 | } 62 | 63 | it_has_behavior "rejects invalid version strings", { 64 | "8.8.8.8" => "too many segments: MAJOR.MINOR.PATCH.EXTRA", 65 | "01.1.1" => "leading zero invalid", 66 | "1.01.1" => "leading zero invalid", 67 | "1.1.01" => "leading zero invalid", 68 | "1.0.0-" => "empty prerelease identifier", 69 | "1.0.0-alpha.." => "empty prerelease identifier", 70 | "1.0.0-01.02.03" => "leading zero invalid", 71 | "1.0.0-alpha.01" => "leading zero invalid", 72 | "6.3.1+" => "empty build identifier", 73 | "6.4.8-alpha.1.2.3+build." => "empty build identifier", 74 | "4.0.000000000002-alpha.1" => "leading zero invalid", 75 | "007.2.3-rc.1+build.16" => "leading zero invalid", 76 | "12.0005.1-alpha.12" => "leading zero invalid", 77 | "12.2.10-beta.00000017" => "leading zero invalid", 78 | } 79 | 80 | describe "build qualification" do 81 | context "release version" do 82 | let(:version_string) { "1.0.0" } 83 | its(:release?) { should be_truthy } 84 | its(:prerelease?) { should be_falsey } 85 | its(:build?) { should be_falsey } 86 | its(:release_build?) { should be_falsey } 87 | its(:prerelease_build?) { should be_falsey } 88 | end 89 | 90 | context "pre-release version" do 91 | let(:version_string) { "1.0.0-alpha.1" } 92 | its(:release?) { should be_falsey } 93 | its(:prerelease?) { should be_truthy } 94 | its(:build?) { should be_falsey } 95 | its(:release_build?) { should be_falsey } 96 | its(:prerelease_build?) { should be_falsey } 97 | end 98 | 99 | context "pre-release build version" do 100 | let(:version_string) { "1.0.0-alpha.1+20130308110833" } 101 | its(:release?) { should be_falsey } 102 | its(:prerelease?) { should be_falsey } 103 | its(:build?) { should be_truthy } 104 | its(:release_build?) { should be_falsey } 105 | its(:prerelease_build?) { should be_truthy } 106 | end 107 | 108 | context "release build version" do 109 | let(:version_string) { "1.0.0+20130308110833" } 110 | its(:release?) { should be_falsey } 111 | its(:prerelease?) { should be_falsey } 112 | its(:build?) { should be_truthy } 113 | its(:release_build?) { should be_truthy } 114 | its(:prerelease_build?) { should be_falsey } 115 | end 116 | end 117 | end 118 | --------------------------------------------------------------------------------