├── .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 | [](https://buildkite.com/chef-oss/chef-mixlib-versioning-master-verify)
3 | [](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 |
--------------------------------------------------------------------------------