Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 18:09:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 18:09:24 +0300
commit8bcfcd53f3e3fe8df944eea6dab02556976fd4e3 (patch)
tree6f8cfaf7442b3ab092a107e249689e9049ee4738
parent0549ffef0d4f862a7354847dd185725cc196eed0 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/images/learn_gitlab/get_started.svg1
-rw-r--r--app/assets/images/learn_gitlab/rectangle.svg1
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue2
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue84
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql2
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/queries/get_http_integration.query.graphql (renamed from app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql)5
-rw-r--r--app/assets/javascripts/alerts_settings/utils/cache_updates.js33
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js2
-rw-r--r--app/controllers/projects/commit_controller.rb2
-rw-r--r--app/helpers/learn_gitlab_helper.rb12
-rw-r--r--app/policies/nil_policy.rb5
-rw-r--r--app/services/merge_requests/refresh_service.rb7
-rw-r--r--app/views/admin/runners/show.html.haml7
-rw-r--r--app/views/devise/mailer/reset_password_instructions.html.haml11
-rw-r--r--app/views/projects/blob/viewers/_download.html.haml2
-rw-r--r--app/views/projects/settings/operations/_configuration_banner.html.haml2
-rw-r--r--app/views/sherlock/queries/show.html.haml2
-rw-r--r--changelogs/unreleased/326316-improve-async-diff-rendering.yml5
-rw-r--r--changelogs/unreleased/327559-feature-flag-rollout-of-code_review_async_pipeline_creation.yml5
-rw-r--r--changelogs/unreleased/Externalize-strings-in-reset_password_instructions-html-haml.yml5
-rw-r--r--changelogs/unreleased/Externalize-strings-in-viewers-_download-html-haml.yml5
-rw-r--r--changelogs/unreleased/final-button-sweep.yml5
-rw-r--r--changelogs/unreleased/retain-timelogs-for-deleted-notes.yml5
-rw-r--r--config/feature_flags/development/code_review_async_pipeline_creation.yml8
-rw-r--r--config/initializers/declarative_policy.rb5
-rw-r--r--config/routes.rb1
-rw-r--r--db/post_migrate/20210403022952_remove_notes_delete_cascade_timelogs.rb26
-rw-r--r--db/schema_migrations/202104030229521
-rw-r--r--db/structure.sql6
-rw-r--r--doc/administration/geo/replication/faq.md36
-rw-r--r--doc/api/groups.md4
-rw-r--r--lib/declarative_policy.rb112
-rw-r--r--lib/declarative_policy/base.rb354
-rw-r--r--lib/declarative_policy/cache.rb39
-rw-r--r--lib/declarative_policy/condition.rb105
-rw-r--r--lib/declarative_policy/delegate_dsl.rb18
-rw-r--r--lib/declarative_policy/policy_dsl.rb46
-rw-r--r--lib/declarative_policy/preferred_scope.rb31
-rw-r--r--lib/declarative_policy/rule.rb312
-rw-r--r--lib/declarative_policy/rule_dsl.rb47
-rw-r--r--lib/declarative_policy/runner.rb196
-rw-r--r--lib/declarative_policy/step.rb88
-rw-r--r--lib/gitlab/diff/highlight.rb8
-rw-r--r--locale/gitlab.pot15
-rw-r--r--package.json2
-rw-r--r--spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap1
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_form_spec.js3
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js141
-rw-r--r--spec/frontend/alerts_settings/components/util.js24
-rw-r--r--spec/lib/declarative_policy/overrides_spec.rb82
-rw-r--r--spec/lib/declarative_policy_spec.rb38
-rw-r--r--spec/policies/project_policy_spec.rb8
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb14
-rw-r--r--yarn.lock18
57 files changed, 278 insertions, 1728 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index f264434e92a..1ad98df6dbb 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-cec4c4e53eef279eafd16882f0f0bd37841229c1
+7e05394f21ee383c94854f5b6f1316d0ec94d788
diff --git a/Gemfile b/Gemfile
index 876bcfaabf9..556b40317a7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,6 +23,9 @@ gem 'grape-path-helpers', '~> 1.6.1'
gem 'faraday', '~> 1.0'
gem 'marginalia', '~> 1.10.0'
+# Authorization
+gem 'declarative_policy', '~> 1.0.0'
+
# Authentication libraries
gem 'devise', '~> 4.7.2'
gem 'bcrypt', '~> 3.1', '>= 3.1.14'
diff --git a/Gemfile.lock b/Gemfile.lock
index 19fad573b63..358d58cf566 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -244,6 +244,7 @@ GEM
html-pipeline
declarative (0.0.20)
declarative-option (0.1.0)
+ declarative_policy (1.0.0)
default_value_for (3.4.0)
activerecord (>= 3.2.0, < 7.0)
deprecation_toolkit (1.5.1)
@@ -1383,6 +1384,7 @@ DEPENDENCIES
crystalball (~> 0.7.0)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1)
+ declarative_policy (~> 1.0.0)
default_value_for (~> 3.4.0)
deprecation_toolkit (~> 1.5.1)
derailed_benchmarks
diff --git a/app/assets/images/learn_gitlab/get_started.svg b/app/assets/images/learn_gitlab/get_started.svg
new file mode 100644
index 00000000000..0e682842b1f
--- /dev/null
+++ b/app/assets/images/learn_gitlab/get_started.svg
@@ -0,0 +1 @@
+<svg width="468" height="96" xmlns="http://www.w3.org/2000/svg"><g transform="translate(4)" fill="none" fill-rule="evenodd"><path d="M19.817 6.79c.398-1.258 1.516-2.107 2.776-2.107h1.634c1.26 0 2.378.849 2.776 2.107l1.537 4.858c1.088.35 2.13.807 3.117 1.36l4.35-2.29c1.127-.593 2.487-.36 3.378.577l1.156 1.217c.89.938 1.111 2.37.548 3.557l-2.175 4.582c.525 1.038.96 2.136 1.291 3.281l4.613 1.62c1.195.419 2.001 1.597 2.001 2.923v1.721c0 1.326-.806 2.504-2 2.923l-4.614 1.62a18.947 18.947 0 01-1.291 3.281l2.175 4.582c.563 1.186.342 2.619-.548 3.557l-1.156 1.217c-.89.938-2.251 1.17-3.378.577l-4.35-2.29a16.947 16.947 0 01-3.117 1.36l-1.537 4.858c-.398 1.258-1.517 2.107-2.776 2.107h-1.634c-1.26 0-2.378-.849-2.776-2.107l-1.538-4.858a16.973 16.973 0 01-3.116-1.36l-4.35 2.29c-1.127.593-2.488.36-3.379-.577L6.28 46.159c-.89-.938-1.112-2.37-.548-3.557l2.175-4.582a18.93 18.93 0 01-1.292-3.281l-4.613-1.62C.806 32.7 0 31.522 0 30.196v-1.721c0-1.326.806-2.504 2-2.923l4.614-1.62a18.932 18.932 0 011.292-3.281L5.73 16.069c-.564-1.186-.343-2.62.548-3.557l1.155-1.217c.89-.938 2.252-1.17 3.378-.577l4.35 2.29a16.975 16.975 0 013.117-1.36l1.538-4.858zm3.593 34.872c6.464 0 11.705-5.52 11.705-12.327S29.874 17.01 23.41 17.01c-6.465 0-11.705 5.519-11.705 12.326 0 6.808 5.24 12.327 11.705 12.327z" stroke="#6E49CB" stroke-width="2" fill="#EFEDF8"/><path d="M23.41 37.039c4.04 0 7.315-3.45 7.315-7.704 0-4.255-3.275-7.704-7.315-7.704-4.04 0-7.316 3.45-7.316 7.704 0 4.255 3.276 7.704 7.316 7.704z" stroke="#6E49CB" stroke-linecap="round"/><path d="M218.894 5.854c1.177 0 2.186.207 2.858.497.337.145.536.288.633.388a.555.555 0 01.034.037v44.362c-.008.01-.019.023-.034.038-.097.099-.296.242-.633.388-.672.289-1.681.497-2.858.497-1.176 0-2.186-.208-2.857-.497-.338-.146-.537-.289-.634-.388-.015-.015-.026-.028-.034-.038V6.776a.554.554 0 01.034-.037c.097-.1.296-.243.634-.388.671-.29 1.681-.497 2.857-.497z" stroke="#6E49CB" stroke-width="2" fill="#EFEDF8" fill-rule="nonzero"/><path d="M223.59 6.665l30.511 9.4c2.93.902 2.983 2.492.135 3.545l-30.647 11.33V6.664z" fill="#6E49CB"/><path d="M434.38 42.624h-.119l-.11.046c-2.675 1.14-5.224 1.423-7.758.61l-.283-.09-.241.176a4.655 4.655 0 01-6.045-.465v0l-3.178-3.179a4.659 4.659 0 01-.159-6.422l.206-.227-.069-.298c-.496-2.14-.302-4.32.467-6.575l.183-.533-.526-.202-4.709-1.818a3.605 3.605 0 01-2.195-2.467 3.622 3.622 0 01.73-3.221s0 0 0 0l6.166-7.35v0a3.612 3.612 0 014.064-1.047h.001l6.025 2.326.361.14.27-.28c.577-.594 1.167-1.193 1.772-1.798 5.89-5.893 13.552-9.022 23.03-9.36.258-.01.515-.004.773.016v0a6.906 6.906 0 016.359 7.412c-.692 9.033-3.963 16.452-9.804 22.292v0c-.413.414-.826.826-1.242 1.237l-.272.269.137.357 2.433 6.302s0 0 0 0a3.6 3.6 0 01.127 2.2 3.603 3.603 0 01-1.175 1.865h0l-7.347 6.168.376.448-.376-.448a3.613 3.613 0 01-3.221.73 3.614 3.614 0 01-2.466-2.196v0l-1.638-4.244-.144-.374h-.403zm-17.378-19.038l.391.15.268-.32 7.795-9.291.526-.628-.764-.295-5.11-1.972v0a1.785 1.785 0 00-2.008.517v0l-6.166 7.35v0a1.789 1.789 0 00-.36 1.592 1.78 1.78 0 001.085 1.22v0l4.343 1.677zm19.584 18.482v.109l.04.102 1.676 4.345v0c.103.265.266.501.478.69l.389-.437-.389.437a1.78 1.78 0 002.332.034l7.348-6.167v0c.285-.24.488-.561.58-.922l-.566-.146.567.146c.093-.36.071-.74-.062-1.087h-.001l-1.972-5.111-.294-.764-.628.526-9.289 7.796-.21.175v.274zm17.83-39.285l-.22.542.22-.542a5.079 5.079 0 00-2.089-.369h0c-8.997.321-16.228 3.251-21.803 8.827-6.376 6.377-10.292 11.317-11.722 15.569-.723 2.148-.817 4.138-.263 6.047.553 1.901 1.732 3.67 3.47 5.41 1.676 1.675 3.328 2.828 5.103 3.357 1.788.532 3.639.412 5.67-.351 4.005-1.504 8.816-5.55 15.517-12.253 5.54-5.54 8.618-12.556 9.273-21.141a5.076 5.076 0 00-3.156-5.096z" stroke="#6E49CB" fill="#6E49CB" fill-rule="nonzero"/><path d="M440.596 20.006a2.998 2.998 0 004.26.023 2.99 2.99 0 00.877-2.135 3.003 3.003 0 00-.901-2.126 2.996 2.996 0 10-4.237 4.238zm-2.12 2.12a6.001 6.001 0 01-1.3-6.532 5.98 5.98 0 013.244-3.245 5.99 5.99 0 016.53 1.3 5.992 5.992 0 01-8.473 8.475l-.001.001z" fill="#C2B7E6" fill-rule="nonzero"/><path d="M412.525 39.607a1.498 1.498 0 010 2.118l-8.473 8.475a1.502 1.502 0 01-2.137.02 1.513 1.513 0 01-.328-.493 1.49 1.49 0 01.345-1.645l8.475-8.476a1.501 1.501 0 011.632-.325c.183.075.348.186.486.325zm7.415 7.416a1.498 1.498 0 010 2.118l-6.356 6.357a1.501 1.501 0 01-1.631.325 1.495 1.495 0 01-.811-1.958c.075-.181.185-.347.324-.486l6.356-6.356a1.5 1.5 0 012.118 0z" fill="#E0DBF2"/><path d="M416.232 43.314a1.498 1.498 0 010 2.12l-11.65 11.654a1.497 1.497 0 01-2.558-1.06c0-.398.158-.779.439-1.06l11.652-11.653a1.497 1.497 0 012.117 0z" fill="#C2B7E6"/><path d="M7.468 84.21h1.474c-.022-.644-.147-1.196-.377-1.655a3.111 3.111 0 00-.917-1.163 3.704 3.704 0 00-1.344-.672 6.253 6.253 0 00-1.671-.213c-.536 0-1.06.07-1.573.213a4.175 4.175 0 00-1.36.622c-.394.274-.71.629-.951 1.066-.24.426-.36.934-.36 1.524 0 .535.103.984.31 1.344.22.35.503.64.853.869.36.218.765.399 1.213.54.447.132.9.252 1.36.361.47.099.928.197 1.376.295.448.099.847.23 1.196.394.36.153.645.355.853.606.218.251.327.58.327.983 0 .427-.087.776-.262 1.05a1.98 1.98 0 01-.688.655 3.252 3.252 0 01-.967.328c-.35.065-.7.098-1.049.098-.437 0-.863-.054-1.278-.164a3.269 3.269 0 01-1.098-.508 2.762 2.762 0 01-.754-.868c-.185-.361-.278-.787-.278-1.279H.028c0 .71.126 1.328.377 1.852.262.514.612.94 1.049 1.279a4.79 4.79 0 001.54.737 6.948 6.948 0 003.474.05 4.641 4.641 0 001.475-.59 3.472 3.472 0 001.065-1.082c.284-.448.426-.984.426-1.607 0-.579-.11-1.06-.328-1.442a2.736 2.736 0 00-.852-.95 4.215 4.215 0 00-1.196-.59 14.217 14.217 0 00-1.377-.394c-.458-.11-.912-.208-1.36-.295a7.987 7.987 0 01-1.212-.36 2.535 2.535 0 01-.852-.542c-.208-.229-.312-.524-.312-.885 0-.382.071-.699.213-.95.153-.263.35-.47.59-.623a2.66 2.66 0 01.852-.328c.317-.065.64-.098.967-.098.808 0 1.47.19 1.983.573.524.372.83.978.918 1.82zm9.511 3.23h-4.867a2.82 2.82 0 01.213-.918c.12-.295.284-.552.492-.77.207-.22.453-.388.737-.509.295-.13.623-.196.983-.196.35 0 .667.065.95.196.296.12.547.29.755.508.218.208.387.46.508.754.13.295.207.607.229.935zm1.344 2.36h-1.377c-.12.557-.371.972-.754 1.245-.37.274-.852.41-1.442.41-.458 0-.857-.076-1.196-.23a2.394 2.394 0 01-.836-.606 2.483 2.483 0 01-.475-.885 3.438 3.438 0 01-.13-1.065h6.34a6.499 6.499 0 00-.147-1.623 4.48 4.48 0 00-.622-1.573 3.533 3.533 0 00-1.197-1.18c-.491-.317-1.114-.476-1.868-.476-.579 0-1.114.11-1.606.328-.48.219-.9.525-1.261.918-.35.393-.623.858-.82 1.393a5.1 5.1 0 00-.295 1.77c.022.645.115 1.24.279 1.787.175.546.431 1.016.77 1.41.339.393.754.699 1.246.917.502.219 1.092.328 1.77.328.96 0 1.758-.24 2.392-.721s1.043-1.197 1.229-2.147zm3.508-5.786v-2.54h-1.393v2.54h-1.442v1.23h1.442v5.392c0 .393.039.71.115.95.077.24.191.426.344.558.164.13.372.224.623.278.262.044.573.066.934.066h1.065v-1.23h-.639a7.31 7.31 0 01-.54-.016.742.742 0 01-.312-.115.446.446 0 01-.164-.229 1.883 1.883 0 01-.033-.393v-5.262h1.688v-1.229h-1.688zm14.81 8.474v-8.474h-1.393V88.8c0 .382-.055.738-.164 1.065a2.31 2.31 0 01-.459.836c-.207.24-.47.426-.786.558-.306.13-.672.196-1.098.196-.535 0-.956-.153-1.262-.459-.306-.306-.459-.72-.459-1.245v-5.737h-1.393v5.573c0 .459.044.88.131 1.262.099.371.263.694.492.967.23.273.53.486.901.639.372.142.836.213 1.393.213.623 0 1.164-.12 1.622-.36.46-.252.836-.64 1.131-1.164h.033v1.344h1.31zm2.141-8.474v11.703h1.393v-4.36h.033c.153.251.338.464.557.64.23.163.47.294.72.392.252.099.503.17.755.214.262.043.497.065.704.065.645 0 1.207-.115 1.688-.344.492-.23.896-.541 1.213-.934a3.92 3.92 0 00.72-1.41c.165-.535.246-1.104.246-1.704s-.081-1.17-.245-1.705a4.132 4.132 0 00-.738-1.41 3.35 3.35 0 00-1.212-.983c-.481-.24-1.05-.36-1.705-.36-.59 0-1.13.109-1.622.327a2.05 2.05 0 00-1.081 1.016h-.033v-1.147h-1.393zm6.555 4.163c0 .415-.044.82-.131 1.213a3.21 3.21 0 01-.426 1.049 2.22 2.22 0 01-.787.738c-.317.185-.716.278-1.196.278-.481 0-.89-.087-1.23-.262a2.55 2.55 0 01-.835-.721 3.082 3.082 0 01-.459-1.016 4.983 4.983 0 01-.147-1.213c0-.394.043-.781.13-1.164.1-.382.252-.721.46-1.016.207-.306.475-.552.803-.738.327-.186.726-.278 1.196-.278.448 0 .836.087 1.163.262.339.175.612.41.82.705a2.9 2.9 0 01.475 1.016c.109.371.164.754.164 1.147zm129.786-1.786v-4.295h3.048c.885 0 1.53.186 1.934.558.415.36.622.89.622 1.59 0 .699-.207 1.234-.622 1.606-.404.371-1.05.552-1.934.54h-3.048zm-1.557-5.606v11.703h1.557v-4.786h3.572c1.18.01 2.071-.29 2.671-.902.613-.612.918-1.464.918-2.556 0-1.093-.305-1.94-.918-2.541-.6-.612-1.49-.918-2.67-.918h-5.13zm10.478 0v11.703h1.393V80.785h-1.393zm11.057 11.67c-.24.142-.574.213-1 .213-.36 0-.65-.098-.868-.295-.207-.207-.312-.54-.312-1-.382.46-.83.793-1.343 1a4.456 4.456 0 01-1.639.295c-.383 0-.748-.044-1.098-.131a2.545 2.545 0 01-.885-.41 2.06 2.06 0 01-.606-.721c-.142-.306-.213-.672-.213-1.098 0-.48.082-.874.246-1.18.164-.306.377-.552.639-.738.273-.196.58-.344.917-.442.35-.099.705-.18 1.066-.246.382-.077.743-.131 1.081-.164.35-.044.656-.098.918-.164.262-.076.47-.18.622-.311.154-.142.23-.345.23-.607 0-.306-.06-.552-.18-.737a1.1 1.1 0 00-.443-.427 1.72 1.72 0 00-.606-.196 4.412 4.412 0 00-.656-.05c-.59 0-1.081.115-1.474.345-.394.218-.607.639-.64 1.262h-1.392c.022-.525.13-.967.327-1.328.197-.36.46-.65.787-.868a3.21 3.21 0 011.114-.492 6.032 6.032 0 011.36-.148c.383 0 .76.028 1.131.082.383.055.727.17 1.032.345.306.163.552.398.738.704.186.306.278.705.278 1.197v4.36c0 .327.017.568.05.72.043.154.174.23.393.23.12 0 .262-.027.426-.082v1.082zm-2.262-4.343c-.174.13-.403.23-.688.295-.284.054-.584.103-.901.147a14.79 14.79 0 00-.934.131c-.317.044-.6.12-.852.23a1.58 1.58 0 00-.623.475c-.153.197-.23.47-.23.82 0 .229.044.426.132.59.098.153.219.278.36.377.154.098.328.169.525.213.196.043.404.065.622.065.46 0 .852-.06 1.18-.18.328-.131.596-.29.803-.475.207-.197.36-.405.459-.623.098-.23.147-.443.147-.64v-1.425zm3.559-4.098v8.474h1.393v-4.786c0-.383.049-.732.147-1.05.109-.327.267-.611.475-.851.208-.24.464-.427.77-.558a2.898 2.898 0 011.115-.196c.535 0 .955.153 1.262.459.305.305.458.72.458 1.245v5.737h1.393v-5.573c0-.459-.049-.874-.147-1.246a2.302 2.302 0 00-.475-.983 2.294 2.294 0 00-.902-.64c-.372-.152-.835-.229-1.393-.229-1.257 0-2.174.514-2.753 1.541h-.032v-1.344h-1.311zm17.123 1.278l-.394-.491a7.294 7.294 0 01-.36-.492 4.216 4.216 0 01-.279-.524 1.485 1.485 0 01-.098-.525c0-.426.143-.737.426-.934.285-.208.59-.312.918-.312.415 0 .743.126.983.377.241.24.36.53.36.87 0 .25-.049.48-.147.687a2.28 2.28 0 01-.377.541 3.227 3.227 0 01-.508.443c-.185.131-.36.251-.524.36zm2.999 5.737l1.245 1.459h1.819l-2.278-2.639c.12-.262.225-.492.311-.688.088-.208.16-.416.213-.623.066-.208.115-.432.148-.672.044-.252.082-.552.115-.902h-1.328a6.424 6.424 0 01-.377 1.82l-2.113-2.574c.284-.164.557-.344.819-.54a4.65 4.65 0 00.705-.689c.208-.251.372-.524.491-.82.12-.305.18-.633.18-.983 0-.393-.082-.737-.245-1.032a2.093 2.093 0 00-.623-.754 2.587 2.587 0 00-.918-.46 3.74 3.74 0 00-1.049-.147c-.426 0-.802.066-1.13.197a2.327 2.327 0 00-1.344 1.311 2.498 2.498 0 00-.164.902c0 .295.033.562.099.803.077.23.175.453.294.672.132.207.274.415.427.622.153.208.31.427.475.656a11.6 11.6 0 00-1.065.64c-.328.218-.617.47-.869.753-.24.273-.436.585-.59.935-.141.35-.213.753-.213 1.212 0 .24.039.541.115.902.088.36.262.71.524 1.049.262.339.635.628 1.115.869.48.24 1.114.36 1.9.36.645 0 1.268-.137 1.869-.41a3.23 3.23 0 001.442-1.229zm-3.458-4.196l2.687 3.229c-.283.426-.639.765-1.065 1.016-.414.252-.89.377-1.425.377-.284 0-.562-.049-.836-.147a2.615 2.615 0 01-.705-.394 2.164 2.164 0 01-.508-.622 1.886 1.886 0 01-.18-.82c0-.35.055-.65.164-.901.11-.263.257-.498.442-.705.186-.208.4-.394.64-.558.251-.163.513-.322.786-.475zm17.767.607h-4.867a2.82 2.82 0 01.213-.918 2.46 2.46 0 01.491-.77c.208-.22.453-.388.738-.509.295-.13.623-.196.983-.196.35 0 .666.065.95.196.295.12.547.29.754.508.22.208.388.46.508.754.131.295.207.607.23.935zm1.343 2.36h-1.376c-.12.557-.371.972-.754 1.245-.37.274-.852.41-1.442.41-.459 0-.858-.076-1.196-.23a2.4 2.4 0 01-.836-.606 2.477 2.477 0 01-.475-.885 3.448 3.448 0 01-.131-1.065h6.342a6.466 6.466 0 00-.148-1.623 4.48 4.48 0 00-.623-1.573 3.536 3.536 0 00-1.196-1.18c-.492-.317-1.114-.476-1.868-.476-.58 0-1.114.11-1.606.328a3.863 3.863 0 00-1.262.918c-.35.393-.622.858-.819 1.393a5.1 5.1 0 00-.295 1.77c.022.645.115 1.24.279 1.787.174.546.432 1.016.77 1.41.338.393.754.699 1.245.917.502.219 1.092.328 1.77.328.96 0 1.76-.24 2.392-.721.634-.48 1.043-1.197 1.23-2.147zm3.854-1.77l-3.18 4.458h1.688l2.36-3.508 2.36 3.508h1.786l-3.277-4.573 2.916-3.901h-1.671l-2.114 2.967-2.032-2.967h-1.786l2.95 4.016zm12.086-.59h-4.867a2.79 2.79 0 01.213-.918c.12-.295.283-.552.492-.77.207-.22.453-.388.737-.509.295-.13.623-.196.983-.196.35 0 .666.065.95.196.296.12.546.29.755.508.217.208.387.46.508.754.13.295.207.607.23.935zm1.344 2.36h-1.377c-.12.557-.372.972-.754 1.245-.372.274-.852.41-1.442.41-.458 0-.858-.076-1.196-.23a2.379 2.379 0 01-.835-.606 2.477 2.477 0 01-.476-.885 3.413 3.413 0 01-.13-1.065h6.34a6.577 6.577 0 00-.147-1.623 4.503 4.503 0 00-.622-1.573 3.536 3.536 0 00-1.197-1.18c-.491-.317-1.114-.476-1.868-.476-.579 0-1.114.11-1.606.328a3.863 3.863 0 00-1.261.918c-.35.393-.623.858-.82 1.393a5.1 5.1 0 00-.295 1.77c.021.645.115 1.24.279 1.787a3.98 3.98 0 00.77 1.41c.338.393.754.699 1.246.917.502.219 1.092.328 1.77.328.96 0 1.757-.24 2.392-.721.633-.48 1.043-1.197 1.229-2.147zm7.326-3.065h1.442c-.054-.503-.185-.934-.393-1.295a2.832 2.832 0 00-.803-.918 3.084 3.084 0 00-1.115-.524 4.806 4.806 0 00-1.343-.18c-.666 0-1.25.12-1.754.36-.502.23-.922.552-1.262.967a4.095 4.095 0 00-.737 1.442 6.135 6.135 0 00-.246 1.77c0 .634.082 1.219.246 1.754.176.525.426.978.754 1.36.34.383.754.678 1.245.886.504.207 1.077.311 1.72.311 1.082 0 1.934-.284 2.557-.852.635-.568 1.028-1.377 1.18-2.426h-1.426c-.086.656-.327 1.164-.72 1.524-.382.36-.918.541-1.607.541-.436 0-.813-.087-1.13-.262a2.238 2.238 0 01-.77-.688 3.318 3.318 0 01-.443-1 5.037 5.037 0 01-.13-1.148c0-.426.044-.835.13-1.229.088-.404.23-.76.426-1.065.209-.306.481-.552.82-.738.339-.186.76-.278 1.261-.278.59 0 1.06.147 1.41.442.35.295.579.71.688 1.246zm10.064 5.753v-8.474h-1.393V88.8c0 .382-.055.738-.164 1.065a2.316 2.316 0 01-.459.836c-.208.24-.47.426-.786.558-.307.13-.672.196-1.098.196-.536 0-.957-.153-1.262-.459-.307-.306-.459-.72-.459-1.245v-5.737h-1.393v5.573c0 .459.043.88.131 1.262.099.371.262.694.492.967.23.273.529.486.901.639.371.142.836.213 1.393.213.623 0 1.163-.12 1.622-.36.46-.252.836-.64 1.13-1.164h.034v1.344h1.31zm4.025-8.474v-2.54h-1.393v2.54h-1.442v1.23h1.442v5.392c0 .393.039.71.115.95.076.24.19.426.344.558.164.13.37.224.623.278.262.044.573.066.934.066h1.065v-1.23h-.64c-.218 0-.398-.005-.54-.016a.74.74 0 01-.311-.115.448.448 0 01-.164-.229 1.86 1.86 0 01-.033-.393v-5.262h1.688v-1.229h-1.688zm9.118 3.426h-4.867a2.82 2.82 0 01.213-.918 2.46 2.46 0 01.492-.77c.207-.22.453-.388.737-.509.295-.13.623-.196.983-.196.35 0 .666.065.95.196.296.12.547.29.755.508.218.208.387.46.508.754.13.295.207.607.229.935zm1.344 2.36h-1.377c-.12.557-.37.972-.754 1.245-.37.274-.852.41-1.442.41-.458 0-.858-.076-1.196-.23a2.4 2.4 0 01-.836-.606 2.477 2.477 0 01-.475-.885 3.448 3.448 0 01-.13-1.065h6.34a6.466 6.466 0 00-.147-1.623 4.48 4.48 0 00-.622-1.573 3.536 3.536 0 00-1.197-1.18c-.491-.317-1.114-.476-1.868-.476-.58 0-1.114.11-1.606.328a3.863 3.863 0 00-1.261.918c-.35.393-.623.858-.82 1.393a5.1 5.1 0 00-.295 1.77 7.05 7.05 0 00.279 1.787c.174.546.432 1.016.77 1.41.338.393.754.699 1.245.917.503.219 1.093.328 1.77.328.961 0 1.76-.24 2.393-.721s1.043-1.197 1.229-2.147zm126.596 1.377v-9.08h2.622c.72 0 1.327.103 1.819.31.491.197.89.493 1.196.886.316.382.54.852.672 1.41.141.546.213 1.169.213 1.868 0 .721-.078 1.338-.23 1.852-.143.503-.328.923-.557 1.262-.23.339-.492.607-.786.803-.285.197-.574.35-.869.46a4.367 4.367 0 01-.836.196 8.132 8.132 0 01-.655.033h-2.59zm-1.557-10.392v11.703h4.015c.971 0 1.813-.137 2.523-.41.71-.273 1.295-.666 1.754-1.18.458-.525.797-1.164 1.016-1.918.217-.765.327-1.639.327-2.622 0-1.88-.487-3.278-1.458-4.196-.973-.918-2.36-1.377-4.162-1.377h-4.015zm17.19 6.655h-4.866a2.85 2.85 0 01.213-.918 2.46 2.46 0 01.492-.77c.208-.22.454-.388.737-.509.295-.13.623-.196.983-.196.35 0 .667.065.95.196.296.12.547.29.754.508.22.208.389.46.508.754.132.295.209.607.23.935zm1.345 2.36h-1.377c-.12.557-.371.972-.754 1.245-.37.274-.852.41-1.442.41-.459 0-.857-.076-1.196-.23a2.4 2.4 0 01-.836-.606 2.495 2.495 0 01-.475-.885 3.448 3.448 0 01-.131-1.065h6.342a6.466 6.466 0 00-.148-1.623 4.458 4.458 0 00-.623-1.573 3.526 3.526 0 00-1.196-1.18c-.491-.317-1.114-.476-1.868-.476a3.91 3.91 0 00-1.606.328c-.48.219-.901.525-1.262.918a4.322 4.322 0 00-.819 1.393 5.1 5.1 0 00-.295 1.77 7.05 7.05 0 00.279 1.787c.175.546.432 1.016.77 1.41.34.393.754.699 1.245.917.504.219 1.094.328 1.77.328.962 0 1.76-.24 2.393-.721.634-.48 1.044-1.197 1.229-2.147zm1.624-5.786v11.703h1.393v-4.36h.033a2.4 2.4 0 00.557.64c.23.163.47.294.721.392.25.099.502.17.754.214.262.043.496.065.704.065.644 0 1.207-.115 1.688-.344.492-.23.896-.541 1.213-.934.328-.405.568-.875.72-1.41.165-.535.247-1.104.247-1.704s-.082-1.17-.246-1.705a4.132 4.132 0 00-.737-1.41 3.353 3.353 0 00-1.213-.983c-.481-.24-1.049-.36-1.704-.36-.59 0-1.131.109-1.623.327a2.05 2.05 0 00-1.081 1.016h-.033v-1.147h-1.393zm6.555 4.163c0 .415-.045.82-.131 1.213-.088.393-.23.743-.426 1.049a2.219 2.219 0 01-.787.738c-.317.185-.716.278-1.196.278-.481 0-.89-.087-1.23-.262a2.534 2.534 0 01-.835-.721 3.065 3.065 0 01-.459-1.016 5.185 5.185 0 01-.016-2.377c.098-.382.25-.721.459-1.016.207-.306.475-.552.803-.738.328-.186.725-.278 1.196-.278.447 0 .836.087 1.163.262.339.175.611.41.82.705.218.295.377.633.475 1.016.109.371.164.754.164 1.147zm3.192-7.392v11.703h1.393V80.785h-1.393zm4.566 7.474a4.3 4.3 0 01.197-1.36c.141-.405.333-.743.573-1.016.24-.274.519-.481.836-.623a2.57 2.57 0 012.048 0c.328.142.612.35.852.623s.426.611.557 1.016c.142.393.213.847.213 1.36s-.071.973-.213 1.377a2.868 2.868 0 01-.557 1c-.24.262-.524.464-.852.606a2.57 2.57 0 01-2.048 0 2.518 2.518 0 01-.836-.606 3.075 3.075 0 01-.573-1 4.456 4.456 0 01-.197-1.377zm-1.475 0c0 .623.088 1.202.262 1.737.175.536.437 1.006.787 1.41.35.393.78.705 1.294.934.514.219 1.104.328 1.77.328.678 0 1.268-.11 1.77-.328.514-.23.944-.54 1.294-.934.35-.404.613-.874.787-1.41a5.579 5.579 0 00.262-1.737c0-.623-.088-1.202-.262-1.737a3.852 3.852 0 00-.787-1.41 3.659 3.659 0 00-1.294-.95c-.502-.23-1.092-.345-1.77-.345-.666 0-1.256.115-1.77.345-.513.229-.944.546-1.294.95-.35.394-.612.863-.787 1.41a5.579 5.579 0 00-.262 1.737zm13.195 5.36a8.003 8.003 0 01-.492 1.049 2.468 2.468 0 01-.524.688 1.554 1.554 0 01-.64.393c-.229.088-.495.132-.802.132-.164 0-.328-.011-.492-.033a2.326 2.326 0 01-.475-.115v-1.278c.12.054.258.098.41.13.164.044.3.066.41.066.284 0 .52-.07.704-.213.197-.13.344-.322.442-.573l.574-1.426-3.36-8.425h1.574l2.474 6.933h.033l2.376-6.933h1.475l-3.687 9.605z" fill="#303030" fill-rule="nonzero"/><rect fill="#C2B7E6" fill-rule="nonzero" x="62.036" y="26.927" width="126.412" height="4.683" rx="2"/><rect fill="#C2B7E6" fill-rule="nonzero" x="266.87" y="26.927" width="125.242" height="4.683" rx="2"/></g></svg>
diff --git a/app/assets/images/learn_gitlab/rectangle.svg b/app/assets/images/learn_gitlab/rectangle.svg
new file mode 100644
index 00000000000..51667e77158
--- /dev/null
+++ b/app/assets/images/learn_gitlab/rectangle.svg
@@ -0,0 +1 @@
+<svg width="108" height="4" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="108" height="4" rx="2" fill="#C2B7E6"/></svg>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
index ef29fc5e8b4..d9e5878b9e3 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
@@ -116,7 +116,7 @@ export default {
methods: {
tbodyTrClass(item) {
return {
- [bodyTrClass]: this.integrations.length,
+ [bodyTrClass]: this.integrations?.length,
'gl-bg-blue-50': (item !== null && item.id) === this.currentIntegration?.id,
};
},
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
index f51c8d7e9f7..3917e4c5fdd 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -14,13 +14,12 @@ import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_cu
import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
-import getHttpIntegrationsQuery from '../graphql/queries/get_http_integrations.query.graphql';
+import getHttpIntegrationQuery from '../graphql/queries/get_http_integration.query.graphql';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import service from '../services';
import {
updateStoreAfterIntegrationDelete,
updateStoreAfterIntegrationAdd,
- updateStoreAfterHttpIntegrationAdd,
} from '../utils/cache_updates';
import {
DELETE_INTEGRATION_ERROR,
@@ -68,33 +67,8 @@ export default {
};
},
update(data) {
- const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {};
-
- return {
- list,
- };
- },
- error(err) {
- createFlash({ message: err });
- },
- },
- // TODO: we'll need to update the logic to request specific http integration by its id on edit
- // when BE adds support for it https://gitlab.com/gitlab-org/gitlab/-/issues/321674
- // currently the request for ALL http integrations is made and on specific integration edit we search it in the list
- httpIntegrations: {
- fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
- query: getHttpIntegrationsQuery,
- variables() {
- return {
- projectPath: this.projectPath,
- };
- },
- update(data) {
- const { alertManagementHttpIntegrations: { nodes: list = [] } = {} } = data.project || {};
-
- return {
- list,
- };
+ const { alertManagementIntegrations: { nodes = [] } = {} } = data.project || {};
+ return nodes;
},
error(err) {
createFlash({ message: err });
@@ -107,9 +81,9 @@ export default {
data() {
return {
isUpdating: false,
- integrations: {},
- httpIntegrations: {},
+ integrations: [],
currentIntegration: null,
+ currentHttpIntegration: null,
newIntegration: null,
formVisible: false,
showSuccessfulCreateAlert: false,
@@ -121,7 +95,7 @@ export default {
return this.$apollo.queries.integrations.loading;
},
canAddIntegration() {
- return this.multiIntegrations || this.integrations?.list?.length < 2;
+ return this.multiIntegrations || this.integrations.length < 2;
},
},
methods: {
@@ -142,11 +116,6 @@ export default {
},
update(store, { data }) {
updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
- if (isHttp) {
- updateStoreAfterHttpIntegrationAdd(store, getHttpIntegrationsQuery, data, {
- projectPath,
- });
- }
},
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
@@ -253,15 +222,38 @@ export default {
});
},
editIntegration({ id, type }) {
- let currentIntegration = this.integrations.list.find((integration) => integration.id === id);
- if (this.isHttp(type)) {
- const httpIntegrationMappingData = this.httpIntegrations.list.find(
- (integration) => integration.id === id,
- );
- currentIntegration = { ...currentIntegration, ...httpIntegrationMappingData };
- }
+ const currentIntegration = this.integrations.find((integration) => integration.id === id);
- this.viewIntegration(currentIntegration, tabIndices.viewCredentials);
+ if (this.multiIntegrations && this.isHttp(type)) {
+ this.$apollo.addSmartQuery('currentHttpIntegration', {
+ query: getHttpIntegrationQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ id,
+ };
+ },
+ update(data) {
+ const {
+ project: {
+ alertManagementHttpIntegrations: { nodes = [{}] },
+ },
+ } = data;
+ return nodes[0];
+ },
+ result() {
+ this.viewIntegration(
+ { ...currentIntegration, ...this.currentHttpIntegration },
+ tabIndices.viewCredentials,
+ );
+ },
+ error() {
+ createFlash({ message: DEFAULT_ERROR });
+ },
+ });
+ } else {
+ this.viewIntegration(currentIntegration, tabIndices.viewCredentials);
+ }
},
viewIntegration(integration, tabIndex) {
this.$apollo
@@ -368,7 +360,7 @@ export default {
</gl-alert>
<integrations-list
- :integrations="integrations.list"
+ :integrations="integrations"
:loading="loading"
@edit-integration="editIntegration"
@delete-integration="deleteIntegration"
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
index 5bd63820629..e9230812db2 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
@@ -6,7 +6,6 @@ mutation updateCurrentPrometheusIntegration(
$type: String
$url: String
$apiUrl: String
- $samplePayload: String
) {
updateCurrentIntegration(
id: $id
@@ -16,6 +15,5 @@ mutation updateCurrentPrometheusIntegration(
type: $type
url: $url
apiUrl: $apiUrl
- samplePayload: $samplePayload
) @client
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integration.query.graphql
index 833a2d6c12f..d20a8b8334b 100644
--- a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integration.query.graphql
@@ -1,9 +1,8 @@
#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql"
-# TODO: this query need to accept http integration id to request a sepcific integration
-query getHttpIntegrations($projectPath: ID!) {
+query getHttpIntegration($projectPath: ID!, $id: ID) {
project(fullPath: $projectPath) {
- alertManagementHttpIntegrations {
+ alertManagementHttpIntegrations(id: $id) {
nodes {
...HttpIntegrationPayloadData
}
diff --git a/app/assets/javascripts/alerts_settings/utils/cache_updates.js b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
index 716c709a931..a50b6515afa 100644
--- a/app/assets/javascripts/alerts_settings/utils/cache_updates.js
+++ b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
@@ -58,31 +58,6 @@ const addIntegrationToStore = (
});
};
-const addHttpIntegrationToStore = (store, query, { httpIntegrationCreate }, variables) => {
- const integration = httpIntegrationCreate?.integration;
- if (!integration) {
- return;
- }
-
- const sourceData = store.readQuery({
- query,
- variables,
- });
-
- const data = produce(sourceData, (draftData) => {
- draftData.project.alertManagementHttpIntegrations.nodes = [
- integration,
- ...draftData.project.alertManagementHttpIntegrations.nodes,
- ];
- });
-
- store.writeQuery({
- query,
- variables,
- data,
- });
-};
-
const onError = (data, message) => {
createFlash({ message });
throw new Error(data.errors);
@@ -105,11 +80,3 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) =>
addIntegrationToStore(store, query, data, variables);
}
};
-
-export const updateStoreAfterHttpIntegrationAdd = (store, query, data, variables) => {
- if (hasErrors(data)) {
- onError(data, ADD_INTEGRATION_ERROR);
- } else {
- addHttpIntegrationToStore(store, query, data, variables);
- }
-};
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 90a663802d2..d75c3cc6b8b 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -33,7 +33,7 @@ if (filesContainer.length) {
axios
.get(batchPath)
.then(({ data }) => {
- filesContainer.html($(data.html));
+ filesContainer.html($(data));
syntaxHighlight(filesContainer);
handleLocationHash();
new Diff();
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 0c3ff07bc76..04186062344 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -49,7 +49,7 @@ class Projects::CommitController < Projects::ApplicationController
end
def diff_files
- render json: { html: view_to_html_string('projects/commit/diff_files', diffs: @diffs, environment: @environment) }
+ render template: 'projects/commit/diff_files', layout: false, locals: { diffs: @diffs, environment: @environment }
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
index 81896fb9fa4..4fff9990c29 100644
--- a/app/helpers/learn_gitlab_helper.rb
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -3,7 +3,7 @@
module LearnGitlabHelper
def learn_gitlab_experiment_enabled?(project)
return false unless current_user
- return false unless experiment_enabled_for_user?
+ return false unless continous_onboarding_experiment_enabled_for_user?
learn_gitlab_onboarding_available?(project)
end
@@ -21,6 +21,11 @@ module LearnGitlabHelper
end
end
+ def continous_onboarding_experiment_enabled_for_user?
+ Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) ||
+ Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user)
+ end
+
private
ACTION_ISSUE_IDS = {
@@ -50,11 +55,6 @@ module LearnGitlabHelper
OnboardingProgress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord
end
- def experiment_enabled_for_user?
- Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) ||
- Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user)
- end
-
def learn_gitlab_onboarding_available?(project)
OnboardingProgress.onboarding?(project.namespace) &&
LearnGitlab.new(current_user).available?
diff --git a/app/policies/nil_policy.rb b/app/policies/nil_policy.rb
deleted file mode 100644
index fc969f8cd05..00000000000
--- a/app/policies/nil_policy.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-class NilPolicy < BasePolicy
- rule { default }.prevent_all
-end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index e04c5168cef..1f40c21d650 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -162,12 +162,7 @@ module MergeRequests
end
def refresh_pipelines_on_merge_requests(merge_request)
- if Feature.enabled?(:code_review_async_pipeline_creation, project, default_enabled: :yaml)
- create_pipeline_for(merge_request, current_user, async: true)
- else
- create_pipeline_for(merge_request, current_user, async: false)
- UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id)
- end
+ create_pipeline_for(merge_request, current_user, async: true)
end
def abort_auto_merges(merge_request)
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 705716c09b7..9d23ddc7b68 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -46,9 +46,10 @@
%tr
%td
= form_tag admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do
- .form-group
- = search_field_tag :search, params[:search], class: 'form-control', spellcheck: false
- = submit_tag 'Search', class: 'btn'
+ .input-group
+ = search_field_tag :search, params[:search], class: 'form-control gl-form-input', spellcheck: false
+ .input-group-append
+ = submit_tag 'Search', class: 'gl-button btn btn-default'
%td
- @projects.each do |project|
diff --git a/app/views/devise/mailer/reset_password_instructions.html.haml b/app/views/devise/mailer/reset_password_instructions.html.haml
index 47e192afa52..717f51b662f 100644
--- a/app/views/devise/mailer/reset_password_instructions.html.haml
+++ b/app/views/devise/mailer/reset_password_instructions.html.haml
@@ -1,10 +1,9 @@
-= email_default_heading("Hello, #{@resource.name}!")
+= email_default_heading(_("Hello, %{name}!") % { name: @resource.name })
%p
- Someone, hopefully you, has requested to reset the password for your
- GitLab account on #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}.
+ = _('Someone, hopefully you, has requested to reset the password for your GitLab account on %{link_to_gitlab}.').html_safe % { link_to_gitlab: Gitlab.config.gitlab.url }
%p
- If you did not perform this request, you can safely ignore this email.
+ = _('If you did not perform this request, you can safely ignore this email.')
%p
- Otherwise, click the link below to complete the process.
+ = _('Otherwise, click the link below to complete the process.')
#cta
- = link_to('Reset password', edit_password_url(@resource, reset_password_token: @token))
+ = link_to(_('Reset password'), edit_password_url(@resource, reset_password_token: @token))
diff --git a/app/views/projects/blob/viewers/_download.html.haml b/app/views/projects/blob/viewers/_download.html.haml
index fda4b9c92cd..61f64177be8 100644
--- a/app/views/projects/blob/viewers/_download.html.haml
+++ b/app/views/projects/blob/viewers/_download.html.haml
@@ -4,4 +4,4 @@
%h1.light
= sprite_icon('download')
%h4
- Download (#{number_to_human_size(viewer.blob.raw_size)})
+ = _('Download (%{size})').html_safe % { size: number_to_human_size(viewer.blob.raw_size) }
diff --git a/app/views/projects/settings/operations/_configuration_banner.html.haml b/app/views/projects/settings/operations/_configuration_banner.html.haml
index 8551aa5380e..6fa6b23b0da 100644
--- a/app/views/projects/settings/operations/_configuration_banner.html.haml
+++ b/app/views/projects/settings/operations/_configuration_banner.html.haml
@@ -14,7 +14,7 @@
.col-sm-10
%p.text-success.gl-mt-3
= s_('PrometheusService|GitLab manages Prometheus on your clusters.')
- = link_to s_('PrometheusService|Manage clusters'), project_clusters_path(project), class: 'btn'
+ = link_to s_('PrometheusService|Manage clusters'), project_clusters_path(project), class: 'gl-button btn btn-default'
- else
.col-sm-2
= image_tag 'illustrations/monitoring/loading.svg'
diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml
index e4a48943115..eea13d105d8 100644
--- a/app/views/sherlock/queries/show.html.haml
+++ b/app/views/sherlock/queries/show.html.haml
@@ -11,7 +11,7 @@
.row-content-block
.float-right
- = link_to(sherlock_transaction_path(@transaction), class: 'btn') do
+ = link_to(sherlock_transaction_path(@transaction), class: 'btn gl-button btn-default') do
= sprite_icon('arrow-left')
= t('sherlock.transaction')
.oneline
diff --git a/changelogs/unreleased/326316-improve-async-diff-rendering.yml b/changelogs/unreleased/326316-improve-async-diff-rendering.yml
new file mode 100644
index 00000000000..27f836157ac
--- /dev/null
+++ b/changelogs/unreleased/326316-improve-async-diff-rendering.yml
@@ -0,0 +1,5 @@
+---
+title: Improve diff_files endpoint performance
+merge_request: 59489
+author:
+type: performance
diff --git a/changelogs/unreleased/327559-feature-flag-rollout-of-code_review_async_pipeline_creation.yml b/changelogs/unreleased/327559-feature-flag-rollout-of-code_review_async_pipeline_creation.yml
new file mode 100644
index 00000000000..5aaf6073f8f
--- /dev/null
+++ b/changelogs/unreleased/327559-feature-flag-rollout-of-code_review_async_pipeline_creation.yml
@@ -0,0 +1,5 @@
+---
+title: Create the merge request pipeline asynchronously on push
+merge_request: 59624
+author:
+type: performance
diff --git a/changelogs/unreleased/Externalize-strings-in-reset_password_instructions-html-haml.yml b/changelogs/unreleased/Externalize-strings-in-reset_password_instructions-html-haml.yml
new file mode 100644
index 00000000000..d2bd8587d93
--- /dev/null
+++ b/changelogs/unreleased/Externalize-strings-in-reset_password_instructions-html-haml.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings in reset_password_instructions.html.haml
+merge_request: 58224
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/Externalize-strings-in-viewers-_download-html-haml.yml b/changelogs/unreleased/Externalize-strings-in-viewers-_download-html-haml.yml
new file mode 100644
index 00000000000..05c887a5604
--- /dev/null
+++ b/changelogs/unreleased/Externalize-strings-in-viewers-_download-html-haml.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings in viewers/_download.html.haml
+merge_request: 58450
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/final-button-sweep.yml b/changelogs/unreleased/final-button-sweep.yml
new file mode 100644
index 00000000000..bf277855341
--- /dev/null
+++ b/changelogs/unreleased/final-button-sweep.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrades the final few bootstrap buttons
+merge_request: 59641
+author:
+type: other
diff --git a/changelogs/unreleased/retain-timelogs-for-deleted-notes.yml b/changelogs/unreleased/retain-timelogs-for-deleted-notes.yml
new file mode 100644
index 00000000000..630a104a00c
--- /dev/null
+++ b/changelogs/unreleased/retain-timelogs-for-deleted-notes.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent loss of timelogs when deleting notes
+merge_request: 58525
+author: Lee Tickett @leetickett
+type: fixed
diff --git a/config/feature_flags/development/code_review_async_pipeline_creation.yml b/config/feature_flags/development/code_review_async_pipeline_creation.yml
deleted file mode 100644
index d0e5a3286aa..00000000000
--- a/config/feature_flags/development/code_review_async_pipeline_creation.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: code_review_async_pipeline_creation
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58542
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327559
-milestone: '13.11'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/initializers/declarative_policy.rb b/config/initializers/declarative_policy.rb
new file mode 100644
index 00000000000..49267584809
--- /dev/null
+++ b/config/initializers/declarative_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+DeclarativePolicy.configure do
+ named_policy :global, ::GlobalPolicy
+end
diff --git a/config/routes.rb b/config/routes.rb
index 1258675df86..1f31598dd0e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -51,6 +51,7 @@ Rails.application.routes.draw do
Gitlab.ee do
get :trial_getting_started, on: :collection
get :trial_onboarding_board, on: :collection
+ get :continuous_onboarding_getting_started, on: :collection
end
end
diff --git a/db/post_migrate/20210403022952_remove_notes_delete_cascade_timelogs.rb b/db/post_migrate/20210403022952_remove_notes_delete_cascade_timelogs.rb
new file mode 100644
index 00000000000..832ba584931
--- /dev/null
+++ b/db/post_migrate/20210403022952_remove_notes_delete_cascade_timelogs.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class RemoveNotesDeleteCascadeTimelogs < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ CONSTRAINT_NAME = 'fk_timelogs_note_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :timelogs, :notes, column: :note_id, on_delete: :nullify, name: CONSTRAINT_NAME
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :timelogs, :notes, column: :note_id, on_delete: :cascade
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key :timelogs, :notes, column: :note_id, on_delete: :cascade
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :timelogs, :notes, column: :note_id, on_delete: :nullify, name: CONSTRAINT_NAME
+ end
+ end
+end
diff --git a/db/schema_migrations/20210403022952 b/db/schema_migrations/20210403022952
new file mode 100644
index 00000000000..581bc5f89c2
--- /dev/null
+++ b/db/schema_migrations/20210403022952
@@ -0,0 +1 @@
+c62c2e13bdad42cc1f112f9854fe8d25e1e2aa082cb28341a661c93b8587f1f8 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index db463645810..ad34d16bbda 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -25028,9 +25028,6 @@ ALTER TABLE ONLY issues
ALTER TABLE ONLY protected_branch_merge_access_levels
ADD CONSTRAINT fk_8a3072ccb3 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
-ALTER TABLE ONLY timelogs
- ADD CONSTRAINT fk_8d058cd571 FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY releases
ADD CONSTRAINT fk_8e4456f90f FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
@@ -26885,6 +26882,9 @@ ALTER TABLE ONLY timelogs
ALTER TABLE ONLY timelogs
ADD CONSTRAINT fk_timelogs_merge_requests_merge_request_id FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+ALTER TABLE ONLY timelogs
+ ADD CONSTRAINT fk_timelogs_note_id FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY u2f_registrations
ADD CONSTRAINT fk_u2f_registrations_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md
index 73a36f5e674..a83a1c22db6 100644
--- a/doc/administration/geo/replication/faq.md
+++ b/doc/administration/geo/replication/faq.md
@@ -13,61 +13,61 @@ The requirements are listed [on the index page](../index.md#requirements-for-run
## How does Geo know which projects to sync?
-On each **secondary** node, there is a read-only replicated copy of the GitLab database.
-A **secondary** node also has a tracking database where it stores which projects have been synced.
+On each **secondary** site, there is a read-only replicated copy of the GitLab database.
+A **secondary** site also has a tracking database where it stores which projects have been synced.
Geo compares the two databases to find projects that are not yet tracked.
At the start, this tracking database is empty, so Geo will start trying to update from every project that it can see in the GitLab database.
For each project to sync:
-1. Geo will issue a `git fetch geo --mirror` to get the latest information from the **primary** node.
+1. Geo will issue a `git fetch geo --mirror` to get the latest information from the **primary** site.
If there are no changes, the sync will be fast and end quickly. Otherwise, it will pull the latest commits.
-1. The **secondary** node will update the tracking database to store the fact that it has synced projects A, B, C, etc.
+1. The **secondary** site will update the tracking database to store the fact that it has synced projects A, B, C, etc.
1. Repeat until all projects are synced.
-When someone pushes a commit to the **primary** node, it generates an event in the GitLab database that the repository has changed.
-The **secondary** node sees this event, marks the project in question as dirty, and schedules the project to be resynced.
+When someone pushes a commit to the **primary** site, it generates an event in the GitLab database that the repository has changed.
+The **secondary** site sees this event, marks the project in question as dirty, and schedules the project to be resynced.
To ensure that problems with pipelines (for example, syncs failing too many times or jobs being lost) don't permanently stop projects syncing, Geo also periodically checks the tracking database for projects that are marked as dirty. This check happens when
the number of concurrent syncs falls below `repos_max_capacity` and there are no new projects waiting to be synced.
Geo also has a checksum feature which runs a SHA256 sum across all the Git references to the SHA values.
-If the refs don't match between the **primary** node and the **secondary** node, then the **secondary** node will mark that project as dirty and try to resync it.
+If the refs don't match between the **primary** site and the **secondary** site, then the **secondary** site will mark that project as dirty and try to resync it.
So even if we have an outdated tracking database, the validation should activate and find discrepancies in the repository state and resync.
## Can I use Geo in a disaster recovery situation?
Yes, but there are limitations to what we replicate (see
-[What data is replicated to a **secondary** node?](#what-data-is-replicated-to-a-secondary-node)).
+[What data is replicated to a **secondary** site?](#what-data-is-replicated-to-a-secondary-site)).
Read the documentation for [Disaster Recovery](../disaster_recovery/index.md).
-## What data is replicated to a **secondary** node?
+## What data is replicated to a **secondary** site?
We currently replicate project repositories, LFS objects, generated
attachments / avatars and the whole database. This means user accounts,
issues, merge requests, groups, project data, etc., will be available for
query.
-## Can I `git push` to a **secondary** node?
+## Can I `git push` to a **secondary** site?
-Yes! Pushing directly to a **secondary** node (for both HTTP and SSH, including Git LFS) was [introduced](https://about.gitlab.com/releases/2018/09/22/gitlab-11-3-released/) in [GitLab Premium](https://about.gitlab.com/pricing/#self-managed) 11.3.
+Yes! Pushing directly to a **secondary** site (for both HTTP and SSH, including Git LFS) was [introduced](https://about.gitlab.com/releases/2018/09/22/gitlab-11-3-released/) in [GitLab Premium](https://about.gitlab.com/pricing/#self-managed) 11.3.
-## How long does it take to have a commit replicated to a **secondary** node?
+## How long does it take to have a commit replicated to a **secondary** site?
All replication operations are asynchronous and are queued to be dispatched. Therefore, it depends on a lot of
factors including the amount of traffic, how big your commit is, the
-connectivity between your nodes, your hardware, etc.
+connectivity between your sites, your hardware, etc.
## What if the SSH server runs at a different port?
-That's totally fine. We use HTTP(s) to fetch repository changes from the **primary** node to all **secondary** nodes.
+That's totally fine. We use HTTP(s) to fetch repository changes from the **primary** site to all **secondary** sites.
-## Is this possible to set up a Docker Registry for a **secondary** node that mirrors the one on the **primary** node?
+## Is this possible to set up a Docker Registry for a **secondary** site that mirrors the one on the **primary** site?
-Yes. See [Docker Registry for a **secondary** node](docker_registry.md).
+Yes. See [Docker Registry for a **secondary** site](docker_registry.md).
-## Can I login to a secondary node?
+## Can I login to a secondary site?
-Yes, but secondary nodes receive all authentication data (like user accounts and logins) from the primary instance. This means you will be re-directed to the primary for authentication and routed back afterwards.
+Yes, but secondary sites receive all authentication data (like user accounts and logins) from the primary instance. This means you will be re-directed to the primary for authentication and routed back afterwards.
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 6c01b2cf2a6..728d5149e73 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -756,7 +756,7 @@ Parameters:
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (Maintainers), or `developer` (Developers + Maintainers). |
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
-| `subgroup_creation_level` | string | no | Allowed to create subgroups. Can be `owner` (Owners), or `maintainer` (Maintainers). |
+| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#creating-a-subgroup). Can be `owner` (Owners), or `maintainer` (Maintainers). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
@@ -830,7 +830,7 @@ PUT /groups/:id
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (Maintainers), or `developer` (Developers + Maintainers). |
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
-| `subgroup_creation_level` | string | no | Allowed to create subgroups. Can be `owner` (Owners), or `maintainer` (Maintainers). |
+| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#creating-a-subgroup). Can be `owner` (Owners), or `maintainer` (Maintainers). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
diff --git a/lib/declarative_policy.rb b/lib/declarative_policy.rb
deleted file mode 100644
index bd1c121fe79..00000000000
--- a/lib/declarative_policy.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-
-require_dependency 'declarative_policy/cache'
-require_dependency 'declarative_policy/condition'
-require_dependency 'declarative_policy/delegate_dsl'
-require_dependency 'declarative_policy/policy_dsl'
-require_dependency 'declarative_policy/rule_dsl'
-require_dependency 'declarative_policy/preferred_scope'
-require_dependency 'declarative_policy/rule'
-require_dependency 'declarative_policy/runner'
-require_dependency 'declarative_policy/step'
-
-require_dependency 'declarative_policy/base'
-
-module DeclarativePolicy
- extend PreferredScope
-
- CLASS_CACHE_MUTEX = Mutex.new
- CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
-
- class << self
- def policy_for(user, subject, opts = {})
- cache = opts[:cache] || {}
- key = Cache.policy_key(user, subject)
-
- cache[key] ||=
- # to avoid deadlocks in multi-threaded environment when
- # autoloading is enabled, we allow concurrent loads,
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/48263
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
- class_for(subject).new(user, subject, opts)
- end
- end
-
- def class_for(subject)
- return GlobalPolicy if subject == :global
- return NilPolicy if subject.nil?
-
- subject = find_delegate(subject)
-
- policy_class = class_for_class(subject.class)
- raise "no policy for #{subject.class.name}" if policy_class.nil?
-
- policy_class
- end
-
- def has_policy?(subject)
- !class_for_class(subject.class).nil?
- end
-
- private
-
- # This method is heavily cached because there are a lot of anonymous
- # modules in play in a typical rails app, and #name performs quite
- # slowly for anonymous classes and modules.
- #
- # See https://bugs.ruby-lang.org/issues/11119
- #
- # if the above bug is resolved, this caching could likely be removed.
- def class_for_class(subject_class)
- unless subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
- CLASS_CACHE_MUTEX.synchronize do
- # re-check in case of a race
- break if subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
-
- policy_class = compute_class_for_class(subject_class)
- subject_class.instance_variable_set(CLASS_CACHE_IVAR, policy_class)
- end
- end
-
- subject_class.instance_variable_get(CLASS_CACHE_IVAR)
- end
-
- def compute_class_for_class(subject_class)
- if subject_class.respond_to?(:declarative_policy_class)
- return subject_class.declarative_policy_class.constantize
- end
-
- subject_class.ancestors.each do |klass|
- name = klass.name
-
- next unless name
-
- begin
- policy_class = "#{name}Policy".constantize
-
- # NOTE: the < operator here tests whether policy_class
- # inherits from Base. We can't use #is_a? because that
- # tests for *instances*, not *subclasses*.
- return policy_class if policy_class < Base
- rescue NameError
- nil
- end
- end
-
- nil
- end
-
- def find_delegate(subject)
- seen = Set.new
-
- while subject.respond_to?(:declarative_policy_delegate)
- raise ArgumentError, "circular delegations" if seen.include?(subject.object_id)
-
- seen << subject.object_id
- subject = subject.declarative_policy_delegate
- end
-
- subject
- end
- end
-end
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
deleted file mode 100644
index 49cbdd2aeb4..00000000000
--- a/lib/declarative_policy/base.rb
+++ /dev/null
@@ -1,354 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- class Base
- # A map of ability => list of rules together with :enable
- # or :prevent actions. Used to look up which rules apply to
- # a given ability. See Base.ability_map
- class AbilityMap
- attr_reader :map
- def initialize(map = {})
- @map = map
- end
-
- # This merge behavior is different than regular hashes - if both
- # share a key, the values at that key are concatenated, rather than
- # overridden.
- def merge(other)
- conflict_proc = proc { |key, my_val, other_val| my_val + other_val }
- AbilityMap.new(@map.merge(other.map, &conflict_proc))
- end
-
- def actions(key)
- @map[key] ||= []
- end
-
- def enable(key, rule)
- actions(key) << [:enable, rule]
- end
-
- def prevent(key, rule)
- actions(key) << [:prevent, rule]
- end
- end
-
- class << self
- # The `own_ability_map` vs `ability_map` distinction is used so that
- # the data structure is properly inherited - with subclasses recursively
- # merging their parent class.
- #
- # This pattern is also used for conditions, global_actions, and delegations.
- def ability_map
- if self == Base
- own_ability_map
- else
- superclass.ability_map.merge(own_ability_map)
- end
- end
-
- def own_ability_map
- @own_ability_map ||= AbilityMap.new
- end
-
- # an inheritable map of conditions, by name
- def conditions
- if self == Base
- own_conditions
- else
- superclass.conditions.merge(own_conditions)
- end
- end
-
- def own_conditions
- @own_conditions ||= {}
- end
-
- # a list of global actions, generated by `prevent_all`. these aren't
- # stored in `ability_map` because they aren't indexed by a particular
- # ability.
- def global_actions
- if self == Base
- own_global_actions
- else
- superclass.global_actions + own_global_actions
- end
- end
-
- def own_global_actions
- @own_global_actions ||= []
- end
-
- # an inheritable map of delegations, indexed by name (which may be
- # autogenerated)
- def delegations
- if self == Base
- own_delegations
- else
- superclass.delegations.merge(own_delegations)
- end
- end
-
- def own_delegations
- @own_delegations ||= {}
- end
-
- # all the [rule, action] pairs that apply to a particular ability.
- # we combine the specific ones looked up in ability_map with the global
- # ones.
- def configuration_for(ability)
- ability_map.actions(ability) + global_actions
- end
-
- ### declaration methods ###
-
- def delegate(name = nil, &delegation_block)
- if name.nil?
- @delegate_name_counter ||= 0
- @delegate_name_counter += 1
- name = :"anonymous_#{@delegate_name_counter}"
- end
-
- name = name.to_sym
-
- if delegation_block.nil?
- delegation_block = proc { @subject.__send__(name) } # rubocop:disable GitlabSecurity/PublicSend
- end
-
- own_delegations[name] = delegation_block
- end
-
- # Declare that the given abilities should not be read from delegates.
- #
- # This is useful if you have an ability that you want to define
- # differently in a policy than in a delegated policy, but still want to
- # delegate all other abilities.
- #
- # example:
- #
- # delegate { @subect.parent }
- #
- # overrides :drive_car, :watch_tv
- #
- def overrides(*names)
- @overrides ||= [].to_set
- @overrides.merge(names)
- end
-
- # Declares a rule, constructed using RuleDsl, and returns
- # a PolicyDsl which is used for registering the rule with
- # this class. PolicyDsl will call back into Base.enable_when,
- # Base.prevent_when, and Base.prevent_all_when.
- def rule(&block)
- rule = RuleDsl.new(self).instance_eval(&block)
- PolicyDsl.new(self, rule)
- end
-
- # A hash in which to store calls to `desc` and `with_scope`, etc.
- def last_options
- @last_options ||= {}.with_indifferent_access
- end
-
- # retrieve and zero out the previously set options (used in .condition)
- def last_options!
- last_options.tap { @last_options = nil }
- end
-
- # Declare a description for the following condition. Currently unused,
- # but opens the potential for explaining to users why they were or were
- # not able to do something.
- def desc(description)
- last_options[:description] = description
- end
-
- def with_options(opts = {})
- last_options.merge!(opts)
- end
-
- def with_scope(scope)
- with_options scope: scope
- end
-
- def with_score(score)
- with_options score: score
- end
-
- # Declares a condition. It gets stored in `own_conditions`, and generates
- # a query method based on the condition's name.
- def condition(name, opts = {}, &value)
- name = name.to_sym
-
- opts = last_options!.merge(opts)
- opts[:context_key] ||= self.name
-
- condition = Condition.new(name, opts, &value)
-
- self.own_conditions[name] = condition
-
- define_method(:"#{name}?") { condition(name).pass? }
- end
-
- # These next three methods are mainly called from PolicyDsl,
- # and are responsible for "inverting" the relationship between
- # an ability and a rule. We store in `ability_map` a map of
- # abilities to rules that affect them, together with a
- # symbol indicating :prevent or :enable.
- def enable_when(abilities, rule)
- abilities.each { |a| own_ability_map.enable(a, rule) }
- end
-
- def prevent_when(abilities, rule)
- abilities.each { |a| own_ability_map.prevent(a, rule) }
- end
-
- # we store global prevents (from `prevent_all`) separately,
- # so that they can be combined into every decision made.
- def prevent_all_when(rule)
- own_global_actions << [:prevent, rule]
- end
- end
-
- # A policy object contains a specific user and subject on which
- # to compute abilities. For this reason it's sometimes called
- # "context" within the framework.
- #
- # It also stores a reference to the cache, so it can be used
- # to cache computations by e.g. ManifestCondition.
- attr_reader :user, :subject
- def initialize(user, subject, opts = {})
- @user = user
- @subject = subject
- @cache = opts[:cache] || {}
- end
-
- # helper for checking abilities on this and other subjects
- # for the current user.
- def can?(ability, new_subject = :_self)
- return allowed?(ability) if new_subject == :_self
-
- policy_for(new_subject).allowed?(ability)
- end
-
- # This is the main entry point for permission checks. It constructs
- # or looks up a Runner for the given ability and asks it if it passes.
- def allowed?(*abilities)
- abilities.all? { |a| runner(a).pass? }
- end
-
- # The inverse of #allowed?, used mainly in specs.
- def disallowed?(*abilities)
- abilities.all? { |a| !runner(a).pass? }
- end
-
- # computes the given ability and prints a helpful debugging output
- # showing which
- def debug(ability, *args)
- runner(ability).debug(*args)
- end
-
- desc "Unknown user"
- condition(:anonymous, scope: :user, score: 0) { @user.nil? }
-
- desc "By default"
- condition(:default, scope: :global, score: 0) { true }
-
- def repr
- subject_repr =
- if @subject.respond_to?(:id)
- "#{@subject.class.name}/#{@subject.id}"
- else
- @subject.inspect
- end
-
- user_repr =
- if @user
- @user.to_reference
- else
- "<anonymous>"
- end
-
- "(#{user_repr} : #{subject_repr})"
- end
-
- def inspect
- "#<#{self.class.name} #{repr}>"
- end
-
- # returns a Runner for the given ability, capable of computing whether
- # the ability is allowed. Runners are cached on the policy (which itself
- # is cached on @cache), and caches its result. This is how we perform caching
- # at the ability level.
- def runner(ability)
- ability = ability.to_sym
- @runners ||= {}
- @runners[ability] ||=
- begin
- own_runner = Runner.new(own_steps(ability))
- if self.class.overrides.include?(ability)
- own_runner
- else
- delegated_runners = delegated_policies.values.compact.map { |p| p.runner(ability) }
- delegated_runners.inject(own_runner, &:merge_runner)
- end
- end
- end
-
- # Helpers for caching. Used by ManifestCondition in performing condition
- # computation.
- #
- # NOTE we can't use ||= here because the value might be the
- # boolean `false`
- def cache(key)
- return @cache[key] if cached?(key)
-
- @cache[key] = yield
- end
-
- def cached?(key)
- !@cache[key].nil?
- end
-
- # returns a ManifestCondition capable of computing itself. The computation
- # will use our own @cache.
- def condition(name)
- name = name.to_sym
- @_conditions ||= {}
- @_conditions[name] ||=
- begin
- raise "invalid condition #{name}" unless self.class.conditions.key?(name)
-
- ManifestCondition.new(self.class.conditions[name], self)
- end
- end
-
- # used in specs - returns true if there is no possible way for any action
- # to be allowed, determined only by the global :prevent_all rules.
- def banned?
- global_steps = self.class.global_actions.map { |(action, rule)| Step.new(self, rule, action) }
- !Runner.new(global_steps).pass?
- end
-
- # A list of other policies that we've delegated to (see `Base.delegate`)
- def delegated_policies
- @delegated_policies ||= self.class.delegations.transform_values do |block|
- new_subject = instance_eval(&block)
-
- # never delegate to nil, as that would immediately prevent_all
- next if new_subject.nil?
-
- policy_for(new_subject)
- end
- end
-
- def policy_for(other_subject)
- DeclarativePolicy.policy_for(@user, other_subject, cache: @cache)
- end
-
- protected
-
- # constructs steps that come from this policy and not from any delegations
- def own_steps(ability)
- rules = self.class.configuration_for(ability)
- rules.map { |(action, rule)| Step.new(self, rule, action) }
- end
- end
-end
diff --git a/lib/declarative_policy/cache.rb b/lib/declarative_policy/cache.rb
deleted file mode 100644
index 13006e56454..00000000000
--- a/lib/declarative_policy/cache.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- module Cache
- class << self
- def user_key(user)
- return '<anonymous>' if user.nil?
-
- id_for(user)
- end
-
- def policy_key(user, subject)
- u = user_key(user)
- s = subject_key(subject)
- "/dp/policy/#{u}/#{s}"
- end
-
- def subject_key(subject)
- return '<nil>' if subject.nil?
- return subject.inspect if subject.is_a?(Symbol)
-
- "#{subject.class.name}:#{id_for(subject)}"
- end
-
- private
-
- def id_for(obj)
- id =
- begin
- obj.id
- rescue NoMethodError
- nil
- end
-
- id || "##{obj.object_id}"
- end
- end
- end
-end
diff --git a/lib/declarative_policy/condition.rb b/lib/declarative_policy/condition.rb
deleted file mode 100644
index b77f40b1093..00000000000
--- a/lib/declarative_policy/condition.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- # A Condition is the data structure that is created by the
- # `condition` declaration on DeclarativePolicy::Base. It is
- # more or less just a struct of the data passed to that
- # declaration. It holds on to the block to be instance_eval'd
- # on a context (instance of Base) later, via #compute.
- class Condition
- attr_reader :name, :description, :scope
- attr_reader :manual_score
- attr_reader :context_key
- def initialize(name, opts = {}, &compute)
- @name = name
- @compute = compute
- @scope = opts.fetch(:scope, :normal)
- @description = opts.delete(:description)
- @context_key = opts[:context_key]
- @manual_score = opts.fetch(:score, nil)
- end
-
- def compute(context)
- !!context.instance_eval(&@compute)
- end
-
- def key
- "#{@context_key}/#{@name}"
- end
- end
-
- # In contrast to a Condition, a ManifestCondition contains
- # a Condition and a context object, and is capable of calculating
- # a result itself. This is the return value of Base#condition.
- class ManifestCondition
- def initialize(condition, context)
- @condition = condition
- @context = context
- end
-
- # The main entry point - does this condition pass? We reach into
- # the context's cache here so that we can share in the global
- # cache (often RequestStore or similar).
- def pass?
- @context.cache(cache_key) { @condition.compute(@context) }
- end
-
- # Whether we've already computed this condition.
- def cached?
- @context.cached?(cache_key)
- end
-
- # This is used to score Rule::Condition. See Rule::Condition#score
- # and Runner#steps_by_score for how scores are used.
- #
- # The number here is intended to represent, abstractly, how
- # expensive it would be to calculate this condition.
- #
- # See #cache_key for info about @condition.scope.
- def score
- # If we've been cached, no computation is necessary.
- return 0 if cached?
-
- # Use the override from condition(score: ...) if present
- return @condition.manual_score if @condition.manual_score
-
- # Global scope rules are cheap due to max cache sharing
- return 2 if @condition.scope == :global
-
- # "Normal" rules can't share caches with any other policies
- return 16 if @condition.scope == :normal
-
- # otherwise, we're :user or :subject scope, so it's 4 if
- # the caller has declared a preference
- return 4 if @condition.scope == DeclarativePolicy.preferred_scope
-
- # and 8 for all other :user or :subject scope conditions.
- 8
- end
-
- private
-
- # This method controls the caching for the condition. This is where
- # the condition(scope: ...) option comes into play. Notice that
- # depending on the scope, we may cache only by the user or only by
- # the subject, resulting in sharing across different policy objects.
- def cache_key
- @cache_key ||=
- case @condition.scope
- when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
- when :user then "/dp/condition/#{@condition.key}/#{user_key}"
- when :subject then "/dp/condition/#{@condition.key}/#{subject_key}"
- when :global then "/dp/condition/#{@condition.key}"
- else raise 'invalid scope'
- end
- end
-
- def user_key
- Cache.user_key(@context.user)
- end
-
- def subject_key
- Cache.subject_key(@context.subject)
- end
- end
-end
diff --git a/lib/declarative_policy/delegate_dsl.rb b/lib/declarative_policy/delegate_dsl.rb
deleted file mode 100644
index 67e3429b696..00000000000
--- a/lib/declarative_policy/delegate_dsl.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- # Used when the name of a delegate is mentioned in
- # the rule DSL.
- class DelegateDsl
- def initialize(rule_dsl, delegate_name)
- @rule_dsl = rule_dsl
- @delegate_name = delegate_name
- end
-
- def method_missing(msg, *args)
- return super unless args.empty? && !block_given?
-
- @rule_dsl.delegate(@delegate_name, msg)
- end
- end
-end
diff --git a/lib/declarative_policy/policy_dsl.rb b/lib/declarative_policy/policy_dsl.rb
deleted file mode 100644
index 69a2bbcc79e..00000000000
--- a/lib/declarative_policy/policy_dsl.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- # The return value of a rule { ... } declaration.
- # Can call back to register rules with the containing
- # Policy class (context_class here). See Base.rule
- #
- # Note that the #policy method just performs an #instance_eval,
- # which is useful for multiple #enable or #prevent calls.
- #
- # Also provides a #method_missing proxy to the context
- # class's class methods, so that helper methods can be
- # defined and used in a #policy { ... } block.
- class PolicyDsl
- def initialize(context_class, rule)
- @context_class = context_class
- @rule = rule
- end
-
- def policy(&block)
- instance_eval(&block)
- end
-
- def enable(*abilities)
- @context_class.enable_when(abilities, @rule)
- end
-
- def prevent(*abilities)
- @context_class.prevent_when(abilities, @rule)
- end
-
- def prevent_all
- @context_class.prevent_all_when(@rule)
- end
-
- def method_missing(msg, *args, &block)
- return super unless @context_class.respond_to?(msg)
-
- @context_class.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
- end
-
- def respond_to_missing?(msg)
- @context_class.respond_to?(msg) || super
- end
- end
-end
diff --git a/lib/declarative_policy/preferred_scope.rb b/lib/declarative_policy/preferred_scope.rb
deleted file mode 100644
index 9e512086593..00000000000
--- a/lib/declarative_policy/preferred_scope.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- module PreferredScope
- PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
-
- def with_preferred_scope(scope)
- old_scope = Thread.current[PREFERRED_SCOPE_KEY]
- Thread.current[PREFERRED_SCOPE_KEY] = scope
- yield
- ensure
- Thread.current[PREFERRED_SCOPE_KEY] = old_scope
- end
-
- def preferred_scope
- Thread.current[PREFERRED_SCOPE_KEY]
- end
-
- def user_scope(&block)
- with_preferred_scope(:user, &block)
- end
-
- def subject_scope(&block)
- with_preferred_scope(:subject, &block)
- end
-
- def preferred_scope=(scope)
- Thread.current[PREFERRED_SCOPE_KEY] = scope
- end
- end
-end
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
deleted file mode 100644
index 964d35cde9e..00000000000
--- a/lib/declarative_policy/rule.rb
+++ /dev/null
@@ -1,312 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- module Rule
- # A Rule is the object that results from the `rule` declaration,
- # usually built using the DSL in `RuleDsl`. It is a basic logical
- # combination of building blocks, and is capable of deciding,
- # given a context (instance of DeclarativePolicy::Base) whether it
- # passes or not. Note that this decision doesn't by itself know
- # how that affects the actual ability decision - for that, a
- # `Step` is used.
- class Base
- def self.make(*args)
- new(*args).simplify
- end
-
- # true or false whether this rule passes.
- # `context` is a policy - an instance of
- # DeclarativePolicy::Base.
- def pass?(context)
- raise 'abstract'
- end
-
- # same as #pass? except refuses to do any I/O,
- # returning nil if the result is not yet cached.
- # used for accurately scoring And/Or
- def cached_pass?(context)
- raise 'abstract'
- end
-
- # abstractly, how long would it take to compute
- # this rule? lower-scored rules are tried first.
- def score(context)
- raise 'abstract'
- end
-
- # unwrap double negatives and nested and/or
- def simplify
- self
- end
-
- # convenience combination methods
- def or(other)
- Or.make([self, other])
- end
-
- def and(other)
- And.make([self, other])
- end
-
- def negate
- Not.make(self)
- end
-
- alias_method :|, :or
- alias_method :&, :and
- alias_method :~@, :negate
-
- def inspect
- "#<Rule #{repr}>"
- end
- end
-
- # A rule that checks a condition. This is the
- # type of rule that results from a basic bareword
- # in the rule dsl (see RuleDsl#method_missing).
- class Condition < Base
- def initialize(name)
- @name = name
- end
-
- # we delegate scoring to the condition. See
- # ManifestCondition#score.
- def score(context)
- context.condition(@name).score
- end
-
- # Let the ManifestCondition from the context
- # decide whether we pass.
- def pass?(context)
- context.condition(@name).pass?
- end
-
- # returns nil unless it's already cached
- def cached_pass?(context)
- condition = context.condition(@name)
- return unless condition.cached?
-
- condition.pass?
- end
-
- def description(context)
- context.class.conditions[@name].description
- end
-
- def repr
- @name.to_s
- end
- end
-
- # A rule constructed from DelegateDsl - using a condition from a
- # delegated policy.
- class DelegatedCondition < Base
- # Internal use only - this is rescued each time it's raised.
- MissingDelegate = Class.new(StandardError)
-
- def initialize(delegate_name, name)
- @delegate_name = delegate_name
- @name = name
- end
-
- def delegated_context(context)
- policy = context.delegated_policies[@delegate_name]
- raise MissingDelegate if policy.nil?
-
- policy
- end
-
- def score(context)
- delegated_context(context).condition(@name).score
- rescue MissingDelegate
- 0
- end
-
- def cached_pass?(context)
- condition = delegated_context(context).condition(@name)
- return unless condition.cached?
-
- condition.pass?
- rescue MissingDelegate
- false
- end
-
- def pass?(context)
- delegated_context(context).condition(@name).pass?
- rescue MissingDelegate
- false
- end
-
- def repr
- "#{@delegate_name}.#{@name}"
- end
- end
-
- # A rule constructed from RuleDsl#can?. Computes a different ability
- # on the same subject.
- class Ability < Base
- attr_reader :ability
- def initialize(ability)
- @ability = ability
- end
-
- # We ask the ability's runner for a score
- def score(context)
- context.runner(@ability).score
- end
-
- def pass?(context)
- context.allowed?(@ability)
- end
-
- def cached_pass?(context)
- runner = context.runner(@ability)
- return unless runner.cached?
-
- runner.pass?
- end
-
- def description(context)
- "User can #{@ability.inspect}"
- end
-
- def repr
- "can?(#{@ability.inspect})"
- end
- end
-
- # Logical `and`, containing a list of rules. Only passes
- # if all of them do.
- class And < Base
- attr_reader :rules
- def initialize(rules)
- @rules = rules
- end
-
- def simplify
- simplified_rules = @rules.flat_map do |rule|
- simplified = rule.simplify
- case simplified
- when And then simplified.rules
- else [simplified]
- end
- end
-
- And.new(simplified_rules)
- end
-
- def score(context)
- return 0 unless cached_pass?(context).nil?
-
- # note that cached rules will have score 0 anyways.
- @rules.map { |r| r.score(context) }.inject(0, :+)
- end
-
- def pass?(context)
- # try to find a cached answer before
- # checking in order
- cached = cached_pass?(context)
- return cached unless cached.nil?
-
- @rules.all? { |r| r.pass?(context) }
- end
-
- def cached_pass?(context)
- @rules.each do |rule|
- pass = rule.cached_pass?(context)
-
- return pass if pass.nil? || pass == false
- end
-
- true
- end
-
- def repr
- "all?(#{rules.map(&:repr).join(', ')})"
- end
- end
-
- # Logical `or`. Mirrors And.
- class Or < Base
- attr_reader :rules
- def initialize(rules)
- @rules = rules
- end
-
- def pass?(context)
- cached = cached_pass?(context)
- return cached unless cached.nil?
-
- @rules.any? { |r| r.pass?(context) }
- end
-
- def simplify
- simplified_rules = @rules.flat_map do |rule|
- simplified = rule.simplify
- case simplified
- when Or then simplified.rules
- else [simplified]
- end
- end
-
- Or.new(simplified_rules)
- end
-
- def cached_pass?(context)
- @rules.each do |rule|
- pass = rule.cached_pass?(context)
-
- return pass if pass.nil? || pass == true
- end
-
- false
- end
-
- def score(context)
- return 0 unless cached_pass?(context).nil?
-
- @rules.map { |r| r.score(context) }.inject(0, :+)
- end
-
- def repr
- "any?(#{@rules.map(&:repr).join(', ')})"
- end
- end
-
- class Not < Base
- attr_reader :rule
- def initialize(rule)
- @rule = rule
- end
-
- def simplify
- case @rule
- when And then Or.new(@rule.rules.map(&:negate)).simplify
- when Or then And.new(@rule.rules.map(&:negate)).simplify
- when Not then @rule.rule.simplify
- else Not.new(@rule.simplify)
- end
- end
-
- def pass?(context)
- !@rule.pass?(context)
- end
-
- def cached_pass?(context)
- case @rule.cached_pass?(context)
- when nil then nil
- when true then false
- when false then true
- end
- end
-
- def score(context)
- @rule.score(context)
- end
-
- def repr
- "~#{@rule.repr}"
- end
- end
- end
-end
diff --git a/lib/declarative_policy/rule_dsl.rb b/lib/declarative_policy/rule_dsl.rb
deleted file mode 100644
index 85da7f261fa..00000000000
--- a/lib/declarative_policy/rule_dsl.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- # The DSL evaluation context inside rule { ... } blocks.
- # Responsible for creating and combining Rule objects.
- #
- # See Base.rule
- class RuleDsl
- def initialize(context_class)
- @context_class = context_class
- end
-
- def can?(ability)
- Rule::Ability.new(ability)
- end
-
- def all?(*rules)
- Rule::And.make(rules)
- end
-
- def any?(*rules)
- Rule::Or.make(rules)
- end
-
- def none?(*rules)
- ~Rule::Or.new(rules)
- end
-
- def cond(condition)
- Rule::Condition.new(condition)
- end
-
- def delegate(delegate_name, condition)
- Rule::DelegatedCondition.new(delegate_name, condition)
- end
-
- def method_missing(msg, *args)
- return super unless args.empty? && !block_given?
-
- if @context_class.delegations.key?(msg)
- DelegateDsl.new(self, msg)
- else
- cond(msg.to_sym)
- end
- end
- end
-end
diff --git a/lib/declarative_policy/runner.rb b/lib/declarative_policy/runner.rb
deleted file mode 100644
index 59588b4d84e..00000000000
--- a/lib/declarative_policy/runner.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- class Runner
- class State
- def initialize
- @enabled = false
- @prevented = false
- end
-
- def enable!
- @enabled = true
- end
-
- def enabled?
- @enabled
- end
-
- def prevent!
- @prevented = true
- end
-
- def prevented?
- @prevented
- end
-
- def pass?
- !prevented? && enabled?
- end
- end
-
- # a Runner contains a list of Steps to be run.
- attr_reader :steps
- def initialize(steps)
- @steps = steps
- @state = nil
- end
-
- # We make sure only to run any given Runner once,
- # and just continue to use the resulting @state
- # that's left behind.
- def cached?
- !!@state
- end
-
- # used by Rule::Ability. See #steps_by_score
- def score
- return 0 if cached?
-
- steps.map(&:score).inject(0, :+)
- end
-
- def merge_runner(other)
- Runner.new(@steps + other.steps)
- end
-
- # The main entry point, called for making an ability decision.
- # See #run and DeclarativePolicy::Base#can?
- def pass?
- run unless cached?
-
- @state.pass?
- end
-
- # see DeclarativePolicy::Base#debug
- def debug(out = $stderr)
- run(out)
- end
-
- private
-
- def flatten_steps!
- @steps = @steps.flat_map { |s| s.flattened(@steps) }
- end
-
- # This method implements the semantic of "one enable and no prevents".
- # It relies on #steps_by_score for the main loop, and updates @state
- # with the result of the step.
- def run(debug = nil)
- @state = State.new
-
- steps_by_score do |step, score|
- break if !debug && @state.prevented?
-
- passed = nil
- case step.action
- when :enable then
- # we only check :enable actions if they have a chance of
- # changing the outcome - if no other rule has enabled or
- # prevented.
- unless @state.enabled? || @state.prevented?
- passed = step.pass?
- @state.enable! if passed
- end
-
- debug << inspect_step(step, score, passed) if debug
- when :prevent then
- # we only check :prevent actions if the state hasn't already
- # been prevented.
- unless @state.prevented?
- passed = step.pass?
- @state.prevent! if passed
- end
-
- debug << inspect_step(step, score, passed) if debug
- else raise "invalid action #{step.action.inspect}"
- end
- end
-
- @state
- end
-
- # This is the core spot where all those `#score` methods matter.
- # It is critical for performance to run steps in the correct order,
- # so that we don't compute expensive conditions (potentially n times
- # if we're called on, say, a large list of users).
- #
- # In order to determine the cheapest step to run next, we rely on
- # Step#score, which returns a numerical rating of how expensive
- # it would be to calculate - the lower the better. It would be
- # easy enough to statically sort by these scores, but we can do
- # a little better - the scores are cache-aware (conditions that
- # are already in the cache have score 0), which means that running
- # a step can actually change the scores of other steps.
- #
- # So! The way we sort here involves re-scoring at every step. This
- # is by necessity quadratic, but most of the time the number of steps
- # will be low. But just in case, if the number of steps exceeds 50,
- # we print a warning and fall back to a static sort.
- #
- # For each step, we yield the step object along with the computed score
- # for debugging purposes.
- def steps_by_score
- flatten_steps!
-
- if @steps.size > 50
- warn "DeclarativePolicy: large number of steps (#{steps.size}), falling back to static sort"
-
- @steps.map { |s| [s.score, s] }.sort_by { |(score, _)| score }.each do |(score, step)|
- yield step, score
- end
-
- return
- end
-
- remaining_steps = Set.new(@steps)
- remaining_enablers, remaining_preventers = remaining_steps.partition(&:enable?).map { |s| Set.new(s) }
-
- loop do
- if @state.enabled?
- # Once we set this, we never need to unset it, because a single
- # prevent will stop this from being enabled
- remaining_steps = remaining_preventers
- else
- # if the permission hasn't yet been enabled and we only have
- # prevent steps left, we short-circuit the state here
- @state.prevent! if remaining_enablers.empty?
- end
-
- return if remaining_steps.empty?
-
- lowest_score = Float::INFINITY
- next_step = nil
-
- remaining_steps.each do |step|
- score = step.score
-
- if score < lowest_score
- next_step = step
- lowest_score = score
- end
-
- break if lowest_score == 0
- end
-
- [remaining_steps, remaining_enablers, remaining_preventers].each do |set|
- set.delete(next_step)
- end
-
- yield next_step, lowest_score
- end
- end
-
- # Formatter for debugging output.
- def inspect_step(step, original_score, passed)
- symbol =
- case passed
- when true then '+'
- when false then '-'
- when nil then ' '
- end
-
- "#{symbol} [#{original_score.to_i}] #{step.repr}\n"
- end
- end
-end
diff --git a/lib/declarative_policy/step.rb b/lib/declarative_policy/step.rb
deleted file mode 100644
index c289c17cc19..00000000000
--- a/lib/declarative_policy/step.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-module DeclarativePolicy
- # This object represents one step in the runtime decision of whether
- # an ability is allowed. It contains a Rule and a context (instance
- # of DeclarativePolicy::Base), which contains the user, the subject,
- # and the cache. It also contains an "action", which is the symbol
- # :prevent or :enable.
- class Step
- attr_reader :context, :rule, :action
- def initialize(context, rule, action)
- @context = context
- @rule = rule
- @action = action
- end
-
- # In the flattening process, duplicate steps may be generated in the
- # same rule. This allows us to eliminate those (see Runner#steps_by_score
- # and note its use of a Set)
- def ==(other)
- @context == other.context && @rule == other.rule && @action == other.action
- end
-
- # In the runner, steps are sorted dynamically by score, so that
- # we are sure to compute them in close to the optimal order.
- #
- # See also Rule#score, ManifestCondition#score, and Runner#steps_by_score.
- def score
- # we slightly prefer the preventative actions
- # since they are more likely to short-circuit
- case @action
- when :prevent
- @rule.score(@context) * (7.0 / 8)
- when :enable
- @rule.score(@context)
- end
- end
-
- def with_action(action)
- Step.new(@context, @rule, action)
- end
-
- def enable?
- @action == :enable
- end
-
- def prevent?
- @action == :prevent
- end
-
- # This rather complex method allows us to split rules into parts so that
- # they can be sorted independently for better optimization
- def flattened(roots)
- case @rule
- when Rule::Or
- # A single `Or` step is the same as each of its elements as separate steps
- @rule.rules.flat_map { |r| Step.new(@context, r, @action).flattened(roots) }
- when Rule::Ability
- # This looks like a weird micro-optimization but it buys us quite a lot
- # in some cases. If we depend on an Ability (i.e. a `can?(...)` rule),
- # and that ability *only* has :enable actions (modulo some actions that
- # we already have taken care of), then its rules can be safely inlined.
- steps = @context.runner(@rule.ability).steps.reject { |s| roots.include?(s) }
-
- if steps.all?(&:enable?)
- # in the case that we are a :prevent step, each inlined step becomes
- # an independent :prevent, even though it was an :enable in its initial
- # context.
- steps.map! { |s| s.with_action(:prevent) } if prevent?
-
- steps.flat_map { |s| s.flattened(roots) }
- else
- [self]
- end
- else
- [self]
- end
- end
-
- def pass?
- @rule.pass?(@context)
- end
-
- def repr
- "#{@action} when #{@rule.repr} (#{@context.repr})"
- end
- end
-end
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index 8385bbbb3de..d86eb83083b 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -3,6 +3,8 @@
module Gitlab
module Diff
class Highlight
+ PREFIX_REGEXP = /\A(.)/.freeze
+
attr_reader :diff_file, :diff_lines, :repository, :project
delegate :old_path, :new_path, :old_sha, :new_sha, to: :diff_file, prefix: :diff
@@ -97,12 +99,12 @@ module Gitlab
rich_line = syntax_highlighter(diff_line).highlight(
diff_line.text(prefix: false),
context: { line_number: diff_line.line }
- )&.html_safe
+ )
# Only update text if line is found. This will prevent
# issues with submodules given the line only exists in diff content.
if rich_line
- line_prefix = diff_line.text =~ /\A(.)/ ? Regexp.last_match(1) : ' '
+ line_prefix = diff_line.text =~ PREFIX_REGEXP ? Regexp.last_match(1) : ' '
rich_line.prepend(line_prefix).concat("\n")
end
end
@@ -131,7 +133,7 @@ module Gitlab
# Only update text if line is found. This will prevent
# issues with submodules given the line only exists in diff content.
if rich_line
- line_prefix = diff_line.text =~ /\A(.)/ ? Regexp.last_match(1) : ' '
+ line_prefix = diff_line.text =~ PREFIX_REGEXP ? Regexp.last_match(1) : ' '
"#{line_prefix}#{rich_line}".html_safe
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d847d9176b8..7d5a8538317 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11498,6 +11498,9 @@ msgstr ""
msgid "Download %{name} artifact"
msgstr ""
+msgid "Download (%{size})"
+msgstr ""
+
msgid "Download CSV"
msgstr ""
@@ -16163,6 +16166,9 @@ msgstr ""
msgid "If you did not initiate this change, please contact your administrator immediately."
msgstr ""
+msgid "If you did not perform this request, you can safely ignore this email."
+msgstr ""
+
msgid "If you did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -22469,6 +22475,9 @@ msgstr ""
msgid "Other visibility settings have been disabled by the administrator."
msgstr ""
+msgid "Otherwise, click the link below to complete the process."
+msgstr ""
+
msgid "Our documentation includes an example DevOps Score report."
msgstr ""
@@ -26018,6 +26027,9 @@ msgstr ""
msgid "Read more about related issues"
msgstr ""
+msgid "Ready to get started with GitLab? Follow these steps to set up your workspace, plan and commit changes, and deploy your project."
+msgstr ""
+
msgid "Real-time features"
msgstr ""
@@ -29322,6 +29334,9 @@ msgstr ""
msgid "Someone edited this test case at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
+msgid "Someone, hopefully you, has requested to reset the password for your GitLab account on %{link_to_gitlab}."
+msgstr ""
+
msgid "Something went wrong"
msgstr ""
diff --git a/package.json b/package.json
index a1db0434eb1..d198ce4e525 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.189.0",
"@gitlab/tributejs": "1.0.0",
- "@gitlab/ui": "29.6.0",
+ "@gitlab/ui": "29.7.3",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",
diff --git a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
index 33c29cea6d8..0b86c10ea46 100644
--- a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
+++ b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap
@@ -7,6 +7,7 @@ exports[`~/access_tokens/components/expires_at_field should render datepicker wi
container=""
displayfield="true"
firstday="0"
+ inputlabel="Enter date"
mindate="Mon Jul 06 2020 00:00:00 GMT+0000 (Greenwich Mean Time)"
placeholder="YYYY-MM-DD"
theme=""
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
index 9912ac433a5..298596085ef 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
@@ -8,7 +8,6 @@ import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_for
import { typeSet } from '~/alerts_settings/constants';
import alertFields from '../mocks/alert_fields.json';
import parsedMapping from '../mocks/parsed_mapping.json';
-import { defaultAlertSettingsConfig } from './util';
const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
@@ -29,7 +28,6 @@ describe('AlertsSettingsForm', () => {
...props,
},
provide: {
- ...defaultAlertSettingsConfig,
multiIntegrations,
},
mocks: {
@@ -50,7 +48,6 @@ describe('AlertsSettingsForm', () => {
const findFormToggle = () => wrapper.findComponent(GlToggle);
const findSamplePayloadSection = () => wrapper.findByTestId('sample-payload-section');
const findMappingBuilder = () => wrapper.findComponent(MappingBuilder);
-
const findSubmitButton = () => wrapper.findByTestId('integration-form-submit');
const findMultiSupportText = () => wrapper.findByTestId('multi-integrations-not-supported');
const findJsonTestSubmit = () => wrapper.findByTestId('send-test-alert');
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index dd8ce838dfd..595c3f1a289 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -20,6 +20,7 @@ import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/re
import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql';
import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
+import getHttpIntegrationQuery from '~/alerts_settings/graphql/queries/get_http_integration.query.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import alertsUpdateService from '~/alerts_settings/services';
import {
@@ -47,7 +48,6 @@ import {
destroyIntegrationResponseWithErrors,
} from './mocks/apollo_mock';
import mockIntegrations from './mocks/integrations.json';
-import { defaultAlertSettingsConfig } from './util';
jest.mock('~/flash');
@@ -58,27 +58,12 @@ describe('AlertsSettingsWrapper', () => {
let fakeApollo;
let destroyIntegrationHandler;
useMockIntersectionObserver();
+
const httpMappingData = {
payloadExample: '{"test: : "field"}',
payloadAttributeMappings: [],
payloadAlertFields: [],
};
- const httpIntegrations = {
- list: [
- {
- id: mockIntegrations[0].id,
- ...httpMappingData,
- },
- {
- id: mockIntegrations[1].id,
- ...httpMappingData,
- },
- {
- id: mockIntegrations[2].id,
- httpMappingData,
- },
- ],
- };
const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon);
const findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
@@ -109,13 +94,14 @@ describe('AlertsSettingsWrapper', () => {
return { ...data };
},
provide: {
- ...defaultAlertSettingsConfig,
...provide,
},
mocks: {
$apollo: {
mutate: jest.fn(),
- query: jest.fn(),
+ addSmartQuery: jest.fn((_, options) => {
+ options.result.call(wrapper.vm);
+ }),
queries: {
integrations: {
loading,
@@ -143,9 +129,6 @@ describe('AlertsSettingsWrapper', () => {
wrapper = mount(AlertsSettingsWrapper, {
localVue,
apolloProvider: fakeApollo,
- provide: {
- ...defaultAlertSettingsConfig,
- },
});
}
@@ -158,17 +141,29 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => {
createComponent({
data: {
- integrations: { list: mockIntegrations },
- httpIntegrations: { list: [] },
+ integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
},
loading: false,
});
});
- it('renders alerts integrations list and add new integration button by default', () => {
+ it('renders alerts integrations list', () => {
expect(findLoader().exists()).toBe(false);
expect(findIntegrations()).toHaveLength(mockIntegrations.length);
+ });
+
+ it('renders `Add new integration` button when multiple integrations are supported ', () => {
+ createComponent({
+ data: {
+ integrations: mockIntegrations,
+ currentIntegration: mockIntegrations[0],
+ },
+ provide: {
+ multiIntegrations: true,
+ },
+ loading: false,
+ });
expect(findAddIntegrationBtn().exists()).toBe(true);
});
@@ -177,6 +172,16 @@ describe('AlertsSettingsWrapper', () => {
});
it('hides `add new integration` button and displays setting form on btn click', async () => {
+ createComponent({
+ data: {
+ integrations: mockIntegrations,
+ currentIntegration: mockIntegrations[0],
+ },
+ provide: {
+ multiIntegrations: true,
+ },
+ loading: false,
+ });
const addNewIntegrationBtn = findAddIntegrationBtn();
expect(addNewIntegrationBtn.exists()).toBe(true);
await addNewIntegrationBtn.trigger('click');
@@ -186,7 +191,7 @@ describe('AlertsSettingsWrapper', () => {
it('shows loading indicator inside the IntegrationsList table', () => {
createComponent({
- data: { integrations: {} },
+ data: { integrations: [] },
loading: true,
});
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
@@ -198,7 +203,7 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => {
createComponent({
data: {
- integrations: { list: mockIntegrations },
+ integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
formVisible: true,
},
@@ -283,7 +288,7 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
data: {
- integrations: { list: mockIntegrations },
+ integrations: mockIntegrations,
currentIntegration: mockIntegrations[3],
formVisible: true,
},
@@ -374,39 +379,61 @@ describe('AlertsSettingsWrapper', () => {
});
});
- it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation` on HTTP integration edit', () => {
- createComponent({
- data: {
- integrations: { list: mockIntegrations },
- currentIntegration: mockIntegrations[0],
- httpIntegrations,
- },
- loading: false,
- });
+ describe('Edit integration', () => {
+ describe('HTTP', () => {
+ beforeEach(() => {
+ createComponent({
+ data: {
+ integrations: mockIntegrations,
+ currentIntegration: mockIntegrations[0],
+ currentHttpIntegration: { id: mockIntegrations[0].id, ...httpMappingData },
+ },
+ provide: {
+ multiIntegrations: true,
+ },
+ loading: false,
+ });
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValueOnce({});
+ findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables);
+ });
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValueOnce({});
- findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: updateCurrentHttpIntegrationMutation,
- variables: { ...mockIntegrations[0], ...httpMappingData },
- });
- });
+ it('requests `currentHttpIntegration`', () => {
+ expect(wrapper.vm.$apollo.addSmartQuery).toHaveBeenCalledWith(
+ 'currentHttpIntegration',
+ expect.objectContaining({
+ query: getHttpIntegrationQuery,
+ result: expect.any(Function),
+ update: expect.any(Function),
+ variables: expect.any(Function),
+ }),
+ );
+ });
- it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation` on PROMETHEUS integration edit', () => {
- createComponent({
- data: {
- integrations: { list: mockIntegrations },
- currentIntegration: mockIntegrations[3],
- httpIntegrations,
- },
- loading: false,
+ it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation`', () => {
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateCurrentHttpIntegrationMutation,
+ variables: { ...mockIntegrations[0], ...httpMappingData },
+ });
+ });
});
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
- findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: updateCurrentPrometheusIntegrationMutation,
- variables: mockIntegrations[3],
+ describe('Prometheus', () => {
+ it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation`', () => {
+ createComponent({
+ data: {
+ integrations: mockIntegrations,
+ currentIntegration: mockIntegrations[3],
+ },
+ loading: false,
+ });
+
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
+ findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables);
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateCurrentPrometheusIntegrationMutation,
+ variables: mockIntegrations[3],
+ });
+ });
});
});
diff --git a/spec/frontend/alerts_settings/components/util.js b/spec/frontend/alerts_settings/components/util.js
deleted file mode 100644
index 5c07f22f1c9..00000000000
--- a/spec/frontend/alerts_settings/components/util.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const PROMETHEUS_URL = '/prometheus/alerts/notify.json';
-const GENERIC_URL = '/alerts/notify.json';
-const KEY = 'abcedfg123';
-const INVALID_URL = 'http://invalid';
-const ACTIVE = false;
-
-export const defaultAlertSettingsConfig = {
- generic: {
- authorizationKey: KEY,
- formPath: INVALID_URL,
- url: GENERIC_URL,
- alertsSetupUrl: INVALID_URL,
- alertsUsageUrl: INVALID_URL,
- active: ACTIVE,
- },
- prometheus: {
- authorizationKey: KEY,
- prometheusFormPath: INVALID_URL,
- url: PROMETHEUS_URL,
- active: ACTIVE,
- },
- projectPath: '',
- multiIntegrations: true,
-};
diff --git a/spec/lib/declarative_policy/overrides_spec.rb b/spec/lib/declarative_policy/overrides_spec.rb
deleted file mode 100644
index 84dc8f7ac71..00000000000
--- a/spec/lib/declarative_policy/overrides_spec.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require_dependency 'rspec-parameterized'
-
-RSpec.describe 'DeclarativePolicy overrides' do
- let(:foo_policy) do
- Class.new(DeclarativePolicy::Base) do
- condition(:foo_prop_cond) { @subject.foo_prop }
-
- rule { foo_prop_cond }.policy do
- enable :common_ability
- enable :foo_prop_ability
- end
- end
- end
-
- let(:bar_policy) do
- Class.new(DeclarativePolicy::Base) do
- delegate { @subject.foo }
-
- overrides :common_ability
-
- condition(:bar_prop_cond) { @subject.bar_prop }
-
- rule { bar_prop_cond }.policy do
- enable :common_ability
- enable :bar_prop_ability
- end
-
- rule { bar_prop_cond & can?(:foo_prop_ability) }.policy do
- enable :combined_ability
- end
- end
- end
-
- before do
- stub_const('Foo', Struct.new(:foo_prop))
- stub_const('FooPolicy', foo_policy)
- stub_const('Bar', Struct.new(:foo, :bar_prop))
- stub_const('BarPolicy', bar_policy)
- end
-
- where(:foo_prop, :bar_prop) do
- [
- [true, true],
- [true, false],
- [false, true],
- [false, false]
- ]
- end
-
- with_them do
- let(:foo) { Foo.new(foo_prop) }
- let(:bar) { Bar.new(foo, bar_prop) }
-
- it 'determines the correct bar_prop_ability (non-delegated) permissions for bar' do
- policy = DeclarativePolicy.policy_for(nil, bar)
- expect(policy.allowed?(:bar_prop_ability)).to eq(bar_prop)
- end
-
- it 'determines the correct foo_prop (non-overridden) permissions for bar' do
- policy = DeclarativePolicy.policy_for(nil, bar)
- expect(policy.allowed?(:foo_prop_ability)).to eq(foo_prop)
- end
-
- it 'determines the correct common_ability (overridden) permissions for bar' do
- policy = DeclarativePolicy.policy_for(nil, bar)
- expect(policy.allowed?(:common_ability)).to eq(bar_prop)
- end
-
- it 'determines the correct common_ability permissions for foo' do
- policy = DeclarativePolicy.policy_for(nil, foo)
- expect(policy.allowed?(:common_ability)).to eq(foo_prop)
- end
-
- it 'allows combinations of overridden and inherited values' do
- policy = DeclarativePolicy.policy_for(nil, bar)
- expect(policy.allowed?(:combined_ability)).to eq(foo_prop && bar_prop)
- end
- end
-end
diff --git a/spec/lib/declarative_policy_spec.rb b/spec/lib/declarative_policy_spec.rb
deleted file mode 100644
index fc21bd43f48..00000000000
--- a/spec/lib/declarative_policy_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe DeclarativePolicy do
- describe '.class_for' do
- it 'uses declarative_policy_class if present' do
- instance = Gitlab::ErrorTracking::ErrorEvent.new
-
- expect(described_class.class_for(instance)).to eq(ErrorTracking::BasePolicy)
- end
-
- it 'infers policy class from name' do
- instance = PersonalSnippet.new
-
- expect(described_class.class_for(instance)).to eq(PersonalSnippetPolicy)
- end
-
- it 'raises error if not found' do
- instance = Object.new
-
- expect { described_class.class_for(instance) }.to raise_error('no policy for Object')
- end
-
- context 'when found policy class does not inherit base' do
- before do
- stub_const('Foo', Class.new)
- stub_const('FooPolicy', Class.new)
- end
-
- it 'raises error if inferred class does not inherit Base' do
- instance = Foo.new
-
- expect { described_class.class_for(instance) }.to raise_error('no policy for Foo')
- end
- end
- end
-end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index f2c941080b5..540647fb699 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -892,6 +892,8 @@ RSpec.describe ProjectPolicy do
end
describe 'design permissions' do
+ include DesignManagementTestHelpers
+
let(:current_user) { guest }
let(:design_permissions) do
@@ -899,12 +901,14 @@ RSpec.describe ProjectPolicy do
end
context 'when design management is not available' do
+ before do
+ enable_design_management(false)
+ end
+
it { is_expected.not_to be_allowed(*design_permissions) }
end
context 'when design management is available' do
- include DesignManagementTestHelpers
-
before do
enable_design_management
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index f9b76db877b..26f757d5ba0 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -198,7 +198,7 @@ RSpec.describe MergeRequests::RefreshService do
end
end
- shared_examples 'Pipelines for merge requests' do
+ context 'Pipelines for merge requests', :sidekiq_inline do
before do
stub_ci_pipeline_yaml_file(config)
end
@@ -364,18 +364,6 @@ RSpec.describe MergeRequests::RefreshService do
end
end
- context 'when the code_review_async_pipeline_creation feature flag is on', :sidekiq_inline do
- it_behaves_like 'Pipelines for merge requests'
- end
-
- context 'when the code_review_async_pipeline_creation feature flag is off', :sidekiq_inline do
- before do
- stub_feature_flags(code_review_async_pipeline_creation: false)
- end
-
- it_behaves_like 'Pipelines for merge requests'
- end
-
context 'push to origin repo source branch' do
let(:refresh_service) { service.new(@project, @user) }
let(:notification_service) { spy('notification_service') }
diff --git a/yarn.lock b/yarn.lock
index 0d312032ce2..c1d2ddbf4e2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -907,14 +907,14 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
-"@gitlab/ui@29.6.0":
- version "29.6.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.6.0.tgz#5e8369d7aeab56edab570ef148dbc289b51901fc"
- integrity sha512-bomMDBzS/S+ztFgAC23oDjMEK3cFZ3UcKJGs3iKXtCTKIxtzXKrL0LWYxkidywIwWm9L+1udgsx/GTKoVW0ItQ==
+"@gitlab/ui@29.7.3":
+ version "29.7.3"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.7.3.tgz#f7762cc6a20fc8f1c6403822f8cbef821f2438dd"
+ integrity sha512-2pU7t+kFB4ndq3ZW8PzZ26LEj+vAd8AaJLvBmBDba1hEx5KlH4B9U30lGl+UYzgjY3yoe68eObK26vJjJ1rb0g==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
- bootstrap-vue "2.13.1"
+ bootstrap-vue "2.14.0"
copy-to-clipboard "^3.0.8"
dompurify "^2.2.7"
echarts "^4.9.0"
@@ -2480,10 +2480,10 @@ bonjour@^3.5.0:
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
-bootstrap-vue@2.13.1:
- version "2.13.1"
- resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.13.1.tgz#dcefca78f2b0345508fbb85adb0a9897cce65fa1"
- integrity sha512-FF1GLRvvj+TgpLkMG/A8+tCMQfWnfKpsPqt7s097VTX2lZv2/YmyhehRC1qraPHYqT7qQVy48TCxSw1H8hNh3Q==
+bootstrap-vue@2.14.0:
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.14.0.tgz#de88b607627431980b707e6f069f13ef8cc897bd"
+ integrity sha512-sqbS7iHYCZEj/dDx4Yaze99HcX6bZjO4bSWZ0xSgJwtWQlbfB2VDJ9Qjzjp9XI8TT32wYNGAMpnXpYjQvv5qyQ==
dependencies:
"@nuxt/opencollective" "^0.3.0"
bootstrap ">=4.4.1 <5.0.0"