veragem 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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +53 -0
  3. data/LICENSE-APACHE +201 -0
  4. data/LICENSE-MIT +7 -0
  5. data/README.md +151 -0
  6. data/lib/veragem/generator.rb +109 -0
  7. data/lib/veragem/name.rb +111 -0
  8. data/lib/veragem/options.rb +53 -0
  9. data/lib/veragem/project.rb +77 -0
  10. data/lib/veragem/version.rb +5 -0
  11. data/lib/veragem.rb +23 -0
  12. data/plugins.rb +15 -0
  13. data/sig/bundler.rbs +10 -0
  14. data/sig/veragem.rbs +79 -0
  15. data/template/%name%.gemspec.erb +30 -0
  16. data/template/.editorconfig +15 -0
  17. data/template/.githooks/commit-msg +12 -0
  18. data/template/.githooks/pre-commit +6 -0
  19. data/template/.githooks/pre-push +17 -0
  20. data/template/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  21. data/template/.github/ISSUE_TEMPLATE/config.yml.erb +5 -0
  22. data/template/.github/ISSUE_TEMPLATE/documentation.md +19 -0
  23. data/template/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
  24. data/template/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  25. data/template/.github/dependabot.yml +85 -0
  26. data/template/.github/image-registries.sh +31 -0
  27. data/template/.github/release-notes.sh +13 -0
  28. data/template/.github/workflows/dependabot-automerge.yml +70 -0
  29. data/template/.github/workflows/dependency-review.yml +50 -0
  30. data/template/.github/workflows/image.yml +140 -0
  31. data/template/.github/workflows/nix.yml +46 -0
  32. data/template/.github/workflows/release.yml +117 -0
  33. data/template/.github/workflows/rubocop-autocorrect.yml +114 -0
  34. data/template/.github/workflows/ruby-ci.yml +62 -0
  35. data/template/.github/workflows/security.yml +43 -0
  36. data/template/.github/workflows/style-check-cocogitto.yml +56 -0
  37. data/template/.github/workflows/style-check-dprint.yml +44 -0
  38. data/template/.gitignore +12 -0
  39. data/template/.rubocop.yml +27 -0
  40. data/template/AGENTS.md.erb +75 -0
  41. data/template/CODE_OF_CONDUCT.md.erb +18 -0
  42. data/template/CONTRIBUTING.md.erb +183 -0
  43. data/template/Gemfile +24 -0
  44. data/template/LICENSE-APACHE.erb +201 -0
  45. data/template/LICENSE-MIT.erb +7 -0
  46. data/template/README.md.erb +82 -0
  47. data/template/Rakefile +136 -0
  48. data/template/SECURITY.md.erb +21 -0
  49. data/template/Steepfile.erb +11 -0
  50. data/template/bin/console.erb +8 -0
  51. data/template/bin/setup +5 -0
  52. data/template/cliff.toml +70 -0
  53. data/template/cog.toml +8 -0
  54. data/template/dprint.json +23 -0
  55. data/template/exe/%name%.erb +6 -0
  56. data/template/flake.lock +27 -0
  57. data/template/flake.nix +213 -0
  58. data/template/lib/%path%/cli.rb.erb +45 -0
  59. data/template/lib/%path%/version.rb.erb +3 -0
  60. data/template/lib/%path%.rb.erb +13 -0
  61. data/template/sig/%path%.rbs.erb +16 -0
  62. data/template/test/test_%underscored%.rb.erb +27 -0
  63. data/template/test/test_cli.rb.erb +40 -0
  64. data/template/test/test_helper.rb.erb +15 -0
  65. data/template/typeprof.conf.jsonc +7 -0
  66. metadata +111 -0
