toys-release 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CHANGELOG.md +5 -0
  4. data/LICENSE.md +21 -0
  5. data/README.md +87 -0
  6. data/docs/guide.md +7 -0
  7. data/lib/toys/release/version.rb +11 -0
  8. data/lib/toys-release.rb +23 -0
  9. data/toys/.data/templates/gh-pages-404.html.erb +25 -0
  10. data/toys/.data/templates/gh-pages-empty.html.erb +11 -0
  11. data/toys/.data/templates/gh-pages-gitignore.erb +1 -0
  12. data/toys/.data/templates/gh-pages-index.html.erb +15 -0
  13. data/toys/.data/templates/release-hook-on-closed.yml.erb +34 -0
  14. data/toys/.data/templates/release-hook-on-open.yml.erb +30 -0
  15. data/toys/.data/templates/release-hook-on-push.yml.erb +32 -0
  16. data/toys/.data/templates/release-perform.yml.erb +46 -0
  17. data/toys/.data/templates/release-request.yml.erb +37 -0
  18. data/toys/.data/templates/release-retry.yml.erb +42 -0
  19. data/toys/.lib/toys/release/artifact_dir.rb +70 -0
  20. data/toys/.lib/toys/release/change_set.rb +259 -0
  21. data/toys/.lib/toys/release/changelog_file.rb +136 -0
  22. data/toys/.lib/toys/release/component.rb +388 -0
  23. data/toys/.lib/toys/release/environment_utils.rb +246 -0
  24. data/toys/.lib/toys/release/performer.rb +346 -0
  25. data/toys/.lib/toys/release/pull_request.rb +154 -0
  26. data/toys/.lib/toys/release/repo_settings.rb +855 -0
  27. data/toys/.lib/toys/release/repository.rb +661 -0
  28. data/toys/.lib/toys/release/request_logic.rb +217 -0
  29. data/toys/.lib/toys/release/request_spec.rb +188 -0
  30. data/toys/.lib/toys/release/semver.rb +112 -0
  31. data/toys/.lib/toys/release/steps.rb +580 -0
  32. data/toys/.lib/toys/release/version_rb_file.rb +91 -0
  33. data/toys/.toys.rb +5 -0
  34. data/toys/_onclosed.rb +113 -0
  35. data/toys/_onopen.rb +158 -0
  36. data/toys/_onpush.rb +57 -0
  37. data/toys/create-labels.rb +115 -0
  38. data/toys/gen-gh-pages.rb +146 -0
  39. data/toys/gen-settings.rb +46 -0
  40. data/toys/gen-workflows.rb +70 -0
  41. data/toys/perform.rb +152 -0
  42. data/toys/request.rb +162 -0
  43. data/toys/retry.rb +133 -0
  44. metadata +106 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6f2bad43c37b4109aee66b380e4e0e6497a390d4d7411727ded7b6fb583f2655
