diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 36bfaa0789f..2da37184725 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -71,6 +71,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4649bc4fec7..3c1a1bf61c1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # master + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # master - name: Cache Docker layers uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0f8daf2864a..deb16137cf1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 with: bundler-cache: true - name: Rubocop @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 with: bundler-cache: true - name: Brakeman @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 with: bundler-cache: true - name: Importmap Verify @@ -51,7 +51,7 @@ jobs: - name: login to Github Packages run: echo "${{ github.token }}" | docker login https://ghcr.io -u ${GITHUB_ACTOR} --password-stdin - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 + - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 with: bundler-cache: true - name: krane render diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 97289616a63..ed918f9dde5 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: sarif_file: results.sarif diff --git a/Gemfile b/Gemfile index 5cbd45b347c..93f1ef48b8e 100644 --- a/Gemfile +++ b/Gemfile @@ -15,12 +15,12 @@ gem "dogstatsd-ruby", "~> 5.5" gem "google-protobuf", "~> 4.27" gem "faraday", "~> 2.10" gem "faraday-retry", "~> 2.2" -gem "good_job", "~> 3.29" +gem "good_job", "~> 3.99" gem "gravtastic", "~> 3.2" gem "honeybadger", "~> 5.5.1", require: false # see https://github.com/rubygems/rubygems.org/pull/4598 gem "http_accept_language", "~> 2.1" gem "kaminari", "~> 1.2" -gem "launchdarkly-server-sdk", "~> 8.6" +gem "launchdarkly-server-sdk", "~> 8.7" gem "mail", "~> 2.8" gem "octokit", "~> 9.1" gem "omniauth-github", "~> 2.0" @@ -124,7 +124,7 @@ group :development do end group :test do - gem "datadog-ci", "~> 1.2" + gem "datadog-ci", "~> 1.3" gem "minitest", "~> 5.24", require: false gem "minitest-retry", "~> 0.2.2" gem "capybara", "~> 3.40" @@ -141,4 +141,5 @@ group :test do gem "aggregate_assertions", "~> 0.2.0" gem "minitest-gcstats", "~> 1.3" gem "minitest-reporters", "~> 1.7" + gem "gem_server_conformance", "~> 0.1.4" end diff --git a/Gemfile.lock b/Gemfile.lock index 8d8ceb2aaa0..852608207c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -199,8 +199,8 @@ GEM libdatadog (~> 10.0.0.1.0) libddwaf (~> 1.14.0.0.0) msgpack - datadog-ci (1.2.0) - datadog (~> 2.0) + datadog-ci (1.3.0) + datadog (~> 2.2) msgpack date (3.3.4) dead_end (4.0.0) @@ -217,6 +217,7 @@ GEM rake (> 10, < 14) ruby-statistics (>= 2.1) thor (>= 0.19, < 2) + diff-lcs (1.5.1) discard (1.3.0) activerecord (>= 4.2, < 8) docile (1.4.0) @@ -260,11 +261,13 @@ GEM fugit (1.11.0) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) + gem_server_conformance (0.1.4) + rspec (~> 3.0) get_process_mem (0.2.7) ffi (~> 1.0) globalid (1.2.1) activesupport (>= 6.1) - good_job (3.29.5) + good_job (3.99.1) activejob (>= 6.0.0) activerecord (>= 6.0.0) concurrent-ruby (>= 1.0.2) @@ -336,13 +339,14 @@ GEM kaminari-core (= 1.2.2) kaminari-core (1.2.2) language_server-protocol (3.17.0.3) - launchdarkly-server-sdk (8.6.0) + launchdarkly-server-sdk (8.7.0) concurrent-ruby (~> 1.1) http (>= 4.4.0, < 6.0.0) json (~> 2.3) ld-eventsource (= 2.2.2) observer (~> 0.1.2) semantic (~> 1.6) + zlib (~> 3.1) launchy (3.0.1) addressable (~> 2.8) childprocess (~> 5.0) @@ -413,7 +417,7 @@ GEM ruby-progressbar minitest-retry (0.2.2) minitest (>= 5.0) - mocha (2.4.2) + mocha (2.4.5) ruby2_keywords (>= 0.0.5) msgpack (1.7.2) multi_json (1.15.0) @@ -433,7 +437,7 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.0) - nokogiri (1.16.6) + nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) oauth2 (2.0.9) @@ -485,7 +489,7 @@ GEM parser (3.3.4.0) ast (~> 2.4.1) racc - pg (1.5.6) + pg (1.5.7) pg_query (5.1.0) google-protobuf (>= 3.22.3) pghero (3.6.0) @@ -506,7 +510,7 @@ GEM pry (>= 0.13, < 0.15) psych (5.1.2) stringio - public_suffix (6.0.0) + public_suffix (6.0.1) puma (6.4.2) nio4r (~> 2.0) pundit (2.3.2) @@ -610,13 +614,26 @@ GEM chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) - rubocop (1.64.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rubocop (1.65.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) + regexp_parser (>= 2.4, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) @@ -627,7 +644,7 @@ GEM rubocop (~> 1.41) rubocop-factory_bot (2.26.1) rubocop (~> 1.61) - rubocop-minitest (0.35.0) + rubocop-minitest (0.35.1) rubocop (>= 1.61, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-performance (1.21.1) @@ -707,7 +724,7 @@ GEM attr_required (>= 0.0.5) faraday (~> 2.0) faraday-follow_redirects - tailwindcss-rails (2.6.3) + tailwindcss-rails (2.6.4) railties (>= 7.0.0) terser (1.2.3) execjs (>= 0.3.0, < 3) @@ -775,6 +792,7 @@ GEM nokogiri (~> 1.8) yard (0.9.36) zeitwerk (2.6.16) + zlib (3.1.1) PLATFORMS ruby @@ -799,7 +817,7 @@ DEPENDENCIES dalli (~> 3.2) dartsass-sprockets (~> 3.1) datadog (~> 2.2) - datadog-ci (~> 1.2) + datadog-ci (~> 1.3) derailed_benchmarks (~> 2.1) discard (~> 1.3) dogstatsd-ruby (~> 5.5) @@ -809,7 +827,8 @@ DEPENDENCIES faraday-multipart (~> 1.0) faraday-retry (~> 2.2) faraday_middleware-aws-sigv4 (~> 1.0) - good_job (~> 3.29) + gem_server_conformance (~> 0.1.4) + good_job (~> 3.99) google-protobuf (~> 4.27) gravtastic (~> 3.2) groupdate (~> 6.2) @@ -817,7 +836,7 @@ DEPENDENCIES http_accept_language (~> 2.1) importmap-rails (~> 2.0) kaminari (~> 1.2) - launchdarkly-server-sdk (~> 8.6) + launchdarkly-server-sdk (~> 8.7) launchy (~> 3.0) letter_opener (~> 1.10) letter_opener_web (~> 3.0) @@ -956,11 +975,12 @@ CHECKSUMS dalli (3.2.8) sha256=2e63595084d91fae2655514a02c5d4fc0f16c0799893794abe23bf628bebaaa5 dartsass-sprockets (3.1.0) sha256=c238ec9f7f496489ac5a7813cd1f83d1e077a1826921acefc7e290a521b7a20a datadog (2.2.0) sha256=fa96d565e055593294706d7767c4d3213b5250f0ddd2b8697b868ae60dbdda4a - datadog-ci (1.2.0) sha256=e563d4e1a890402f7f9e60a0510ed30d428c1a9070b71afec95e31ee96b6b241 + datadog-ci (1.3.0) sha256=3c1c4721d14a1f7736330a0fdc6c5dc63c14235a53e9f07af10a34feef23eab7 date (3.3.4) sha256=971f2cb66b945bcbea4ddd9c7908c9400b31a71bc316833cb42fa584b59d3291 dead_end (4.0.0) sha256=695c8438993bb4c5415b1618a1b6e0afcae849ef2812fb8cb3846723904307eb debase-ruby_core_source (3.3.1) sha256=ed904cae290edf0cf274ad707f8981bf1cefad8081e78d4bb71be2a483bc2c08 derailed_benchmarks (2.1.2) sha256=eaadc6206ceeb5538ff8f5e04a0023d54ebdd95d04f33e8960fb95a5f189a14f + diff-lcs (1.5.1) sha256=273223dfb40685548436d32b4733aa67351769c7dea621da7d9dd4813e63ddfe discard (1.3.0) sha256=55218997ca4dc11f0594f1bb2d1196c4a959ceb562f1ab6490130233598dda67 docile (1.4.0) sha256=5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3 dogstatsd-ruby (5.6.1) sha256=615dd1328c57ab66fb48cbf83b38ce2060d8353707be0bc3deb0ae960a659982 @@ -984,9 +1004,10 @@ CHECKSUMS ffi (1.17.0) sha256=51630e43425078311c056ca75f961bb3bda1641ab36e44ad4c455e0b0e4a231c ffi-compiler (1.3.2) sha256=a94f3d81d12caf5c5d4ecf13980a70d0aeaa72268f3b9cc13358bcc6509184a0 fugit (1.11.0) sha256=addc9cd3031611921d1dbac094de3a645bc8858828639fd035c9cedd3b460bb9 + gem_server_conformance (0.1.4) sha256=ee404d5405eabcb6f7ab440d2193177375481a77bfa0ec3106165dd6c8e733bb get_process_mem (0.2.7) sha256=4afd3c3641dd6a817c09806c7d6d509d8a9984512ac38dea8b917426bbf77eba globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9 - good_job (3.29.5) sha256=3d71236c7e89b9678ba0cf16d6a93b9ee2ff12795959a0c10f77fd52f6479a13 + good_job (3.99.1) sha256=7d3869d8a8ee8ef7048fee5d746f41c21987b7822c20038a2f773036bef0830a google-protobuf (4.27.2) sha256=9f69eb20acde6e3cf3cd197c09f38911cd7eed5fdf1bf4d4bce4c55e9dc9966f gravtastic (3.2.6) sha256=ef98abcecf7c402b61cff1ae7c50a2c6d97dd22bac21ea9b421ce05bc03d734f groupdate (6.4.0) sha256=65940645bf2a48f9b2d10ab7a1d19bdc78f3c89559d8fce39cea3448a15aec54 @@ -1016,7 +1037,7 @@ CHECKSUMS kaminari-activerecord (1.2.2) sha256=0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430 kaminari-core (1.2.2) sha256=3bd26fec7370645af40ca73b9426a448d09b8a8ba7afa9ba3c3e0d39cdbb83ff language_server-protocol (3.17.0.3) sha256=3d5c58c02f44a20d972957a9febe386d7e7468ab3900ce6bd2b563dd910c6b3f - launchdarkly-server-sdk (8.6.0) sha256=c18c62d08f90b795105e98f07a24997a5628126623fadbd3b80c0d23b9409b75 + launchdarkly-server-sdk (8.7.0) sha256=062e7dc42c2884ed9deed42c49937917faa911128a659386e4bd45d1ad1506eb launchy (3.0.1) sha256=b7fa60bda0197cf57614e271a250a8ca1f6a34ab889a3c73f67ec5d57c8a7f2c ld-eventsource (2.2.2) sha256=5ea087a6f06bbd8e325d2c1aaead50f37f13d025b952985739e9380a78a96beb letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2 @@ -1042,7 +1063,7 @@ CHECKSUMS minitest-gcstats (1.3.1) sha256=cb25490f93aac02e3a5ff307e560d41afcdcafa7952c1c32efdeb9886b1f4711 minitest-reporters (1.7.1) sha256=5060413a0c95b8c32fe73e0606f3631c173a884d7900e50013e15094eb50562c minitest-retry (0.2.2) sha256=ea39f8abc3d67a8145ead04ff3828eb45169655c9e6078f182c0271516c03fb0 - mocha (2.4.2) sha256=5fb89b7b42f2bcd9ae3ee6042d4435890bfc9226c1a0ea63925cd33c50cc3558 + mocha (2.4.5) sha256=b4c17e103a9b20ec9e21374f4e9be41006dbd20c5e7cb1c215f8ec56599a6112 msgpack (1.7.2) sha256=59ab62fd8a4d0dfbde45009f87eb6f158ab2628a7c48886b0256f175166baaa8 multi_json (1.15.0) sha256=1fd04138b6e4a90017e8d1b804c039031399866ff3fbabb7822aea367c78615d multi_xml (0.7.1) sha256=4fce100c68af588ff91b8ba90a0bb3f0466f06c909f21a32f4962059140ba61b @@ -1054,7 +1075,7 @@ CHECKSUMS net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 net-smtp (0.5.0) sha256=5fc0415e6ea1cc0b3dfea7270438ec22b278ca8d524986a3ae4e5ae8d087b42a nio4r (2.7.0) sha256=9586a685eca8246d6406e712a525e705d15bb88f709d78fc3f141e864df97276 - nokogiri (1.16.6) sha256=935fe4dd67d4377f4a05002acb1ffbadbcae265ea8e7869fc40e3a8121f3e1ef + nokogiri (1.16.7) sha256=f819cbfdfb0a7b19c9c52c6f2ca63df0e58a6125f4f139707b586b9511d7fe95 oauth2 (2.0.9) sha256=b21f9defcf52dc1610e0dfab4c868342173dcd707fd15c777d9f4f04e153f7fb observer (0.1.2) sha256=d8a3107131ba661138d748e7be3dbafc0d82e732fffba9fccb3d7829880950ac octokit (9.1.0) sha256=7849a659d2722c629181f48d1d7e567c9539f1a85c9676144dbdbfc6ce288253 @@ -1070,7 +1091,7 @@ CHECKSUMS pagy (8.4.0) sha256=21dd68453d24e752a1bc81aef5b6f1346ff95e1c2e5b53448355f7856f3ef901 parallel (1.25.1) sha256=12e089b9aa36ea2343f6e93f18cfcebd031798253db8260590d26a7f70b1ab90 parser (3.3.4.0) sha256=8d247769c3873fe92201d591a7463384022a1a25e214853df5d6806623179e82 - pg (1.5.6) sha256=4bc3ad2438825eea68457373555e3fd4ea1a82027b8a6be98ef57c0d57292b1c + pg (1.5.7) sha256=07a7d86e5aac8b964a0288076a7a7c699c27b25c9b40b001726f6eb03ed13427 pg_query (5.1.0) sha256=b7f7f47c864f08ccbed46a8244906fb6ee77ee344fd27250717963928c93145d pghero (3.6.0) sha256=cad9cb865f99ff40bb5ba47d3dae20d06be06ac8ea6b01172f6a8ccc85671109 phlex (1.10.2) sha256=49dca7df081258f937be5e4ee0a81b11743f2b4fea25ac7537912b9c9344b1e6 @@ -1081,7 +1102,7 @@ CHECKSUMS pry (0.14.1) sha256=99b6df0665875dd5a39d85e0150aa5a12e2bb4fef401b6c4f64d32ee502f8454 pry-byebug (3.10.1) sha256=c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8 psych (5.1.2) sha256=337322f58fc2bf24827d2b9bd5ab595f6a72971867d151bb39980060ea40a368 - public_suffix (6.0.0) sha256=d2c8e12e076813f8fca0307205c088c6903adc70b92c65ab22479dfa9bc0a89e + public_suffix (6.0.1) sha256=61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f puma (6.4.2) sha256=3eb41999d00733280be3faeb00d610331b6965a537a8cd3d905f316c38575b44 pundit (2.3.2) sha256=7ca09a5801ebaedad1966f7eb0b1c52ecb8c94b3b6ab70122cb22856ac187fa3 pwned (2.3.0) sha256=63f5a9576f109203684e9dd053f815649fd5bc0a0348b7190568272641b22353 @@ -1119,11 +1140,16 @@ CHECKSUMS rouge (4.3.0) sha256=9ee3d9ec53338e78c03fff0cbcd08881d80d69152349b046761e48ccf2de581c rqrcode (2.2.0) sha256=23eea88bb44c7ee6d6cab9354d08c287f7ebcdc6112e1fe7bcc2d010d1ffefc1 rqrcode_core (1.2.0) sha256=cf4989dc82d24e2877984738c4ee569308625fed2a810960f1b02d68d0308d1a - rubocop (1.64.1) sha256=3145bf1863771e400a1c041060e751e5ff0edd9ceb99d01df36db1902f611f3b + rspec (3.13.0) sha256=d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993 + rspec-core (3.13.0) sha256=557792b4e88da883d580342b263d9652b6a10a12d5bda9ef967b01a48f15454c + rspec-expectations (3.13.1) sha256=814cf8dadc797b00be55a84d7bc390c082735e5c914e62cbe8d0e19774b74200 + rspec-mocks (3.13.1) sha256=087189899c337937bcf1d66a50dc3fc999ac88335bbeba4d385c2a38c87d7b38 + rspec-support (3.13.1) sha256=48877d4f15b772b7538f3693c22225f2eda490ba65a0515c4e7cf6f2f17de70f + rubocop (1.65.0) sha256=624316407a3f8e3999c6f75c528471ed3d4513ca39cec3bede1964c69630e4a1 rubocop-ast (1.31.3) sha256=1b07d618d8776993ec6053a706d1c09f0bf15139fd69415924656cbff07e7818 rubocop-capybara (2.21.0) sha256=5d264efdd8b6c7081a3d4889decf1451a1cfaaec204d81534e236bc825b280ab rubocop-factory_bot (2.26.1) sha256=8de13cd4edcee5ca800f255188167ecef8dbfc3d1fae9f15734e9d2e755392aa - rubocop-minitest (0.35.0) sha256=8411f3a858a445f1930f41876eabf4a0511aa258b339ec19010fdca159272e00 + rubocop-minitest (0.35.1) sha256=afd0a630a6212e46d855fc1eca09caf62f4abbdb6bf9c2afe89dc4a2fe536a12 rubocop-performance (1.21.1) sha256=5cf20002a544275ad6aa99abca4b945d2a2ed71be925c38fe83700360ed8734e rubocop-rails (2.25.1) sha256=4988933ee02fdb213d22d0f61dc57d6c582317b43867d5106ea1c7a628aae6a5 ruby-graphviz (1.2.5) sha256=1c2bb44e3f6da9e2b829f5e7fd8d75a521485fb6b4d1fc66ff0f93f906121504 @@ -1157,7 +1183,7 @@ CHECKSUMS strong_migrations (2.0.0) sha256=88750f294403e18ec674eda6901f2fc195f553ed6a7928c52e8a3f5b57ff501d strscan (3.1.0) sha256=01b8a81d214fbf7b5308c6fb51b5972bbfc4a6aa1f166fd3618ba97e0fcd5555 swd (2.0.3) sha256=4cdbe2a4246c19f093fce22e967ec3ebdd4657d37673672e621bf0c7eb770655 - tailwindcss-rails (2.6.3) sha256=63a5f3d6f92d0784024b352ab472f0bb85047081c2b51e2679fce558721bb6a6 + tailwindcss-rails (2.6.4) sha256=175cd2009a6a27b10d16772c072c44974e03a6516e58beb37f185d6bfcd7ebe5 terser (1.2.3) sha256=c03111b9b01a7e70cd456b5d9aedf0a56fb99314a19311c4278c01ccebe3da9c thor (1.3.1) sha256=fa7e3471d4f6a27138e3d9c9b0d4daac9c3d7383927667ae83e9ab42ae7401ef tilt (2.3.0) sha256=82dd903d61213c63679d28e404ee8e10d1b0fdf5270f1ad0898ec314cc3e745c @@ -1187,6 +1213,7 @@ CHECKSUMS xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e yard (0.9.36) sha256=5505736c1b00c926f71053a606ab75f02070c5960d0778b901fe9d8b0a470be4 zeitwerk (2.6.16) sha256=17df48537f09a937804f79bd9a92817da3b199e268634972d0e98a21ca588e21 + zlib (3.1.1) sha256=f61bb03139bbe256c36ba99ef9fece1fb223e9034ed9e5fa3ddb1588d99abc71 RUBY VERSION ruby 3.3.4p94 diff --git a/app/controllers/api/compact_index_controller.rb b/app/controllers/api/compact_index_controller.rb index 386e3cad3c1..bdae9f274cd 100644 --- a/app/controllers/api/compact_index_controller.rb +++ b/app/controllers/api/compact_index_controller.rb @@ -33,6 +33,7 @@ def render_range(response_body) headers["Digest"] = "sha-256=#{digest}" headers["Repr-Digest"] = "sha-256=:#{digest}:" headers["Accept-Ranges"] = "bytes" + headers["Content-Type"] = "text/plain; charset=utf-8" ranges = Rack::Utils.byte_ranges(request.env, response_body.bytesize) if ranges diff --git a/app/controllers/api_keys_controller.rb b/app/controllers/api_keys_controller.rb index 3be2f3403fa..65663f0cff9 100644 --- a/app/controllers/api_keys_controller.rb +++ b/app/controllers/api_keys_controller.rb @@ -1,5 +1,6 @@ class ApiKeysController < ApplicationController before_action :disable_cache, only: :index + before_action :set_page, only: :index include ApiKeyable @@ -8,7 +9,7 @@ class ApiKeysController < ApplicationController def index @api_key = session.delete(:api_key) - @api_keys = current_user.api_keys.unexpired.not_oidc.preload(ownership: :rubygem) + @api_keys = current_user.api_keys.unexpired.not_oidc.preload(ownership: :rubygem).page(@page) redirect_to new_profile_api_key_path if @api_keys.empty? end diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb index d9ee97a0db8..98bda5678fe 100644 --- a/app/mailers/mailer.rb +++ b/app/mailers/mailer.rb @@ -30,6 +30,17 @@ def email_confirmation(user) end end + def admin_manual(user, subject, body) + @user = user + @body = body + @sub_title = subject + mail to: @user.email, + subject: subject do |format| + format.html + format.text + end + end + def deletion_complete(email) mail to: email, subject: I18n.t("mailer.deletion_complete.subject") diff --git a/app/models/rubygem.rb b/app/models/rubygem.rb index 2bef507f40b..a306bcbfd31 100644 --- a/app/models/rubygem.rb +++ b/app/models/rubygem.rb @@ -1,7 +1,6 @@ class Rubygem < ApplicationRecord include Patterns include RubygemSearchable - include Events::Recordable has_many :ownerships, -> { confirmed }, dependent: :destroy, inverse_of: :rubygem has_many :ownerships_including_unconfirmed, dependent: :destroy, class_name: "Ownership" @@ -27,6 +26,10 @@ class Rubygem < ApplicationRecord has_many :reverse_development_dependencies, -> { merge(Dependency.development) }, through: :incoming_dependencies, source: :version_rubygem has_many :reverse_runtime_dependencies, -> { merge(Dependency.runtime) }, through: :incoming_dependencies, source: :version_rubygem + # needs to come last so its dependent: :destroy works, since yanking a version + # will create an event + include Events::Recordable + has_one :most_recent_version, lambda { order(Arel.sql("case when #{quoted_table_name}.latest AND #{quoted_table_name}.platform = 'ruby' then 2 else 1 end desc")) diff --git a/app/models/version.rb b/app/models/version.rb index 2bf906ccf02..cf29ffbb611 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -47,6 +47,11 @@ class Version < ApplicationRecord # rubocop:disable Metrics/ClassLength allow_blank: true validates :sha256, :spec_sha256, format: { with: Patterns::BASE64_SHA256_PATTERN }, allow_nil: true + validates :number, :platform, :gem_platform, :full_name, :gem_full_name, :canonical_number, + name_format: { requires_letter: false }, + if: -> { validation_context == :create || number_changed? || platform_changed? }, + presence: true + validate :unique_canonical_number, on: :create validate :platform_and_number_are_unique, on: :create validate :gem_platform_and_number_are_unique, on: :create diff --git a/app/views/api_keys/index.html.erb b/app/views/api_keys/index.html.erb index c4ace3d48fe..c1816ee1a9c 100644 --- a/app/views/api_keys/index.html.erb +++ b/app/views/api_keys/index.html.erb @@ -9,6 +9,12 @@ <% end %> +
+

