veragem 0.1.0 → 0.2.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +230 -34
  3. data/Gemfile.lock +254 -0
  4. data/README.md +143 -48
  5. data/lib/veragem/citation.rb +116 -0
  6. data/lib/veragem/generator.rb +105 -56
  7. data/lib/veragem/license.rb +27 -0
  8. data/lib/veragem/name.rb +110 -48
  9. data/lib/veragem/options.rb +116 -30
  10. data/lib/veragem/project.rb +200 -39
  11. data/lib/veragem/questions.rb +163 -0
  12. data/lib/veragem/summary.rb +62 -0
  13. data/lib/veragem/version.rb +2 -1
  14. data/lib/veragem.rb +12 -3
  15. data/plugins.rb +4 -0
  16. data/sig/veragem.rbs +151 -23
  17. data/template/.gitattributes +3 -0
  18. data/template/.githooks/pre-commit +2 -3
  19. data/template/.github/PULL_REQUEST_TEMPLATE.md +2 -1
  20. data/template/.github/dependabot.yml +4 -8
  21. data/template/.github/image-registries.sh +4 -3
  22. data/template/.github/workflows/dependabot-automerge.yml +29 -34
  23. data/template/.github/workflows/dependency-review.yml +19 -15
  24. data/template/.github/workflows/image.yml +65 -14
  25. data/template/.github/workflows/links.yml +35 -0
  26. data/template/.github/workflows/nix.yml +3 -3
  27. data/template/.github/workflows/release.yml +135 -22
  28. data/template/.github/workflows/ruby-ci.yml +31 -9
  29. data/template/.github/workflows/security.yml +15 -9
  30. data/template/.github/workflows/style-check-cocogitto.yml +3 -4
  31. data/template/.github/workflows/style-check-dprint.yml +10 -10
  32. data/template/.gitignore +5 -0
  33. data/template/.mutineer.yml +28 -0
  34. data/template/.rubocop.yml +49 -10
  35. data/template/.simplecov.erb +10 -0
  36. data/template/.yard-lint.yml +386 -0
  37. data/template/.yardopts +2 -0
  38. data/template/AGENTS.md.erb +76 -38
  39. data/template/CITATION.cff.erb +50 -0
  40. data/template/CODE_OF_CONDUCT.md.erb +1 -1
  41. data/template/CONTRIBUTING.md.erb +107 -48
  42. data/template/Gemfile +10 -7
  43. data/template/README.md.erb +12 -12
  44. data/template/Rakefile +167 -31
  45. data/template/Steepfile.erb +1 -1
  46. data/template/bin/setup +2 -0
  47. data/template/cliff.toml +40 -23
  48. data/template/dprint.json +1 -6
  49. data/template/flake.lock +3 -3
  50. data/template/flake.nix +25 -10
  51. data/template/lib/%path%/cli.rb.erb +19 -15
  52. data/template/lib/%path%/version.rb.erb +1 -1
  53. data/template/lib/%path%.rb.erb +3 -1
  54. data/template/sig/%path%.rbs.erb +1 -1
  55. data/template/test/test_%underscored%.rb.erb +4 -3
  56. data/template/test/test_cli.rb.erb +37 -12
  57. data/template/test/test_helper.rb.erb +15 -7
  58. metadata +16 -4
  59. data/template/.github/workflows/rubocop-autocorrect.yml +0 -114
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Steep checks lib/ against sig/, its strictest diagnostics on: `rake steep`.
3
+ # Steep checks lib/ against sig/, its strict diagnostics on: `rake steep`.
4
4
  target :lib do
5
5
  signature "sig"
6
6
  check "lib"
data/template/bin/setup CHANGED
@@ -3,3 +3,5 @@
3
3
 
4
4
  # The gems at Gemfile.lock's versions; the first run writes the lock, to commit.
5
5
  system("bundle", "install", exception: true)
6
+ # Windows' gems too, as CI checks the gem there and installs from the lock alone
7
+ system("bundle", "lock", "--add-platform", "x64-mingw-ucrt", exception: true)
data/template/cliff.toml CHANGED
@@ -1,9 +1,8 @@
1
1
  # git-cliff: CHANGELOG.md from the Conventional Commits, written by `rake bump` (never by hand).
2
- # Grouped by commit type, breaking changes first; what changes nothing a user meets (chores, CI,
3
- # formatting, the release commit itself) is left out.
4
2
  #
