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:
-rw-r--r--.haml-lint_todo.yml1
-rw-r--r--.rubocop.yml5
-rw-r--r--app/helpers/blob_helper.rb8
-rw-r--r--app/models/clusters/concerns/application_version.rb4
-rw-r--r--app/models/concerns/prometheus_adapter.rb2
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/upload.rb2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml13
-rw-r--r--app/views/shared/_confirm_fork_modal.html.haml12
-rw-r--r--changelogs/unreleased/29835-webide-fork.yml6
-rw-r--r--config/initializers/fog_core_patch.rb2
-rw-r--r--config/initializers/zz_metrics.rb4
-rw-r--r--config/settings.rb6
-rw-r--r--doc/administration/geo/disaster_recovery/planned_failover.md18
-rw-r--r--doc/administration/geo/replication/index.md1
-rw-r--r--doc/administration/geo/replication/object_storage.md49
-rw-r--r--doc/administration/troubleshooting/postgresql.md24
-rw-r--r--doc/ci/yaml/README.md52
-rw-r--r--doc/user/analytics/productivity_analytics.md3
-rw-r--r--lib/api/notes.rb2
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/banzai/filter.rb2
-rw-r--r--lib/banzai/filter/audio_link_filter.rb58
-rw-r--r--lib/banzai/filter/playable_link_filter.rb87
-rw-r--r--lib/banzai/filter/video_link_filter.rb61
-rw-r--r--lib/banzai/pipeline.rb2
-rw-r--r--lib/banzai/reference_parser.rb2
-rw-r--r--lib/bitbucket/page.rb2
-rw-r--r--lib/bitbucket_server/page.rb2
-rw-r--r--lib/gitlab/background_migration.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb6
-rw-r--r--lib/gitlab/cache/request_cache.rb2
-rw-r--r--lib/gitlab/ci/build/policy.rb2
-rw-r--r--lib/gitlab/ci/status/factory.rb2
-rw-r--r--lib/gitlab/config/entry/simplifiable.rb2
-rw-r--r--lib/gitlab/cycle_analytics/event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/stage.rb2
-rw-r--r--lib/gitlab/downtime_check.rb4
-rw-r--r--lib/gitlab/gitaly_client.rb2
-rw-r--r--lib/gitlab/gitaly_client/attributes_bag.rb4
-rw-r--r--lib/gitlab/patch/prependable.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/page/validator.rb2
-rw-r--r--qa/qa/runtime/browser.rb2
-rw-r--r--qa/qa/runtime/release.rb2
-rw-r--r--rubocop/cop/gitlab/const_get_inherit_false.rb42
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--spec/factories/uploads.rb2
-rw-r--r--spec/helpers/blob_helper_spec.rb28
-rw-r--r--spec/lib/banzai/filter/video_link_filter_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/status/external/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/simplifiable_spec.rb6
-rw-r--r--spec/lib/gitlab/patch/prependable_spec.rb10
-rw-r--r--spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb80
-rw-r--r--spec/support/shared_examples/models/cluster_application_status_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/cluster_application_version_shared_examples.rb2
-rw-r--r--spec/views/projects/tree/_tree_header.html.haml_spec.rb44
60 files changed, 479 insertions, 226 deletions
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index 8f8cbc2072a..211ad359951 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -356,6 +356,7 @@ linters:
- 'app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml'
- 'app/views/shared/_commit_message_container.html.haml'
- 'app/views/shared/_confirm_modal.html.haml'
+ - 'app/views/shared/_confirm_fork_modal.html.haml'
- 'app/views/shared/_delete_label_modal.html.haml'
- 'app/views/shared/_group_form.html.haml'
- 'app/views/shared/_group_tips.html.haml'
diff --git a/.rubocop.yml b/.rubocop.yml
index 835c321c943..049340f90d4 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -178,6 +178,11 @@ Gitlab/ModuleWithInstanceVariables:
- spec/support/**/*.rb
- features/steps/**/*.rb
+Gitlab/ConstGetInheritFalse:
+ Enabled: true
+ Exclude:
+ - 'qa/bin/*'
+
Gitlab/HTTParty:
Enabled: true
Exclude:
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 6d1ec16b0c2..5c24b0e1704 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -32,6 +32,14 @@ module BlobHelper
File.join(segments)
end
+ def ide_fork_and_edit_path(project = @project, ref = @ref, path = @path, options = {})
+ if current_user
+ project_forks_path(project,
+ namespace_key: current_user&.namespace&.id,
+ continue: edit_blob_fork_params(ide_edit_path(project, ref, path)))
+ end
+ end
+
def encode_ide_path(path)
url_encode(path).gsub('%2F', '/')
end
diff --git a/app/models/clusters/concerns/application_version.rb b/app/models/clusters/concerns/application_version.rb
index db94e8e08c9..6c0b014662c 100644
--- a/app/models/clusters/concerns/application_version.rb
+++ b/app/models/clusters/concerns/application_version.rb
@@ -8,13 +8,13 @@ module Clusters
included do
state_machine :status do
before_transition any => [:installed, :updated] do |application|
- application.version = application.class.const_get(:VERSION)
+ application.version = application.class.const_get(:VERSION, false)
end
end
end
def update_available?
- version != self.class.const_get(:VERSION)
+ version != self.class.const_get(:VERSION, false)
end
end
end
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index aab0589f7ca..9df77b565da 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -44,7 +44,7 @@ module PrometheusAdapter
end
def query_klass_for(query_name)
- Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
+ Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query", false)
end
def build_query_args(*args)
diff --git a/app/models/note.rb b/app/models/note.rb
index 34736482387..4e9fd8d2dd1 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -24,7 +24,7 @@ class Note < ApplicationRecord
class << self
def values
- constants.map {|const| self.const_get(const)}
+ constants.map {|const| self.const_get(const, false)}
end
def value?(val)
diff --git a/app/models/upload.rb b/app/models/upload.rb
index df8f9c56fa8..8c409641452 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -138,7 +138,7 @@ class Upload < ApplicationRecord
end
def uploader_class
- Object.const_get(uploader)
+ Object.const_get(uploader, false)
end
def identifier
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 41cd044a5b0..38422d4533d 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -41,7 +41,7 @@
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
#{ _('New directory') }
- - elsif can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
+ - elsif can_create_mr_from_fork
%li
- continue_params = { to: project_new_blob_path(@project, @id),
notice: edit_in_new_fork_notice,
@@ -81,10 +81,15 @@
= render 'projects/find_file_link'
- - if can_collaborate
+ - if can_create_mr_from_fork
= succeed " " do
- = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
- = _('Web IDE')
+ - if can_collaborate || current_user&.already_forked?(@project)
+ = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
+ = _('Web IDE')
+ - else
+ = link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do
+ = _('Web IDE')
+ = render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path)
- if show_xcode_link?(@project)
.project-action-button.project-xcode.inline
diff --git a/app/views/shared/_confirm_fork_modal.html.haml b/app/views/shared/_confirm_fork_modal.html.haml
new file mode 100644
index 00000000000..db50ea41387
--- /dev/null
+++ b/app/views/shared/_confirm_fork_modal.html.haml
@@ -0,0 +1,12 @@
+#modal-confirm-fork.modal.qa-confirm-fork-modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %h3.page-title= _('Fork project?')
+ %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
+ %span{ "aria-hidden": true } &times;
+ .modal-body.p-3
+ %p= _("You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.") % { tag_start: '', tag_end: ''}
+ .modal-footer
+ = link_to _('Cancel'), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+ = link_to _('Fork project'), fork_path, class: 'btn btn-success', method: :post
diff --git a/changelogs/unreleased/29835-webide-fork.yml b/changelogs/unreleased/29835-webide-fork.yml
new file mode 100644
index 00000000000..1849b414a2d
--- /dev/null
+++ b/changelogs/unreleased/29835-webide-fork.yml
@@ -0,0 +1,6 @@
+---
+title: Web IDE button should fork and open forked project when selected from read-only
+ project
+merge_request: 17672
+author:
+type: added
diff --git a/config/initializers/fog_core_patch.rb b/config/initializers/fog_core_patch.rb
index d3d02216d45..053e0460a19 100644
--- a/config/initializers/fog_core_patch.rb
+++ b/config/initializers/fog_core_patch.rb
@@ -34,6 +34,7 @@ module Fog
# Gems that have not yet updated with the new fog-core namespace
LEGACY_FOG_PROVIDERS = %w(google rackspace aliyun).freeze
+ # rubocop:disable Gitlab/ConstGetInheritFalse
def service_provider_constant(service_name, provider_name)
args = service_provider_search_args(service_name, provider_name)
Fog.const_get(args.first).const_get(*const_get_args(args.second))
@@ -48,5 +49,6 @@ module Fog
[provider_name, service_name]
end
end
+ # rubocop:enable Gitlab/ConstGetInheritFalse
end
end
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index 501ec8ccc06..bc28780cc77 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -13,7 +13,7 @@ def instrument_classes(instrumentation)
instrumentation.instrument_methods(Gitlab::Git)
Gitlab::Git.constants.each do |name|
- const = Gitlab::Git.const_get(name)
+ const = Gitlab::Git.const_get(name, false)
next unless const.is_a?(Module)
@@ -75,7 +75,7 @@ def instrument_classes(instrumentation)
instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
[:XML, :HTML].each do |namespace|
- namespace_mod = Nokogiri.const_get(namespace)
+ namespace_mod = Nokogiri.const_get(namespace, false)
instrumentation.instrument_methods(namespace_mod)
instrumentation.instrument_methods(namespace_mod::Document)
diff --git a/config/settings.rb b/config/settings.rb
index 8756c120645..767c6c56337 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -104,10 +104,10 @@ class Settings < Settingslogic
# check that `current` (string or integer) is a contant in `modul`.
def verify_constant(modul, current, default)
- constant = modul.constants.find { |name| modul.const_get(name) == current }
- value = constant.nil? ? default : modul.const_get(constant)
+ constant = modul.constants.find { |name| modul.const_get(name, false) == current }
+ value = constant.nil? ? default : modul.const_get(constant, false)
if current.is_a? String
- value = modul.const_get(current.upcase) rescue default
+ value = modul.const_get(current.upcase, false) rescue default
end
value
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index 75e07bcf863..8fee172ec64 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -43,23 +43,14 @@ will go smoothly.
### Object storage
-Some classes of non-repository data can use object storage in preference to
-file storage. Geo [does not replicate data in object storage](../replication/object_storage.md),
-leaving that task up to the object store itself. For a planned failover, this
-means you can decouple the replication of this data from the failover of the
-GitLab service.
-
-If you're already using object storage, simply verify that your **secondary**
-node has access to the same data as the **primary** node - they must either they share the
-same object storage configuration, or the **secondary** node should be configured to
-access a [geographically-replicated][os-repl] copy provided by the object store
-itself.
-
If you have a large GitLab installation or cannot tolerate downtime, consider
[migrating to Object Storage][os-conf] **before** scheduling a planned failover.
Doing so reduces both the length of the maintenance window, and the risk of data
loss as a result of a poorly executed planned failover.
+In GitLab 12.4, you can optionally allow GitLab to manage replication of Object Storage for
+**secondary** nodes. For more information, see [Object Storage replication][os-conf].
+
### Review the configuration of each **secondary** node
Database settings are automatically replicated to the **secondary** node, but the
@@ -224,5 +215,4 @@ Don't forget to remove the broadcast message after failover is complete.
[background-verification]: background_verification.md
[limitations]: ../replication/index.md#current-limitations
[moving-repositories]: ../../operations/moving_repositories.md
-[os-conf]: ../replication/object_storage.md#configuration
-[os-repl]: ../replication/object_storage.md#replication
+[os-conf]: ../replication/object_storage.md
diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md
index bcad820531c..0e1ae2a2628 100644
--- a/doc/administration/geo/replication/index.md
+++ b/doc/administration/geo/replication/index.md
@@ -283,7 +283,6 @@ You can keep track of the progress to include the missing items in:
| [Maven Packages](../../../user/packages/maven_repository/index.md) | No | No |
| [Conan Packages](../../../user/packages/conan_repository/index.md) | No | No |
| [External merge request diffs](../../merge_request_diffs.md) | No, if they are on-disk | No |
-| Content in object storage ([track progress](https://gitlab.com/groups/gitlab-org/-/epics/1526)) | No | No |
1. The integrity can be verified manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them.
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index 878b67a8f8e..c85005a3f91 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -1,16 +1,30 @@
# Geo with Object storage **(PREMIUM ONLY)**
-Geo can be used in combination with Object Storage (AWS S3, or
-other compatible object storage).
+Geo can be used in combination with Object Storage (AWS S3, or other compatible object storage).
-## Configuration
+Currently, **secondary** nodes can use either:
-At this time it is required that if object storage is enabled on the
-**primary** node, it must also be enabled on each **secondary** node.
+- The same storage bucket as the **primary** node.
+- A replicated storage bucket.
-**Secondary** nodes can use the same storage bucket as the **primary** node, or
-they can use a replicated storage bucket. At this time GitLab does not
-take care of content replication in object storage.
+To have:
+
+- GitLab manage replication, follow [Enabling GitLab replication](#enabling-gitlab-managed-object-storage-replication).
+- Third-party services manage replication, follow [Third-party replication services](#third-party-replication-services).
+
+## Enabling GitLab managed object storage replication
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/10586) in GitLab 12.4.
+
+**Secondary** nodes can replicate files stored on the **primary** node regardless of
+whether they are stored on the local filesystem or in object storage.
+
+To enable GitLab replication, you must:
+
+1. Go to **Admin Area > Geo**.
+1. Press **Edit** on the **secondary** node.
+1. Enable the **Allow this secondary node to replicate content on Object Storage**
+ checkbox.
For LFS, follow the documentation to
[set up LFS object storage](../../../workflow/lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
@@ -20,12 +34,21 @@ For CI job artifacts, there is similar documentation to configure
For user uploads, there is similar documentation to configure [upload object storage](../../uploads.md#using-object-storage-core-only)
-You should enable and configure object storage on both **primary** and **secondary**
-nodes. Migrating existing data to object storage should be performed on the
-**primary** node only. **Secondary** nodes will automatically notice that the migrated
-files are now in object storage.
+If you want to migrate the **primary** node's files to object storage, you can
+configure the **secondary** in a few ways:
+
+- Use the exact same object storage.
+- Use a separate object store but leverage your object storage solution's built-in
+ replication.
+- Use a separate object store and enable the **Allow this secondary node to replicate
+ content on Object Storage** setting.
+
+GitLab does not currently support the case where both:
+
+- The **primary** node uses local storage.
+- A **secondary** node uses object storage.
-## Replication
+## Third-party replication services
When using Amazon S3, you can use
[CRR](https://docs.aws.amazon.com/AmazonS3/latest/dev/crr.html) to
diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md
index 3bbc3f23d83..f427cd88ce0 100644
--- a/doc/administration/troubleshooting/postgresql.md
+++ b/doc/administration/troubleshooting/postgresql.md
@@ -31,30 +31,30 @@ This section is for links to information elsewhere in the GitLab documentation.
- Destructively reseeding the GitLab database.
- Guidance around updating packaged PostgreSQL, including how to stop it happening automatically.
-- [More about external PostgreSQL](/ee/administration/external_database.html)
+- [More about external PostgreSQL](../external_database.md)
-- [Running GEO with external PostgreSQL](/ee/administration/geo/replication/external_database.html)
+- [Running GEO with external PostgreSQL](../geo/replication/external_database.md)
- [Upgrades when running PostgreSQL configured for HA.](https://docs.gitlab.com/omnibus/settings/database.html#upgrading-a-gitlab-ha-cluster)
-- Consuming PostgreSQL from [within CI runners](/ee/ci/services/postgres.html)
+- Consuming PostgreSQL from [within CI runners](../../ci/services/postgres.md)
-- [Using Slony to update PostgreSQL](/ee/update/upgrading_postgresql_using_slony.html)
+- [Using Slony to update PostgreSQL](../../update/upgrading_postgresql_using_slony.md)
- Uses replication to handle PostgreSQL upgrades - providing the schemas are the same.
- Reduces downtime to a short window for swinging over to the newer vewrsion.
- Managing Omnibus PostgreSQL versions [from the development docs](https://docs.gitlab.com/omnibus/development/managing-postgresql-versions.html)
-- [PostgreSQL scaling and HA](/ee/administration/high_availability/database.html)
- - including [troubleshooting](/ee/administration/high_availability/database.html#troubleshooting) gitlab-ctl repmgr-check-master and pgbouncer errors
+- [PostgreSQL scaling and HA](../high_availability/database.md)
+ - including [troubleshooting](../high_availability/database.md#troubleshooting) gitlab-ctl repmgr-check-master and pgbouncer errors
-- [Developer database documentation](/ee/development/README.html#database-guides) - some of which is absolutely not for production use. Including:
+- [Developer database documentation](../../development/README.md#database-guides) - some of which is absolutely not for production use. Including:
- understanding EXPLAIN plans
### Troubleshooting/Fixes
-- [GitLab database requirements](/ee/install/requirements.html#database) including
- - Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](/ee/update/mysql_to_postgresql.html)
+- [GitLab database requirements](../../install/requirements.md#database) including
+ - Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](../../update/mysql_to_postgresql.md)
- required extension pg_trgm
- required extension postgres_fdw for Geo
@@ -71,7 +71,7 @@ pg_basebackup: could not create temporary replication slot "pg_basebackup_12345"
HINT: Free one or increase max_replication_slots.
```
-- GEO [replication errors](/ee/administration/geo/replication/troubleshooting.html#fixing-replication-errors) including:
+- GEO [replication errors](../geo/replication/troubleshooting.md#fixing-replication-errors) including:
```
ERROR: replication slots can only be used if max_replication_slots > 0
@@ -83,11 +83,11 @@ Command exceeded allowed execution time
PANIC: could not write to file ‘pg_xlog/xlogtemp.123’: No space left on device
```
-- [Checking GEO configuration](/ee/administration/geo/replication/troubleshooting.html#checking-configuration) including
+- [Checking GEO configuration](../geo/replication/troubleshooting.md#checking-configuration) including
- reconfiguring hosts/ports
- checking and fixing user/password mappings
-- [Common GEO errors](/ee/administration/geo/replication/troubleshooting.html#fixing-common-errors)
+- [Common GEO errors](../geo/replication/troubleshooting.md#fixing-common-errors)
## Support topics
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 114581e1e5d..628c3f01043 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1087,7 +1087,7 @@ Manual actions are considered to be write actions, so permissions for
a user wants to trigger an action. In other words, in order to trigger a manual
action assigned to a branch that the pipeline is running for, the user needs to
have the ability to merge to this branch. It is possible to use protected environments
-to more strictly [protect manual deployments](#protecting-manual-jobs) from being
+to more strictly [protect manual deployments](#protecting-manual-jobs-premium) from being
run by unauthorized users.
NOTE: **Note:**
@@ -1095,36 +1095,38 @@ Using `when:manual` and `trigger` together results in the error `jobs:#{job-name
should be on_success, on_failure or always`, because `when:manual` prevents triggers
being used.
-##### Protecting manual jobs
+##### Protecting manual jobs **(PREMIUM)**
It's possible to use [protected environments](../environments/protected_environments.md)
to define a precise list of users authorized to run a manual job. By allowing only
users associated with a protected environment to trigger manual jobs, it is possible
to implement some special use cases, such as:
-- more precisely limiting who can deploy to an environment.
-- enabling a pipeline to be blocked until an approved user "approves" it.
-
-To do this, you must add an environment to the job. For example:
-
-```yaml
-deploy_prod:
- stage: deploy
- script:
- - echo "Deploy to production server"
- environment:
- name: production
- url: https://example.com
- when: manual
- only:
- - master
-```
-
-Then, in the [protected environments settings](../environments/protected_environments.md#protecting-environments),
-select the environment (`production` in the example above) and add the users, roles or groups
-that are authorized to trigger the manual job to the **Allowed to Deploy** list. Only those in
-this list will be able to trigger this manual job, as well as GitLab admins who are always able
-to use protected environments.
+- More precisely limiting who can deploy to an environment.
+- Enabling a pipeline to be blocked until an approved user "approves" it.
+
+To do this, you must:
+
+1. Add an `environment` to the job. For example:
+
+ ```yaml
+ deploy_prod:
+ stage: deploy
+ script:
+ - echo "Deploy to production server"
+ environment:
+ name: production
+ url: https://example.com
+ when: manual
+ only:
+ - master
+ ```
+
+1. In the [protected environments settings](../environments/protected_environments.md#protecting-environments),
+ select the environment (`production` in the example above) and add the users, roles or groups
+ that are authorized to trigger the manual job to the **Allowed to Deploy** list. Only those in
+ this list will be able to trigger this manual job, as well as GitLab administrators
+ who are always able to use protected environments.
Additionally, if a manual job is defined as blocking by adding `allow_failure: false`,
the next stages of the pipeline will not run until the manual job is triggered. This
diff --git a/doc/user/analytics/productivity_analytics.md b/doc/user/analytics/productivity_analytics.md
index bd3cbd62ba0..87a92fcbf2a 100644
--- a/doc/user/analytics/productivity_analytics.md
+++ b/doc/user/analytics/productivity_analytics.md
@@ -21,6 +21,7 @@ Productivity Analytics allows GitLab users to:
- Visualize typical merge request (MR) lifetime and statistics. Use a histogram that shows the distribution of the time elapsed between creating and merging merge requests.
- Drill down into the most time consuming merge requests, select a number of outliers, and filter down all subsequent charts to investigate potential causes.
- Filter by group, project, author, label, milestone, or a specific date range. Filter down, for example, to the merge requests of a specific author in a group or project during a milestone or specific date range.
+- Measure velocity over time. Visualize the trends of each metric from the charts above over time in order to observe progress. Zoom in on a particular date range if you notice outliers.
## Accessing metrics and visualizations
@@ -40,6 +41,8 @@ The following metrics and visualizations are available on a project or group lev
- Number of commits per merge request.
- Number of lines of code per commit.
- Number of files touched.
+- Scatterplot showing all MRs merged on a certain date, together with the days it took to complete the action and a 30 day rolling median.
+ - Users can zoom in and out on specific days of interest.
- Table showing the list of merge requests with their respective time duration metrics.
- Users can sort by any of the above metrics.
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 16fca9acccb..89e4da5a42e 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -80,7 +80,7 @@ module API
note = create_note(noteable, opts)
if note.valid?
- present note, with: Entities.const_get(note.class.name)
+ present note, with: Entities.const_get(note.class.name, false)
else
bad_request!("Note #{note.errors.messages}")
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 404675bfaec..e3f3aca27df 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -49,7 +49,7 @@ module API
resource :todos do
helpers do
def issuable_and_awardable?(type)
- obj_type = Object.const_get(type)
+ obj_type = Object.const_get(type, false)
(obj_type < Issuable) && (obj_type < Awardable)
rescue NameError
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index 7d9766c906c..2438cb3c166 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -3,7 +3,7 @@
module Banzai
module Filter
def self.[](name)
- const_get("#{name.to_s.camelize}Filter")
+ const_get("#{name.to_s.camelize}Filter", false)
end
end
end
diff --git a/lib/banzai/filter/audio_link_filter.rb b/lib/banzai/filter/audio_link_filter.rb
index 83aa520dc4b..50472c3cf81 100644
--- a/lib/banzai/filter/audio_link_filter.rb
+++ b/lib/banzai/filter/audio_link_filter.rb
@@ -3,63 +3,15 @@
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/audio.js
module Banzai
module Filter
- # Find every image that isn't already wrapped in an `a` tag, and that has
- # a `src` attribute ending with an audio extension, add a new audio node and
- # a "Download" link in the case the audio cannot be played.
- class AudioLinkFilter < HTML::Pipeline::Filter
- def call
- doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el|
- el.replace(audio_node(doc, el)) if has_audio_extension?(el)
- end
-
- doc
- end
-
+ class AudioLinkFilter < PlayableLinkFilter
private
- def has_audio_extension?(element)
- src = element.attr('data-canonical-src').presence || element.attr('src')
-
- return unless src.present?
-
- src_ext = File.extname(src).sub('.', '').downcase
- Gitlab::FileTypeDetection::SAFE_AUDIO_EXT.include?(src_ext)
+ def media_type
+ "audio"
end
- def audio_node(doc, element)
- container = doc.document.create_element(
- 'div',
- class: 'audio-container'
- )
-
- audio = doc.document.create_element(
- 'audio',
- src: element['src'],
- controls: true,
- 'data-setup' => '{}',
- 'data-title' => element['title'] || element['alt'])
-
- link = doc.document.create_element(
- 'a',
- element['title'] || element['alt'],
- href: element['src'],
- target: '_blank',
- rel: 'noopener noreferrer',
- title: "Download '#{element['title'] || element['alt']}'")
-
- # make sure the original non-proxied src carries over
- if element['data-canonical-src']
- audio['data-canonical-src'] = element['data-canonical-src']
- link['data-canonical-src'] = element['data-canonical-src']
- end
-
- download_paragraph = doc.document.create_element('p')
- download_paragraph.children = link
-
- container.add_child(audio)
- container.add_child(download_paragraph)
-
- container
+ def safe_media_ext
+ Gitlab::FileTypeDetection::SAFE_AUDIO_EXT
end
end
end
diff --git a/lib/banzai/filter/playable_link_filter.rb b/lib/banzai/filter/playable_link_filter.rb
new file mode 100644
index 00000000000..0a043aa809c
--- /dev/null
+++ b/lib/banzai/filter/playable_link_filter.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # Find every image that isn't already wrapped in an `a` tag, and that has
+ # a `src` attribute ending with an audio or video extension, add a new audio or video node and
+ # a "Download" link in the case the media cannot be played.
+ class PlayableLinkFilter < HTML::Pipeline::Filter
+ def call
+ doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el|
+ el.replace(media_node(doc, el)) if has_media_extension?(el)
+ end
+
+ doc
+ end
+
+ private
+
+ def media_type
+ raise NotImplementedError
+ end
+
+ def safe_media_ext
+ raise NotImplementedError
+ end
+
+ def extra_element_attrs
+ {}
+ end
+
+ def has_media_extension?(element)
+ src = element.attr('data-canonical-src').presence || element.attr('src')
+
+ return unless src.present?
+
+ src_ext = File.extname(src).sub('.', '').downcase
+ safe_media_ext.include?(src_ext)
+ end
+
+ def media_element(doc, element)
+ media_element_attrs = {
+ src: element['src'],
+ controls: true,
+ 'data-setup': '{}',
+ 'data-title': element['title'] || element['alt']
+ }.merge!(extra_element_attrs)
+
+ if element['data-canonical-src']
+ media_element_attrs['data-canonical-src'] = element['data-canonical-src']
+ end
+
+ doc.document.create_element(media_type, media_element_attrs)
+ end
+
+ def download_paragraph(doc, element)
+ link_content = element['title'] || element['alt']
+
+ link_element_attrs = {
+ href: element['src'],
+ target: '_blank',
+ rel: 'noopener noreferrer',
+ title: "Download '#{link_content}'"
+ }
+
+ # make sure the original non-proxied src carries over
+ if element['data-canonical-src']
+ link_element_attrs['data-canonical-src'] = element['data-canonical-src']
+ end
+
+ link = doc.document.create_element('a', link_content, link_element_attrs)
+
+ doc.document.create_element('p').tap do |paragraph|
+ paragraph.children = link
+ end
+ end
+
+ def media_node(doc, element)
+ container_element_attrs = { class: "#{media_type}-container" }
+
+ doc.document.create_element( "div", container_element_attrs).tap do |container|
+ container.add_child(media_element(doc, element))
+ container.add_child(download_paragraph(doc, element))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index 0e329339474..ed82fbc1f94 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -3,64 +3,19 @@
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/video.js
module Banzai
module Filter
- # Find every image that isn't already wrapped in an `a` tag, and that has
- # a `src` attribute ending with a video extension, add a new video node and
- # a "Download" link in the case the video cannot be played.
- class VideoLinkFilter < HTML::Pipeline::Filter
- def call
- doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el|
- el.replace(video_node(doc, el)) if has_video_extension?(el)
- end
-
- doc
- end
-
+ class VideoLinkFilter < PlayableLinkFilter
private
- def has_video_extension?(element)
- src = element.attr('data-canonical-src').presence || element.attr('src')
-
- return unless src.present?
-
- src_ext = File.extname(src).sub('.', '').downcase
- Gitlab::FileTypeDetection::SAFE_VIDEO_EXT.include?(src_ext)
+ def media_type
+ "video"
end
- def video_node(doc, element)
- container = doc.document.create_element(
- 'div',
- class: 'video-container'
- )
-
- video = doc.document.create_element(
- 'video',
- src: element['src'],
- width: '100%',
- controls: true,
- 'data-setup' => '{}',
- 'data-title' => element['title'] || element['alt'])
-
- link = doc.document.create_element(
- 'a',
- element['title'] || element['alt'],
- href: element['src'],
- target: '_blank',
- rel: 'noopener noreferrer',
- title: "Download '#{element['title'] || element['alt']}'")
-
- # make sure the original non-proxied src carries over
- if element['data-canonical-src']
- video['data-canonical-src'] = element['data-canonical-src']
- link['data-canonical-src'] = element['data-canonical-src']
- end
-
- download_paragraph = doc.document.create_element('p')
- download_paragraph.children = link
-
- container.add_child(video)
- container.add_child(download_paragraph)
+ def safe_media_ext
+ Gitlab::FileTypeDetection::SAFE_VIDEO_EXT
+ end
- container
+ def extra_element_attrs
+ { width: "100%" }
end
end
end
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
index e8a81bebaa9..497d3f27542 100644
--- a/lib/banzai/pipeline.rb
+++ b/lib/banzai/pipeline.rb
@@ -4,7 +4,7 @@ module Banzai
module Pipeline
def self.[](name)
name ||= :full
- const_get("#{name.to_s.camelize}Pipeline")
+ const_get("#{name.to_s.camelize}Pipeline", false)
end
end
end
diff --git a/lib/banzai/reference_parser.rb b/lib/banzai/reference_parser.rb
index efe15096f08..c08d3364a87 100644
--- a/lib/banzai/reference_parser.rb
+++ b/lib/banzai/reference_parser.rb
@@ -10,7 +10,7 @@ module Banzai
#
# This would return the `Banzai::ReferenceParser::IssueParser` class.
def self.[](name)
- const_get("#{name.to_s.camelize}Parser")
+ const_get("#{name.to_s.camelize}Parser", false)
end
end
end
diff --git a/lib/bitbucket/page.rb b/lib/bitbucket/page.rb
index 7cc1342ad65..38c689628dd 100644
--- a/lib/bitbucket/page.rb
+++ b/lib/bitbucket/page.rb
@@ -30,7 +30,7 @@ module Bitbucket
end
def representation_class(type)
- Bitbucket::Representation.const_get(type.to_s.camelize)
+ Bitbucket::Representation.const_get(type.to_s.camelize, false)
end
end
end
diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb
index 5d9a3168876..304f7cd9d72 100644
--- a/lib/bitbucket_server/page.rb
+++ b/lib/bitbucket_server/page.rb
@@ -30,7 +30,7 @@ module BitbucketServer
end
def representation_class(type)
- BitbucketServer::Representation.const_get(type.to_s.camelize)
+ BitbucketServer::Representation.const_get(type.to_s.camelize, false)
end
end
end
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index 2e3a4f3b869..61e0a075018 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -78,7 +78,7 @@ module Gitlab
end
def self.migration_class_for(class_name)
- const_get(class_name)
+ const_get(class_name, false)
end
def self.enqueued_job?(queues, migration_class)
diff --git a/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb b/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb
index 29fa0f18448..3c142327e94 100644
--- a/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb
+++ b/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config.rb
@@ -171,7 +171,11 @@ module Gitlab
end
def schedule_retry(project, retry_count)
- BackgroundMigrationWorker.perform_in(RETRY_DELAY, self.class::RetryOne.name, [project.id, retry_count])
+ # Constants provided to BackgroundMigrationWorker must be within the
+ # scope of Gitlab::BackgroundMigration
+ retry_class_name = self.class::RetryOne.name.sub('Gitlab::BackgroundMigration::', '')
+
+ BackgroundMigrationWorker.perform_in(RETRY_DELAY, retry_class_name, [project.id, retry_count])
end
end
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index 4c658dc0b8d..6e48ca90054 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -23,7 +23,7 @@ module Gitlab
end
def request_cache(method_name, &method_key_block)
- const_get(:RequestCacheExtension).module_eval do
+ const_get(:RequestCacheExtension, false).module_eval do
cache_key_method_name = "#{method_name}_cache_key"
define_method(method_name) do |*args|
diff --git a/lib/gitlab/ci/build/policy.rb b/lib/gitlab/ci/build/policy.rb
index 43c46ad74af..ebeebe7fb5b 100644
--- a/lib/gitlab/ci/build/policy.rb
+++ b/lib/gitlab/ci/build/policy.rb
@@ -6,7 +6,7 @@ module Gitlab
module Policy
def self.fabricate(specs)
specifications = specs.to_h.map do |spec, value|
- self.const_get(spec.to_s.camelize).new(value)
+ self.const_get(spec.to_s.camelize, false).new(value)
end
specifications.compact
diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb
index 2a0bf060c9b..c29dc51f076 100644
--- a/lib/gitlab/ci/status/factory.rb
+++ b/lib/gitlab/ci/status/factory.rb
@@ -20,7 +20,7 @@ module Gitlab
def core_status
Gitlab::Ci::Status
- .const_get(@status.capitalize)
+ .const_get(@status.capitalize, false)
.new(@subject, @user)
.extend(self.class.common_helpers)
end
diff --git a/lib/gitlab/config/entry/simplifiable.rb b/lib/gitlab/config/entry/simplifiable.rb
index a56a89adb35..d58aba07d15 100644
--- a/lib/gitlab/config/entry/simplifiable.rb
+++ b/lib/gitlab/config/entry/simplifiable.rb
@@ -37,7 +37,7 @@ module Gitlab
def self.entry_class(strategy)
if strategy.present?
- self.const_get(strategy.name)
+ self.const_get(strategy.name, false)
else
self::UnknownStrategy
end
diff --git a/lib/gitlab/cycle_analytics/event_fetcher.rb b/lib/gitlab/cycle_analytics/event_fetcher.rb
index 98a30a8fc97..04f4b4f053f 100644
--- a/lib/gitlab/cycle_analytics/event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/event_fetcher.rb
@@ -4,7 +4,7 @@ module Gitlab
module CycleAnalytics
module EventFetcher
def self.[](stage_name)
- CycleAnalytics.const_get("#{stage_name.to_s.camelize}EventFetcher")
+ CycleAnalytics.const_get("#{stage_name.to_s.camelize}EventFetcher", false)
end
end
end
diff --git a/lib/gitlab/cycle_analytics/stage.rb b/lib/gitlab/cycle_analytics/stage.rb
index 1bd40a7aa18..5cfd9ea4730 100644
--- a/lib/gitlab/cycle_analytics/stage.rb
+++ b/lib/gitlab/cycle_analytics/stage.rb
@@ -4,7 +4,7 @@ module Gitlab
module CycleAnalytics
module Stage
def self.[](stage_name)
- CycleAnalytics.const_get("#{stage_name.to_s.camelize}Stage")
+ CycleAnalytics.const_get("#{stage_name.to_s.camelize}Stage", false)
end
end
end
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb
index 31bb6810391..457a3c12206 100644
--- a/lib/gitlab/downtime_check.rb
+++ b/lib/gitlab/downtime_check.rb
@@ -58,13 +58,13 @@ module Gitlab
# Returns true if the given migration can be performed without downtime.
def online?(migration)
- migration.const_get(DOWNTIME_CONST) == false
+ migration.const_get(DOWNTIME_CONST, false) == false
end
# Returns the downtime reason, or nil if none was defined.
def downtime_reason(migration)
if migration.const_defined?(DOWNTIME_REASON_CONST)
- migration.const_get(DOWNTIME_REASON_CONST)
+ migration.const_get(DOWNTIME_REASON_CONST, false)
else
nil
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index befcc004ee4..b0f29d22ad4 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -86,7 +86,7 @@ module Gitlab
if name == :health_check
Grpc::Health::V1::Health::Stub
else
- Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
+ Gitaly.const_get(name.to_s.camelcase.to_sym, false).const_get(:Stub, false)
end
end
diff --git a/lib/gitlab/gitaly_client/attributes_bag.rb b/lib/gitlab/gitaly_client/attributes_bag.rb
index 3f1a0ef4888..f935281ac2e 100644
--- a/lib/gitlab/gitaly_client/attributes_bag.rb
+++ b/lib/gitlab/gitaly_client/attributes_bag.rb
@@ -8,7 +8,7 @@ module Gitlab
extend ActiveSupport::Concern
included do
- attr_accessor(*const_get(:ATTRS))
+ attr_accessor(*const_get(:ATTRS, false))
end
def initialize(params)
@@ -26,7 +26,7 @@ module Gitlab
end
def attributes
- self.class.const_get(:ATTRS)
+ self.class.const_get(:ATTRS, false)
end
end
end
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
index a9f6cfb19cb..22ece0a6a8b 100644
--- a/lib/gitlab/patch/prependable.rb
+++ b/lib/gitlab/patch/prependable.rb
@@ -24,7 +24,7 @@ module Gitlab
super
if const_defined?(:ClassMethods)
- klass_methods = const_get(:ClassMethods)
+ klass_methods = const_get(:ClassMethods, false)
base.singleton_class.prepend klass_methods
base.instance_variable_set(:@_prepended_class_methods, klass_methods)
end
@@ -40,7 +40,7 @@ module Gitlab
super
if instance_variable_defined?(:@_prepended_class_methods)
- const_get(:ClassMethods).prepend @_prepended_class_methods
+ const_get(:ClassMethods, false).prepend @_prepended_class_methods
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 01d4765833e..237b2b7cb0d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7250,6 +7250,9 @@ msgstr ""
msgid "Fork project"
msgstr ""
+msgid "Fork project?"
+msgstr ""
+
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
diff --git a/qa/qa/page/validator.rb b/qa/qa/page/validator.rb
index 9b2d0a1a41d..75e48b5785e 100644
--- a/qa/qa/page/validator.rb
+++ b/qa/qa/page/validator.rb
@@ -17,7 +17,7 @@ module QA
def constants
@consts ||= @module.constants.map do |const|
- @module.const_get(const)
+ @module.const_get(const, false)
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 77d04aa7594..4789b380377 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -65,7 +65,7 @@ module QA
# QA::Runtime::Env.browser.capitalize will work for every driver type except PhantomJS.
# We will have no use to use PhantomJS so this shouldn't be a problem.
- options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize)::Options.new
+ options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize, false)::Options.new
if QA::Runtime::Env.browser == :chrome
options.add_argument("window-size=1480,2200")
diff --git a/qa/qa/runtime/release.rb b/qa/qa/runtime/release.rb
index 4f96e0cf44b..18a6736afcf 100644
--- a/qa/qa/runtime/release.rb
+++ b/qa/qa/runtime/release.rb
@@ -19,7 +19,7 @@ module QA
end
def strategy
- QA.const_get("QA::#{version}::Strategy")
+ Object.const_get("QA::#{version}::Strategy", false)
end
def self.method_missing(name, *args)
diff --git a/rubocop/cop/gitlab/const_get_inherit_false.rb b/rubocop/cop/gitlab/const_get_inherit_false.rb
new file mode 100644
index 00000000000..3d3bbc4c8d3
--- /dev/null
+++ b/rubocop/cop/gitlab/const_get_inherit_false.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ module Gitlab
+ # Cop that encourages usage of inherit=false for 2nd argument when using const_get.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab/issues/27678
+ class ConstGetInheritFalse < RuboCop::Cop::Cop
+ MSG = 'Use inherit=false when using const_get.'
+
+ def_node_matcher :const_get?, <<~PATTERN
+ (send _ :const_get ...)
+ PATTERN
+
+ def on_send(node)
+ return unless const_get?(node)
+ return if second_argument(node)&.false_type?
+
+ add_offense(node, location: :selector)
+ end
+
+ def autocorrect(node)
+ lambda do |corrector|
+ if arg = second_argument(node)
+ corrector.replace(arg.source_range, 'false')
+ else
+ first_argument = node.arguments[0]
+ corrector.insert_after(first_argument.source_range, ', false')
+ end
+ end
+ end
+
+ private
+
+ def second_argument(node)
+ node.arguments[1]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index 8e7df62ea75..70679aa1e78 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -1,3 +1,4 @@
+require_relative 'cop/gitlab/const_get_inherit_false'
require_relative 'cop/gitlab/module_with_instance_variables'
require_relative 'cop/gitlab/predicate_memoization'
require_relative 'cop/gitlab/httparty'
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index ef464d3d6e0..a060cd7d6f8 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -15,7 +15,7 @@ FactoryBot.define do
end
path do
- uploader_instance = Object.const_get(uploader.to_s).new(model, mount_point)
+ uploader_instance = Object.const_get(uploader.to_s, false).new(model, mount_point)
File.join(uploader_instance.store_dir, filename)
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index e5e16e69833..4996e27c2e6 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -270,4 +270,32 @@ describe BlobHelper do
end
end
end
+
+ describe '#ide_fork_and_edit_path' do
+ let(:project) { create(:project) }
+ let(:current_user) { create(:user) }
+ let(:can_push_code) { true }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(helper).to receive(:can?).and_return(can_push_code)
+ end
+
+ it 'returns path to fork the repo with a redirect param to the full IDE path' do
+ uri = URI(helper.ide_fork_and_edit_path(project, "master", ""))
+ params = CGI.unescape(uri.query)
+
+ expect(uri.path).to eq("/#{project.namespace.path}/#{project.path}/-/forks")
+ expect(params).to include("continue[to]=/-/ide/project/#{project.namespace.path}/#{project.path}/edit/master")
+ expect(params).to include("namespace_key=#{current_user.namespace.id}")
+ end
+
+ context 'when user is not logged in' do
+ let(:current_user) { nil }
+
+ it 'returns nil' do
+ expect(helper.ide_fork_and_edit_path(project, "master", "")).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb
index 332817d6585..a395b021f32 100644
--- a/spec/lib/banzai/filter/video_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/video_link_filter_spec.rb
@@ -32,6 +32,7 @@ describe Banzai::Filter::VideoLinkFilter do
expect(video.name).to eq 'video'
expect(video['src']).to eq src
+ expect(video['width']).to eq "100%"
expect(paragraph.name).to eq 'p'
diff --git a/spec/lib/gitlab/ci/status/external/factory_spec.rb b/spec/lib/gitlab/ci/status/external/factory_spec.rb
index 3b90fb60cca..9d7dfc42848 100644
--- a/spec/lib/gitlab/ci/status/external/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/external/factory_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::Ci::Status::External::Factory do
end
let(:expected_status) do
- Gitlab::Ci::Status.const_get(simple_status.capitalize)
+ Gitlab::Ci::Status.const_get(simple_status.capitalize, false)
end
it "fabricates a core status #{simple_status}" do
diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb
index b51c0bec47e..c6d7a1ec5d9 100644
--- a/spec/lib/gitlab/ci/status/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/factory_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Ci::Status::Factory do
let(:resource) { double('resource', status: simple_status) }
let(:expected_status) do
- Gitlab::Ci::Status.const_get(simple_status.capitalize)
+ Gitlab::Ci::Status.const_get(simple_status.capitalize, false)
end
it "fabricates a core status #{simple_status}" do
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
index 8a36cd1b658..3acc767ab7a 100644
--- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
let(:pipeline) { create(:ci_pipeline, status: simple_status) }
let(:expected_status) do
- Gitlab::Ci::Status.const_get(simple_status.capitalize)
+ Gitlab::Ci::Status.const_get(simple_status.capitalize, false)
end
it "matches correct core status for #{simple_status}" do
diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
index 8f5b1ff62a5..dcb53712157 100644
--- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
@@ -34,7 +34,7 @@ describe Gitlab::Ci::Status::Stage::Factory do
it "fabricates a core status #{core_status}" do
expect(status).to be_a(
- Gitlab::Ci::Status.const_get(core_status.capitalize))
+ Gitlab::Ci::Status.const_get(core_status.capitalize, false))
end
it 'extends core status with common stage methods' do
diff --git a/spec/lib/gitlab/config/entry/simplifiable_spec.rb b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
index 65e18fe3f10..5c208cab449 100644
--- a/spec/lib/gitlab/config/entry/simplifiable_spec.rb
+++ b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
@@ -24,9 +24,9 @@ describe Gitlab::Config::Entry::Simplifiable do
let(:unknown) { double('unknown strategy') }
before do
- stub_const("#{described_class.name}::Something", first)
- stub_const("#{described_class.name}::DifferentOne", second)
- stub_const("#{described_class.name}::UnknownStrategy", unknown)
+ entry::Something = first
+ entry::DifferentOne = second
+ entry::UnknownStrategy = unknown
end
context 'when first strategy should be used' do
diff --git a/spec/lib/gitlab/patch/prependable_spec.rb b/spec/lib/gitlab/patch/prependable_spec.rb
index 725d733d176..255324f89d5 100644
--- a/spec/lib/gitlab/patch/prependable_spec.rb
+++ b/spec/lib/gitlab/patch/prependable_spec.rb
@@ -72,8 +72,8 @@ describe Gitlab::Patch::Prependable do
expect(subject.ancestors.take(3)).to eq([subject, ee, ce])
expect(subject.singleton_class.ancestors.take(3))
.to eq([subject.singleton_class,
- ee.const_get(:ClassMethods),
- ce.const_get(:ClassMethods)])
+ ee.const_get(:ClassMethods, false),
+ ce.const_get(:ClassMethods, false)])
end
it 'prepends only once even if called twice' do
@@ -115,8 +115,8 @@ describe Gitlab::Patch::Prependable do
it 'has the expected ancestors' do
expect(subject.ancestors.take(3)).to eq([ee, ce, subject])
expect(subject.singleton_class.ancestors.take(3))
- .to eq([ee.const_get(:ClassMethods),
- ce.const_get(:ClassMethods),
+ .to eq([ee.const_get(:ClassMethods, false),
+ ce.const_get(:ClassMethods, false),
subject.singleton_class])
end
@@ -152,7 +152,7 @@ describe Gitlab::Patch::Prependable do
it 'has the expected ancestors' do
expect(subject.ancestors.take(2)).to eq([ee, subject])
expect(subject.singleton_class.ancestors.take(2))
- .to eq([ee.const_get(:ClassMethods),
+ .to eq([ee.const_get(:ClassMethods, false),
subject.singleton_class])
end
diff --git a/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
new file mode 100644
index 00000000000..0ff06b431eb
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/gitlab/const_get_inherit_false'
+
+describe RuboCop::Cop::Gitlab::ConstGetInheritFalse do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'Object.const_get' do
+ it 'registers an offense with no 2nd argument' do
+ expect_offense(<<~PATTERN.strip_indent)
+ Object.const_get(:CONSTANT)
+ ^^^^^^^^^ Use inherit=false when using const_get.
+ PATTERN
+ end
+
+ it 'autocorrects' do
+ expect(autocorrect_source('Object.const_get(:CONSTANT)')).to eq('Object.const_get(:CONSTANT, false)')
+ end
+
+ context 'inherit=false' do
+ it 'does not register an offense' do
+ expect_no_offenses(<<~PATTERN.strip_indent)
+ Object.const_get(:CONSTANT, false)
+ PATTERN
+ end
+ end
+
+ context 'inherit=true' do
+ it 'registers an offense' do
+ expect_offense(<<~PATTERN.strip_indent)
+ Object.const_get(:CONSTANT, true)
+ ^^^^^^^^^ Use inherit=false when using const_get.
+ PATTERN
+ end
+
+ it 'autocorrects' do
+ expect(autocorrect_source('Object.const_get(:CONSTANT, true)')).to eq('Object.const_get(:CONSTANT, false)')
+ end
+ end
+ end
+
+ context 'const_get for a nested class' do
+ it 'registers an offense on reload usage' do
+ expect_offense(<<~PATTERN.strip_indent)
+ Nested::Blog.const_get(:CONSTANT)
+ ^^^^^^^^^ Use inherit=false when using const_get.
+ PATTERN
+ end
+
+ it 'autocorrects' do
+ expect(autocorrect_source('Nested::Blag.const_get(:CONSTANT)')).to eq('Nested::Blag.const_get(:CONSTANT, false)')
+ end
+
+ context 'inherit=false' do
+ it 'does not register an offense' do
+ expect_no_offenses(<<~PATTERN.strip_indent)
+ Nested::Blog.const_get(:CONSTANT, false)
+ PATTERN
+ end
+ end
+
+ context 'inherit=true' do
+ it 'registers an offense if inherit is true' do
+ expect_offense(<<~PATTERN.strip_indent)
+ Nested::Blog.const_get(:CONSTANT, true)
+ ^^^^^^^^^ Use inherit=false when using const_get.
+ PATTERN
+ end
+
+ it 'autocorrects' do
+ expect(autocorrect_source('Nested::Blag.const_get(:CONSTANT, true)')).to eq('Nested::Blag.const_get(:CONSTANT, false)')
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
index 6f06d323a82..f95b612eb70 100644
--- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
@@ -75,7 +75,7 @@ shared_examples 'cluster application status specs' do |application_name|
subject.reload
- expect(subject.version).to eq(subject.class.const_get(:VERSION))
+ expect(subject.version).to eq(subject.class.const_get(:VERSION, false))
end
context 'application is updating' do
@@ -104,7 +104,7 @@ shared_examples 'cluster application status specs' do |application_name|
subject.reload
- expect(subject.version).to eq(subject.class.const_get(:VERSION))
+ expect(subject.version).to eq(subject.class.const_get(:VERSION, false))
end
end
end
diff --git a/spec/support/shared_examples/models/cluster_application_version_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_version_shared_examples.rb
index 181b102e685..ba02da41b53 100644
--- a/spec/support/shared_examples/models/cluster_application_version_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_version_shared_examples.rb
@@ -12,7 +12,7 @@ shared_examples 'cluster application version specs' do |application_name|
context 'version is the same as VERSION' do
let(:application) { build(application_name) }
- let(:version) { application.class.const_get(:VERSION) }
+ let(:version) { application.class.const_get(:VERSION, false) }
it { is_expected.to be_falsey }
end
diff --git a/spec/views/projects/tree/_tree_header.html.haml_spec.rb b/spec/views/projects/tree/_tree_header.html.haml_spec.rb
new file mode 100644
index 00000000000..4b71ea9ffe3
--- /dev/null
+++ b/spec/views/projects/tree/_tree_header.html.haml_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'projects/tree/_tree_header' do
+ let(:project) { create(:project, :repository) }
+ let(:current_user) { create(:user) }
+ let(:repository) { project.repository }
+
+ before do
+ assign(:project, project)
+ assign(:repository, repository)
+ assign(:id, File.join('master', ''))
+ assign(:ref, 'master')
+
+ allow(view).to receive(:current_user).and_return(current_user)
+ allow(view).to receive(:can_collaborate_with_project?) { true }
+ end
+
+ it 'does not render the WebIDE button when user cannot create fork or cannot open MR' do
+ allow(view).to receive(:can?) { false }
+
+ render
+
+ expect(rendered).not_to have_link('Web IDE')
+ end
+
+ it 'renders the WebIDE button when user can create fork and can open MR in project' do
+ allow(view).to receive(:can?) { true }
+
+ render
+
+ expect(rendered).to have_link('Web IDE')
+ end
+
+ it 'opens a popup confirming a fork if the user can create fork/MR but cannot collaborate with the project' do
+ allow(view).to receive(:can?) { true }
+ allow(view).to receive(:can_collaborate_with_project?) { false }
+
+ render
+
+ expect(rendered).to have_link('Web IDE', href: '#modal-confirm-fork')
+ end
+end