+ <%= page_entries_info @api_keys, entry_name: 'API keys' %> +

+
+
@@ -85,6 +91,7 @@ <% end %> + @@ -114,6 +121,8 @@
+ <%= paginate @api_keys %> +

<%= button_to t(".new_key"), new_profile_api_key_path, method: "get", class: "form__submit" %>

<% if current_user.oidc_api_key_roles.any? %>

<%= link_to t("oidc.api_key_roles.index.api_key_roles"), profile_oidc_api_key_roles_path, class: "t-link t-underline" %> →

diff --git a/app/views/mailer/admin_manual.html.erb b/app/views/mailer/admin_manual.html.erb new file mode 100644 index 00000000000..e53371b916d --- /dev/null +++ b/app/views/mailer/admin_manual.html.erb @@ -0,0 +1,20 @@ +<% @title = t(".title") %> + + + + + + + + +
+
 
+ +
+ <%= simple_format @body %>
+
+ +
 
+ +
+ diff --git a/app/views/mailer/admin_manual.text.erb b/app/views/mailer/admin_manual.text.erb new file mode 100644 index 00000000000..9e66ef58e6d --- /dev/null +++ b/app/views/mailer/admin_manual.text.erb @@ -0,0 +1,3 @@ +Hi <%= @user.handle %> + +<%= strip_tags @body %> diff --git a/config/application.rb b/config/application.rb index 7de954883d6..548551b8d00 100644 --- a/config/application.rb +++ b/config/application.rb @@ -69,6 +69,10 @@ class Application < Rails::Application config.active_support.cache_format_version = 7.1 config.action_dispatch.rescue_responses["Rack::Multipart::EmptyContentError"] = :bad_request + + config.action_dispatch.default_headers.merge!( + "Cross-Origin-Opener-Policy" => "same-origin" + ) end def self.config diff --git a/config/initializers/better_html.rb b/config/initializers/better_html.rb index 07b30994ef3..3eb5dc80cfe 100644 --- a/config/initializers/better_html.rb +++ b/config/initializers/better_html.rb @@ -1,5 +1,5 @@ BetterHtml.configure do |config| config.template_exclusion_filter = proc { |filename| - filename.include?("avo") + filename.include?("avo") || filename.include?("/railties-") } end diff --git a/config/initializers/statsd.rb b/config/initializers/statsd.rb index 2e91b49bc84..a3721b165bb 100644 --- a/config/initializers/statsd.rb +++ b/config/initializers/statsd.rb @@ -57,8 +57,10 @@ ActiveSupport::Notifications.subscribe("perform_job.good_job") do |event| execution = event.payload[:execution] + # TODO: remove || execution after GoodJob 4 upgrade + job = event.payload[:job] || execution - result = if event.payload[:retried] || execution.retried_good_job_id.present? + result = if event.payload[:retried] || job.retried_good_job_id.present? :retried elsif event.payload[:unhandled_error] :unhandled_error @@ -72,7 +74,7 @@ job_class: execution.serialized_params['job_class'], exception: event.payload.dig(:exception, 0), queue: execution.queue_name, - priority: execution.priority, + priority: job.priority, result: } diff --git a/config/locales/de.yml b/config/locales/de.yml index 05ea1a97e7c..45dfb79110c 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -427,6 +427,8 @@ de: gepusht wurde. gem_trusted_publisher_added: title: VERTRAUENSWÜRDIGER PUBLISHER HINZUGEFÜGT + admin_manual: + title: news: show: title: Neue Veröffentlichungen — Alle Gems diff --git a/config/locales/en.yml b/config/locales/en.yml index 7bd9587da3e..24b224df018 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -377,6 +377,8 @@ en: gem_html: This webhook was previously called when %{gem} was pushed. gem_trusted_publisher_added: title: TRUSTED PUBLISHER ADDED + admin_manual: + title: MESSAGE FROM RUBYGEMS.ORG ADMINS news: show: title: New Releases — All Gems diff --git a/config/locales/es.yml b/config/locales/es.yml index 315466f7d70..53126188d4b 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -418,6 +418,8 @@ es: gem_html: Este webhook se ejecutaba antes cuando se subía %{gem}. gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: Nuevos lanzamientos — Todas las Gemas diff --git a/config/locales/fr.yml b/config/locales/fr.yml index d9fdef24909..6cf3dc8fa38 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -383,6 +383,8 @@ fr: gem_html: gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: Nouvelles Versions - Toutes les Gems diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 254592e147c..c07d1cfe93d 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -377,6 +377,8 @@ ja: gem_html: このwebhookは以前%{gem}がプッシュされたときに呼ばれました。 gem_trusted_publisher_added: title: 信頼できる発行元が追加されました + admin_manual: + title: news: show: title: 新しいリリース - 全てのgem diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 0bbba0c11d6..f4d63d2a5e7 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -368,6 +368,8 @@ nl: gem_html: gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 0d8c9a12263..e095dd86717 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -380,6 +380,8 @@ pt-BR: gem_html: gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: Novos Releases - Todas as Gems diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index b2282b754fb..35eeae1c007 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -382,6 +382,8 @@ zh-CN: 被推送时被调用。 gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: 新的发布 — 所有 Gem diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 72988f60f0b..dedf71f927f 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -374,6 +374,8 @@ zh-TW: 被推送的時候被呼叫。 gem_trusted_publisher_added: title: + admin_manual: + title: news: show: title: 最新發佈 diff --git a/db/migrate/20240722182907_create_good_job_execution_duration.rb b/db/migrate/20240722182907_create_good_job_execution_duration.rb new file mode 100644 index 00000000000..fef37f07bc1 --- /dev/null +++ b/db/migrate/20240722182907_create_good_job_execution_duration.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateGoodJobExecutionDuration < ActiveRecord::Migration[7.1] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_job_executions, :duration) + end + end + + add_column :good_job_executions, :duration, :interval + end +end diff --git a/db/schema.rb b/db/schema.rb index d96543466c8..36bfa9037c0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_20_201622) do +ActiveRecord::Schema[7.1].define(version: 2024_07_22_182907) do # These are extensions that must be enabled in order to support this database enable_extension "hstore" enable_extension "pgcrypto" @@ -202,6 +202,7 @@ t.integer "error_event", limit: 2 t.text "error_backtrace", array: true t.uuid "process_id" + t.interval "duration" t.index ["active_job_id", "created_at"], name: "index_good_job_executions_on_active_job_id_and_created_at" t.index ["process_id", "created_at"], name: "index_good_job_executions_on_process_id_and_created_at" end diff --git a/lib/name_format_validator.rb b/lib/name_format_validator.rb index d8fae743d2c..fa59e37b185 100644 --- a/lib/name_format_validator.rb +++ b/lib/name_format_validator.rb @@ -2,14 +2,18 @@ class NameFormatValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - if value.class != String - record.errors.add attribute, "must be a String" - elsif !Patterns::LETTER_REGEXP.match?(value) - record.errors.add attribute, "must include at least one letter" - elsif !Patterns::NAME_PATTERN.match?(value) - record.errors.add attribute, "can only include letters, numbers, dashes, and underscores" - elsif Patterns::SPECIAL_CHAR_PREFIX_REGEXP.match?(value) - record.errors.add attribute, "can not begin with a period, dash, or underscore" - end + return record.errors.add attribute, "must be a String" if value.class != String + + record.errors.add attribute, "must include at least one letter" if requires_letter? && !Patterns::LETTER_REGEXP.match?(value) + record.errors.add attribute, "can only include letters, numbers, dashes, and underscores" unless Patterns::NAME_PATTERN.match?(value) + record.errors.add attribute, "can not begin with a period, dash, or underscore" if Patterns::SPECIAL_CHAR_PREFIX_REGEXP.match?(value) + record.errors.add attribute, "can not end with a period, dash, or underscore" if Patterns::SPECIAL_CHAR_SUFFIX_REGEXP.match?(value) + record.errors.add attribute, "can not end with a common file extension" if Patterns::BANNED_EXTENSION_REGEXP.match?(value) + end + + private + + def requires_letter? + options.fetch(:requires_letter, true) end end diff --git a/lib/patterns.rb b/lib/patterns.rb index ee994260dfb..edde659b41f 100644 --- a/lib/patterns.rb +++ b/lib/patterns.rb @@ -4,14 +4,17 @@ module Patterns JAVA_HTTP_USER_AGENT = /^java/i SPECIAL_CHARACTERS = ".-_".freeze ALLOWED_CHARACTERS = "[A-Za-z0-9#{Regexp.escape(SPECIAL_CHARACTERS)}]+".freeze - ROUTE_PATTERN = /#{ALLOWED_CHARACTERS}/ + ROUTE_PATTERN = /#{ALLOWED_CHARACTERS}(?with html

