toys-release 0.8.2 → 0.9.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/docs/guide.md +112 -25
- data/lib/toys/release/version.rb +1 -1
- data/toys/.lib/toys/release/change_set.rb +1 -1
- data/toys/.lib/toys/release/gh_pages_logic.rb +284 -0
- data/toys/.lib/toys/release/repo_settings.rb +46 -0
- data/toys/.lib/toys/release/request_spec.rb +26 -1
- data/toys/.lib/toys/release/semver.rb +19 -8
- data/toys/.lib/toys/release/steps.rb +7 -35
- data/toys/.lib/toys/release/version_rb_file.rb +2 -2
- data/toys/gen-gh-pages.rb +28 -123
- data/toys/gen-workflows.rb +26 -9
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b45ad6653e6cf95dc462d60917fec18dc4cab282eb44fa6ea28952aa982bf5d
|
|
4
|
+
data.tar.gz: 8d00cbe1a53a9c6f31b982919660e04d1d10559e579293fe32f0ecd963d7e696
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0583c614ebdba3ac5cf9e0988dbab656cfd49e437e5543ec3dafab95fc0cd1506cbfb42c85f72627da3895561957e40b6f07d1f22c1cd28eb3944971f3f064b0'
|
|
7
|
+
data.tar.gz: 0e53dbb9821ea6b5e9df0f33814fc18d0da48ba6a0330b5e941e7c9b0b5cf93c8778527b5bd9b90ea32ea36183f2f594c852f37b76a6a5d436c070b8217fca13
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.9.0 / 2026-03-23
|
|
4
|
+
|
|
5
|
+
* ADDED: Provided a `version_from_code` setting that allows the current VERSION constant to determine the release version
|
|
6
|
+
* FIXED: The gen-gh-pages and gen-workflows tools no longer ask for confirmation if a file to generate is unchanged
|
|
7
|
+
* FIXED: Release versions can now omit version fields beyond major, e.g. version "1" is now legal
|
|
8
|
+
* FIXED: Minor UI improvements for the gen-gh-pages tool
|
|
9
|
+
* DOCS: Minor fixes to the toys-release guide
|
|
10
|
+
|
|
3
11
|
### v0.8.2 / 2026-03-18
|
|
4
12
|
|
|
5
13
|
* FIXED: Tighten version constant regex
|
data/docs/guide.md
CHANGED
|
@@ -70,14 +70,14 @@ If you do not have Ruby or Toys installed locally, do so first. Install
|
|
|
70
70
|
Ruby 2.7 or later, and then install the Toys RubyGem using:
|
|
71
71
|
|
|
72
72
|
```sh
|
|
73
|
-
gem install toys
|
|
73
|
+
$ gem install toys
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Toys-Release requires Toys 0.20 or later. If you have an older version of Toys,
|
|
77
77
|
update it using:
|
|
78
78
|
|
|
79
79
|
```sh
|
|
80
|
-
toys system update
|
|
80
|
+
$ toys system update
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Finally, you also need the GitHub command line tool, `gh`. Find installation
|
|
@@ -85,28 +85,37 @@ instructions at https://cli.github.com/. If you are running on MacOS, for
|
|
|
85
85
|
example, the easiest way to install it is via homebrew:
|
|
86
86
|
|
|
87
87
|
```sh
|
|
88
|
-
brew install gh
|
|
88
|
+
$ brew install gh
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
### Install the release tool
|
|
92
92
|
|
|
93
|
-
The Toys-Release tool needs to be installed in your repository
|
|
94
|
-
|
|
93
|
+
The Toys-Release tool needs to be installed in your repository. This means
|
|
94
|
+
creating a Toys tool whose implementation is provided by the
|
|
95
|
+
[toys-release](https://rubygems.org/gems/toys-release) gem.
|
|
95
96
|
|
|
96
97
|
Create `.toys/release.rb` (note the leading period in the directory name) in
|
|
97
98
|
your git repository. Use the following content:
|
|
98
99
|
|
|
99
100
|
```ruby
|
|
101
|
+
# frozen_string_literal: true
|
|
100
102
|
load_gem "toys-release"
|
|
101
103
|
```
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
Test the install by displaying the online help for toys-release:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
$ toys release --help
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If you do not already have the toys-release gem installed, this will install it
|
|
112
|
+
for you (after asking for permission.) By default, it will install the latest
|
|
113
|
+
version, but you can pin to a specific version of the gem by specifying version
|
|
114
|
+
requirements similar to how you would in RubyGems or Bundler:
|
|
107
115
|
|
|
108
116
|
```ruby
|
|
109
|
-
|
|
117
|
+
# frozen_string_literal: true
|
|
118
|
+
load_gem "toys-release", "~> 0.8"
|
|
110
119
|
```
|
|
111
120
|
|
|
112
121
|
Commit and push this change to your repository.
|
|
@@ -124,7 +133,7 @@ release tool is installed as described above, you can run this from your local
|
|
|
124
133
|
repository clone directory:
|
|
125
134
|
|
|
126
135
|
```sh
|
|
127
|
-
toys release gen-config
|
|
136
|
+
$ toys release gen-config
|
|
128
137
|
```
|
|
129
138
|
|
|
130
139
|
This will analyze your repository and generate an initial configuration file
|
|
@@ -146,13 +155,13 @@ line using the release tool.
|
|
|
146
155
|
To create the GitHub repo labels, run this from your local repo clone directory:
|
|
147
156
|
|
|
148
157
|
```sh
|
|
149
|
-
toys release create-labels
|
|
158
|
+
$ toys release create-labels
|
|
150
159
|
```
|
|
151
160
|
|
|
152
161
|
Then, to generate the GitHub Actions workflows, run:
|
|
153
162
|
|
|
154
163
|
```sh
|
|
155
|
-
toys release gen-workflows
|
|
164
|
+
$ toys release gen-workflows
|
|
156
165
|
```
|
|
157
166
|
|
|
158
167
|
This will generate files in a `.github/workflows` directory in your repository.
|
|
@@ -287,15 +296,15 @@ appending the version to the component name, separated by a colon.
|
|
|
287
296
|
For example, to request releases of the `toys` and `toys-release` components,
|
|
288
297
|
you can enter the following text into "Components to release":
|
|
289
298
|
|
|
290
|
-
```
|
|
299
|
+
```
|
|
291
300
|
toys toys-release
|
|
292
301
|
```
|
|
293
302
|
|
|
294
|
-
To make the above request but specifically request version 0.
|
|
303
|
+
To make the above request but specifically request version 1.0.0 of the
|
|
295
304
|
`toys-release` component:
|
|
296
305
|
|
|
297
|
-
```
|
|
298
|
-
toys toys-release:0.
|
|
306
|
+
```
|
|
307
|
+
toys toys-release:1.0.0
|
|
299
308
|
```
|
|
300
309
|
|
|
301
310
|
### Managing release pull requests
|
|
@@ -444,7 +453,7 @@ To set up documentation, do the following:
|
|
|
444
453
|
* Create a starting gh-pages branch by running:
|
|
445
454
|
|
|
446
455
|
```sh
|
|
447
|
-
toys release gen-gh-pages
|
|
456
|
+
$ toys release gen-gh-pages
|
|
448
457
|
```
|
|
449
458
|
|
|
450
459
|
This will generate the gh-pages branch and push some key files to it,
|
|
@@ -536,6 +545,49 @@ and affect the behavior of that and other commits.
|
|
|
536
545
|
no-touch-component: my_gem
|
|
537
546
|
```
|
|
538
547
|
|
|
548
|
+
### Using code-specified versions
|
|
549
|
+
|
|
550
|
+
By default, Toys-Release determines the version to release by analyzing the
|
|
551
|
+
conventional commit history since the last release. For each component, it
|
|
552
|
+
selects the semver bump implied by the commits and applies it to the last
|
|
553
|
+
released version.
|
|
554
|
+
|
|
555
|
+
As an alternative, you can configure a component to use the version specified
|
|
556
|
+
directly in the code — specifically, the `VERSION` constant in the `version.rb`
|
|
557
|
+
file (as identified by the **version_rb_path** setting) — as the target release
|
|
558
|
+
version. This is useful when you prefer to control the version number manually
|
|
559
|
+
or when your workflow requires the version to be set in the code before a
|
|
560
|
+
release is requested.
|
|
561
|
+
|
|
562
|
+
To enable this, set `version_from_code: true` in the component's configuration:
|
|
563
|
+
|
|
564
|
+
```yaml
|
|
565
|
+
components:
|
|
566
|
+
- name: my_gem
|
|
567
|
+
version_from_code: true
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
With this setting, when a release is requested, Toys-Release reads the version
|
|
571
|
+
constant from the code at the tip of the release branch. If this version is
|
|
572
|
+
newer than the last released version, it is used as the release version.
|
|
573
|
+
|
|
574
|
+
If the code version is not newer than the last released version (for example,
|
|
575
|
+
if you forgot to update it before requesting the release), the release request
|
|
576
|
+
will fail with an error by default. You can configure the optional `bump`
|
|
577
|
+
sub-setting to specify a fallback semver bump level to apply to the last
|
|
578
|
+
released version instead:
|
|
579
|
+
|
|
580
|
+
```yaml
|
|
581
|
+
components:
|
|
582
|
+
- name: my_gem
|
|
583
|
+
version_from_code:
|
|
584
|
+
bump: patch2
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
With `bump: patch2` (or `patch`, `minor`, or `major`), if the code version is
|
|
588
|
+
not newer than the last release, the last released version is bumped by the
|
|
589
|
+
given level to produce a releasable version.
|
|
590
|
+
|
|
539
591
|
### Running on the command line
|
|
540
592
|
|
|
541
593
|
The implementation of Toys-Release is done via Toys (i.e. command line) tools.
|
|
@@ -1177,6 +1229,41 @@ The **name** key is required. The others are optional.
|
|
|
1177
1229
|
libraries up to date. If this setting is not present, automatic updating is
|
|
1178
1230
|
not performed for this component.
|
|
1179
1231
|
|
|
1232
|
+
* **version_from_code**: *boolean or dictionary* (optional) --
|
|
1233
|
+
When set, uses the version constant in the component's code (as identified
|
|
1234
|
+
by the **version_rb_path** setting) as the version to release, rather than
|
|
1235
|
+
computing it automatically from the conventional commit history.
|
|
1236
|
+
|
|
1237
|
+
Set this to `true` to enable the feature with default settings:
|
|
1238
|
+
|
|
1239
|
+
```yaml
|
|
1240
|
+
components:
|
|
1241
|
+
- name: my_gem
|
|
1242
|
+
version_from_code: true
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
Or set it to a dictionary to provide additional configuration:
|
|
1246
|
+
|
|
1247
|
+
```yaml
|
|
1248
|
+
components:
|
|
1249
|
+
- name: my_gem
|
|
1250
|
+
version_from_code:
|
|
1251
|
+
bump: patch
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
The dictionary supports the following optional key:
|
|
1255
|
+
|
|
1256
|
+
* **bump**: *string* (optional) --
|
|
1257
|
+
What to do if the version specified in the code has already been
|
|
1258
|
+
released (i.e. the code version is not newer than the most recently
|
|
1259
|
+
released version). Possible values are `patch2`, `patch`, `minor`,
|
|
1260
|
+
`major`, and `none`. If `none` (the default), the release request will
|
|
1261
|
+
fail with an error. If a semver bump level is given, the last released
|
|
1262
|
+
version is bumped by that level to produce a releasable version.
|
|
1263
|
+
|
|
1264
|
+
See also [Using code-specified versions](#using-code-specified-versions)
|
|
1265
|
+
for a conceptual overview.
|
|
1266
|
+
|
|
1180
1267
|
* **version_rb_path**: *string* (optional) --
|
|
1181
1268
|
The path to a Ruby file that contains the current version of the component.
|
|
1182
1269
|
This file *must* include Ruby code that looks like this:
|
|
@@ -1200,20 +1287,20 @@ released with updated dependency versions, due to one or more of those
|
|
|
1200
1287
|
dependencies being released. It is typically used to keep "kitchen sink"
|
|
1201
1288
|
libraries up to date.
|
|
1202
1289
|
|
|
1203
|
-
For example, consider two components "
|
|
1204
|
-
component "
|
|
1205
|
-
or greater release of either "
|
|
1290
|
+
For example, consider two components "gem_a" and "gem_b", and a "kitchen sink"
|
|
1291
|
+
component "gem_all" that depends on both the others. Suppose whenever a patch
|
|
1292
|
+
or greater release of either "gem_a" or "gem_b" happens, we also want "gem_all"
|
|
1206
1293
|
to be released with its corresponding dependency bumped to the same version. We
|
|
1207
1294
|
might set up the configuration like so:
|
|
1208
1295
|
|
|
1209
1296
|
```yaml
|
|
1210
1297
|
components:
|
|
1211
|
-
- name:
|
|
1212
|
-
- name:
|
|
1213
|
-
- name:
|
|
1298
|
+
- name: gem_a
|
|
1299
|
+
- name: gem_b
|
|
1300
|
+
- name: gem_all
|
|
1214
1301
|
update_dependencies:
|
|
1215
1302
|
dependency_semver_threshold: patch
|
|
1216
|
-
dependencies: [
|
|
1303
|
+
dependencies: [gem_a, gem_b]
|
|
1217
1304
|
```
|
|
1218
1305
|
|
|
1219
1306
|
The update-dependencies configuration for a kitchen sink component can include
|
data/lib/toys/release/version.rb
CHANGED
|
@@ -162,7 +162,7 @@ module Toys
|
|
|
162
162
|
def suggested_version(last)
|
|
163
163
|
raise "ChangeSet not finished" unless finished?
|
|
164
164
|
return nil unless semver.significant?
|
|
165
|
-
semver.bump(last)
|
|
165
|
+
semver.bump(last, minimum_fill: Semver::PATCH, prevent_bump_to_v1: true)
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
##
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Toys
|
|
7
|
+
module Release
|
|
8
|
+
##
|
|
9
|
+
# Logic for generating and updating gh-pages documentation site files.
|
|
10
|
+
#
|
|
11
|
+
class GhPagesLogic
|
|
12
|
+
##
|
|
13
|
+
# Create a GhPagesLogic instance.
|
|
14
|
+
#
|
|
15
|
+
# @param repo_settings [Toys::Release::RepoSettings] Repository settings
|
|
16
|
+
#
|
|
17
|
+
def initialize(repo_settings)
|
|
18
|
+
@enabled_component_settings = repo_settings.all_component_settings.select(&:gh_pages_enabled)
|
|
19
|
+
raise ::ArgumentError, "No components have gh-pages enabled" if @enabled_component_settings.empty?
|
|
20
|
+
@url_base_path = "#{repo_settings.repo_owner}.github.io/#{repo_settings.repo_name}"
|
|
21
|
+
@default_redirect_url = "https://#{component_base_path(@enabled_component_settings.first)}/latest"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Clean up non-index files from the v0 subdirectory of each gh-pages-
|
|
26
|
+
# enabled component. The given block is called for each component whose
|
|
27
|
+
# v0 directory contains files other than index.html, and receives the
|
|
28
|
+
# directory path and the list of files to remove. The block should return
|
|
29
|
+
# true to remove the files, or false to skip. If no block is given, files
|
|
30
|
+
# are removed unconditionally.
|
|
31
|
+
#
|
|
32
|
+
# @param gh_pages_dir [String] Path to the gh-pages working tree
|
|
33
|
+
# @yieldparam directory [String] Path relative to gh_pages_dir of the v0 dir
|
|
34
|
+
# @yieldparam children [Array<String>] Non-index filenames to remove
|
|
35
|
+
# @yieldreturn [boolean] Whether to remove the files
|
|
36
|
+
# @return [Array<Hash>] Results, one per enabled component, each with
|
|
37
|
+
# keys :directory (relative path), :children, and :removed
|
|
38
|
+
#
|
|
39
|
+
def cleanup_v0_directories(gh_pages_dir, &confirm)
|
|
40
|
+
@enabled_component_settings.map do |comp_settings|
|
|
41
|
+
cleanup_component_v0(gh_pages_dir, comp_settings, &confirm)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Generate all gh-pages scaffold files into the given directory.
|
|
47
|
+
# The given block is called for each file that needs to be created or
|
|
48
|
+
# overwritten (but NOT for unchanged files), and receives the destination
|
|
49
|
+
# path, the status (:new or :overwrite), and the existing file type
|
|
50
|
+
# (only meaningful for :overwrite). The block should return true to
|
|
51
|
+
# write the file, or false to skip.
|
|
52
|
+
#
|
|
53
|
+
# @param gh_pages_dir [String] Path to the gh-pages working tree
|
|
54
|
+
# @param template_dir [String] Path to the directory containing ERB
|
|
55
|
+
# templates for gh-pages files
|
|
56
|
+
# @yieldparam destination [String] Path relative to gh_pages_dir of the destination file
|
|
57
|
+
# @yieldparam status [Symbol] :new or :overwrite
|
|
58
|
+
# @yieldparam existing_ftype [String,nil] The ftype of the existing entry
|
|
59
|
+
# @yieldreturn [boolean] Whether to write the file
|
|
60
|
+
# @return [Array<Hash>] Results, one per file considered, each with
|
|
61
|
+
# keys :destination (relative path) and :outcome (:wrote, :skipped, or :unchanged)
|
|
62
|
+
#
|
|
63
|
+
def generate_files(gh_pages_dir, template_dir, &confirm)
|
|
64
|
+
results = []
|
|
65
|
+
@enabled_component_settings.each do |comp_settings|
|
|
66
|
+
generate_component_files(gh_pages_dir, template_dir, comp_settings, results, &confirm)
|
|
67
|
+
end
|
|
68
|
+
generate_toplevel_files(gh_pages_dir, template_dir, results, &confirm)
|
|
69
|
+
generate_html404(gh_pages_dir, template_dir, results, &confirm)
|
|
70
|
+
results
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# Update the 404 page and redirect index pages for a new component
|
|
75
|
+
# release. The optional block is called with a warning message when a
|
|
76
|
+
# required file is not found.
|
|
77
|
+
#
|
|
78
|
+
# @param gh_pages_dir [String] Path to the gh-pages working tree
|
|
79
|
+
# @param component_settings [Toys::Release::ComponentSettings] Settings
|
|
80
|
+
# for the component being released
|
|
81
|
+
# @param version [Gem::Version] The new version being released
|
|
82
|
+
# @yieldparam warning [String] A warning message for a missing file
|
|
83
|
+
#
|
|
84
|
+
def update_version_pages(gh_pages_dir, component_settings, version, &on_warning)
|
|
85
|
+
update_404_page(gh_pages_dir, component_settings, version, &on_warning)
|
|
86
|
+
update_index_pages(gh_pages_dir, component_settings, version, &on_warning)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Context object for ERB template rendering
|
|
92
|
+
class ErbContext
|
|
93
|
+
def initialize(data)
|
|
94
|
+
data.each { |name, value| instance_variable_set("@#{name}", value) }
|
|
95
|
+
freeze
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @private
|
|
99
|
+
def self.get(data)
|
|
100
|
+
new(data).instance_eval { binding }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
private_constant :ErbContext
|
|
104
|
+
|
|
105
|
+
# Struct carrying info about a component for the 404 template
|
|
106
|
+
CompInfo = ::Struct.new(:base_path, :regexp_source, :version_var)
|
|
107
|
+
private_constant :CompInfo
|
|
108
|
+
|
|
109
|
+
# Cleans up a single component's v0 directory, yielding to the caller for confirmation.
|
|
110
|
+
def cleanup_component_v0(gh_pages_dir, comp_settings)
|
|
111
|
+
relative_dir = simplifying_join(comp_settings.gh_pages_directory, "v0")
|
|
112
|
+
directory = ::File.expand_path(relative_dir, gh_pages_dir)
|
|
113
|
+
::FileUtils.mkdir_p(directory)
|
|
114
|
+
children = ::Dir.children(directory) - ["index.html"]
|
|
115
|
+
removed = false
|
|
116
|
+
if !children.empty? && (!block_given? || yield(relative_dir, children))
|
|
117
|
+
children.each { |child| ::FileUtils.remove_entry(::File.join(directory, child), true) }
|
|
118
|
+
removed = true
|
|
119
|
+
end
|
|
120
|
+
{directory: relative_dir, children: children, removed: removed}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Returns the URL base path for a component, incorporating its gh_pages_directory if set.
|
|
124
|
+
def component_base_path(comp_settings)
|
|
125
|
+
simplifying_join(@url_base_path, comp_settings.gh_pages_directory)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Scans the component's gh-pages directory and returns the highest existing released version,
|
|
129
|
+
# or "0" if no versioned subdirectories exist yet.
|
|
130
|
+
def current_component_version(gh_pages_dir, comp_settings)
|
|
131
|
+
base_dir = ::File.expand_path(comp_settings.gh_pages_directory, gh_pages_dir)
|
|
132
|
+
latest = ::Gem::Version.new("0")
|
|
133
|
+
return latest unless ::File.directory?(base_dir)
|
|
134
|
+
::Dir.children(base_dir).each do |child|
|
|
135
|
+
next unless /^v\d+(\.\d+)*$/.match?(child)
|
|
136
|
+
next unless ::File.directory?(::File.join(base_dir, child))
|
|
137
|
+
version = ::Gem::Version.new(child[1..])
|
|
138
|
+
latest = version if version > latest
|
|
139
|
+
end
|
|
140
|
+
latest
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Renders an ERB template from template_dir with the given data hash and returns the result.
|
|
144
|
+
def render_template(template_dir, template_name, data)
|
|
145
|
+
template_path = ::File.join(template_dir, template_name)
|
|
146
|
+
raise "Unable to find template #{template_name}" unless ::File.file?(template_path)
|
|
147
|
+
erb = ::ERB.new(::File.read(template_path))
|
|
148
|
+
erb.result(ErbContext.get(data))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Updates the version variable assignment in 404.html for the given component.
|
|
152
|
+
def update_404_page(gh_pages_dir, component_settings, version)
|
|
153
|
+
path = ::File.join(gh_pages_dir, "404.html")
|
|
154
|
+
unless ::File.file?(path)
|
|
155
|
+
yield "404.html not found. Skipping." if block_given?
|
|
156
|
+
return
|
|
157
|
+
end
|
|
158
|
+
content = ::File.read(path)
|
|
159
|
+
version_var = component_settings.gh_pages_version_var
|
|
160
|
+
content.sub!(/#{::Regexp.escape(version_var)} = "[\w.]+";/,
|
|
161
|
+
"#{version_var} = \"#{version}\";")
|
|
162
|
+
::File.write(path, content)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Updates the redirect URLs in index.html and latest/index.html to point at the new version.
|
|
166
|
+
def update_index_pages(gh_pages_dir, component_settings, version)
|
|
167
|
+
redirect_url = "https://#{component_base_path(component_settings)}/v#{version}"
|
|
168
|
+
["index.html", "latest/index.html"].each do |filename|
|
|
169
|
+
relative_path = simplifying_join(component_settings.gh_pages_directory, filename)
|
|
170
|
+
absolute_path = ::File.expand_path(relative_path, gh_pages_dir)
|
|
171
|
+
unless ::File.file?(absolute_path)
|
|
172
|
+
yield "#{relative_path} not found. Skipping." if block_given?
|
|
173
|
+
next
|
|
174
|
+
end
|
|
175
|
+
content = ::File.read(absolute_path)
|
|
176
|
+
content.gsub!(/ href="[^"]+"/, " href=\"#{redirect_url}\"")
|
|
177
|
+
content.gsub!(/ content="0; url=[^"]+"/, " content=\"0; url=#{redirect_url}\"")
|
|
178
|
+
content.gsub!(/window\.location\.replace\("[^"]+"\)/, "window.location.replace(\"#{redirect_url}\")")
|
|
179
|
+
::File.write(absolute_path, content)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Returns File.lstat for path, or nil if the path does not exist.
|
|
184
|
+
def safe_lstat(path)
|
|
185
|
+
::File.lstat(path)
|
|
186
|
+
rescue ::SystemCallError
|
|
187
|
+
nil
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Returns the contents of path as a string, or nil if the file cannot be read.
|
|
191
|
+
def safe_read(path)
|
|
192
|
+
::File.read(path)
|
|
193
|
+
rescue ::SystemCallError
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Joins paths, simplifying if either argument is "."
|
|
198
|
+
def simplifying_join(path1, path2)
|
|
199
|
+
if path1 == "."
|
|
200
|
+
path2
|
|
201
|
+
elsif path2 == "."
|
|
202
|
+
path1
|
|
203
|
+
else
|
|
204
|
+
"#{path1}/#{path2}"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Writes content to a relative destination, appending to results. Skips unchanged files
|
|
209
|
+
# without calling the block; calls the block for new or overwrite cases to confirm.
|
|
210
|
+
def write_file(gh_pages_dir, relative_destination, content, results, &confirm)
|
|
211
|
+
destination = ::File.expand_path(relative_destination, gh_pages_dir)
|
|
212
|
+
stat = safe_lstat(destination)
|
|
213
|
+
if stat
|
|
214
|
+
if stat.file? && safe_read(destination) == content
|
|
215
|
+
results << {destination: relative_destination, outcome: :unchanged}
|
|
216
|
+
return
|
|
217
|
+
end
|
|
218
|
+
status = :overwrite
|
|
219
|
+
ftype = stat.ftype
|
|
220
|
+
else
|
|
221
|
+
status = :new
|
|
222
|
+
ftype = nil
|
|
223
|
+
end
|
|
224
|
+
proceed = confirm ? confirm.call(relative_destination, status, ftype) : true
|
|
225
|
+
if proceed
|
|
226
|
+
::FileUtils.mkdir_p(::File.dirname(destination))
|
|
227
|
+
::FileUtils.remove_entry(destination, true) if stat
|
|
228
|
+
::File.write(destination, content)
|
|
229
|
+
results << {destination: relative_destination, outcome: :wrote}
|
|
230
|
+
else
|
|
231
|
+
results << {destination: relative_destination, outcome: :skipped}
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Generates the v0 placeholder, component index, and latest/index redirect for one component.
|
|
236
|
+
def generate_component_files(gh_pages_dir, template_dir, comp_settings, results, &confirm)
|
|
237
|
+
version = current_component_version(gh_pages_dir, comp_settings)
|
|
238
|
+
redirect_url = "https://#{component_base_path(comp_settings)}/v#{version}"
|
|
239
|
+
subdir = comp_settings.gh_pages_directory
|
|
240
|
+
|
|
241
|
+
write_file(gh_pages_dir, simplifying_join(subdir, "v0/index.html"),
|
|
242
|
+
render_template(template_dir, "empty.html.erb", {name: comp_settings.name}),
|
|
243
|
+
results, &confirm)
|
|
244
|
+
write_file(gh_pages_dir, simplifying_join(subdir, "index.html"),
|
|
245
|
+
render_template(template_dir, "redirect.html.erb", {redirect_url: redirect_url}),
|
|
246
|
+
results, &confirm)
|
|
247
|
+
write_file(gh_pages_dir, simplifying_join(subdir, "latest/index.html"),
|
|
248
|
+
render_template(template_dir, "redirect.html.erb", {redirect_url: redirect_url}),
|
|
249
|
+
results, &confirm)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Generates .nojekyll, .gitignore, and (when no root component exists) the root index redirect.
|
|
253
|
+
def generate_toplevel_files(gh_pages_dir, template_dir, results, &confirm)
|
|
254
|
+
write_file(gh_pages_dir, ".nojekyll", "", results, &confirm)
|
|
255
|
+
write_file(gh_pages_dir, ".gitignore", render_template(template_dir, "gitignore.erb", {}), results, &confirm)
|
|
256
|
+
|
|
257
|
+
return if @enabled_component_settings.any? { |s| s.gh_pages_directory == "." }
|
|
258
|
+
|
|
259
|
+
write_file(gh_pages_dir, "index.html",
|
|
260
|
+
render_template(template_dir, "redirect.html.erb", {redirect_url: @default_redirect_url}),
|
|
261
|
+
results, &confirm)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Generates 404.html with version variables and redirect-replacement regexps for all components.
|
|
265
|
+
def generate_html404(gh_pages_dir, template_dir, results, &confirm)
|
|
266
|
+
version_vars = {}
|
|
267
|
+
replacement_info = @enabled_component_settings.map do |comp_settings|
|
|
268
|
+
version_vars[comp_settings.gh_pages_version_var] =
|
|
269
|
+
current_component_version(gh_pages_dir, comp_settings)
|
|
270
|
+
base_path = component_base_path(comp_settings)
|
|
271
|
+
regexp_source = "//#{::Regexp.escape(base_path)}/latest(/|$)"
|
|
272
|
+
CompInfo.new(base_path, regexp_source, comp_settings.gh_pages_version_var)
|
|
273
|
+
end
|
|
274
|
+
template_params = {
|
|
275
|
+
default_redirect_url: @default_redirect_url,
|
|
276
|
+
version_vars: version_vars,
|
|
277
|
+
replacement_info: replacement_info,
|
|
278
|
+
}
|
|
279
|
+
write_file(gh_pages_dir, "404.html", render_template(template_dir, "404.html.erb", template_params),
|
|
280
|
+
results, &confirm)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
@@ -170,6 +170,36 @@ module Toys
|
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
+
##
|
|
174
|
+
# This configuration is present for a component if version_from_code is
|
|
175
|
+
# requested, i.e. the version to release is the version in the code.
|
|
176
|
+
#
|
|
177
|
+
class VersionFromCodeSettings
|
|
178
|
+
# @private
|
|
179
|
+
def initialize(info, errors)
|
|
180
|
+
unless info.is_a?(::Hash)
|
|
181
|
+
errors << "version_from_code expected to be true or a dictionary" unless info == true
|
|
182
|
+
info = {}
|
|
183
|
+
end
|
|
184
|
+
bump_name = info.delete("bump") || "none"
|
|
185
|
+
@bump = Semver.for_name(bump_name)
|
|
186
|
+
unless @bump
|
|
187
|
+
errors << "Unrecognized semver bump value: #{bump_name.inspect}"
|
|
188
|
+
@bump = Semver::NONE
|
|
189
|
+
end
|
|
190
|
+
info.each_key do |key|
|
|
191
|
+
errors << "Unknown key #{key.inspect} for version_from_code"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# @return [Semver] What semver field to bump if the requested version has
|
|
197
|
+
# already been released. If this is {Semver::NONE} and the requested
|
|
198
|
+
# version is already released, the release request will error out.
|
|
199
|
+
#
|
|
200
|
+
attr_reader :bump
|
|
201
|
+
end
|
|
202
|
+
|
|
173
203
|
##
|
|
174
204
|
# Configuration of a single component
|
|
175
205
|
#
|
|
@@ -191,6 +221,7 @@ module Toys
|
|
|
191
221
|
read_steps_info(info, repo_settings)
|
|
192
222
|
read_commit_tag_info(info, repo_settings)
|
|
193
223
|
read_update_deps(info, repo_settings)
|
|
224
|
+
read_version_from_code(info, repo_settings)
|
|
194
225
|
check_problems(info, repo_settings)
|
|
195
226
|
end
|
|
196
227
|
|
|
@@ -292,6 +323,13 @@ module Toys
|
|
|
292
323
|
#
|
|
293
324
|
attr_reader :update_dependencies
|
|
294
325
|
|
|
326
|
+
##
|
|
327
|
+
# @return [VersionFromCodeSettings,nil] Configuration for using
|
|
328
|
+
# the version in code as the version to release instead of
|
|
329
|
+
# interpreting conventional commit tags.
|
|
330
|
+
#
|
|
331
|
+
attr_reader :version_from_code
|
|
332
|
+
|
|
295
333
|
##
|
|
296
334
|
# @return [StepSettings,nil] The unique step with the given name
|
|
297
335
|
#
|
|
@@ -376,6 +414,14 @@ module Toys
|
|
|
376
414
|
end
|
|
377
415
|
end
|
|
378
416
|
|
|
417
|
+
def read_version_from_code(info, repo_settings)
|
|
418
|
+
vfc_info = info.delete("version_from_code")
|
|
419
|
+
@version_from_code =
|
|
420
|
+
if vfc_info
|
|
421
|
+
VersionFromCodeSettings.new(vfc_info, repo_settings.errors)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
379
425
|
def camelize(str)
|
|
380
426
|
str.to_s
|
|
381
427
|
.sub(/^_/, "")
|
|
@@ -249,13 +249,38 @@ module Toys
|
|
|
249
249
|
@utils.log("Creating #{component.name} changeset from #{latest_tag || 'start'} to #{@release_sha}")
|
|
250
250
|
changeset = component.make_change_set(from: latest_tag, to: @release_sha)
|
|
251
251
|
unless requested_version
|
|
252
|
-
cur_suggested_version =
|
|
252
|
+
cur_suggested_version = determine_suggested_version(component, changeset, requested_bump, last_version)
|
|
253
253
|
if !best_suggested_version || (cur_suggested_version && cur_suggested_version > best_suggested_version)
|
|
254
254
|
best_suggested_version = cur_suggested_version
|
|
255
255
|
end
|
|
256
256
|
end
|
|
257
257
|
[ResolvedComponent.new(component.name, changeset, last_version, nil), best_suggested_version]
|
|
258
258
|
end
|
|
259
|
+
|
|
260
|
+
def determine_suggested_version(component, changeset, requested_bump, last_version)
|
|
261
|
+
vfc_settings = component.settings.version_from_code
|
|
262
|
+
if vfc_settings
|
|
263
|
+
requested_version = component.current_constant_version(at: @release_sha)
|
|
264
|
+
if requested_version.nil?
|
|
265
|
+
@utils.error("Unable to read code-specified version for #{component.name}")
|
|
266
|
+
elsif last_version.nil? || requested_version > last_version
|
|
267
|
+
@utils.log("Using code-specified version for #{component.name}")
|
|
268
|
+
requested_version
|
|
269
|
+
elsif vfc_settings.bump == Semver::NONE
|
|
270
|
+
@utils.error("Requested #{component.name} #{requested_version} but #{last_version} is the latest.")
|
|
271
|
+
else
|
|
272
|
+
@utils.log("Requested #{component.name} #{requested_version} but #{last_version} is the latest. " \
|
|
273
|
+
"Bumping to get a releasable version.")
|
|
274
|
+
vfc_settings.bump.bump(last_version, minimum_fill: Semver::PATCH, prevent_bump_to_v1: true)
|
|
275
|
+
end
|
|
276
|
+
elsif requested_bump
|
|
277
|
+
@utils.log("Using requested semver bump of #{requested_bump} for #{component.name}")
|
|
278
|
+
requested_bump.bump(last_version, minimum_fill: Semver::PATCH, prevent_bump_to_v1: true)
|
|
279
|
+
else
|
|
280
|
+
@utils.log("Using changeset-suggested version for #{component.name}")
|
|
281
|
+
changeset.suggested_version(last_version)
|
|
282
|
+
end
|
|
283
|
+
end
|
|
259
284
|
end
|
|
260
285
|
end
|
|
261
286
|
end
|
|
@@ -74,19 +74,30 @@ module Toys
|
|
|
74
74
|
#
|
|
75
75
|
# @param version [::Gem::Version] The original version. If nil is passed
|
|
76
76
|
# in, it is treated as synonymous with `0.0.0`.
|
|
77
|
+
# @param minimum_fill [Semver] Remove zeros down to this length. Default
|
|
78
|
+
# is NONE, indicating to leave the length the same as the input.
|
|
79
|
+
# @param prevent_bump_to_v1 [boolean] If set to true, major version bumps
|
|
80
|
+
# prior to 1.0 will actually bump the minor version, preventing a
|
|
81
|
+
# normal bump to version 1.0.
|
|
77
82
|
# @return [::Gem::Version] The new version
|
|
78
83
|
#
|
|
79
|
-
def bump(version)
|
|
84
|
+
def bump(version, minimum_fill: Semver::NONE, prevent_bump_to_v1: false)
|
|
80
85
|
version ||= ::Gem::Version.new("0.0.0")
|
|
81
86
|
return version if segment.nil?
|
|
82
87
|
bump_seg = segment
|
|
83
|
-
version_segs = version.segments
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
version_segs = version.segments.dup
|
|
89
|
+
bump_seg = 1 if prevent_bump_to_v1 && bump_seg.zero? && version_segs[0].zero?
|
|
90
|
+
if version_segs.size > bump_seg
|
|
91
|
+
fill_seg = minimum_fill.segment || (version_segs.size - 1)
|
|
92
|
+
fill_seg = bump_seg if fill_seg < bump_seg
|
|
93
|
+
version_segs.slice!((fill_seg + 1)..)
|
|
94
|
+
version_segs[bump_seg] += 1
|
|
95
|
+
version_segs.fill(0, bump_seg + 1)
|
|
96
|
+
else
|
|
97
|
+
version_segs.concat(::Array.new(bump_seg - version_segs.size, 0))
|
|
98
|
+
version_segs << 1
|
|
99
|
+
end
|
|
100
|
+
::Gem::Version.new(version_segs.join("."))
|
|
90
101
|
end
|
|
91
102
|
|
|
92
103
|
##
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "toys/release/gh_pages_logic"
|
|
4
5
|
require "toys/utils/gems"
|
|
5
6
|
|
|
6
7
|
module Toys
|
|
@@ -300,8 +301,12 @@ module Toys
|
|
|
300
301
|
dest_dir = ::File.join(component_dir, "v#{step_context.release_version}")
|
|
301
302
|
check_existence(step_context, dest_dir)
|
|
302
303
|
copy_docs_dir(step_context, dest_dir)
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
logic = ::Toys::Release::GhPagesLogic.new(step_context.repository.settings)
|
|
305
|
+
logic.update_version_pages(
|
|
306
|
+
gh_pages_dir,
|
|
307
|
+
step_context.component.settings,
|
|
308
|
+
step_context.release_version
|
|
309
|
+
) { |msg| step_context.warning(msg) }
|
|
305
310
|
push_docs_to_git(step_context, gh_pages_dir)
|
|
306
311
|
end
|
|
307
312
|
|
|
@@ -341,39 +346,6 @@ module Toys
|
|
|
341
346
|
::FileUtils.cp_r(source_dir, dest_dir)
|
|
342
347
|
end
|
|
343
348
|
|
|
344
|
-
def update_404_page(step_context, gh_pages_dir)
|
|
345
|
-
path = ::File.join(gh_pages_dir, "404.html")
|
|
346
|
-
unless ::File.file?(path)
|
|
347
|
-
step_context.warning("404.html not found. Skipping.")
|
|
348
|
-
return
|
|
349
|
-
end
|
|
350
|
-
content = ::File.read(path)
|
|
351
|
-
version_var = step_context.component.settings.gh_pages_version_var
|
|
352
|
-
content.sub!(/#{Regexp.escape(version_var)} = "[\w.]+";/,
|
|
353
|
-
"#{version_var} = \"#{step_context.release_version}\";")
|
|
354
|
-
::File.write(path, content)
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def update_index_pages(step_context, gh_pages_dir)
|
|
358
|
-
subdir = step_context.component.settings.gh_pages_directory
|
|
359
|
-
dir_suffix = subdir == "." ? "" : "/#{subdir}"
|
|
360
|
-
settings = step_context.repository.settings
|
|
361
|
-
version = step_context.release_version
|
|
362
|
-
redirect_url = "https://#{settings.repo_owner}.github.io/#{settings.repo_name}#{dir_suffix}/v#{version}"
|
|
363
|
-
["index.html", "latest/index.html"].each do |filename|
|
|
364
|
-
path = "#{gh_pages_dir}#{dir_suffix}/#{filename}"
|
|
365
|
-
unless ::File.file?(path)
|
|
366
|
-
step_context.warning("#{path} not found. Skipping.")
|
|
367
|
-
next
|
|
368
|
-
end
|
|
369
|
-
content = ::File.read(path)
|
|
370
|
-
content.gsub!(/ href="[^"]+"/, " href=\"#{redirect_url}\"")
|
|
371
|
-
content.gsub!(/ content="0; url=[^"]+"/, " content=\"0; url=#{redirect_url}\"")
|
|
372
|
-
content.gsub!(/window\.location\.replace\("[^"]+"\)/, "window.location.replace(\"#{redirect_url}\")")
|
|
373
|
-
::File.write(path, content)
|
|
374
|
-
end
|
|
375
|
-
end
|
|
376
|
-
|
|
377
349
|
def push_docs_to_git(step_context, gh_pages_dir)
|
|
378
350
|
::Dir.chdir(gh_pages_dir) do
|
|
379
351
|
step_context.repository.git_commit("Generated docs for #{step_context.release_description}",
|
|
@@ -50,7 +50,7 @@ module Toys
|
|
|
50
50
|
#
|
|
51
51
|
def update_version(version)
|
|
52
52
|
@utils.log("Updating #{path} to set VERSION=#{version}")
|
|
53
|
-
new_content = content.sub(/(?<=\W)VERSION\s*=\s*(["'])(\d+(?:\.[a-zA-Z0-9]+)
|
|
53
|
+
new_content = content.sub(/(?<=\W)VERSION\s*=\s*(["'])(\d+(?:\.[a-zA-Z0-9]+)*)\1/,
|
|
54
54
|
"VERSION = \\1#{version}\\1")
|
|
55
55
|
::File.write(path, new_content)
|
|
56
56
|
self
|
|
@@ -64,7 +64,7 @@ module Toys
|
|
|
64
64
|
# @return [nil] if no version was found
|
|
65
65
|
#
|
|
66
66
|
def self.current_version_from_content(content)
|
|
67
|
-
match = /(?<=\W)VERSION\s*=\s*(["'])(\d+(?:\.[a-zA-Z0-9]+)
|
|
67
|
+
match = /(?<=\W)VERSION\s*=\s*(["'])(\d+(?:\.[a-zA-Z0-9]+)*)\1/.match(content)
|
|
68
68
|
match ? ::Gem::Version.new(match[2]) : nil
|
|
69
69
|
end
|
|
70
70
|
end
|
data/toys/gen-gh-pages.rb
CHANGED
|
@@ -21,18 +21,6 @@ flag :dry_run
|
|
|
21
21
|
include :exec
|
|
22
22
|
include :terminal, styled: true
|
|
23
23
|
|
|
24
|
-
# Context for ERB templates
|
|
25
|
-
class ErbContext
|
|
26
|
-
def initialize(data)
|
|
27
|
-
data.each { |name, value| instance_variable_set("@#{name}", value) }
|
|
28
|
-
freeze
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def self.get(data)
|
|
32
|
-
new(data).instance_eval { binding }
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
24
|
def run
|
|
37
25
|
setup
|
|
38
26
|
generate_gh_pages
|
|
@@ -41,10 +29,9 @@ def run
|
|
|
41
29
|
end
|
|
42
30
|
|
|
43
31
|
def setup
|
|
44
|
-
require "erb"
|
|
45
|
-
require "fileutils"
|
|
46
32
|
require "toys/release/artifact_dir"
|
|
47
33
|
require "toys/release/environment_utils"
|
|
34
|
+
require "toys/release/gh_pages_logic"
|
|
48
35
|
require "toys/release/repo_settings"
|
|
49
36
|
require "toys/release/repository"
|
|
50
37
|
|
|
@@ -57,129 +44,47 @@ def setup
|
|
|
57
44
|
branch: "gh-pages", remote: git_remote, dir: @artifact_dir.get("gh-pages"),
|
|
58
45
|
gh_token: ::ENV["GITHUB_TOKEN"], create: true
|
|
59
46
|
)
|
|
60
|
-
|
|
61
|
-
if @relevant_component_settings.empty?
|
|
47
|
+
if @settings.all_component_settings.none?(&:gh_pages_enabled)
|
|
62
48
|
puts "No components have gh-pages enabled", :red, :bold
|
|
63
49
|
exit(1)
|
|
64
50
|
end
|
|
51
|
+
@template_dir = find_data("gh-pages", type: :directory)
|
|
52
|
+
raise "Fatal: Unable to find gh-pages template data directory" unless @template_dir
|
|
53
|
+
@logic = Toys::Release::GhPagesLogic.new(@settings)
|
|
65
54
|
end
|
|
66
55
|
|
|
67
56
|
def cleanup
|
|
68
57
|
@artifact_dir.cleanup
|
|
69
58
|
end
|
|
70
59
|
|
|
71
|
-
CompInfo = ::Struct.new(:base_path, :regexp_source, :version_var)
|
|
72
|
-
|
|
73
60
|
def generate_gh_pages
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
generate_toplevel_files
|
|
79
|
-
generate_html404
|
|
61
|
+
@logic.cleanup_v0_directories(@gh_pages_dir) do |directory, _children|
|
|
62
|
+
puts "Non-index files exist in #{directory}.", :yellow, :bold
|
|
63
|
+
yes || confirm("Remove? ", default: true)
|
|
80
64
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
prepare_v0_directory("#{comp_settings.gh_pages_directory}/v0")
|
|
86
|
-
generate_file("#{comp_settings.gh_pages_directory}/v0/index.html",
|
|
87
|
-
"empty.html.erb", {name: comp_settings.name})
|
|
88
|
-
component_redirect_url = "https://#{component_base_path(comp_settings)}/v#{current_component_version(comp_settings)}"
|
|
89
|
-
generate_file("#{comp_settings.gh_pages_directory}/index.html",
|
|
90
|
-
"redirect.html.erb", {redirect_url: component_redirect_url})
|
|
91
|
-
generate_file("#{comp_settings.gh_pages_directory}/latest/index.html",
|
|
92
|
-
"redirect.html.erb", {redirect_url: component_redirect_url})
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def prepare_v0_directory(directory)
|
|
96
|
-
::FileUtils.mkdir_p(directory)
|
|
97
|
-
children = ::Dir.children(directory) - ["index.html"]
|
|
98
|
-
return if children.empty?
|
|
99
|
-
puts "Non-index files exist in #{directory}.", :yellow, :bold
|
|
100
|
-
return unless yes || confirm("Remove? ", default: true)
|
|
101
|
-
children.each do |child|
|
|
102
|
-
::FileUtils.remove_entry(::File.join(directory, child), true)
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def current_component_version(comp_settings)
|
|
107
|
-
base_dir = comp_settings.gh_pages_directory
|
|
108
|
-
latest = ::Gem::Version.new("0")
|
|
109
|
-
return latest unless ::File.directory?(base_dir)
|
|
110
|
-
::Dir.children(base_dir).each do |child|
|
|
111
|
-
next unless /^v\d+(\.\d+)*$/.match?(child)
|
|
112
|
-
next unless ::File.directory?(::File.join(base_dir, child))
|
|
113
|
-
version = ::Gem::Version.new(child[1..])
|
|
114
|
-
latest = version if version > latest
|
|
115
|
-
end
|
|
116
|
-
latest
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def generate_toplevel_files
|
|
120
|
-
::File.write(".nojekyll", "")
|
|
121
|
-
generate_file(".gitignore", "gitignore.erb", {})
|
|
122
|
-
unless @relevant_component_settings.any? { |settings| settings.gh_pages_directory == "." }
|
|
123
|
-
generate_file("index.html", "redirect.html.erb", {redirect_url: default_redirect_url})
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def generate_html404
|
|
128
|
-
version_vars = {}
|
|
129
|
-
replacement_info = @relevant_component_settings.map do |comp_settings|
|
|
130
|
-
version_vars[comp_settings.gh_pages_version_var] = current_component_version(comp_settings)
|
|
131
|
-
base_path = component_base_path(comp_settings)
|
|
132
|
-
regexp_source = "//#{::Regexp.escape(base_path)}/latest(/|$)"
|
|
133
|
-
CompInfo.new(base_path, regexp_source, comp_settings.gh_pages_version_var)
|
|
134
|
-
end
|
|
135
|
-
template_params = {
|
|
136
|
-
default_redirect_url: default_redirect_url,
|
|
137
|
-
version_vars: version_vars,
|
|
138
|
-
replacement_info: replacement_info,
|
|
139
|
-
}
|
|
140
|
-
generate_file("404.html", "404.html.erb", template_params)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def url_base_path
|
|
144
|
-
@url_base_path ||= "#{@settings.repo_owner}.github.io/#{@settings.repo_name}"
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def component_base_path(component_settings)
|
|
148
|
-
if component_settings.gh_pages_directory == "."
|
|
149
|
-
url_base_path
|
|
150
|
-
else
|
|
151
|
-
"#{url_base_path}/#{component_settings.gh_pages_directory}"
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def default_redirect_url
|
|
156
|
-
@default_redirect_url ||= "https://#{component_base_path(@relevant_component_settings.first)}/latest"
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def generate_file(destination, template, data)
|
|
160
|
-
return unless file_generation_confirmations(destination)
|
|
161
|
-
template_path = find_data("gh-pages/#{template}")
|
|
162
|
-
raise "Unable to find template #{template}" unless template_path
|
|
163
|
-
erb = ::ERB.new(::File.read(template_path))
|
|
164
|
-
content = erb.result(ErbContext.get(data))
|
|
165
|
-
::FileUtils.mkdir_p(::File.dirname(destination))
|
|
166
|
-
::File.write(destination, content)
|
|
167
|
-
puts "Wrote #{destination}.", :green
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def file_generation_confirmations(destination)
|
|
171
|
-
if ::File.exist?(destination)
|
|
172
|
-
if ::File.directory?(destination)
|
|
173
|
-
puts "Destination #{destination} exists and is a DIRECTORY.", :yellow, :bold
|
|
65
|
+
results = @logic.generate_files(@gh_pages_dir, @template_dir) do |destination, status, existing_ftype|
|
|
66
|
+
if status == :overwrite
|
|
67
|
+
puts "Destination #{destination} exists (type: #{existing_ftype})", :yellow, :bold
|
|
68
|
+
yes || confirm("Overwrite? ", default: true)
|
|
174
69
|
else
|
|
175
|
-
|
|
70
|
+
yes || confirm("Create file #{destination}? ", default: true)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
output_results(results)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def output_results(results)
|
|
77
|
+
results.each do |r|
|
|
78
|
+
case r[:outcome]
|
|
79
|
+
when :wrote
|
|
80
|
+
puts "Wrote #{r[:destination]}.", :green
|
|
81
|
+
when :unchanged
|
|
82
|
+
puts "Unchanged: #{r[:destination]}.", :green
|
|
83
|
+
when :skipped
|
|
84
|
+
puts "Skipped: #{r[:destination]}.", :yellow
|
|
176
85
|
end
|
|
177
|
-
return false unless yes || confirm("Overwrite? ", default: true)
|
|
178
|
-
::FileUtils.remove_entry(destination)
|
|
179
|
-
else
|
|
180
|
-
return false unless yes || confirm("Create file #{destination}? ", default: true)
|
|
181
86
|
end
|
|
182
|
-
|
|
87
|
+
puts "Files generated into #{@gh_pages_dir}", :bold
|
|
183
88
|
end
|
|
184
89
|
|
|
185
90
|
def push_gh_pages
|
data/toys/gen-workflows.rb
CHANGED
|
@@ -69,20 +69,37 @@ def generate_all_files
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def generate_file(name)
|
|
72
|
+
template_path = find_data("templates/#{name}.erb")
|
|
73
|
+
raise "Unable to find template #{name}.erb" unless template_path
|
|
74
|
+
erb = ::ERB.new(::File.read(template_path))
|
|
75
|
+
content = erb.result(ErbContext.get(@settings))
|
|
76
|
+
|
|
72
77
|
destination = ::File.join(workflows_dir, name)
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
stat = safe_lstat(destination)
|
|
79
|
+
if stat
|
|
80
|
+
if stat.file? && safe_read(destination) == content
|
|
81
|
+
puts "Unchanged: #{destination}.", :green
|
|
82
|
+
return
|
|
83
|
+
end
|
|
84
|
+
puts "Destination #{destination} exists (type: #{stat.ftype})", :yellow, :bold
|
|
75
85
|
return unless yes || confirm("Overwrite? ", default: true)
|
|
76
86
|
else
|
|
77
87
|
return unless yes || confirm("Create file #{destination}? ", default: true)
|
|
78
88
|
end
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
::FileUtils.remove_entry(destination, true)
|
|
91
|
+
::File.write(destination, content)
|
|
92
|
+
puts "Wrote #{destination}.", :green
|
|
93
|
+
end
|
|
83
94
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
def safe_lstat(path)
|
|
96
|
+
::File.lstat(path)
|
|
97
|
+
rescue ::SystemCallError
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def safe_read(path)
|
|
102
|
+
::File.read(path)
|
|
103
|
+
rescue ::SystemCallError
|
|
104
|
+
nil
|
|
88
105
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: toys-release
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Azuma
|
|
@@ -76,6 +76,7 @@ files:
|
|
|
76
76
|
- toys/.lib/toys/release/component.rb
|
|
77
77
|
- toys/.lib/toys/release/environment_utils.rb
|
|
78
78
|
- toys/.lib/toys/release/gemspec_file.rb
|
|
79
|
+
- toys/.lib/toys/release/gh_pages_logic.rb
|
|
79
80
|
- toys/.lib/toys/release/performer.rb
|
|
80
81
|
- toys/.lib/toys/release/pipeline.rb
|
|
81
82
|
- toys/.lib/toys/release/pull_request.rb
|
|
@@ -101,10 +102,10 @@ homepage: https://github.com/dazuma/toys
|
|
|
101
102
|
licenses:
|
|
102
103
|
- MIT
|
|
103
104
|
metadata:
|
|
104
|
-
changelog_uri: https://dazuma.github.io/toys/gems/toys-release/v0.
|
|
105
|
-
source_code_uri: https://github.com/dazuma/toys/tree/toys-release/v0.
|
|
105
|
+
changelog_uri: https://dazuma.github.io/toys/gems/toys-release/v0.9.0/file.CHANGELOG.html
|
|
106
|
+
source_code_uri: https://github.com/dazuma/toys/tree/toys-release/v0.9.0/toys-release
|
|
106
107
|
bug_tracker_uri: https://github.com/dazuma/toys/issues
|
|
107
|
-
documentation_uri: https://dazuma.github.io/toys/gems/toys-release/v0.
|
|
108
|
+
documentation_uri: https://dazuma.github.io/toys/gems/toys-release/v0.9.0
|
|
108
109
|
rdoc_options: []
|
|
109
110
|
require_paths:
|
|
110
111
|
- lib
|