5
- # It names no project: the Rakefile passes the repository the headings' compare links need
6
- # (`--github-repo`, from the gemspec's source_code_uri). Run bare, the headings carry no link.
3
+ # It names no project: the Rakefile passes the repository the links need (`--github-repo`, from
4
+ # the gemspec's source_code_uri). Run bare, it takes the current branch's upstream remote where that
5
+ # is GitHub's, and writes no link otherwise.
7
6
 
8
7
  [changelog]
9
8
  header = """
@@ -33,8 +32,11 @@ body = """
33
32
  {% for group, commits in commits | group_by(attribute="group") %}
34
33
  ### {{ group | striptags | trim }}
35
34
  {% for commit in commits %}
35
+ {# the description as its author wrote it: a name keeps its case (RuboCop, v0.1.0) -#}
36
+ {% set short = commit.id | truncate(length=7, end="") -%}
36
37
  - {% if commit.scope %}{{ commit.scope }}: {% endif %}\
37
- {{ commit.message | split(pat="\n") | first | upper_first | trim }}\
38
+ {{ commit.message | split(pat="\n") | first | trim }} \
39
+ ({% if url %}[{{ short }}]({{ url }}/commit/{{ commit.id }}){% else %}{{ short }}{% endif %})\
38
40
  {% endfor %}
39
41
  {% endfor %}\n
40
42
  """
@@ -42,28 +44,43 @@ trim = true
42
44
 
43
45
  [git]
44
46
  conventional_commits = true
45
- # a body right under its subject, which cog takes, set apart as git-cliff's parser needs it: its
46
- # footers (BREAKING CHANGE) read
47
- commit_preprocessors = [{ pattern = '\A([^\n]*)\n([^\n])', replace = "${1}\n\n${2}" }]
48
- # what cog lets through unconventional -- a fixup (cog.toml) -- left out
49
- filter_unconventional = true
47
+ commit_preprocessors = [
48
+ # a body right under its subject, which cog takes, set apart as git-cliff's parser needs it:
49
+ # its footers (BREAKING CHANGE) read
50
+ { pattern = '\A([^\n]*)\n([^\n])', replace = "${1}\n\n${2}" },
51
+ # GitHub's markers that skip a commit's workflows, at a line's end: no changelog text
52
+ { pattern = '(?m)( *\[(skip ci|ci skip|no ci|skip actions|actions skip)\])+ *$', replace = "" },
53
+ ]
54
+ # every commit to the parsers, then an unconventional one they keep fails the changelog: cog lets
55
+ # through merges and fixups alone, which they skip (one that got past cog anyway: its SHA in
56
+ # .cliffignore, which git-cliff skips). Filtered before the parsers instead, a merge warns as a
57
+ # parse error
58
+ require_conventional = true
50
59
  protect_breaking_commits = true # a breaking change is listed whatever its type, first
51
- # by type, in any case (cog takes `Feat:`), the HTML comments ordering the groups
60
+ # every conventional commit, by type, in any case (cog takes `Feat:`); the HTML comments order the
61
+ # groups, two digits as they sort as text
52
62
  commit_parsers = [
53
63
  # a merge, whatever its message: cog checks none (ignore_merge_commits)
54
64
  { field = "merge_commit", pattern = "true", skip = true },
55
- { message = "^[a-zA-Z]+(\\([^)]*\\))?!:", group = "<!-- 0 -->Breaking changes" },
56
- { footer = "^BREAKING[ -]CHANGE", group = "<!-- 0 -->Breaking changes" },
57
- { message = "(?i)^(chore|ci|style)", skip = true },
58
- { message = "(?i)^feat", group = "<!-- 1 -->Features" },
59
- { message = "(?i)^fix", group = "<!-- 2 -->Fixes" },
60
- { message = "(?i)^perf", group = "<!-- 3 -->Performance" },
61
- { message = "(?i)^refactor", group = "<!-- 4 -->Refactoring" },
62
- { message = "(?i)^build", group = "<!-- 5 -->Build" },
63
- { message = "(?i)^doc", group = "<!-- 6 -->Documentation" },
64
- { message = "(?i)^test", group = "<!-- 7 -->Tests" },
65
- { message = "(?i)^revert", group = "<!-- 8 -->Reverts" },
66
- { message = ".*", skip = true },
65
+ # what else cog lets through unconventional (ignore_fixup_commits)
66
+ { message = "^(fixup|squash|amend)!", skip = true },
67
+ # the release commit (`rake bump`'s), tagged: in its own section, which a later bump would
68
+ # rewrite once published
69
+ { message = "^chore\\(release\\): ", skip = true },
70
+ { message = "^[a-zA-Z]+(\\([^)]*\\))?!:", group = "<!-- 00 -->Breaking changes" },
71
+ { footer = "^BREAKING[ -]CHANGE", group = "<!-- 00 -->Breaking changes" },
72
+ { message = "(?i)^feat", group = "<!-- 01 -->Features" },
73
+ { message = "(?i)^fix", group = "<!-- 02 -->Fixes" },
74
+ { message = "(?i)^perf", group = "<!-- 03 -->Performance" },
75
+ { message = "(?i)^refactor", group = "<!-- 04 -->Refactoring" },
76
+ { message = "(?i)^build", group = "<!-- 05 -->Build" },
77
+ { message = "(?i)^doc", group = "<!-- 06 -->Documentation" },
78
+ { message = "(?i)^test", group = "<!-- 07 -->Tests" },
79
+ { message = "(?i)^revert", group = "<!-- 08 -->Reverts" },
80
+ { message = "(?i)^style", group = "<!-- 09 -->Styling" },
81
+ { message = "(?i)^(chore|ci)", group = "<!-- 10 -->Miscellaneous Tasks" },
82
+ # any other type cog takes
83
+ { message = ".*", group = "<!-- 11 -->Other" },
67
84
  ]
68
85
  sort_commits = "oldest"
69
86
  # the release tags, `v0.1.0` on (Bundler's own form, `rake release`)
data/template/dprint.json CHANGED
@@ -1,23 +1,18 @@
1
- // Markdown and TOML (`rake dprint`, style-check-dprint.yml). The plugins pinned:
2
- // `dprint config update` moves them to their latest.
3
1
  {
4
2
  "markdown": {
5
3
  "textWrap": "maintain",
6
- // the plugin's defaults are underscores
7
4
  "emphasisKind": "asterisks",
8
5
  "strongKind": "asterisks"
9
6
  },
10
- // four spaces, as cliff.toml and cog.toml are written
11
7
  "toml": { "indentWidth": 4 },
12
8
  "excludes": [
13
- // git-cliff's (cliff.toml): regenerated at each release, never edited
14
9
  "CHANGELOG.md",
15
10
  "coverage/",
16
11
  "pkg/",
17
12
  "vendor/"
18
13
  ],
19
14
  "plugins": [
20
- "https://plugins.dprint.dev/markdown-0.26.0.wasm",
15
+ "https://plugins.dprint.dev/markdown-0.28.2.wasm",
21
16
  "https://plugins.dprint.dev/toml-0.9.0.wasm"
22
17
  ]
23
18
  }
data/template/flake.lock CHANGED
@@ -2,11 +2,11 @@
2
2
  "nodes": {
3
3
  "nixpkgs": {
4
4
  "locked": {
5
- "lastModified": 1792773339,
6
- "narHash": "sha256-rZLrzrC6i+jnTAlvPZHogKDulY7WWDEv9hJeHieO7Zw=",
5
+ "lastModified": 1793540411,
6
+ "narHash": "sha256-fiPJ88izTVucjeo2EQ3jk4aWmLg03I1D4vxnX2QjEgE=",
7
7
  "owner": "NixOS",
8
8
  "repo": "nixpkgs",
9
- "rev": "3ec636362e1816bc2f0888fa0fa2c69f1bc9e504",
9
+ "rev": "0bb2eedc20e4739f29e8045034f2828ad7054f8f",
10
10
  "type": "github"
11
11
  },
12
12
  "original": {
data/template/flake.nix CHANGED
@@ -1,15 +1,16 @@
1
1
  # The development shell, the gem as a package, and the checks that need no gem from the bundle:
2
2
  #
3
- # nix develop Ruby 4.0 and every tool `rake checks` runs besides the gems (Bundler's, from
3
+ # nix develop Ruby 4.0 and every tool the Rakefile runs besides the gems (Bundler's, from
4
4
  # Gemfile.lock: `bin/setup`): cocogitto, git-cliff, dprint, shellcheck, shfmt,
5
- # actionlint, zizmor, nixfmt
5
+ # actionlint, zizmor, nixfmt, lychee
6
6
  # nix build the gem, built from its gemspec and installed (nixpkgs' buildRubyGem);
7
7
  # with a command (exe/<name>): `nix run` runs it, and on Linux
8
8
  # `nix build .#image` builds it as an OCI image (image.yml publishes it)
9
9
  # nix fmt the Nix, by nixfmt
10
10
  # nix flake check the Nix format, the shell scripts (shellcheck, shfmt), the workflows
11
11
  # (actionlint, zizmor), the package and its library loaded -- and the command
12
- # run, and its image -- offline in the sandbox: git's files alone
12
+ # run, and its image built -- and CITATION.cff where there is one (cffconvert);
13
+ # offline in the sandbox: git's files alone
13
14
  #
14
15
  # Nothing here names the gem: its name is the gemspec's file; its version, lib/<path>/version.rb's
15
16
  # VERSION; its summary, homepage and licences, the gemspec's lines that set them.
@@ -36,7 +37,9 @@
36
37
  if lib.length gemspecs == 1 then
37
38
  lib.removeSuffix ".gemspec" (lib.head gemspecs)
38
39
  else
39
- throw "flake.nix: one *.gemspec expected, found ${toString gemspecs}";
40
+ throw "flake.nix: one *.gemspec expected, found ${
41
+ if gemspecs == [ ] then "none" else toString gemspecs
42
+ }";
40
43
  path = lib.replaceStrings [ "-" ] [ "/" ] name;
41
44
  # the groups of the first of +file+'s lines that +pattern+ matches whole, or null
42
45
  line =
@@ -60,7 +63,7 @@
60
63
  found = line ''[[:space:]]*VERSION[[:space:]]*=[[:space:]]*"([^"]+)"${rest}'' ./lib/${path}/version.rb;
61
64
  in
62
65
  if found == null then
63
- throw "flake.nix: no VERSION = \"...\" line in version.rb"
66
+ throw "flake.nix: no VERSION = \"...\" line in lib/${path}/version.rb"
64
67
  else
65
68
  lib.head found;
66
69
  # the SPDX identifiers of `spec.licenses = [...]` or `spec.license = "..."`
@@ -73,6 +76,7 @@
73
76
  else
74
77
  lib.concatLists (lib.filter lib.isList (builtins.split ''"([^"]*)"'' (lib.head found)));
75
78
  command = builtins.pathExists ./exe/${name};
79
+ citation = builtins.pathExists ./CITATION.cff;
76
80
 
77
81
  check =
78
82
  pkgs: checkName: tools: script:
@@ -91,6 +95,9 @@
91
95
  dependencies = [ ];
92
96
  package = pkgs.buildRubyGem {
93
97
  gemName = name;
98
+ # the root's: unset, buildRubyGem builds the first *.gemspec `find` lists, a test's
99
+ # fixture too
100
+ gemspec = "${name}.gemspec";
94
101
  inherit version ruby;
95
102
  src = self;
96
103
  gemPath = dependencies;
@@ -141,15 +148,17 @@
141
148
 
142
149
  checks = {
143
150
  format = check pkgs "format" [ pkgs.nixfmt ] "find . -name '*.nix' -exec nixfmt --check {} +";
144
- # shfmt finds the scripts, by extension or shebang, and formats them as .editorconfig says
151
+ # shfmt finds the scripts, by extension or shebang, and checks them per .editorconfig
145
152
  shell = check pkgs "shell" [ pkgs.shellcheck pkgs.shfmt ] ''
146
- shfmt --find . | xargs shellcheck
153
+ shfmt --find . | xargs -d '\n' shellcheck
147
154
  shfmt --diff .
148
155
  '';
149
156
  # actionlint runs shellcheck on each `run:` script too; zizmor audits Dependabot's
150
- # configuration as well
157
+ # configuration as well. The workflows named: actionlint finds them in a git repository
158
+ # alone, and the sandbox has none
151
159
  workflows = check pkgs "workflows" [ pkgs.actionlint pkgs.shellcheck pkgs.zizmor ] ''
152
- actionlint .github/workflows/*.yml
160
+ shopt -s nullglob
161
+ actionlint .github/workflows/*.{yml,yaml}
153
162
  zizmor --offline --quiet --strict-collection .
154
163
  '';
155
164
  inherit package;
@@ -186,6 +195,10 @@
186
195
  touch $out
187
196
  '';
188
197
  }
198
+ // lib.optionalAttrs citation {
199
+ # CFF's schema alone: Zenodo's import refuses more, which the generator never writes
200
+ citation = check pkgs "citation" [ pkgs.cffconvert ] "cffconvert --validate -i CITATION.cff";
201
+ }
189
202
  // lib.optionalAttrs linuxCommand { inherit image; };
190
203
 
191
204
  devShells.default = pkgs.mkShell {
@@ -199,10 +212,12 @@
199
212
  pkgs.actionlint
200
213
  pkgs.zizmor
201
214
  pkgs.nixfmt
215
+ pkgs.lychee
202
216
  ];
203
217
  };
204
218
  };
205
- output = attribute: forAllSystems (pkgs: (perSystem pkgs).${attribute});
219
+ all = forAllSystems perSystem;
220
+ output = attribute: lib.mapAttrs (_: outputs: outputs.${attribute}) all;
206
221
  in
207
222
  {
208
223
  packages = output "packages";
@@ -3,43 +3,47 @@
3
3
  require "optparse"
4
4
  require_relative "../<%= File.basename(path) %>"
5
5
 
6
+ <%# `::` where one of the gem's modules is named OptionParser, which its lookup would find first -%>
7
+ <%- option_parser = "#{"::" if modules.include?("OptionParser")}OptionParser" -%>
6
8
  <%= nest(<<~RUBY)
7
- # The command line: `#{name} [NAME]` prints the greeting.
9
+ # The command line, `#{name} [options] [NAME]`:
10
+ # it greets NAME, or prints the help or the version.
8
11
  module CLI
9
- # Runs the command line with +argv+; returns the exit status.
12
+ # Runs the command line with +argv+.
13
+ # @param argv [Array<String>] the command's arguments
14
+ # @return [Integer] the exit status: 0, or 2 for an option refused, or more than one name
10
15
  def self.run(argv)
11
16
  options = { help: false, version: false }
12
- names = parser.parse(argv, into: options)
13
- puts output(options, names.first || "")
17
+ name, *others = parser.parse(argv, into: options)
18
+ raise #{option_parser}::NeedlessArgument.new(*others) unless others.empty?
19
+
20
+ puts output(options, name.to_s)
14
21
  0
15
- rescue ::OptionParser::ParseError => e
16
- warn e.message, parser.help
22
+ rescue #{option_parser}::ParseError => e
23
+ warn "#{name}: \#{e}", parser
17
24
  2
18
25
  end
19
26
 
20
- # What +options+ ask for, the help or the version, else the greeting for +name+.
21
- def self.output(options, name)
27
+ private_class_method def self.output(options, name)
22
28
  if options[:help]
23
- parser.help
29
+ parser
24
30
  elsif options[:version]
25
31
  parser.ver
26
32
  else
27
33
  #{modules.last}.greeting(name)
28
34
  end
29
35
  end
30
- private_class_method :output
31
36
 
32
- # --help as an option of its own: OptionParser's built-in one exits the process. `::`: a module of
33
- # the gem's could be named OptionParser
34
- def self.parser
35
- ::OptionParser.new("Usage: #{name} [options] [NAME]") do |opts|
37
+ # --help and --version as options of their own: OptionParser's own exit the process, as its
38
+ # shell completion still does (--*-completion-bash=WORD).
39
+ private_class_method def self.parser
40
+ #{option_parser}.new("Usage: #{name} [options] [NAME]") do |opts|
36
41
  opts.program_name = "#{name}"
37
42
  opts.version = VERSION
38
43
  opts.on("-h", "--help", "Print this help")
39
44
  opts.on("--version", "Print the version")
40
45
  end
41
46
  end
42
- private_class_method :parser
43
47
  end
44
48
  RUBY
45
49
  -%>
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- <%= nest(%(VERSION = "0.1.0"\n)) -%>
3
+ <%= nest(%(# The gem's version.\nVERSION = "0.1.0"\n)) -%>
@@ -3,7 +3,9 @@
3
3
  require_relative "<%= File.basename(path) %>/version"
4
4
 
5
5
  <%= nest(<<~RUBY, doc: summary)
6
- # A greeting for +name+, its surrounding whitespace stripped; nobody named, the world.
6
+ # A greeting for +name+, its surrounding ASCII whitespace stripped; nobody named, the world.
7
+ # @param name [String] who to greet, "" for nobody
8
+ # @return [String]
7
9
  def self.greeting(name)
8
10
  who = name.strip
9
11
  who = "world" if who.empty?
@@ -8,7 +8,7 @@ RBS
8
8
  module CLI
9
9
  def self.run: (::Array[::String] argv) -> ::Integer
10
10
 
11
- private def self.output: (::Hash[::Symbol, bool] options, ::String name) -> ::String?
11
+ private def self.output: (::Hash[::Symbol, bool] options, ::String name) -> (::OptionParser | ::String)?
12
12
 
13
13
  private def self.parser: () -> ::OptionParser
14
14
  end
@@ -15,12 +15,13 @@ class Test<%= constant.delete(":") %> < Minitest::Test
15
15
  assert_equal "Hello, world!", <%= constant %>.greeting(" \n")
16
16
  end
17
17
 
18
- # A property, checked on many generated names (pbt): surrounding whitespace changes nothing.
18
+ # A property, checked on many generated names and whitespace around them (pbt).
19
19
  def test_surrounding_whitespace_changes_nothing
20
20
  gem = <%= constant %>
21
+ whitespace = Pbt.array(Pbt.one_of(*" \t\n\v\f\r".chars))
21
22
  Pbt.assert do
22
- Pbt.property(Pbt.printable_string) do |name|
23
- assert_equal gem.greeting(name), gem.greeting(" #{name}\n")
23
+ Pbt.property(Pbt.printable_string, whitespace, whitespace) do |name, before, after|
24
+ assert_equal gem.greeting(name), gem.greeting("#{before.join}#{name}#{after.join}")
24
25
  end
25
26
  end
26
27
  end
@@ -4,7 +4,6 @@ require "test_helper"
4
4
  require "<%= path %>/cli"
5
5
 
6
6
  class Test<%= constant.delete(":") %>CLI < Minitest::Test
7
- # no module of the gem's can be named so: its names have no `_`
8
7
  COMMAND_LINE = <%= constant %>::CLI
9
8
 
10
9
  def test_it_greets_the_name_given
@@ -15,26 +14,52 @@ class Test<%= constant.delete(":") %>CLI < Minitest::Test
15
14
  assert_output("Hello, world!\n") { assert_equal 0, COMMAND_LINE.run([]) }
16
15
  end
17
16
 
18
- def test_version
17
+ def test_it_prints_its_version
19
18
  expected = "<%= name %> #{<%= constant %>::VERSION}\n"
20
- assert_output(expected) { assert_equal 0, COMMAND_LINE.run(["--version"]) }
19
+ assert_output(expected) { assert_equal 0, command("--version") }
21
20
  end
22
21
 
23
- def test_help
24
- usage = /\AUsage: <%= name %> \[options\] \[NAME\]\n/
25
-
26
- assert_output(usage) { assert_equal 0, COMMAND_LINE.run(["--help"]) }
22
+ def test_it_prints_its_help
23
+ help = <<~HELP
24
+ Usage: <%= name %> [options] [NAME]
25
+ -h, --help Print this help
26
+ --version Print the version
27
+ HELP
28
+ %w[-h --help].each { |option| assert_output(help) { assert_equal 0, command(option) } }
27
29
  end
28
30
 
29
31
  def test_an_unknown_option_is_refused
30
- assert_output("", /invalid option: --nope/) { assert_equal 2, COMMAND_LINE.run(["--nope"]) }
32
+ assert_output("", /\A<%= name %>: invalid option: --nope\nUsage: /) do
33
+ assert_equal 2, COMMAND_LINE.run(["--nope"])
34
+ end
35
+ end
36
+
37
+ def test_more_than_one_name_is_refused
38
+ assert_output("", /\A<%= name %>: needless argument: Bob\n/) do
39
+ assert_equal 2, COMMAND_LINE.run(%w[Ada Bob])
40
+ end
31
41
  end
32
42
 
33
- def test_the_executable_runs_it
34
- executable = File.expand_path("../exe/<%= name %>", __dir__)
43
+ def test_the_executable_runs_it_and_exits_with_its_status
44
+ assert_equal ["Hello, Ada!\n", 0], executable("Ada")
45
+ assert_equal 2, executable("--nope").last
46
+ end
47
+
48
+ private
49
+
50
+ # exe/<%= name %>'s output, and its exit status
51
+ def executable(*argv)
52
+ exe = File.expand_path("../exe/<%= name %>", __dir__)
35
53
  lib = File.expand_path("../lib", __dir__)
36
- out, = capture_subprocess_io { system(RbConfig.ruby, "-I", lib, executable, "Ada", exception: true) }
54
+ out, = capture_subprocess_io { system(RbConfig.ruby, "-I", lib, exe, *argv) }
55
+ [out, Process.last_status.exitstatus]
56
+ end
37
57
 
38
- assert_equal "Hello, Ada!\n", out
58
+ # OptionParser's own --help and --version print as these do, then exit the process: the command
59
+ # returns its status instead
60
+ def command(option)
61
+ COMMAND_LINE.run([option])
62
+ rescue SystemExit
63
+ flunk "#{option} exited the process"
39
64
  end
40
65
  end
@@ -1,13 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Coverage first, before lib/ loads: every line, branch and method of it, a file the tests never
4
- # load included (`cover`). But version.rb: the gemspec requires it, as Bundler reads the gemspec
5
- # before any test runs -- a constant alone.
6
- require "simplecov"
7
- SimpleCov.start "strict" do
8
- cover "lib/**/*.rb"
9
- skip "lib/<%= path %>/version.rb"
3
+ # A warning Ruby reports from lib/ or the tests raises, failing the test, or the run when they load;
4
+ # a gem's only prints. Every category is on: `ruby -w` turns on the deprecations alone.
5
+ ours = [File.expand_path("../lib", __dir__), __dir__].map { "#{it}/" }
6
+ # Anonymous: a constant would be the module of a gem named for it (own_warnings' OwnWarnings)
7
+ own_warnings = Module.new do
8
+ define_method(:warn) do |message, **options|
9
+ # "PATH:LINE: ..." or, evaluated code's, "(eval at PATH:LINE):LINE: ..."; PATH relative or
10
+ # absolute (Windows': C:/...)
11
+ path = message[/\A(?:\(eval at )*(.+?):\d+[:)]/, 1]
12
+ raise message if path && File.expand_path(path).start_with?(*ours)
13
+
14
+ super(message, **options)
15
+ end
10
16
  end
17
+ Warning.singleton_class.prepend(own_warnings)
18
+ Warning.categories.each { Warning[it] = true }
11
19
 
12
20
  require "<%= path %>"
13
21
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: veragem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - veralvx
@@ -11,27 +11,34 @@ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: 'A Bundler plugin that generates a Ruby 4.0+ gem with what `bundle gem`
13
13
  gives and the rest: Steep and TypeProf, RuboCop and its extensions, Minitest with
14
- a 100% coverage gate and property tests, bundler-audit, Conventional Commits, a
15
- generated changelog, a Nix flake, and a release by Trusted Publishing.'
14
+ a 100% coverage gate, property tests and mutation testing, YARD held by yard-lint,
15
+ bundler-audit, Conventional Commits, a generated changelog, a Nix flake, and a release
16
+ by Trusted Publishing.'
16
17
  executables: []
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files:
20
21
  - CHANGELOG.md
22
+ - Gemfile.lock
21
23
  - LICENSE-APACHE
22
24
  - LICENSE-MIT
23
25
  - README.md
24
26
  - lib/veragem.rb
27
+ - lib/veragem/citation.rb
25
28
  - lib/veragem/generator.rb
29
+ - lib/veragem/license.rb
26
30
  - lib/veragem/name.rb
27
31
  - lib/veragem/options.rb
28
32
  - lib/veragem/project.rb
33
+ - lib/veragem/questions.rb
34
+ - lib/veragem/summary.rb
29
35
  - lib/veragem/version.rb
30
36
  - plugins.rb
31
37
  - sig/bundler.rbs
32
38
  - sig/veragem.rbs
33
39
  - template/%name%.gemspec.erb
34
40
  - template/.editorconfig
41
+ - template/.gitattributes
35
42
  - template/.githooks/commit-msg
36
43
  - template/.githooks/pre-commit
37
44
  - template/.githooks/pre-push
@@ -46,16 +53,21 @@ files:
46
53
  - template/.github/workflows/dependabot-automerge.yml
47
54
  - template/.github/workflows/dependency-review.yml
48
55
  - template/.github/workflows/image.yml
56
+ - template/.github/workflows/links.yml
49
57
  - template/.github/workflows/nix.yml
50
58
  - template/.github/workflows/release.yml
51
- - template/.github/workflows/rubocop-autocorrect.yml
52
59
  - template/.github/workflows/ruby-ci.yml
53
60
  - template/.github/workflows/security.yml
54
61
  - template/.github/workflows/style-check-cocogitto.yml
55
62
  - template/.github/workflows/style-check-dprint.yml
56
63
  - template/.gitignore
64
+ - template/.mutineer.yml
57
65
  - template/.rubocop.yml
66
+ - template/.simplecov.erb
67
+ - template/.yard-lint.yml
68
+ - template/.yardopts
58
69
  - template/AGENTS.md.erb
70
+ - template/CITATION.cff.erb
59
71
  - template/CODE_OF_CONDUCT.md.erb
60
72
  - template/CONTRIBUTING.md.erb
61
73
  - template/Gemfile
@@ -1,114 +0,0 @@
1
- # A Dependabot pull request that moves Gemfile.lock gets RuboCop's safe corrections
2
- # (`rake rubocop:autocorrect`), when the bumped RuboCop finds any, as a commit of their own on its
3
- # branch, then merges as any bump once its checks pass. No token is stored: the job's GITHUB_TOKEN
4
- # pushes, and GitHub may hold the checks of that token's commit until a maintainer approves them
5
- # (the pull request's "Approve workflows to run"); no workflow can start them sooner -- dispatched,
6
- # theirs are not the pull request's required checks. `[dependabot skip]`: Dependabot rebases the
7
- # bump still, over the commit, and this workflow corrects the new one.
8
- name: RuboCop Autocorrect
9
-
10
- on:
11
- pull_request:
12
- paths:
13
- - "Gemfile.lock"
14
-
15
- permissions: {}
16
-
17
- jobs:
18
- correct:
19
- name: Safe corrections
20
- # Dependabot's own branches alone: never a fork's, never a person's pull request
21
- if: >-
22
- github.event.pull_request.user.login == 'dependabot[bot]' &&
23
- github.event.pull_request.head.repo.full_name == github.repository
24
- runs-on: ubuntu-latest
25
- # the bumped gems run here, so this job's token writes nothing: the next job, which pushes,
26
- # runs no gem
27
- permissions:
28
- contents: read
29
- outputs:
30
- changed: ${{ steps.correct.outputs.changed }}
31
- steps:
32
- - name: Checkout
33
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
34
- with:
35
- ref: ${{ github.event.pull_request.head.sha }}
36
- persist-credentials: false
37
-
38
- - name: Set up Ruby
39
- uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
40
- with:
41
- ruby-version: "4.0"
42
- bundler-cache: true
43
-
44
- - name: RuboCop's safe corrections
45
- id: correct
46
- run: |
47
- set -euo pipefail
48
- # what is left once corrected fails CI's own RuboCop, for a person to fix
49
- bundle exec rake rubocop:autocorrect || true
50
- git diff --binary > "$RUNNER_TEMP/corrections.patch"
51
- if [[ -s $RUNNER_TEMP/corrections.patch ]]; then
52
- echo "changed=true" >> "$GITHUB_OUTPUT"
53
- fi
54
-
55
- - name: Keep the corrections
56
- if: steps.correct.outputs.changed == 'true'
57
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
58
- with:
59
- name: corrections
60
- path: ${{ runner.temp }}/corrections.patch
61
- if-no-files-found: error
62
-
63
- push:
64
- name: Commit them
65
- needs: correct
66
- if: needs.correct.outputs.changed == 'true'
67
- runs-on: ubuntu-latest
68
- permissions:
69
- contents: write
70
- steps:
71
- - name: Checkout
72
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
73
- with:
74
- ref: ${{ github.event.pull_request.head.ref }}
75
- # the push below uses it
76
- persist-credentials: true
77
-
78
- - name: The corrections
79
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
80
- with:
81
- name: corrections
82
- path: ${{ runner.temp }}
83
-
84
- - name: Commit and push
85
- env:
86
- BRANCH: ${{ github.event.pull_request.head.ref }}
87
- CORRECTED: ${{ github.event.pull_request.head.sha }}
88
- PATCH: ${{ runner.temp }}/corrections.patch
89
- run: |
90
- set -euo pipefail
91
- # corrections of the commit they were made on: on a newer one, Dependabot's or these
92
- # corrections', a re-run has nothing to do
93
- if [[ $(git rev-parse HEAD) != "$CORRECTED" ]]; then
94
- echo "$BRANCH is past $CORRECTED: corrected already, or a newer commit its own run corrects"
95
- exit 0
96
- fi
97
- # RuboCop's files alone, edited in place: nothing else the bumped gems could write
98
- ruby_files='^([^/]+/)*([^/]+\.(rb|gemspec)|Gemfile|Rakefile|Steepfile|(bin|exe)/[^/]+)$'
99
- if [[ -n $(git apply --summary "$PATCH") ]] ||
100
- git apply --numstat "$PATCH" | cut -f 3 | grep -Ev "$ruby_files"; then
101
- echo "::error::the corrections change more than RuboCop's files, in place: not committed"
102
- exit 1
103
- fi
104
- git apply "$PATCH"
105
- git -c user.name="github-actions[bot]" \
106
- -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
107
- commit --all --quiet --message "style: apply RuboCop's safe corrections [dependabot skip]"
108
- if ! git push origin "HEAD:$BRANCH"; then
109
- # refused because Dependabot pushed meanwhile, whose own run corrects its commit;
110
- # refused otherwise, a failure
111
- remote=$(git ls-remote origin "refs/heads/$BRANCH" | cut -f 1)
112
- if [[ $remote == "$CORRECTED" ]]; then exit 1; fi
113
- echo "$BRANCH moved on: its own run corrects it"
114
- fi