and a link + TEXT + end end diff --git a/test/models/rubygem_test.rb b/test/models/rubygem_test.rb index 7ccddf44871..68626ff4a44 100644 --- a/test/models/rubygem_test.rb +++ b/test/models/rubygem_test.rb @@ -21,11 +21,15 @@ class RubygemTest < ActiveSupport::TestCase should allow_value("factory_girl").for(:name) should allow_value("rack-test").for(:name) should allow_value("perftools.rb").for(:name) + should allow_value("s3-4-2").for(:name) + should allow_value("its.a.gem.ok").for(:name) should_not allow_value("\342\230\203").for(:name) should_not allow_value("2.2").for(:name) should_not allow_value(".omghi").for(:name) should_not allow_value("-omghi").for(:name) should_not allow_value("_omghi").for(:name) + should_not allow_value("omg-").for(:name) + should_not allow_value("omg.gem").for(:name) context "with reserved Ruby gem" do setup do @@ -265,6 +269,18 @@ class RubygemTest < ActiveSupport::TestCase assert_nil dependency.rubygem_id assert_equal dependency.unresolved_name, @rubygem.name end + + should "allow destroying a gem with versions" do + @rubygem.save! + create(:ownership, rubygem: @rubygem) + create(:version, rubygem: @rubygem) + v2 = create(:version, rubygem: @rubygem) + create(:deletion, version: v2) + + @rubygem.destroy! + + assert_predicate Rubygem.where(id: @rubygem.id), :none? + end end context "with reverse dependencies" do diff --git a/test/models/version_test.rb b/test/models/version_test.rb index 0e361d26b46..726ecf497e9 100644 --- a/test/models/version_test.rb +++ b/test/models/version_test.rb @@ -406,16 +406,21 @@ class VersionTest < ActiveSupport::TestCase end subject { @version } + should allow_value("1.2.3.pre").for(:number) should_not allow_value("#YAML").for(:number) should_not allow_value("1.2.3-\"[javalol]\"").for(:number) should_not allow_value("0.8.45::Gem::PLATFORM::FAILBOAT").for(:number) should_not allow_value("1.2.3\n").for(:number) should_not allow_value("1.2.3-bad").for(:number) + should_not allow_value("1.2.3.").for(:number) + should_not allow_value("1.2.3.gem").for(:number) should allow_value("ruby").for(:platform) should allow_value("mswin32").for(:platform) should allow_value("x86_64-linux").for(:platform) + should allow_value("it.is.fine").for(:platform) should_not allow_value("Gem::Platform::Ruby").for(:platform) + should_not allow_value("ruby.gem").for(:platform) should "be invalid with platform longer than maximum field length" do @version.platform = "r" * (Gemcutter::MAX_FIELD_LENGTH + 1) diff --git a/test/system/gem_server_conformance_test.rb b/test/system/gem_server_conformance_test.rb new file mode 100644 index 00000000000..ec64139de96 --- /dev/null +++ b/test/system/gem_server_conformance_test.rb @@ -0,0 +1,68 @@ +require "application_system_test_case" + +require_relative "../../lib/gemcutter/middleware/hostess" + +class GemServerConformanceTest < ApplicationSystemTestCase + include ActionDispatch::Assertions::RoutingAssertions + include ActiveJob::TestHelper + + setup do + @tmp_versions_file = Tempfile.new("tmp_versions_file") + tmp_path = @tmp_versions_file.path + + Rails.application.config.rubygems.stubs(:[]).with("versions_file_location").returns(tmp_path) + Rails.application.config.rubygems.stubs(:[]).with("s3_compact_index_bucket").returns("s3_compact_index_bucket") + Rails.application.config.rubygems.stubs(:[]).with("s3_contents_bucket").returns("s3_contents_bucket") + + test = self + Rails.application.routes.disable_clear_and_finalize = true + Rails.application.routes.draw do + post "/set_time", to: lambda { |env| + test.travel_to Time.iso8601(Rack::Request.new(env).body.read) + [200, {}, ["OK"]] + } + post "/rebuild_versions_list", to: lambda { |_env| + Rake::Task["compact_index:update_versions_file"].execute + Rake::Task["compact_index:update_versions_file"].reenable + [200, {}, ["OK"]] + } + hostess = Gemcutter::Middleware::Hostess.new(nil) + to = lambda { |env| + hostess.call(env) + .tap do |response| + response[1].delete("x-cascade") + end + } + match "/quick/Marshal.4.8/:name", to:, via: :get, constraints: { name: /[A-Za-z0-9._-]+/ } + match "/gems/:name", to:, via: :get, constraints: { name: Patterns::ROUTE_PATTERN } + match "/specs.4.8.gz", to:, via: :get + match "/prerelease_specs.4.8.gz", to:, via: :get + match "/latest_specs.4.8.gz", to:, via: :get + end + + Indexer.perform_now + @subscriber = ActiveSupport::Notifications.subscribe("process_action.action_controller") do + perform_enqueued_jobs only: [Indexer] + end + end + + teardown do + ActiveSupport::Notifications.unsubscribe(@subscriber) + Rails.application.reload_routes! + end + + test "is a conformant gem server" do + create(:api_key, scopes: %w[push_rubygem yank_rubygem]) + + output, status = Open3.capture2e( + { + "UPSTREAM" => "http://#{Capybara.current_session.config.server_host}:#{Capybara.current_session.config.server_port}", + "GEM_HOST_API_KEY" => "12345" + }, + "gem_server_conformance", + "--fail-fast", "--tag=~content_type_header", "--tag=~content_length_header" + ) + + assert_predicate status, :success?, output + end +end