develoz-rails 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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +31 -0
- data/.github/workflows/release.yml +459 -0
- data/.rubocop.yml +52 -0
- data/.sisyphus/notepads/develoz-rails-template/learnings.md +855 -0
- data/.tool-versions +2 -0
- data/CHANGELOG.md +28 -0
- data/CONTRIBUTING.md +120 -0
- data/README.md +76 -0
- data/Rakefile +18 -0
- data/config/generators.yml +105 -0
- data/docs/guide/installation.md +574 -0
- data/exe/develoz +6 -0
- data/install +98 -0
- data/lib/develoz/canonical_fetcher.rb +56 -0
- data/lib/develoz/cli.rb +84 -0
- data/lib/develoz/generators/base.rb +134 -0
- data/lib/develoz/manifest.rb +44 -0
- data/lib/develoz/options.rb +55 -0
- data/lib/develoz/version.rb +5 -0
- data/lib/develoz/version_resolver.rb +103 -0
- data/lib/develoz.rb +15 -0
- data/lib/generators/develoz/active_resource/active_resource_generator.rb +25 -0
- data/lib/generators/develoz/active_resource/templates/application_resource.rb.tt +7 -0
- data/lib/generators/develoz/active_resource/templates/example_resource.rb.tt +5 -0
- data/lib/generators/develoz/admin/admin_generator.rb +44 -0
- data/lib/generators/develoz/admin/templates/admin_base_controller.rb.tt +7 -0
- data/lib/generators/develoz/admin/templates/admin_layout.html.erb.tt +38 -0
- data/lib/generators/develoz/admin/templates/dashboard_controller.rb.tt +7 -0
- data/lib/generators/develoz/admin/templates/dashboard_index.html.erb.tt +2 -0
- data/lib/generators/develoz/admin/templates/routes.rb.tt +3 -0
- data/lib/generators/develoz/agents_docs/agents_docs_generator.rb +50 -0
- data/lib/generators/develoz/agents_docs/templates/.github/pull_request_template.md.tt +13 -0
- data/lib/generators/develoz/agents_docs/templates/AGENTS.md.tt +36 -0
- data/lib/generators/develoz/agents_docs/templates/docs/development.md.tt +121 -0
- data/lib/generators/develoz/agents_docs/templates/docs/performance.md.tt +46 -0
- data/lib/generators/develoz/agents_docs/templates/docs/testing.md.tt +92 -0
- data/lib/generators/develoz/agents_docs/templates/spec/cassettes/Example_API/fetches_data_from_an_external_API.yml.tt +23 -0
- data/lib/generators/develoz/agents_docs/templates/spec/requests/example_api_spec.rb.tt +14 -0
- data/lib/generators/develoz/agents_docs/templates/spec/support/faraday.rb.tt +19 -0
- data/lib/generators/develoz/agents_docs/templates/spec/support/vcr.rb.tt +11 -0
- data/lib/generators/develoz/api/api_generator.rb +55 -0
- data/lib/generators/develoz/api/templates/app/blueprints/example_blueprint.rb.tt +6 -0
- data/lib/generators/develoz/api/templates/app/controllers/api/v1/base_controller.rb.tt +96 -0
- data/lib/generators/develoz/api/templates/config/initializers/blueprinter.rb.tt +16 -0
- data/lib/generators/develoz/api/templates/config/initializers/rswag_api.rb.tt +15 -0
- data/lib/generators/develoz/api/templates/config/initializers/rswag_ui.rb.tt +17 -0
- data/lib/generators/develoz/api/templates/spec/requests/api/v1/examples_spec.rb.tt +25 -0
- data/lib/generators/develoz/auth/auth_generator.rb +82 -0
- data/lib/generators/develoz/auth/templates/app/controllers/concerns/authentication.rb.tt +53 -0
- data/lib/generators/develoz/auth/templates/app/controllers/passwords_controller.rb.tt +35 -0
- data/lib/generators/develoz/auth/templates/app/controllers/sessions_controller.rb.tt +22 -0
- data/lib/generators/develoz/auth/templates/app/mailers/passwords_mailer.rb.tt +8 -0
- data/lib/generators/develoz/auth/templates/app/models/current.rb.tt +5 -0
- data/lib/generators/develoz/auth/templates/app/models/user.rb.tt +7 -0
- data/lib/generators/develoz/auth/templates/app/views/passwords/edit.html.erb.tt +9 -0
- data/lib/generators/develoz/auth/templates/app/views/passwords_mailer/reset.html.erb.tt +6 -0
- data/lib/generators/develoz/auth/templates/app/views/sessions/new.html.erb.tt +11 -0
- data/lib/generators/develoz/auth/templates/db/migrate/create_users.rb.tt +13 -0
- data/lib/generators/develoz/auth/templates/spec/requests/passwords_spec.rb.tt +60 -0
- data/lib/generators/develoz/auth/templates/spec/requests/sessions_spec.rb.tt +37 -0
- data/lib/generators/develoz/ci/ci_generator.rb +66 -0
- data/lib/generators/develoz/ci/templates/.github/workflows/ci.yml.tt +18 -0
- data/lib/generators/develoz/ci/templates/.haml-lint.yml.tt +12 -0
- data/lib/generators/develoz/ci/templates/.markdownlint.json.tt +10 -0
- data/lib/generators/develoz/ci/templates/.reek.yml.tt +28 -0
- data/lib/generators/develoz/ci/templates/.rubocop.yml.tt +22 -0
- data/lib/generators/develoz/ci/templates/.stylelintrc.json.tt +4 -0
- data/lib/generators/develoz/ci/templates/.yamllint.tt +14 -0
- data/lib/generators/develoz/ci/templates/Rakefile.tt +73 -0
- data/lib/generators/develoz/ci/templates/bin/ci.tt +6 -0
- data/lib/generators/develoz/ci/templates/biome.json.tt +41 -0
- data/lib/generators/develoz/ci/templates/config/ci.rb.tt +23 -0
- data/lib/generators/develoz/concerns/concerns_generator.rb +48 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/configurable.rb.tt +120 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/optimized_finders.rb.tt +93 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/searchable_concern.rb.tt +31 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/transitionable.rb.tt +144 -0
- data/lib/generators/develoz/concerns/templates/app/models/configuration.rb.tt +104 -0
- data/lib/generators/develoz/concerns/templates/db/migrate/add_status_transitions.rb.tt +30 -0
- data/lib/generators/develoz/concerns/templates/db/migrate/create_configurations.rb.tt +25 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/configurable_spec.rb.tt +172 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/optimized_finders_spec.rb.tt +200 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/searchable_concern_spec.rb.tt +84 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/transitionable_spec.rb.tt +234 -0
- data/lib/generators/develoz/concerns/templates/spec/models/configuration_spec.rb.tt +223 -0
- data/lib/generators/develoz/database/database_generator.rb +44 -0
- data/lib/generators/develoz/database/templates/config/database.yml.tt +53 -0
- data/lib/generators/develoz/database/templates/config/initializers/pg_search.rb.tt +5 -0
- data/lib/generators/develoz/database/templates/db/migrate/create_pg_extensions.rb.tt +13 -0
- data/lib/generators/develoz/db_backup/db_backup_generator.rb +47 -0
- data/lib/generators/develoz/db_backup/templates/backup_rake.tt +48 -0
- data/lib/generators/develoz/db_backup/templates/bin_db_backup.tt +25 -0
- data/lib/generators/develoz/db_backup/templates/compose_service.tt +14 -0
- data/lib/generators/develoz/doc_specs/doc_specs_generator.rb +33 -0
- data/lib/generators/develoz/doc_specs/templates/bin/generate-docs.tt +392 -0
- data/lib/generators/develoz/doc_specs/templates/lib/tasks/docs_check.rake.tt +8 -0
- data/lib/generators/develoz/doc_specs/templates/spec/support/doc_screenshot_helper.rb.tt +17 -0
- data/lib/generators/develoz/doc_specs/templates/spec/system/example_doc_spec.rb.tt +38 -0
- data/lib/generators/develoz/docker/docker_generator.rb +54 -0
- data/lib/generators/develoz/docker/templates/bin_dev.tt +5 -0
- data/lib/generators/develoz/docker/templates/bin_docker_entrypoint.tt +8 -0
- data/lib/generators/develoz/docker/templates/bin_run.tt +152 -0
- data/lib/generators/develoz/docker/templates/bin_setup.tt +15 -0
- data/lib/generators/develoz/docker/templates/docker-compose.yml.tt +59 -0
- data/lib/generators/develoz/docker/templates/dockerfile_dev.tt +24 -0
- data/lib/generators/develoz/docs_render/docs_render_generator.rb +46 -0
- data/lib/generators/develoz/docs_render/templates/app/assets/stylesheets/documentation.scss.tt +145 -0
- data/lib/generators/develoz/docs_render/templates/app/controllers/docs_controller.rb.tt +7 -0
- data/lib/generators/develoz/docs_render/templates/app/javascript/docs.js.tt +15 -0
- data/lib/generators/develoz/docs_render/templates/app/models/document.rb.tt +189 -0
- data/lib/generators/develoz/docs_render/templates/app/views/docs/show.html.erb.tt +7 -0
- data/lib/generators/develoz/docs_render/templates/config/initializers/redcarpet_rouge.rb.tt +16 -0
- data/lib/generators/develoz/frontend_core/frontend_core_generator.rb +40 -0
- data/lib/generators/develoz/frontend_core/templates/.annotaterb.yml.tt +16 -0
- data/lib/generators/develoz/frontend_core/templates/config/importmap.rb.tt +12 -0
- data/lib/generators/develoz/frontend_core/templates/config/initializers/pagy.rb.tt +10 -0
- data/lib/generators/develoz/install/install_generator.rb +51 -0
- data/lib/generators/develoz/kamal/kamal_generator.rb +59 -0
- data/lib/generators/develoz/kamal/templates/accessories_postgres.yml.tt +14 -0
- data/lib/generators/develoz/kamal/templates/deploy.yml.tt +43 -0
- data/lib/generators/develoz/kamal/templates/dockerfile_prod.tt +54 -0
- data/lib/generators/develoz/kamal/templates/kamal_secrets.tt +22 -0
- data/lib/generators/develoz/maintenance/maintenance_generator.rb +29 -0
- data/lib/generators/develoz/maintenance/templates/app/tasks/maintenance/example_task.rb.tt +20 -0
- data/lib/generators/develoz/maintenance/templates/lib/tasks/maintenance_counters.rake.tt +27 -0
- data/lib/generators/develoz/push/push_generator.rb +61 -0
- data/lib/generators/develoz/push/templates/app/javascript/pwa/subscription.js.tt +57 -0
- data/lib/generators/develoz/push/templates/app/models/push_subscription.rb.tt +14 -0
- data/lib/generators/develoz/push/templates/app/services/push_notification_service.rb.tt +36 -0
- data/lib/generators/develoz/push/templates/app/views/pwa/sw_push_handlers.js.tt +49 -0
- data/lib/generators/develoz/push/templates/constants_env.tt +5 -0
- data/lib/generators/develoz/push/templates/db/migrate/create_push_subscriptions.rb.tt +15 -0
- data/lib/generators/develoz/pwa/pwa_generator.rb +46 -0
- data/lib/generators/develoz/pwa/templates/app/controllers/pwa_controller.rb.tt +17 -0
- data/lib/generators/develoz/pwa/templates/app/javascript/pwa/registration.js.tt +25 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/manifest.json.erb.tt +24 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/offline.html.erb.tt +51 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/service-worker.js.tt +109 -0
- data/lib/generators/develoz/pwa/templates/routes.rb.tt +3 -0
- data/lib/generators/develoz/solid/solid_generator.rb +52 -0
- data/lib/generators/develoz/solid/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/develoz/solid/templates/config/cable.yml.tt +10 -0
- data/lib/generators/develoz/solid/templates/config/cache.yml.tt +16 -0
- data/lib/generators/develoz/solid/templates/config/initializers/mission_control.rb.tt +8 -0
- data/lib/generators/develoz/solid/templates/config/initializers/solid.rb.tt +7 -0
- data/lib/generators/develoz/solid/templates/config/queue.yml.tt +18 -0
- data/lib/generators/develoz/solid/templates/config/recurring.yml.tt +20 -0
- data/lib/generators/develoz/strict_loading/strict_loading_generator.rb +17 -0
- data/lib/generators/develoz/strict_loading/templates/config/initializers/strict_loading.rb.tt +13 -0
- data/lib/generators/develoz/testing/templates/rspec.tt +2 -0
- data/lib/generators/develoz/testing/templates/rspec_parallel.tt +2 -0
- data/lib/generators/develoz/testing/templates/spec/rails_helper.rb.tt +32 -0
- data/lib/generators/develoz/testing/templates/spec/spec_helper.rb.tt +31 -0
- data/lib/generators/develoz/testing/testing_generator.rb +39 -0
- data/lib/generators/develoz/tooling/templates/constants.rb.tt +17 -0
- data/lib/generators/develoz/tooling/templates/env.example.tt +4 -0
- data/lib/generators/develoz/tooling/templates/vscode/extensions.json.tt +8 -0
- data/lib/generators/develoz/tooling/templates/vscode/settings.json.tt +26 -0
- data/lib/generators/develoz/tooling/templates/vscode/tasks.json.tt +26 -0
- data/lib/generators/develoz/tooling/tooling_generator.rb +33 -0
- data/lib/generators/develoz/ui/templates/bin/setup_develoz_ui.tt +20 -0
- data/lib/generators/develoz/ui/ui_generator.rb +57 -0
- data/lib/generators/develoz/versioning/templates/app/helpers/application_helper.rb.tt +7 -0
- data/lib/generators/develoz/versioning/templates/app/views/shared/_app_version.html.erb.tt +3 -0
- data/lib/generators/develoz/versioning/templates/spec/helpers/application_helper_spec.rb.tt +11 -0
- data/lib/generators/develoz/versioning/versioning_generator.rb +48 -0
- data/lib/tasks/canonical.rake +27 -0
- data/templates/.keep +0 -0
- data/templates/CANONICAL_SOURCES.md +93 -0
- data/templates/pull_request_template.md.tt +13 -0
- metadata +383 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ee93be7d4583674313e36b90ee84aaecf4f65d08eb23659e179b419608cd38dd
|
|
4
|
+
data.tar.gz: 5e9dc27ad205be346f3c71740e0572294dabbd63357abe6b6ad788589faa9e13
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4c2c7a71236567800a089344d3b15dbae4d42eceed06b3234e8d24bd5ef1c7e115c1ba79c0d5eb473f03696a5de86cc6f5d2759b1b56628da40c2ba1452fcbb9
|
|
7
|
+
data.tar.gz: 8e942cdcb7d0a22f2df4eb50c31bb1719e0ff904d7b0a5c04dbdab69ce0c93e408588973643516f2069d0de2ea1e0e6ba938f11c54e29a78d1a1cbc5083972de
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: 4.0.5
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Run CI checks
|
|
23
|
+
run: bin/ci
|
|
24
|
+
|
|
25
|
+
- name: Upload coverage
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
if: always()
|
|
28
|
+
with:
|
|
29
|
+
name: coverage-report
|
|
30
|
+
path: coverage/
|
|
31
|
+
retention-days: 30
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions: {}
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: release-${{ github.event.release.tag_name }}
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
validate:
|
|
15
|
+
if: ${{ github.event.release.prerelease == false }}
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Check out release tag
|
|
22
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
|
|
23
|
+
with:
|
|
24
|
+
ref: ${{ github.event.release.tag_name }}
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- name: Set up Ruby
|
|
29
|
+
uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600
|
|
30
|
+
with:
|
|
31
|
+
ruby-version: 4.0.5
|
|
32
|
+
bundler-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Validate release identity
|
|
35
|
+
env:
|
|
36
|
+
RELEASE_SHA: ${{ github.sha }}
|
|
37
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
38
|
+
REPOSITORY: ${{ github.repository }}
|
|
39
|
+
run: |
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
|
|
42
|
+
fail() {
|
|
43
|
+
echo "::error::$1"
|
|
44
|
+
exit 1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
[[ "$REPOSITORY" == "develoz-com/rails-template" ]] ||
|
|
48
|
+
fail "release workflow must run in develoz-com/rails-template"
|
|
49
|
+
[[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ||
|
|
50
|
+
fail "release tag must match vX.Y.Z"
|
|
51
|
+
|
|
52
|
+
tag_sha="$(git rev-parse --verify "refs/tags/${RELEASE_TAG}^{commit}")"
|
|
53
|
+
head_sha="$(git rev-parse --verify HEAD)"
|
|
54
|
+
main_sha="$(git rev-parse --verify "refs/remotes/origin/main^{commit}")"
|
|
55
|
+
|
|
56
|
+
[[ "$tag_sha" == "$head_sha" ]] ||
|
|
57
|
+
fail "checked-out commit does not match ${RELEASE_TAG}"
|
|
58
|
+
[[ "$tag_sha" == "$RELEASE_SHA" ]] ||
|
|
59
|
+
fail "${RELEASE_TAG} does not match the release event SHA"
|
|
60
|
+
[[ "$tag_sha" == "$main_sha" ]] ||
|
|
61
|
+
fail "${RELEASE_TAG} is not the current origin/main commit"
|
|
62
|
+
|
|
63
|
+
version="$(ruby -Ilib -rdeveloz/version -e 'print Develoz::VERSION')"
|
|
64
|
+
[[ "$RELEASE_TAG" == "v${version}" ]] ||
|
|
65
|
+
fail "${RELEASE_TAG} does not match Develoz::VERSION ${version}"
|
|
66
|
+
|
|
67
|
+
- name: Run CI
|
|
68
|
+
run: bin/ci
|
|
69
|
+
|
|
70
|
+
- name: Validate changelog update
|
|
71
|
+
env:
|
|
72
|
+
PUBLISHED_AT: ${{ github.event.release.published_at }}
|
|
73
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
74
|
+
REPOSITORY: ${{ github.repository }}
|
|
75
|
+
run: |
|
|
76
|
+
set -euo pipefail
|
|
77
|
+
|
|
78
|
+
release_date="${PUBLISHED_AT%%T*}"
|
|
79
|
+
[[ "$release_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || {
|
|
80
|
+
echo "::error::release published_at must contain a YYYY-MM-DD date"
|
|
81
|
+
exit 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
bin/update_changelog \
|
|
85
|
+
--tag "$RELEASE_TAG" \
|
|
86
|
+
--date "$release_date" \
|
|
87
|
+
--repository "$REPOSITORY" \
|
|
88
|
+
--check \
|
|
89
|
+
CHANGELOG.md
|
|
90
|
+
|
|
91
|
+
publish:
|
|
92
|
+
needs: validate
|
|
93
|
+
if: ${{ github.event.release.prerelease == false && needs.validate.result == 'success' }}
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
environment: rubygems
|
|
96
|
+
permissions:
|
|
97
|
+
contents: read
|
|
98
|
+
id-token: write
|
|
99
|
+
|
|
100
|
+
steps:
|
|
101
|
+
- name: Check out immutable release commit
|
|
102
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
|
|
103
|
+
with:
|
|
104
|
+
ref: ${{ github.sha }}
|
|
105
|
+
fetch-depth: 0
|
|
106
|
+
fetch-tags: true
|
|
107
|
+
persist-credentials: false
|
|
108
|
+
|
|
109
|
+
- name: Validate immutable release refs
|
|
110
|
+
env:
|
|
111
|
+
RELEASE_SHA: ${{ github.sha }}
|
|
112
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
113
|
+
REPOSITORY: ${{ github.repository }}
|
|
114
|
+
run: |
|
|
115
|
+
set -euo pipefail
|
|
116
|
+
|
|
117
|
+
fail() {
|
|
118
|
+
echo "::error::$1"
|
|
119
|
+
exit 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
[[ "$REPOSITORY" == "develoz-com/rails-template" ]] ||
|
|
123
|
+
fail "release workflow must run in develoz-com/rails-template"
|
|
124
|
+
[[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ||
|
|
125
|
+
fail "release tag must match vX.Y.Z"
|
|
126
|
+
|
|
127
|
+
tag_sha="$(git rev-parse --verify "refs/tags/${RELEASE_TAG}^{commit}")"
|
|
128
|
+
head_sha="$(git rev-parse --verify HEAD)"
|
|
129
|
+
main_sha="$(git rev-parse --verify "refs/remotes/origin/main^{commit}")"
|
|
130
|
+
|
|
131
|
+
[[ "$head_sha" == "$RELEASE_SHA" ]] ||
|
|
132
|
+
fail "checked-out commit does not match the release event SHA"
|
|
133
|
+
[[ "$tag_sha" == "$RELEASE_SHA" ]] ||
|
|
134
|
+
fail "${RELEASE_TAG} does not match the release event SHA"
|
|
135
|
+
[[ "$main_sha" == "$RELEASE_SHA" ]] ||
|
|
136
|
+
fail "${RELEASE_TAG} is not the current origin/main commit"
|
|
137
|
+
|
|
138
|
+
- name: Set up Ruby
|
|
139
|
+
uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600
|
|
140
|
+
with:
|
|
141
|
+
ruby-version: 4.0.5
|
|
142
|
+
bundler: none
|
|
143
|
+
|
|
144
|
+
- name: Validate gem version
|
|
145
|
+
env:
|
|
146
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
147
|
+
run: |
|
|
148
|
+
set -euo pipefail
|
|
149
|
+
|
|
150
|
+
version="$(ruby -Ilib -rdeveloz/version -e 'print Develoz::VERSION')"
|
|
151
|
+
if [[ "$RELEASE_TAG" != "v${version}" ]]; then
|
|
152
|
+
echo "::error::${RELEASE_TAG} does not match Develoz::VERSION ${version}"
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
printf 'VERSION=%s\n' "$version" >> "$GITHUB_ENV"
|
|
156
|
+
|
|
157
|
+
- name: Build exact gem
|
|
158
|
+
run: |
|
|
159
|
+
set -euo pipefail
|
|
160
|
+
|
|
161
|
+
gem_path="pkg/develoz-rails-${VERSION}.gem"
|
|
162
|
+
mkdir -p pkg
|
|
163
|
+
rm -f -- "$gem_path"
|
|
164
|
+
gem build develoz-rails.gemspec --output "$gem_path"
|
|
165
|
+
[[ -f "$gem_path" ]] || {
|
|
166
|
+
echo "::error::gem build did not create ${gem_path}"
|
|
167
|
+
exit 1
|
|
168
|
+
}
|
|
169
|
+
printf 'GEM_PATH=%s\n' "$gem_path" >> "$GITHUB_ENV"
|
|
170
|
+
|
|
171
|
+
- name: Inspect built gem
|
|
172
|
+
run: |
|
|
173
|
+
ruby -rrubygems/package <<'RUBY'
|
|
174
|
+
gem_path = ENV.fetch("GEM_PATH")
|
|
175
|
+
version = ENV.fetch("VERSION")
|
|
176
|
+
package = Gem::Package.new(gem_path)
|
|
177
|
+
package.verify
|
|
178
|
+
specification = package.spec
|
|
179
|
+
|
|
180
|
+
abort "expected gem name develoz-rails, got #{specification.name}" unless specification.name == "develoz-rails"
|
|
181
|
+
abort "expected gem version #{version}, got #{specification.version}" unless specification.version.to_s == version
|
|
182
|
+
abort "expected ruby platform, got #{specification.platform}" unless specification.platform.to_s == "ruby"
|
|
183
|
+
abort "expected only the develoz executable" unless specification.executables == ["develoz"]
|
|
184
|
+
|
|
185
|
+
files = package.contents
|
|
186
|
+
required_files = %w[
|
|
187
|
+
CHANGELOG.md
|
|
188
|
+
README.md
|
|
189
|
+
config/generators.yml
|
|
190
|
+
exe/develoz
|
|
191
|
+
lib/develoz.rb
|
|
192
|
+
lib/develoz/version.rb
|
|
193
|
+
lib/generators/develoz/ci/templates/.github/workflows/ci.yml.tt
|
|
194
|
+
templates/pull_request_template.md.tt
|
|
195
|
+
]
|
|
196
|
+
missing_files = required_files - files
|
|
197
|
+
abort "gem is missing required files: #{missing_files.join(', ')}" unless missing_files.empty?
|
|
198
|
+
|
|
199
|
+
excluded_files = %w[.gitignore .rspec Gemfile develoz-rails.gemspec]
|
|
200
|
+
excluded_prefixes = %w[bin/ spec/]
|
|
201
|
+
unexpected_files = files.select do |file|
|
|
202
|
+
excluded_files.include?(file) || excluded_prefixes.any? { |prefix| file.start_with?(prefix) }
|
|
203
|
+
end
|
|
204
|
+
abort "gem contains excluded files: #{unexpected_files.join(', ')}" unless unexpected_files.empty?
|
|
205
|
+
RUBY
|
|
206
|
+
|
|
207
|
+
- name: Check RubyGems version state
|
|
208
|
+
id: version-state
|
|
209
|
+
env:
|
|
210
|
+
RUN_ATTEMPT: ${{ github.run_attempt }}
|
|
211
|
+
run: |
|
|
212
|
+
set -euo pipefail
|
|
213
|
+
|
|
214
|
+
[[ "$RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]] || {
|
|
215
|
+
echo "::error::invalid workflow run attempt ${RUN_ATTEMPT}"
|
|
216
|
+
exit 1
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
api_url="https://rubygems.org/api/v2/rubygems/develoz-rails/versions/${VERSION}.json"
|
|
220
|
+
checks=1
|
|
221
|
+
if (( RUN_ATTEMPT > 1 )); then
|
|
222
|
+
checks=12
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
status=""
|
|
226
|
+
for ((check = 1; check <= checks; check++)); do
|
|
227
|
+
status="$(curl \
|
|
228
|
+
--silent \
|
|
229
|
+
--show-error \
|
|
230
|
+
--location \
|
|
231
|
+
--connect-timeout 10 \
|
|
232
|
+
--max-time 30 \
|
|
233
|
+
--output /dev/null \
|
|
234
|
+
--write-out '%{http_code}' \
|
|
235
|
+
"$api_url")"
|
|
236
|
+
|
|
237
|
+
if [[ "$status" == "200" ]]; then
|
|
238
|
+
break
|
|
239
|
+
fi
|
|
240
|
+
[[ "$status" == "404" ]] || {
|
|
241
|
+
echo "::error::RubyGems API returned HTTP ${status}"
|
|
242
|
+
exit 1
|
|
243
|
+
}
|
|
244
|
+
if (( check < checks )); then
|
|
245
|
+
sleep 5
|
|
246
|
+
fi
|
|
247
|
+
done
|
|
248
|
+
|
|
249
|
+
if [[ "$status" == "200" ]]; then
|
|
250
|
+
if (( RUN_ATTEMPT == 1 )); then
|
|
251
|
+
echo "::error::develoz-rails ${VERSION} already exists on RubyGems during the first run attempt"
|
|
252
|
+
exit 1
|
|
253
|
+
fi
|
|
254
|
+
echo "should-push=false" >> "$GITHUB_OUTPUT"
|
|
255
|
+
echo "develoz-rails ${VERSION} is already visible; skipping gem push on rerun"
|
|
256
|
+
else
|
|
257
|
+
echo "should-push=true" >> "$GITHUB_OUTPUT"
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
- name: Configure RubyGems credentials
|
|
261
|
+
if: ${{ steps.version-state.outputs.should-push == 'true' }}
|
|
262
|
+
uses: rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a
|
|
263
|
+
|
|
264
|
+
- name: Push exact gem
|
|
265
|
+
if: ${{ steps.version-state.outputs.should-push == 'true' }}
|
|
266
|
+
run: gem push "pkg/develoz-rails-${VERSION}.gem"
|
|
267
|
+
|
|
268
|
+
- name: Verify RubyGems API visibility
|
|
269
|
+
run: |
|
|
270
|
+
set -euo pipefail
|
|
271
|
+
|
|
272
|
+
api_url="https://rubygems.org/api/v2/rubygems/develoz-rails/versions/${VERSION}.json"
|
|
273
|
+
api_response="$RUNNER_TEMP/develoz-rails-${VERSION}.json"
|
|
274
|
+
status=""
|
|
275
|
+
|
|
276
|
+
for ((check = 1; check <= 18; check++)); do
|
|
277
|
+
status="$(curl \
|
|
278
|
+
--silent \
|
|
279
|
+
--show-error \
|
|
280
|
+
--location \
|
|
281
|
+
--connect-timeout 10 \
|
|
282
|
+
--max-time 30 \
|
|
283
|
+
--output "$api_response" \
|
|
284
|
+
--write-out '%{http_code}' \
|
|
285
|
+
"$api_url")"
|
|
286
|
+
|
|
287
|
+
if [[ "$status" == "200" ]]; then
|
|
288
|
+
break
|
|
289
|
+
fi
|
|
290
|
+
[[ "$status" == "404" ]] || {
|
|
291
|
+
echo "::error::RubyGems API returned HTTP ${status}"
|
|
292
|
+
exit 1
|
|
293
|
+
}
|
|
294
|
+
if (( check < 18 )); then
|
|
295
|
+
sleep 5
|
|
296
|
+
fi
|
|
297
|
+
done
|
|
298
|
+
|
|
299
|
+
[[ "$status" == "200" ]] || {
|
|
300
|
+
echo "::error::develoz-rails ${VERSION} did not become visible through the RubyGems API"
|
|
301
|
+
exit 1
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
API_RESPONSE="$api_response" ruby -rjson -rdigest <<'RUBY'
|
|
305
|
+
payload = JSON.parse(File.read(ENV.fetch("API_RESPONSE")))
|
|
306
|
+
version = ENV.fetch("VERSION")
|
|
307
|
+
gem_path = ENV.fetch("GEM_PATH")
|
|
308
|
+
expected_sha = Digest::SHA256.file(gem_path).hexdigest
|
|
309
|
+
|
|
310
|
+
abort "RubyGems API returned the wrong gem name" unless payload.fetch("name") == "develoz-rails"
|
|
311
|
+
abort "RubyGems API returned the wrong version" unless payload.fetch("version") == version
|
|
312
|
+
abort "RubyGems API returned the wrong platform" unless payload.fetch("platform") == "ruby"
|
|
313
|
+
abort "RubyGems API reports the release as yanked" if payload.fetch("yanked")
|
|
314
|
+
abort "RubyGems API SHA does not match the built gem" unless payload.fetch("sha") == expected_sha
|
|
315
|
+
RUBY
|
|
316
|
+
|
|
317
|
+
finalize-changelog:
|
|
318
|
+
needs: publish
|
|
319
|
+
if: ${{ github.event.release.prerelease == false && needs.publish.result == 'success' }}
|
|
320
|
+
runs-on: ubuntu-latest
|
|
321
|
+
permissions:
|
|
322
|
+
contents: write
|
|
323
|
+
|
|
324
|
+
steps:
|
|
325
|
+
- name: Check out main
|
|
326
|
+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
|
|
327
|
+
with:
|
|
328
|
+
ref: main
|
|
329
|
+
fetch-depth: 0
|
|
330
|
+
|
|
331
|
+
- name: Set up Ruby
|
|
332
|
+
uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600
|
|
333
|
+
with:
|
|
334
|
+
ruby-version: 4.0.5
|
|
335
|
+
bundler: none
|
|
336
|
+
|
|
337
|
+
- name: Validate main and release tag
|
|
338
|
+
env:
|
|
339
|
+
RELEASE_SHA: ${{ github.sha }}
|
|
340
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
341
|
+
REPOSITORY: ${{ github.repository }}
|
|
342
|
+
run: |
|
|
343
|
+
set -euo pipefail
|
|
344
|
+
|
|
345
|
+
fail() {
|
|
346
|
+
echo "::error::$1"
|
|
347
|
+
exit 1
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
[[ "$REPOSITORY" == "develoz-com/rails-template" ]] ||
|
|
351
|
+
fail "release workflow must run in develoz-com/rails-template"
|
|
352
|
+
[[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ||
|
|
353
|
+
fail "release tag must match vX.Y.Z"
|
|
354
|
+
|
|
355
|
+
tag_sha="$(git rev-parse --verify "refs/tags/${RELEASE_TAG}^{commit}")"
|
|
356
|
+
head_sha="$(git rev-parse --verify HEAD)"
|
|
357
|
+
main_sha="$(git rev-parse --verify "refs/remotes/origin/main^{commit}")"
|
|
358
|
+
|
|
359
|
+
[[ "$tag_sha" == "$RELEASE_SHA" ]] ||
|
|
360
|
+
fail "${RELEASE_TAG} no longer matches the release event SHA"
|
|
361
|
+
[[ "$head_sha" == "$main_sha" ]] ||
|
|
362
|
+
fail "checked-out main does not match origin/main"
|
|
363
|
+
|
|
364
|
+
if [[ "$main_sha" == "$RELEASE_SHA" ]]; then
|
|
365
|
+
finalize_state="fresh"
|
|
366
|
+
else
|
|
367
|
+
parents="$(git show -s --format=%P "$main_sha")"
|
|
368
|
+
[[ "$parents" == "$RELEASE_SHA" ]] ||
|
|
369
|
+
fail "main is not the release commit or its single direct child"
|
|
370
|
+
|
|
371
|
+
subject="$(git show -s --format=%s "$main_sha")"
|
|
372
|
+
expected_subject="docs: finalize ${RELEASE_TAG} changelog"
|
|
373
|
+
[[ "$subject" == "$expected_subject" ]] ||
|
|
374
|
+
fail "the commit after the release has an unexpected subject"
|
|
375
|
+
|
|
376
|
+
changed_files="$(git diff-tree --no-commit-id --name-only -r "$main_sha")"
|
|
377
|
+
[[ "$changed_files" == "CHANGELOG.md" ]] ||
|
|
378
|
+
fail "the commit after the release must change only CHANGELOG.md"
|
|
379
|
+
|
|
380
|
+
finalize_state="already-finalized"
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
version="$(ruby -Ilib -rdeveloz/version -e 'print Develoz::VERSION')"
|
|
384
|
+
[[ "$RELEASE_TAG" == "v${version}" ]] ||
|
|
385
|
+
fail "${RELEASE_TAG} does not match Develoz::VERSION ${version}"
|
|
386
|
+
printf 'FINALIZE_STATE=%s\n' "$finalize_state" >> "$GITHUB_ENV"
|
|
387
|
+
|
|
388
|
+
- name: Finalize changelog
|
|
389
|
+
env:
|
|
390
|
+
PUBLISHED_AT: ${{ github.event.release.published_at }}
|
|
391
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
392
|
+
REPOSITORY: ${{ github.repository }}
|
|
393
|
+
run: |
|
|
394
|
+
set -euo pipefail
|
|
395
|
+
|
|
396
|
+
release_date="${PUBLISHED_AT%%T*}"
|
|
397
|
+
[[ "$release_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || {
|
|
398
|
+
echo "::error::release published_at must contain a YYYY-MM-DD date"
|
|
399
|
+
exit 1
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
bin/update_changelog \
|
|
403
|
+
--tag "$RELEASE_TAG" \
|
|
404
|
+
--date "$release_date" \
|
|
405
|
+
--repository "$REPOSITORY" \
|
|
406
|
+
CHANGELOG.md
|
|
407
|
+
|
|
408
|
+
- name: Commit changelog if changed
|
|
409
|
+
env:
|
|
410
|
+
RELEASE_SHA: ${{ github.sha }}
|
|
411
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
412
|
+
run: |
|
|
413
|
+
set -euo pipefail
|
|
414
|
+
|
|
415
|
+
if [[ "$FINALIZE_STATE" == "already-finalized" ]]; then
|
|
416
|
+
working_tree="$(git status --porcelain --untracked-files=all)"
|
|
417
|
+
if [[ -n "$working_tree" ]]; then
|
|
418
|
+
echo "::error::changelog updater was not idempotent on the finalized commit"
|
|
419
|
+
exit 1
|
|
420
|
+
fi
|
|
421
|
+
echo "CHANGELOG.md is already finalized for ${RELEASE_TAG}"
|
|
422
|
+
exit 0
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
[[ "$FINALIZE_STATE" == "fresh" ]] || {
|
|
426
|
+
echo "::error::unexpected changelog finalization state ${FINALIZE_STATE}"
|
|
427
|
+
exit 1
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if git diff --quiet -- CHANGELOG.md; then
|
|
431
|
+
echo "CHANGELOG.md is already finalized for ${RELEASE_TAG}"
|
|
432
|
+
exit 0
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
unexpected_files="$(git diff --name-only -- . ':(exclude)CHANGELOG.md')"
|
|
436
|
+
[[ -z "$unexpected_files" ]] || {
|
|
437
|
+
echo "::error::changelog finalization changed unexpected files: ${unexpected_files}"
|
|
438
|
+
exit 1
|
|
439
|
+
}
|
|
440
|
+
git diff --check
|
|
441
|
+
|
|
442
|
+
git add -- CHANGELOG.md
|
|
443
|
+
staged_files="$(git diff --cached --name-only)"
|
|
444
|
+
[[ "$staged_files" == "CHANGELOG.md" ]] || {
|
|
445
|
+
echo "::error::only CHANGELOG.md may be committed"
|
|
446
|
+
exit 1
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
git config user.name "github-actions[bot]"
|
|
450
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
451
|
+
git commit -m "docs: finalize ${RELEASE_TAG} changelog" -- CHANGELOG.md
|
|
452
|
+
|
|
453
|
+
tag_sha="$(git rev-parse --verify "refs/tags/${RELEASE_TAG}^{commit}")"
|
|
454
|
+
[[ "$tag_sha" == "$RELEASE_SHA" ]] || {
|
|
455
|
+
echo "::error::release tag changed during changelog finalization"
|
|
456
|
+
exit 1
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
git push origin HEAD:main
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-rails
|
|
3
|
+
- rubocop-rspec
|
|
4
|
+
- rubocop-performance
|
|
5
|
+
|
|
6
|
+
AllCops:
|
|
7
|
+
TargetRubyVersion: 3.4
|
|
8
|
+
NewCops: enable
|
|
9
|
+
SuggestExtensions: false
|
|
10
|
+
Exclude:
|
|
11
|
+
- "bin/**/*"
|
|
12
|
+
- "exe/**/*"
|
|
13
|
+
- "vendor/**/*"
|
|
14
|
+
- ".git/**/*"
|
|
15
|
+
- "config/**/*"
|
|
16
|
+
- "spec/fixtures/canonical/**/*"
|
|
17
|
+
|
|
18
|
+
# Gem-specific relaxations
|
|
19
|
+
Metrics/BlockLength:
|
|
20
|
+
Exclude:
|
|
21
|
+
- "spec/**/*"
|
|
22
|
+
|
|
23
|
+
Metrics/MethodLength:
|
|
24
|
+
Max: 20
|
|
25
|
+
|
|
26
|
+
RSpec/ExampleLength:
|
|
27
|
+
Max: 15
|
|
28
|
+
|
|
29
|
+
RSpec/MultipleExpectations:
|
|
30
|
+
Max: 3
|
|
31
|
+
|
|
32
|
+
# CLI/generator gem legitimately writes to stdout; puts/say is the interface
|
|
33
|
+
Rails/Output:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
# Development dependencies intentionally declared in gemspec per gem best practices
|
|
37
|
+
Gemspec/DevelopmentDependencies:
|
|
38
|
+
Enabled: false
|
|
39
|
+
|
|
40
|
+
# Many small classes across generators; top-level doc comments add noise
|
|
41
|
+
Style/Documentation:
|
|
42
|
+
Enabled: false
|
|
43
|
+
|
|
44
|
+
Style/StringLiterals:
|
|
45
|
+
EnforcedStyle: double_quotes
|
|
46
|
+
|
|
47
|
+
Style/FrozenStringLiteralComment:
|
|
48
|
+
Enabled: true
|
|
49
|
+
|
|
50
|
+
Rails/RakeEnvironment:
|
|
51
|
+
Exclude:
|
|
52
|
+
- "Rakefile"
|