4
+ data.tar.gz: e9a297a316744b9523bab8976a63b43a7f1728424572f561bc8bde3255cdb932
5
+ SHA512:
6
+ metadata.gz: feee3189f4e0fb77d08c506aefb2130b8a00f46ac53066c5407fd1f2200ed8264acff74ec90facf2ec29e5b2b04e37e908421459528c1d08b486af4ef3b8a597
7
+ data.tar.gz: a07912d6c512bf037d71b7394b007af8d918cc9eafb518ef3a5779642c35462c21115ea57e48604c6a3653f59a1781bdaca77321199bcd4dfa1101dc396ba8f5
data/.yardopts ADDED
@@ -0,0 +1,11 @@
1
+ --no-private
2
+ --title=Toys Release System
3
+ --markup=markdown
4
+ --markup-provider redcarpet
5
+ --main=README.md
6
+ ./lib/**/*.rb
7
+ -
8
+ README.md
9
+ LICENSE.md
10
+ CHANGELOG.md
11
+ docs/guide.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Release History
2
+
3
+ ### v0.1.0 / 2025-11-09
4
+
5
+ Initial release of the toys-release gem
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # License
2
+
3
+ Copyright 2025 Daniel Azuma and the Toys contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Toys-Release
2
+
3
+ Toys-Release is a Ruby library release system built on GitHub Actions and the
4
+ Toys gem. It interprets [conventional commit](https://conventionalcommits.org/)
5
+ message format to automate changelog generation and library version updating
6
+ based on semantic versioning, and supports fine tuning and approval of releases
7
+ using GitHub pull requests.
8
+
9
+ Out of the box, Toys-Release knows how to tag GitHub releases, build and push
10
+ gems to Rubygems, and build and publish documentation to gh-pages. You can also
11
+ customize the build pipeline and many aspects of its behavior.
12
+
13
+ ## Description
14
+
15
+ Toys-Release is desigend to be installed on a GitHub repository. This
16
+ installation consists of a set of GitHub Actions workflows, a configuration
17
+ file, and a GitHub Actions secret for your RubyGems credentials.
18
+
19
+ Once installed, Toys-Release provides command line tools and GitHub Actions
20
+ workflows for performing and managing releases. These tools are built atop the
21
+ Toys framework.
22
+
23
+ ### The Toys release process
24
+
25
+ Releases using Toys-Release will generally look like the following.
26
+
27
+ When you are ready to do a release, go to the GitHub Action tab on your
28
+ repository and trigger the "Open Release Request" action. (Alternately, you can
29
+ run this action on the command line.) This action analyzes your repository,
30
+ finding changes that were committed since the last release. If those commit
31
+ messages were formatted properly with Conventional Commit tags, the action will
32
+ be able to generate a new entry in your changelog, and suggeset a proper
33
+ version for the new release in line with Semantic Versioning. It will then open
34
+ a pull request with the version update and changelog addition.
35
+
36
+ You can then review this pull request and modify it if you want to alter the
37
+ version to be released, or the changelog text. Once you are satisfied, merge
38
+ the pull request. Toys-Release will then automatically perform the release,
39
+ tagging it in GitHub and building and pushing a new Ruby gem release. It can
40
+ also be configured to build your gem's documentation and push it to your
41
+ repository's gh-pages branch for publication. You can cancel the release simply
42
+ by closing the pull request without merging.
43
+
44
+ ### Key features
45
+
46
+ * Tight integration with GitHub Actions provides a convenient workflow for
47
+ GitHub-based projects.
48
+ * Automatically generates changelogs based on Conventional Commit messages.
49
+ * Automatically proposes releases with semver-aligned version increments based
50
+ on the semantics implied by the commit messages.
51
+ * Approve and adjust releases and changelogs by editing pull requests.
52
+ * Automatic pre-release checks verify that CI passes and release status is
53
+ consistent.
54
+ * Fix and retry failed releases via GitHub Actions or command line.
55
+ * Supports single-library repositories and multi-library monorepos.
56
+ * Support for groups of libraries that must be released together.
57
+ * Support for publishing reference documentation to gh-pages.
58
+ * Fine-grained configuration of the release pipeline.
59
+
60
+ ### System requirements
61
+
62
+ Toys-Release requires Ruby 2.7 or later, and Toys 0.17 or later. We recommend
63
+ the latest version of the standard C implementation of Ruby. (JRuby or
64
+ TruffleRuby _may_ work, but are unsupported.) The Ruby provided by the standard
65
+ setup-ruby GitHub Action is sufficient.
66
+
67
+ ## License
68
+
69
+ Copyright 2019-2025 Daniel Azuma and the Toys contributors
70
+
71
+ Permission is hereby granted, free of charge, to any person obtaining a copy
72
+ of this software and associated documentation files (the "Software"), to deal
73
+ in the Software without restriction, including without limitation the rights
74
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
75
+ copies of the Software, and to permit persons to whom the Software is
76
+ furnished to do so, subject to the following conditions:
77
+
78
+ The above copyright notice and this permission notice shall be included in
79
+ all copies or substantial portions of the Software.
80
+
81
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
84
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
86
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
87
+ IN THE SOFTWARE.
data/docs/guide.md ADDED
@@ -0,0 +1,7 @@
1
+ <!--
2
+ # @title Toys-Release User Guide
3
+ -->
4
+
5
+ # Toys-Release User Guide
6
+
7
+ (To be written)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ module Release
5
+ ##
6
+ # Current version of the Toys release system.
7
+ # @return [String]
8
+ #
9
+ VERSION = "0.1.0"
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Toys is a configurable command line tool. Write commands in config files
5
+ # using a simple DSL, and Toys will provide the command line executable and
6
+ # take care of all the details such as argument parsing, online help, and error
7
+ # reporting. Toys is designed for software developers, IT professionals, and
8
+ # other power users who want to write and organize scripts to automate their
9
+ # workflows. It can also be used as a Rake replacement, providing a more
10
+ # natural command line interface for your project's build tasks.
11
+ #
12
+ module Toys
13
+ ##
14
+ # The Toys Release system is a set of conventional commits based release
15
+ # tools, distributed in a Rubygem. The functionality is not available via
16
+ # Ruby libraries. Instead use the Toys load_gem functionality to import the
17
+ # release tools into your project. See the readme and user guide for details.
18
+ #
19
+ module Release
20
+ end
21
+ end
22
+
23
+ require "toys/release/version"
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ </head>
6
+ <body>
7
+ <p>
8
+ <a href="https://<%= @url_base %>/index.html">Main documentation page</a>
9
+ </p>
10
+ </body>
11
+ <script>
12
+ <% @relevant_component_settings.map { |comp| comp.gh_pages_version_var }.uniq.each do |version_var| %> var <%= version_var %> = "0.0.0";
13
+ <% end %> var location_updated = false;
14
+ <% @relevant_component_settings.each do |component_settings| %> if (!location_updated && window.location.href.includes("//<%= @url_base %>/<%= component_settings.gh_pages_directory %>/latest")) {
15
+ var loc = window.location.href.replace(
16
+ "//<%= @url_base %>/<%= component_settings.gh_pages_directory %>/latest",
17
+ "//<%= @url_base %>/<%= component_settings.gh_pages_directory %>/v" + <%= component_settings.gh_pages_version_var %>);
18
+ window.location.replace(loc);
19
+ location_updated = true;
20
+ }
21
+ <% end %> if (!location_updated) {
22
+ window.location.replace("<%= @default_url %>");
23
+ }
24
+ </script>
25
+ </html>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ </head>
6
+ <body>
7
+ <p>
8
+ No releases yet for <%= @component_settings.name %>.
9
+ </p>
10
+ </body>
11
+ </html>
@@ -0,0 +1 @@
1
+ .DS_Store
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="refresh" content="0; url=<%= @default_url %>">
6
+ </head>
7
+ <body>
8
+ <p>
9
+ <a href="<%= @default_url %>">Documentation for latest version</a>
10
+ </p>
11
+ </body>
12
+ <script>
13
+ window.location.replace("<%= @default_url %>");
14
+ </script>
15
+ </html>
@@ -0,0 +1,34 @@
1
+ name: "[release hook] Process release"
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+
7
+ permissions:
8
+ contents: write # required for creating releases
9
+ pull-requests: write # required for updating label on PR, posting comments
10
+ issues: write # required for creating new issues on failed releases
11
+
12
+ jobs:
13
+ release-process-request:
14
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
15
+ env:
16
+ ruby_version: "3.4"
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Install Ruby ${{ env.ruby_version }}
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ env.ruby_version }}
23
+ - name: Checkout repo
24
+ uses: actions/checkout@v5
25
+ - name: Install Toys
26
+ run: "gem install --no-document toys"
27
+ - name: Process release request
28
+ env:
29
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
31
+ run: |
32
+ toys release _onclosed --verbose \
33
+ "--event-path=${{ github.event_path }}" \
34
+ < /dev/null
@@ -0,0 +1,30 @@
1
+ name: "[release hook] Check commit messages"
2
+
3
+ on:
4
+ pull_request_target:
5
+ types: [opened, edited, synchronize, reopened]
6
+
7
+ jobs:
8
+ release-check-commits:
9
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
10
+ env:
11
+ ruby_version: "3.4"
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Install Ruby ${{ env.ruby_version }}
15
+ uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ env.ruby_version }}
18
+ - name: Checkout repo
19
+ uses: actions/checkout@v5
20
+ with:
21
+ ref: refs/pull/${{ github.event.pull_request.number }}/merge
22
+ - name: Install Toys
23
+ run: "gem install --no-document toys"
24
+ - name: Check commit messages
25
+ env:
26
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ run: |
28
+ toys release _onopen --verbose \
29
+ "--event-path=${{ github.event_path }}" \
30
+ < /dev/null
@@ -0,0 +1,32 @@
1
+ name: "[release hook] Update open releases"
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - <%= @settings.main_branch %>
7
+
8
+ permissions:
9
+ contents: write # required for pushing changes
10
+ pull-requests: write # required for updating open release PRs
11
+
12
+ jobs:
13
+ release-update-open-requests:
14
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
15
+ env:
16
+ ruby_version: "3.4"
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Install Ruby ${{ env.ruby_version }}
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ env.ruby_version }}
23
+ - name: Checkout repo
24
+ uses: actions/checkout@v5
25
+ - name: Install Toys
26
+ run: "gem install --no-document toys"
27
+ - name: Update open releases
28
+ env:
29
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30
+ run: |
31
+ toys release _onpush --verbose \
32
+ < /dev/null
@@ -0,0 +1,46 @@
1
+ name: Force release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ name:
7
+ description: Component to release
8
+ required: true
9
+ version:
10
+ description: Version to release
11
+ required: true
12
+ flags:
13
+ description: Extra flags to pass to the release script
14
+ required: false
15
+ default: ""
16
+
17
+ permissions:
18
+ contents: write # required for creating releases
19
+ pull-requests: write # required for updating label on PR, posting comments
20
+ issues: write # required for creating new issues on failed releases
21
+
22
+ jobs:
23
+ release-perform:
24
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
25
+ env:
26
+ ruby_version: "3.4"
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - name: Install Ruby ${{ env.ruby_version }}
30
+ uses: ruby/setup-ruby@v1
31
+ with:
32
+ ruby-version: ${{ env.ruby_version }}
33
+ - name: Checkout repo
34
+ uses: actions/checkout@v5
35
+ - name: Install Toys
36
+ run: "gem install --no-document toys"
37
+ - name: Perform release
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
41
+ run: |
42
+ toys release perform --yes --verbose \
43
+ "--release-ref=${{ github.sha }}" \
44
+ ${{ github.event.inputs.flags }} \
45
+ "${{ github.event.inputs.name }}" "${{ github.event.inputs.version }}" \
46
+ < /dev/null
@@ -0,0 +1,37 @@
1
+ name: Open release request
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ names:
7
+ description: Components to release (leave blank to release all components)
8
+ required: false
9
+ default: ""
10
+
11
+ permissions:
12
+ contents: write # required for pushing changes
13
+ pull-requests: write # required for creating release PRs
14
+
15
+ jobs:
16
+ release-request:
17
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
18
+ env:
19
+ ruby_version: "3.3"
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Install Ruby ${{ env.ruby_version }}
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ env.ruby_version }}
26
+ - name: Checkout repo
27
+ uses: actions/checkout@v4
28
+ - name: Install Toys
29
+ run: "gem install --no-document toys"
30
+ - name: Open release pull request
31
+ env:
32
+ GITHUB_TOKEN: ${{ secrets.TOYS_RELEASE_REQUEST_TOKEN || secrets.GITHUB_TOKEN }}
33
+ run: |
34
+ toys release request --yes --verbose \
35
+ "--target-branch=${{ github.ref }}" \
36
+ ${{ github.event.inputs.names }} \
37
+ < /dev/null
@@ -0,0 +1,42 @@
1
+ name: Retry release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ release_pr:
7
+ description: Release PR number
8
+ required: true
9
+ flags:
10
+ description: Extra flags to pass to the release script
11
+ required: false
12
+ default: ""
13
+
14
+ permissions:
15
+ contents: write # required for creating releases
16
+ pull-requests: write # required for updating label on PR, posting comments
17
+ issues: write # required for creating new issues on failed releases
18
+
19
+ jobs:
20
+ release-retry:
21
+ if: ${{ github.repository == '<%= @settings.repo_path %>' }}
22
+ env:
23
+ ruby_version: "3.4"
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Install Ruby ${{ env.ruby_version }}
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ env.ruby_version }}
30
+ - name: Checkout repo
31
+ uses: actions/checkout@v5
32
+ - name: Install Toys
33
+ run: "gem install --no-document toys"
34
+ - name: Retry release
35
+ env:
36
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
38
+ run: |
39
+ toys release retry --yes --verbose \
40
+ ${{ github.event.inputs.flags }} \
41
+ "${{ github.event.inputs.release_pr }}" \
42
+ < /dev/null
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "tmpdir"
5
+
6
+ module Toys
7
+ module Release
8
+ ##
9
+ # Object that manages an artifact directory
10
+ #
11
+ class ArtifactDir
12
+ ##
13
+ # Create an ArtifactDir
14
+ #
15
+ # @param base_dir [String] Optional base directory, within which all the
16
+ # artifact directories will be created. If not provided, a temporary
17
+ # directory will be used.
18
+ #
19
+ def initialize(base_dir = nil)
20
+ @base_dir = base_dir
21
+ @needs_cleanup = false
22
+ @initialized = {}
23
+ end
24
+
25
+ ##
26
+ # Get the path to a directory with a given name. All calls to get with
27
+ # the same name will return the same directory path.
28
+ #
29
+ # @param name [String] Optional name. If not provided, a global default
30
+ # name is used.
31
+ #
32
+ def get(name = nil)
33
+ dir_name = name ? "#{random_id}-#{name}" : random_id
34
+ path = ::File.join(base_dir, dir_name)
35
+ unless @initialized[path]
36
+ @initialized[path] = true
37
+ ::FileUtils.remove_entry(path, true)
38
+ ::FileUtils.mkdir_p(path)
39
+ end
40
+ path
41
+ end
42
+
43
+ ##
44
+ # Perform cleanup, removing the directories if they were created under a
45
+ # temporary directory.
46
+ #
47
+ def cleanup
48
+ if @needs_cleanup
49
+ ::FileUtils.remove_entry(@base_dir, true)
50
+ @needs_cleanup = false
51
+ @base_dir = nil
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def base_dir
58
+ unless @base_dir
59
+ @base_dir = ::Dir.mktmpdir
60
+ @needs_cleanup = true
61
+ end
62
+ @base_dir
63
+ end
64
+
65
+ def random_id
66
+ @random_id ||= "#{rand(36**6).to_s(36)}#{::Time.now.to_i.to_s(36)}"
67
+ end
68
+ end
69
+ end
70
+ end