@@ -0,0 +1,50 @@
1
+ name: Dependency Review
2
+
3
+ # The licences and advisories of what a pull request adds to Gemfile.lock and the workflows, as the
4
+ # templates' Python and Rust ones review theirs. Advisory: it reports only when those files change,
5
+ # so no required check can be it, and dependabot-automerge.yml merges a bump it flags (security.yml's
6
+ # bundler-audit holds the lock daily).
7
+ on:
8
+ pull_request:
9
+ paths:
10
+ - "Gemfile"
11
+ - "Gemfile.lock"
12
+ - "*.gemspec"
13
+ - ".github/workflows/**"
14
+
15
+ permissions: {}
16
+
17
+ # NOTE: it needs the repository's dependency graph (Settings → Advanced
18
+ # Security), and on a private repository GitHub Code Security too (an
19
+ # organization's): without them, this job fails rather than skipping --
20
+ # enable them, or delete the workflow.
21
+ #
22
+ # What it reads: Gemfile.lock has no scopes, so each of its gems counts as
23
+ # runtime -- the gemspec's dependencies and the Gemfile's tools alike --;
24
+ # the actions for licences in effect: advisories match actions by semantic
25
+ # version alone, not the commits these are pinned to ("Dependency graph
26
+ # supported package ecosystems").
27
+ jobs:
28
+ dependency-review:
29
+ name: License & Security Check
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ contents: read
33
+ # no checkout: the action compares the pull request's base and head through GitHub's API
34
+ steps:
35
+ - name: Dependency Review
36
+ uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
37
+ with:
38
+ fail-on-severity: moderate
39
+ # The licences are ClearlyDefined's, not the gemspecs'; a gem's:
40
+ # curl -s https://api.clearlydefined.io/definitions/gem/rubygems/-/NAME/VERSION
41
+ # (.licensed.declared). Add anything legitimate that is missing rather
42
+ # than deleting this list -- an empty allow-list allows everything.
43
+ # Ruby: Ruby's own licence, with MIT or BSD's (concurrent-ruby, reline)
44
+ allow-licenses: >
45
+ 0BSD, Apache-2.0, BlueOak-1.0.0, BSD-2-Clause, BSD-3-Clause,
46
+ BSL-1.0, CC0-1.0, CC-BY-4.0, ISC, MIT, MIT-0, MPL-2.0,
47
+ Ruby, Unlicense, Zlib
48
+ # bundler-audit: GPL-3.0-or-later, a tool run here, never shipped. rake: MIT,
49
+ # to which ClearlyDefined adds a warranty disclaimer it takes for a licence
50
+ allow-dependencies-licenses: pkg:gem/bundler-audit, pkg:gem/rake
@@ -0,0 +1,140 @@
1
+ # A release tag (`rake release`'s v<version>) publishes the command's OCI image to GHCR, as
2
+ # ghcr.io/<owner>/<repository>: the flake's `image` built on each Linux architecture, run once
3
+ # (`--version`), pushed per architecture, then joined under one multi-architecture tag, <version>
4
+ # (and `latest` while it is the highest release). The per-architecture tags (<version>-amd64,
5
+ # <version>-arm64) stay, the multi-architecture tag's parts. It runs beside release.yml, not after
6
+ # it: the image does not wait for the gem's push to RubyGems.
7
+ #
8
+ # The job's token pushes: no secret stored. A first push is private (GHCR's default): make the
9
+ # package public in its settings once.
10
+ #
11
+ # Docker Hub too, the same tags, once vars.DOCKERHUB_USERNAME and secrets.DOCKERHUB_TOKEN (an
12
+ # access token that writes) are set: as vars.DOCKERHUB_REPOSITORY, else <username>/<repository>. A
13
+ # Docker Hub failure in the build (its login, its name, a push) holds GHCR's multi-architecture tag
14
+ # back too; a re-run, once fixed, completes both.
15
+ #
16
+ # Run by hand (workflow_dispatch) on a branch, it builds and runs the image on both architectures,
17
+ # pushing nothing: a rehearsal before the first release; on a tag, it publishes that release again.
18
+ name: Image
19
+
20
+ on:
21
+ push:
22
+ tags: ["v[0-9]*"]
23
+ workflow_dispatch:
24
+
25
+ permissions: {}
26
+
27
+ jobs:
28
+ build:
29
+ name: Build and run (${{ matrix.arch }})
30
+ runs-on: ${{ matrix.runner }}
31
+ strategy:
32
+ matrix:
33
+ include:
34
+ - runner: ubuntu-latest
35
+ arch: amd64
36
+ - runner: ubuntu-24.04-arm
37
+ arch: arm64
38
+ permissions:
39
+ contents: read
40
+ packages: write
41
+ outputs:
42
+ description: ${{ steps.build.outputs.description }}
43
+ steps:
44
+ - name: Checkout
45
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46
+ with:
47
+ persist-credentials: false
48
+
49
+ - name: Install Nix
50
+ uses: cachix/install-nix-action@448cde739abec1eaf0646249e3d25bb8d9bc7702 # v31.11.2
51
+
52
+ - name: Build and run
53
+ id: build
54
+ run: |
55
+ set -euo pipefail
56
+ version="$(nix eval --raw .#image.imageTag)"
57
+ # a tag's version is the one version.rb declares at its commit, the image's tag
58
+ if [[ $GITHUB_REF_TYPE == tag && $GITHUB_REF_NAME != "v$version" ]]; then
59
+ echo "::error::$GITHUB_REF_NAME, but version.rb declares $version" >&2
60
+ exit 1
61
+ fi
62
+ # streamed into docker: no compressed copy of the closure in the store
63
+ stream="$(nix build --no-link --print-out-paths .#image.stream)"
64
+ "$stream" | docker load
65
+ loaded="$(nix eval --raw .#image.imageName):$version"
66
+ # the image as a user runs it: its entrypoint, as its user
67
+ expected="$(nix eval --raw .#image.imageName) $version"
68
+ actual="$(docker run --rm --pull never "$loaded" --version)"
69
+ if [[ $actual != "$expected" ]]; then
70
+ echo "::error::--version gave '$actual', expected '$expected'" >&2
71
+ exit 1
72
+ fi
73
+ # assigned first: a failing command substitution inside echo's arguments would not stop
74
+ # the step
75
+ description="$(nix eval --raw .#default.meta.description)"
76
+ echo "description=$description" >> "$GITHUB_OUTPUT"
77
+
78
+ - name: Push
79
+ if: github.ref_type == 'tag'
80
+ env:
81
+ GH_TOKEN: ${{ github.token }}
82
+ # Docker Hub too, when both are set (.github/image-registries.sh): an unset variable or
83
+ # secret is empty, and no `if:` can read a secret. The username is a variable, as in
84
+ # Docker's guide: a secret would be masked wherever it appears, GHCR's paths included
85
+ DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
86
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
87
+ DOCKERHUB_REPOSITORY: ${{ vars.DOCKERHUB_REPOSITORY }}
88
+ ARCH: ${{ matrix.arch }}
89
+ run: |
90
+ set -euo pipefail
91
+ repositories="$(.github/image-registries.sh)"
92
+ version="${GITHUB_REF_NAME#v}"
93
+ loaded="$(nix eval --raw .#image.imageName):$version"
94
+ # the list on its own descriptor: no command in the loop can read it away
95
+ while read -r repository <&3; do
96
+ docker tag "$loaded" "$repository:$version-$ARCH"
97
+ docker push "$repository:$version-$ARCH"
98
+ done 3<<< "$repositories"
99
+
100
+ publish:
101
+ name: Multi-architecture tag
102
+ needs: build
103
+ if: github.ref_type == 'tag'
104
+ runs-on: ubuntu-latest
105
+ permissions:
106
+ contents: read
107
+ packages: write
108
+ steps:
109
+ - name: Checkout
110
+ # every tag, to tell whether this one is the highest
111
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
112
+ with:
113
+ persist-credentials: false
114
+ fetch-depth: 0
115
+
116
+ - name: Join the architectures
117
+ env:
118
+ GH_TOKEN: ${{ github.token }}
119
+ DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }}
120
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
121
+ DOCKERHUB_REPOSITORY: ${{ vars.DOCKERHUB_REPOSITORY }}
122
+ DESCRIPTION: ${{ needs.build.outputs.description }}
123
+ run: |
124
+ set -euo pipefail
125
+ repositories="$(.github/image-registries.sh)"
126
+ version="${GITHUB_REF_NAME#v}"
127
+ # `latest`: the highest final release (SemVer: no pre-release, no build metadata), so a
128
+ # patch to an older line leaves it where it is
129
+ highest="$(git tag --list 'v[0-9]*' | sed 's/^v//' \
130
+ | { grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' || true; } | sort -V | tail -n 1)"
131
+ # each repository's index from its own pushes: imagetools create's sources must be in
132
+ # the registry it writes to (its docs). The description is the index's annotation, as
133
+ # GHCR reads it for a multi-architecture image
134
+ while read -r repository <&3; do
135
+ tags=(--tag "$repository:$version")
136
+ if [[ $version == "$highest" ]]; then tags+=(--tag "$repository:latest"); fi
137
+ docker buildx imagetools create "${tags[@]}" \
138
+ --annotation "index:org.opencontainers.image.description=$DESCRIPTION" \
139
+ "$repository:$version-amd64" "$repository:$version-arm64"
140
+ done 3<<< "$repositories"
@@ -0,0 +1,46 @@
1
+ # The flake's checks (flake.nix): the Nix format, the shell scripts, the workflows, the gem built
2
+ # as a package (and a command's run, and its OCI image on Linux) -- built on each system the flake
3
+ # declares: x86_64 and aarch64 Linux, Apple silicon (macos-latest). The dev shell is built too, so
4
+ # each system's tools are known to install.
5
+ name: Nix
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ # every pull request, whatever it touches: a path-filtered workflow never reports, and a
11
+ # required check that never reports blocks the merge
12
+ pull_request:
13
+ workflow_dispatch:
14
+
15
+ # a pull request's newest run cancels its older ones; any other run, a group of its own: every
16
+ # commit on main checked (in a shared group, a pending run gives way to the newest: the default)
17
+ concurrency:
18
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
19
+ cancel-in-progress: true
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ flake-check:
26
+ name: nix flake check (${{ matrix.os }})
27
+ runs-on: ${{ matrix.os }}
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
32
+ steps:
33
+ - name: Checkout
34
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - name: Install Nix
39
+ # flakes on, and the job's token for GitHub fetches
40
+ uses: cachix/install-nix-action@448cde739abec1eaf0646249e3d25bb8d9bc7702 # v31.11.2
41
+
42
+ - name: nix flake check
43
+ run: nix flake check --print-build-logs
44
+
45
+ - name: The dev shell
46
+ run: nix develop --command ruby --version
@@ -0,0 +1,117 @@
1
+ # A release tag, v<version> (`rake bump`, then `rake release`), publishes the gem: checked and built
2
+ # once, pushed to RubyGems through Trusted Publishing (no API key stored), then its GitHub release
3
+ # of the same file, its notes the release's section of CHANGELOG.md (release-notes.sh). Trusted
4
+ # Publishing needs, once, a (pending) trusted publisher on rubygems.org naming this repository,
5
+ # this workflow (release.yml) and the `release` environment.
6
+ #
7
+ # Least privilege, as RubyGems' Trusted Publishing advises, after PyPI's security model: the
8
+ # build, which runs the checks and the gemspec, writes nothing but its artifacts and mints no key;
9
+ # only the RubyGems job can mint one, with nothing checked out and no Gemfile or Rakefile loaded;
10
+ # only the release job can write the repository.
11
+ name: Release
12
+
13
+ on:
14
+ push:
15
+ tags: ["v[0-9]*"]
16
+
17
+ permissions: {}
18
+
19
+ jobs:
20
+ build:
21
+ name: Check and build
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
28
+ with:
29
+ persist-credentials: false
30
+
31
+ - name: Set up Ruby
32
+ uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
33
+ with:
34
+ ruby-version: "4.0"
35
+
36
+ - name: The gems, from Gemfile.lock
37
+ # installed afresh, not restored from bundler-cache: a cache any run on main can write is
38
+ # never an input to the gem published
39
+ env:
40
+ BUNDLE_FROZEN: "true"
41
+ run: bundle install
42
+
43
+ - name: RuboCop, Steep, the tests
44
+ run: bundle exec rake
45
+
46
+ - name: Build the gem, and its notes
47
+ run: |
48
+ set -euo pipefail
49
+ version="$(ruby -e 'puts Gem::Specification.load(Dir["*.gemspec"].first).version')"
50
+ if [[ $GITHUB_REF_NAME != "v$version" ]]; then
51
+ echo "::error::$GITHUB_REF_NAME, but the gemspec's version is $version" >&2
52
+ exit 1
53
+ fi
54
+ # into pkg/, as Bundler's gem tasks build it
55
+ bundle exec rake build
56
+ .github/release-notes.sh CHANGELOG.md "$version" > notes.md
57
+
58
+ - name: Keep the gem
59
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
60
+ with:
61
+ name: gem
62
+ path: pkg/
63
+ if-no-files-found: error
64
+
65
+ - name: Keep the notes
66
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
67
+ with:
68
+ name: notes
69
+ path: notes.md
70
+ if-no-files-found: error
71
+
72
+ rubygems:
73
+ name: RubyGems
74
+ needs: build
75
+ runs-on: ubuntu-latest
76
+ environment:
77
+ name: release
78
+ permissions:
79
+ id-token: write # the short-lived token Trusted Publishing exchanges for an API key
80
+ steps:
81
+ - name: The build's gem
82
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
83
+ with:
84
+ name: gem
85
+ path: pkg/
86
+
87
+ - name: Trusted Publishing's API key
88
+ uses: rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a # v2.1.0
89
+
90
+ - name: Push
91
+ # the runner image's RubyGems: no other action in the one job that can mint a key
92
+ run: gem push pkg/*.gem
93
+
94
+ release:
95
+ name: GitHub release
96
+ needs: rubygems
97
+ runs-on: ubuntu-latest
98
+ permissions:
99
+ contents: write
100
+ steps:
101
+ - name: The build's gem
102
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
103
+ with:
104
+ name: gem
105
+ path: pkg/
106
+
107
+ - name: Its notes
108
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
109
+ with:
110
+ name: notes
111
+
112
+ - name: Release
113
+ env:
114
+ GH_TOKEN: ${{ github.token }}
115
+ run: |
116
+ gh release create "$GITHUB_REF_NAME" pkg/*.gem --repo "$GITHUB_REPOSITORY" --verify-tag \
117
+ --title "$GITHUB_REF_NAME" --notes-file notes.md
@@ -0,0 +1,114 @@
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
@@ -0,0 +1,62 @@
1
+ # The Ruby checks, `bundle exec rake` (RuboCop, Steep, the tests with their coverage gate), on each
2
+ # Ruby the matrix lists, the gems installed from Gemfile.lock; then TypeProf's verdict, advisory.
3
+ # nix.yml runs the flake's checks: the Nix format, the shell, the workflows, the package.
4
+ name: Ruby CI
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ # every pull request, whatever it touches: a path-filtered workflow never reports, and a
10
+ # required check that never reports blocks the merge
11
+ pull_request:
12
+ workflow_dispatch:
13
+
14
+ # a pull request's newest run cancels its older ones; any other run, a group of its own: every
15
+ # commit on main checked (in a shared group, a pending run gives way to the newest: the default)
16
+ concurrency:
17
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
18
+ cancel-in-progress: true
19
+
20
+ permissions:
21
+ contents: read
22
+
23
+ jobs:
24
+ checks:
25
+ name: Checks (Ruby ${{ matrix.ruby }})
26
+ runs-on: ubuntu-latest
27
+ strategy:
28
+ fail-fast: false
29
+ matrix:
30
+ # the gemspec's required_ruby_version, and each newer Ruby once released
31
+ ruby: ["4.0"]
32
+ steps:
33
+ - name: Checkout
34
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - name: Set up Ruby
39
+ # bundler-cache: `bundle install`, frozen to Gemfile.lock, cached
40
+ uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
41
+ with:
42
+ ruby-version: ${{ matrix.ruby }}
43
+ bundler-cache: true
44
+
45
+ - name: RuboCop, Steep, the tests
46
+ # RuboCop's offences as annotations too, on a pull request's lines (its github formatter)
47
+ env:
48
+ RUBOCOP_OPTS: --format progress --format github
49
+ run: bundle exec rake
50
+
51
+ - name: RuboCop's safe corrections, as a patch
52
+ # a failed run's: what `rake rubocop:autocorrect` changes, in the run's summary, to
53
+ # `git apply` (a Dependabot bump's, rubocop-autocorrect.yml commits)
54
+ if: failure()
55
+ run: |
56
+ bundle exec rake rubocop:autocorrect || true
57
+ if ! git diff --quiet; then
58
+ { echo '```diff'; git diff; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
59
+ fi
60
+
61
+ - name: TypeProf (advisory)
62
+ run: bundle exec rake typeprof
@@ -0,0 +1,43 @@
1
+ # bundler-audit: Gemfile.lock against the Ruby Advisory Database -- daily, on demand, and when the
2
+ # lock changes (`rake audit`). A finding fails the run, notified to the workflow's creator (or
3
+ # whoever last changed its cron: docs.github.com, "Notifications for workflow runs"). GitHub
4
+ # disables a public repository's schedule after 60 days without activity ("Events that trigger
5
+ # workflows"): Dependabot alerts, the repository's setting, watch the lock without one.
6
+ name: Security
7
+
8
+ on:
9
+ schedule:
10
+ - cron: "0 0 * * *" # daily, midnight UTC
11
+ workflow_dispatch:
12
+ push:
13
+ branches: [main]
14
+ paths:
15
+ - "Gemfile.lock"
16
+ - ".github/workflows/security.yml"
17
+ pull_request:
18
+ paths:
19
+ - "Gemfile.lock"
20
+ - ".github/workflows/security.yml"
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ jobs:
26
+ bundler-audit:
27
+ name: bundler-audit
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
32
+ with:
33
+ persist-credentials: false
34
+
35
+ - name: Set up Ruby
36
+ uses: ruby/setup-ruby@7f797815d8adceb1c412e7abefdd1ab4abc47a5f # v1.335.0
37
+ with:
38
+ ruby-version: "4.0"
39
+ bundler-cache: true
40
+
41
+ - name: bundler-audit
42
+ # the advisory database updated first, then the lock checked
43
+ run: bundle exec rake audit
@@ -0,0 +1,56 @@
1
+ name: Conventional Commits Check
2
+
3
+ # On a push, the whole history rather than `--from-latest-tag`: with no tag
4
+ # yet, cog stops "unable to get any tag" -- every new project's first push.
5
+ # A pull request's title too: a squash merge of two or more commits makes the
6
+ # title the commit, and so a changelog entry -- checked again when edited.
7
+
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ pull_request:
12
+ types: [opened, synchronize, reopened, edited]
13
+
14
+ # a pull request's newest run cancels its older ones; any other run, a group of its own: every
15
+ # commit on main checked (in a shared group, a pending run gives way to the newest: the default)
16
+ concurrency:
17
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
18
+ cancel-in-progress: true
19
+
20
+ permissions:
21
+ contents: read
22
+
23
+ jobs:
24
+ check_commits:
25
+ runs-on: ubuntu-latest
26
+ name: Check Conventional Commits
27
+ steps:
28
+ - name: Checkout
29
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
+ with:
31
+ fetch-depth: 0
32
+ persist-credentials: false
33
+ ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
34
+
35
+ - name: Check PR Commits
36
+ if: github.event_name == 'pull_request'
37
+ uses: cocogitto/cocogitto-action@52d0023b4396897f1815648e553db2ab8d782f3a # v4.2.0
38
+ with:
39
+ command: check
40
+ args: --ignore-merge-commits ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
41
+
42
+ # after the action: `cog verify` panics without git's user.name and user.email, which
43
+ # the action sets globally (its git-user inputs) on every run
44
+ - name: Check PR Title
45
+ if: github.event_name == 'pull_request'
46
+ env:
47
+ TITLE: ${{ github.event.pull_request.title }} # through the environment, never the script
48
+ # after `--`: a title is the message, never an option (`--file=...`)
49
+ run: cog verify -- "$TITLE"
50
+
51
+ - name: Check Push Commits
52
+ if: github.event_name == 'push'
53
+ uses: cocogitto/cocogitto-action@52d0023b4396897f1815648e553db2ab8d782f3a # v4.2.0
54
+ with:
55
+ command: check
56
+ args: --ignore-merge-commits
@@ -0,0 +1,44 @@
1
+ # The action installs dprint's latest release, attested; the formatting is
2
+ # the plugins', pinned in dprint.json.
3
+ name: Dprint Formatting Checks
4
+
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ paths:
9
+ # dprint.json's plugins' extensions, markdown and toml, no more: one
10
+ # no plugin handles (yaml, json, Dockerfile) only schedules runs that
11
+ # check nothing. Add a plugin there and its extensions here together,
12
+ # each its own entry: path filters have no brace expansion {a,b}.
13
+ - "**/*.md"
14
+ - "**/*.markdown"
15
+ - "**/*.toml"
16
+ - ".github/workflows/style-check-dprint.yml"
17
+ - "dprint.json"
18
+ # every pull request, whatever it touches: a path-filtered workflow never
19
+ # reports, and a required check that never reports blocks the merge
20
+ pull_request:
21
+
22
+ # a pull request's newest run cancels its older ones; any other run, a group of its own: every
23
+ # commit on main checked (in a shared group, a pending run gives way to the newest: the default)
24
+ concurrency:
25
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
26
+ cancel-in-progress: true
27
+
28
+ permissions:
29
+ contents: read
30
+
31
+ jobs:
32
+ dprint:
33
+ name: dprint check
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - name: Checkout
37
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
38
+ with:
39
+ persist-credentials: false
40
+
41
+ - name: Dprint format check
42
+ uses: dprint/check@7dc032d8874778fc024f6f9b4a7e6d896782c330 # v2.5
43
+ with:
44
+ config-path: dprint.json