diff options
32 files changed, 309 insertions, 170 deletions
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 64d6f49576f..ac48f08435e 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -71,13 +71,16 @@ review-deploy: - download_chart - date - deploy || (display_deployment_debug && exit 1) + - verify_deploy || exit 1 - disable_sign_ups || (delete_release && exit 1) after_script: # Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan. # Set DAST_RUN to true when jobs are manually scheduled. - if [ "$DAST_RUN" == "true" ]; then source scripts/review_apps/seed-dast-test-data.sh; TRACE=1 trigger_proj_user_creation; fi artifacts: - paths: [environment_url.txt] + paths: + - environment_url.txt + - curl_output.txt expire_in: 7 days when: always diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index d9dc0025658..b58600fa058 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -111,10 +111,17 @@ export default { this.showCount = this.scrollHeight() > Math.ceil(this.listHeight()); }); }, - }, - created() { - eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm); - eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop); + 'list.id': { + handler(id, oldVal) { + if (id) { + eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm); + eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop); + eventHub.$off(`toggle-issue-form-${oldVal}`, this.toggleForm); + eventHub.$off(`scroll-board-list-${oldVal}`, this.scrollToTop); + } + }, + immediate: true, + }, }, beforeDestroy() { eventHub.$off(`toggle-issue-form-${this.list.id}`, this.toggleForm); diff --git a/app/assets/javascripts/boards/components/board_list_deprecated.vue b/app/assets/javascripts/boards/components/board_list_deprecated.vue index c9ced425c1d..9b3e7e1547d 100644 --- a/app/assets/javascripts/boards/components/board_list_deprecated.vue +++ b/app/assets/javascripts/boards/components/board_list_deprecated.vue @@ -91,6 +91,13 @@ export default { } }); }, + 'list.id': { + handler(id) { + if (id) { + eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm); + } + }, + }, }, created() { eventHub.$on(`toggle-issue-form-${this.list.id}`, this.toggleForm); diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb index 3b775af9722..0655d779a4e 100644 --- a/app/controllers/groups/group_links_controller.rb +++ b/app/controllers/groups/group_links_controller.rb @@ -11,8 +11,8 @@ class Groups::GroupLinksController < Groups::ApplicationController if shared_with_group result = Groups::GroupLinks::CreateService - .new(shared_with_group, current_user, group_link_create_params) - .execute(group) + .new(group, shared_with_group, current_user, group_link_create_params) + .execute return render_404 if result[:http_status] == 404 diff --git a/app/models/group.rb b/app/models/group.rb index bbae40f4442..41f8cfa8b26 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -442,6 +442,12 @@ class Group < Namespace end end + def self_and_descendants_ids + strong_memoize(:self_and_descendants_ids) do + self_and_descendants.pluck(:id) + end + end + def direct_members GroupMember.active_without_invites_and_requests .non_minimal_access diff --git a/app/services/groups/group_links/create_service.rb b/app/services/groups/group_links/create_service.rb index 0a60140d037..5f81e5972b0 100644 --- a/app/services/groups/group_links/create_service.rb +++ b/app/services/groups/group_links/create_service.rb @@ -3,27 +3,51 @@ module Groups module GroupLinks class CreateService < Groups::BaseService - def execute(shared_group) - unless group && shared_group && + def initialize(shared_group, shared_with_group, user, params) + @shared_group = shared_group + super(shared_with_group, user, params) + end + + def execute + unless shared_with_group && shared_group && can?(current_user, :admin_group_member, shared_group) && - can?(current_user, :read_group, group) + can?(current_user, :read_group, shared_with_group) && + sharing_allowed? return error('Not Found', 404) end link = GroupGroupLink.new( shared_group: shared_group, - shared_with_group: group, + shared_with_group: shared_with_group, group_access: params[:shared_group_access], expires_at: params[:expires_at] ) if link.save - group.refresh_members_authorized_projects(direct_members_only: true) + shared_with_group.refresh_members_authorized_projects(direct_members_only: true) success(link: link) else error(link.errors.full_messages.to_sentence, 409) end end + + private + + attr_reader :shared_group + + alias_method :shared_with_group, :group + + def sharing_allowed? + sharing_outside_hierarchy_allowed? || within_hierarchy? + end + + def sharing_outside_hierarchy_allowed? + !shared_group.root_ancestor.namespace_settings.prevent_sharing_groups_outside_hierarchy + end + + def within_hierarchy? + shared_group.root_ancestor.self_and_descendants_ids.include?(shared_with_group.id) + end end end end diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb index 1ffc51bec77..f258aa13376 100644 --- a/app/services/web_hook_service.rb +++ b/app/services/web_hook_service.rb @@ -29,15 +29,17 @@ class WebHookService GITLAB_EVENT_HEADER = 'X-Gitlab-Event' attr_accessor :hook, :data, :hook_name, :request_options + attr_reader :uniqueness_token def self.hook_to_event(hook_name) hook_name.to_s.singularize.titleize end - def initialize(hook, data, hook_name) + def initialize(hook, data, hook_name, uniqueness_token = nil) @hook = hook @data = data @hook_name = hook_name.to_s + @uniqueness_token = uniqueness_token @request_options = { timeout: Gitlab.config.gitlab.webhook_timeout, allow_local_requests: hook.allow_local_requests? @@ -123,10 +125,8 @@ class WebHookService end def log_execution(trigger:, url:, request_data:, response:, execution_duration:, error_message: nil) - handle_failure(response, hook) - - WebHookLog.create( - web_hook: hook, + category = response_category(response) + log_data = { trigger: trigger, url: url, execution_duration: execution_duration, @@ -136,16 +136,19 @@ class WebHookService response_body: safe_response_body(response), response_status: response.code, internal_error_message: error_message - ) + } + + ::WebHooks::LogExecutionWorker + .perform_async(hook.id, log_data, category, uniqueness_token) end - def handle_failure(response, hook) + def response_category(response) if response.success? || response.redirection? - hook.enable! + :ok elsif response.internal_server_error? - hook.backoff! + :error else - hook.failed! + :failed end end diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb new file mode 100644 index 00000000000..6e58e15f093 --- /dev/null +++ b/app/services/web_hooks/log_execution_service.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module WebHooks + class LogExecutionService + attr_reader :hook, :log_data, :response_category + + def initialize(hook:, log_data:, response_category:) + @hook = hook + @log_data = log_data + @response_category = response_category + end + + def execute + update_hook_executability + log_execution + end + + private + + def log_execution + WebHookLog.create!(web_hook: hook, **log_data.transform_keys(&:to_sym)) + end + + def update_hook_executability + case response_category + when :ok + hook.enable! + when :error + hook.backoff! + when :failed + hook.failed! + end + end + end +end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index c9ed2e5fc84..985ca73d0d2 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -2828,6 +2828,15 @@ :idempotent: true :tags: - :exclude_from_kubernetes +- :name: web_hooks_log_execution + :worker_name: WebHooks::LogExecutionWorker + :feature_category: :integrations + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: wikis_git_garbage_collect :worker_name: Wikis::GitGarbageCollectWorker :feature_category: :gitaly diff --git a/app/workers/web_hook_worker.rb b/app/workers/web_hook_worker.rb index ded14d6d314..3480f49d640 100644 --- a/app/workers/web_hook_worker.rb +++ b/app/workers/web_hook_worker.rb @@ -16,7 +16,7 @@ class WebHookWorker hook = WebHook.find(hook_id) data = data.with_indifferent_access - WebHookService.new(hook, data, hook_name).execute + WebHookService.new(hook, data, hook_name, jid).execute end end # rubocop:enable Scalability/IdempotentWorker diff --git a/app/workers/web_hooks/log_execution_worker.rb b/app/workers/web_hooks/log_execution_worker.rb new file mode 100644 index 00000000000..58059370200 --- /dev/null +++ b/app/workers/web_hooks/log_execution_worker.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module WebHooks + class LogExecutionWorker + include ApplicationWorker + + idempotent! + feature_category :integrations + urgency :low + + # This worker accepts an extra argument. This enables us to + # treat this worker as idempotent. Currently this is set to + # the Job ID (jid) of the parent worker. + def perform(hook_id, log_data, response_category, _unique_by) + hook = WebHook.find_by_id(hook_id) + + return unless hook # hook has been deleted before we could run. + + ::WebHooks::LogExecutionService + .new(hook: hook, log_data: log_data, response_category: response_category.to_sym) + .execute + end + end +end diff --git a/config/feature_flags/development/disable_service_templates.yml b/config/feature_flags/development/disable_service_templates.yml index 07e52224b98..5e9972a2171 100644 --- a/config/feature_flags/development/disable_service_templates.yml +++ b/config/feature_flags/development/disable_service_templates.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327436 milestone: '13.12' type: development group: group::ecosystem -default_enabled: false +default_enabled: true diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 65c57753486..f315a8cb28e 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -400,6 +400,8 @@ - 1 - - web_hooks_destroy - 1 +- - web_hooks_log_execution + - 1 - - wikis_git_garbage_collect - 1 - - x509_certificate_revoke diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index e9df5857695..caa7429a964 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -713,6 +713,28 @@ Input type: `AwardEmojiToggleInput` | <a id="mutationawardemojitoggleerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationawardemojitoggletoggledon"></a>`toggledOn` | [`Boolean!`](#boolean) | Indicates the status of the emoji. True if the toggle awarded the emoji, and false if the toggle removed the emoji. | +### `Mutation.boardEpicCreate` + +Input type: `BoardEpicCreateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationboardepiccreateboardid"></a>`boardId` | [`BoardsEpicBoardID!`](#boardsepicboardid) | Global ID of the board that the epic is in. | +| <a id="mutationboardepiccreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationboardepiccreategrouppath"></a>`groupPath` | [`ID!`](#id) | Group the epic to create is in. | +| <a id="mutationboardepiccreatelistid"></a>`listId` | [`BoardsEpicListID!`](#boardsepiclistid) | Global ID of the epic board list in which epic will be created. | +| <a id="mutationboardepiccreatetitle"></a>`title` | [`String!`](#string) | Title of the epic. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationboardepiccreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationboardepiccreateepic"></a>`epic` | [`Epic`](#epic) | Epic after creation. | +| <a id="mutationboardepiccreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | + ### `Mutation.boardListCreate` Input type: `BoardListCreateInput` diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index 04f5ae3df7a..0133d36a28c 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -7262,9 +7262,9 @@ Tiers: `premium`, `ultimate` Whether this is a trial license or not -[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/settings/20210204124851_license_trial.yml) +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/license/20210204124851_license_trial.yml) -Group: `group::product intelligence` +Group: `group::license` Status: `data_available` diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index e82c21947e2..38bcc2b9932 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -34,10 +34,3 @@ Visit the links below for details: - [Jira](../integration/jira/index.md) - [Redmine](../user/project/integrations/redmine.md) - [YouTrack](../user/project/integrations/youtrack.md) - -### Service Template - -To avoid configuring each project's service individually, GitLab provides the ability to set -Service Templates. These can then be overridden in each project's settings. - -Read more on [Services Templates](../user/project/integrations/services_templates.md). diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md index fe378122cb3..edcc662d8fd 100644 --- a/doc/topics/autodevops/requirements.md +++ b/doc/topics/autodevops/requirements.md @@ -77,10 +77,9 @@ To make full use of Auto DevOps with Kubernetes, you need: instruct GitLab to query an in-cluster Prometheus by enabling the [Prometheus cluster integration](../../user/clusters/integrations.md#prometheus-cluster-integration). - The [Prometheus service](../../user/project/integrations/prometheus.md) - integration must be enabled for the project, or enabled as a - [default service template](../../user/project/integrations/services_templates.md) - for the entire GitLab installation. + The [Prometheus integration](../../user/project/integrations/prometheus.md) + integration must be activated for the project, or activated at the group or instance level. + Learn more about [Project integration management](../../user/admin_area/settings/project_integration_management.md). To get response metrics (in addition to system metrics), you must [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-nginx-ingress-monitoring). diff --git a/doc/update/index.md b/doc/update/index.md index d2ab0858edf..2ac22631289 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -194,7 +194,7 @@ Find where your version sits in the upgrade path below, and upgrade GitLab accordingly, while also consulting the [version-specific upgrade instructions](#version-specific-upgrading-instructions): -`8.11.Z` -> `8.12.0` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> [latest `13.Y.Z`](https://about.gitlab.com/releases/categories/releases/) +`8.11.Z` -> `8.12.0` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> [latest `13.12.Z`](https://about.gitlab.com/releases/categories/releases/) -> [latest `14.0.Z`](https://about.gitlab.com/releases/categories/releases/) The following table, while not exhaustive, shows some examples of the supported upgrade paths. diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 6c7f985dce6..0da868b78ce 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -35,7 +35,7 @@ The Admin Area is made up of the following sections: | **{location-dot}** Geo | Configure and maintain [Geo nodes](geo_nodes.md). | | **{key}** Deploy keys | Create instance-wide [SSH deploy keys](../project/deploy_keys/index.md). | | **{lock}** Credentials | View [credentials](credentials_inventory.md) that can be used to access your instance. | -| **{template}** Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. | +| **{template}** Integrations | Manage [instance-level default settings](settings/project_integration_management.md) for a project integration. | | **{labels}** Labels | Create and maintain [labels](labels.md) for your GitLab instance. | | **{appearance}** Appearance | Customize [GitLab appearance](appearance.md). | | **{settings}** Settings | Modify the [settings](settings/index.md) for your GitLab instance. | diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md index e74800c176d..b0ae290e7cd 100644 --- a/doc/user/project/integrations/overview.md +++ b/doc/user/project/integrations/overview.md @@ -80,19 +80,6 @@ instance configuration or provide custom settings. Read more about [Project integration management](../../admin_area/settings/project_integration_management.md). -### Service templates - -[Service templates](services_templates.md) were a way to set predefined values for -a project integration across all new projects on the instance. They are deprecated and -[scheduled to be removed](https://gitlab.com/gitlab-org/gitlab/-/issues/268032) -in GitLab 14.0. - -GitLab recommends you use [project integration management](../../admin_area/settings/project_integration_management.md) -instead of service templates. GitLab versions 13.3 and later provide -[instance-level integrations](../../admin_area/settings/project_integration_management.md#project-integration-management) -you can use. -instead. - ## Troubleshooting integrations Some integrations use service hooks for integration with external applications. To confirm which ones use service hooks, see the [integrations listing](#integrations-listing) above. GitLab stores details of service hook requests made within the last 2 days. To view details of the requests, go to that integration's configuration page. diff --git a/doc/user/project/integrations/services_templates.md b/doc/user/project/integrations/services_templates.md index 93ce74eb735..37df48c75f8 100644 --- a/doc/user/project/integrations/services_templates.md +++ b/doc/user/project/integrations/services_templates.md @@ -1,67 +1,9 @@ --- -stage: Create -group: Ecosystem -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +redirect_to: '../../admin_area/settings/project_integration_management.md' +remove_date: '2021-09-09' --- -# Service templates **(FREE)** +This document was moved to [another location](../../admin_area/settings/project_integration_management.md). -WARNING: -Service templates are [deprecated and scheduled to be removed](https://gitlab.com/gitlab-org/gitlab/-/issues/268032) -in GitLab 14.0. Use [project integration management](#central-administration-of-project-integrations) instead. - -Using a service template, GitLab administrators can: - -- Provide default values for configuring integrations when creating new projects. -- Bulk configure all existing projects in one step. - -When you enable a service template: - -- The defaults are applied to **all** existing projects that either: - - Don't already have the integration enabled. - - Don't have custom values stored for already enabled integrations. -- Values are populated on each project's configuration page for the applicable - integration. -- Settings are stored at the project level. - -If you disable the template: - -- GitLab default values again become the default values for integrations on - new projects. -- Projects previously configured using the template continue to use those settings. - -If you change the template, the revised values are applied to new projects. This feature -does not provide central administration of integration settings. - -## Central administration of project integrations - -A new set of features is being introduced in GitLab to provide more control over -how integrations are configured at the instance, group, and project level. For -more information, read more about: - -- [Setting up project integration management](../../admin_area/settings/project_integration_management.md) (introduced in GitLab 13.3) -- [Our plans for managing integrations](https://gitlab.com/groups/gitlab-org/-/epics/2137). - -## Enable a service template - -Navigate to the **Admin Area > Service Templates** and choose the service -template you wish to create. - -Recommendation: - -- Test the settings on some projects individually before enabling a template. -- Copy the working settings from a project to the template. - -There is no "Test settings" option when enabling templates. If the settings do not work, -these incorrect settings are applied to all existing projects that do not already have -the integration configured. Fixing the integration then needs to be done project-by-project. - -## Service for external issue trackers - -The following image shows an example service template for Redmine. - -![Redmine service template](img/services_templates_redmine_example.png) - -For each project, you still need to configure the issue tracking -URLs by replacing `:issues_tracker_id` in the above screenshot with the ID used -by your external issue tracker. +<!-- This redirect file can be deleted after <2021-09-09>. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page --> diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 1a604e70bf1..0efb8b57885 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -372,7 +372,7 @@ module API expires_at: params[:expires_at] } - result = ::Groups::GroupLinks::CreateService.new(shared_with_group, current_user, group_link_create_params).execute(shared_group) + result = ::Groups::GroupLinks::CreateService.new(shared_group, shared_with_group, current_user, group_link_create_params).execute shared_group.preload_shared_group_links if result[:status] == :success diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index 5af270c4cff..5680950bba8 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -11,7 +11,7 @@ # * test: TEST_DISABLED # * code_quality: CODE_QUALITY_DISABLED # * license_management: LICENSE_MANAGEMENT_DISABLED -# * browser_performance: PERFORMANCE_DISABLED +# * browser_performance: BROWSER_PERFORMANCE_DISABLED # * load_performance: LOAD_PERFORMANCE_DISABLED # * sast: SAST_DISABLED # * secret_detection: SECRET_DETECTION_DISABLED diff --git a/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml index 657ac43b78e..d0595491400 100644 --- a/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml @@ -1,14 +1,14 @@ # Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/secret_detection # -# Configure secret detection with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/README.html). -# List of available variables: https://docs.gitlab.com/ee/user/application_security/secret_detection/#available-variables +# Configure the scanning tool through the environment variables. +# List of the variables: https://docs.gitlab.com/ee/user/application_security/secret_detection/#available-variables +# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables variables: SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" SECRETS_ANALYZER_VERSION: "3" SECRET_DETECTION_EXCLUDED_PATHS: "" - .secret-analyzer: stage: test image: "$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION" @@ -20,23 +20,15 @@ variables: reports: secret_detection: gl-secret-detection-report.json -secret_detection_default_branch: - extends: .secret-analyzer - rules: - - if: $SECRET_DETECTION_DISABLED - when: never - - if: $CI_DEFAULT_BRANCH == $CI_COMMIT_BRANCH - script: - - /analyzer run - secret_detection: extends: .secret-analyzer rules: - if: $SECRET_DETECTION_DISABLED when: never - - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH + - if: $CI_COMMIT_BRANCH script: - if [[ $CI_COMMIT_TAG ]]; then echo "Skipping Secret Detection for tags. No code changes have occurred."; exit 0; fi + - if [[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]]; then echo "Running Secret Detection on default branch."; /analyzer run; exit 0; fi - git fetch origin $CI_DEFAULT_BRANCH $CI_COMMIT_REF_NAME - git log --left-right --cherry-pick --pretty=format:"%H" refs/remotes/origin/$CI_DEFAULT_BRANCH...refs/remotes/origin/$CI_COMMIT_REF_NAME > "$CI_COMMIT_SHA"_commit_list.txt - export SECRET_DETECTION_COMMITS_FILE="$CI_COMMIT_SHA"_commit_list.txt diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5daeb97fa0f..968eddfe3d9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -960,9 +960,6 @@ msgstr "" msgid "%{total} warnings found: showing first %{warningsDisplayed}" msgstr "" -msgid "%{type} License" -msgstr "" - msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc." msgstr "" @@ -19599,6 +19596,9 @@ msgstr "" msgid "License ID:" msgstr "" +msgid "License file" +msgstr "" + msgid "License overview" msgstr "" @@ -31671,6 +31671,9 @@ msgstr "" msgid "SuperSonics|Buy subscription" msgstr "" +msgid "SuperSonics|Cloud license" +msgstr "" + msgid "SuperSonics|Enter activation code" msgstr "" @@ -31758,7 +31761,7 @@ msgstr "" msgid "SuperSonics|Type" msgstr "" -msgid "SuperSonics|Upload a legacy license" +msgid "SuperSonics|Upload a license file" msgstr "" msgid "SuperSonics|Users in subscription" diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index c1d274af56d..80b239b7e0d 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -127,13 +127,12 @@ function disable_sign_ups() { fi # Create the root token - local ruby_cmd="token = User.find_by_username('root').personal_access_tokens.create(scopes: [:api], name: 'Token to disable sign-ups'); token.set_token('${REVIEW_APPS_ROOT_TOKEN}'); begin; token.save!; rescue(ActiveRecord::RecordNotUnique); end" - retry "run_task \"${ruby_cmd}\"" + local set_token_rb="token = User.find_by_username('root').personal_access_tokens.create(scopes: [:api], name: 'Token to disable sign-ups'); token.set_token('${REVIEW_APPS_ROOT_TOKEN}'); begin; token.save!; rescue(ActiveRecord::RecordNotUnique); end" + retry "run_task \"${set_token_rb}\"" # Disable sign-ups - local signup_enabled=$(retry 'curl --silent --show-error --request PUT --header "PRIVATE-TOKEN: ${REVIEW_APPS_ROOT_TOKEN}" "${CI_ENVIRONMENT_URL}/api/v4/application/settings?signup_enabled=false" | jq ".signup_enabled"') - - if [[ "${signup_enabled}" == "false" ]]; then + local disable_signup_rb="Gitlab::CurrentSettings.current_application_settings.update!(signup_enabled: false)" + if (retry "run_task \"${disable_signup_rb}\""); then echoinfo "Sign-ups have been disabled successfully." else echoerr "Sign-ups are still enabled!" @@ -365,6 +364,18 @@ EOF eval "${HELM_CMD}" } +function verify_deploy() { + echoinfo "Verifying deployment at ${CI_ENVIRONMENT_URL}" + + if wait_for_url "${CI_ENVIRONMENT_URL}" curl_output.txt; then + echoinfo "Review app is deployed to ${CI_ENVIRONMENT_URL}" + return 0 + else + echoerr "Review app is not available at ${CI_ENVIRONMENT_URL}. See curl_output.txt artifact for detail." + return 1 + fi +} + function display_deployment_debug() { local namespace="${CI_ENVIRONMENT_SLUG}" local release="${CI_ENVIRONMENT_SLUG}" diff --git a/scripts/utils.sh b/scripts/utils.sh index d4436e1171d..6486727c828 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -13,6 +13,18 @@ function retry() { return 1 } +function wait_for_url() { + local url="${1}" + local curl_output="${2}" + + echo "Waiting for ${url}" + + timeout -s 1 60 bash -c \ + 'while [[ "$(curl -s -o ${1} -L -w ''%{http_code}'' ${0})" != "200" ]]; \ + do echo "." && sleep 5; \ + done' ${url} ${curl_output} +} + function bundle_install_script() { local extra_install_args="${1}" diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 129d03d17f3..d423377886a 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -120,6 +120,32 @@ RSpec.describe 'Issue Boards new issue', :js do expect(page).to have_content 'Label 1' end end + + it 'allows creating an issue in newly created list' do + click_button 'Create list' + wait_for_all_requests + + click_button 'Select a label' + find('label', text: label.title).click + click_button 'Add to board' + + wait_for_all_requests + + page.within('.board:nth-child(2)') do + click_button('New issue') + + page.within(first('.board-new-issue-form')) do + find('.form-control').set('new issue') + click_button 'Create issue' + end + + wait_for_all_requests + + page.within('.board-card') do + expect(page).to have_content 'new issue' + end + end + end end context 'unauthorized user' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 33cf928c0cb..052727401dd 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -533,7 +533,7 @@ RSpec.describe Ci::CreatePipelineService do it 'pull it from Auto-DevOps' do pipeline = execute_service expect(pipeline).to be_auto_devops_source - expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection_default_branch semgrep-sast test]) + expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection semgrep-sast test]) end end diff --git a/spec/services/groups/group_links/create_service_spec.rb b/spec/services/groups/group_links/create_service_spec.rb index df994b9f2a3..b1bb9a8de23 100644 --- a/spec/services/groups/group_links/create_service_spec.rb +++ b/spec/services/groups/group_links/create_service_spec.rb @@ -11,8 +11,8 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do let_it_be(:group) { create(:group, :private, parent: group_parent) } let_it_be(:group_child) { create(:group, :private, parent: group) } - let_it_be(:shared_group_parent) { create(:group, :private) } - let_it_be(:shared_group) { create(:group, :private, parent: shared_group_parent) } + let_it_be(:shared_group_parent, refind: true) { create(:group, :private) } + let_it_be(:shared_group, refind: true) { create(:group, :private, parent: shared_group_parent) } let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) } let_it_be(:project_parent) { create(:project, group: shared_group_parent) } @@ -28,7 +28,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do let(:user) { group_user } - subject { described_class.new(group, user, opts) } + subject { described_class.new(shared_group, group, user, opts) } before do group.add_guest(group_user) @@ -36,11 +36,11 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do end it 'adds group to another group' do - expect { subject.execute(shared_group) }.to change { group.shared_group_links.count }.from(0).to(1) + expect { subject.execute }.to change { group.shared_group_links.count }.from(0).to(1) end it 'returns false if shared group is blank' do - expect { subject.execute(nil) }.not_to change { group.shared_group_links.count } + expect { described_class.new(nil, group, user, opts) }.not_to change { group.shared_group_links.count } end context 'user does not have access to group' do @@ -51,7 +51,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do end it 'returns error' do - result = subject.execute(shared_group) + result = subject.execute expect(result[:status]).to eq(:error) expect(result[:http_status]).to eq(404) @@ -67,7 +67,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do end it 'returns error' do - result = subject.execute(shared_group) + result = subject.execute expect(result[:status]).to eq(:error) expect(result[:http_status]).to eq(404) @@ -85,7 +85,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do it 'is executed only for the direct members of the group' do expect(UserProjectAccessChangedService).to receive(:new).with(contain_exactly(group_user.id)).and_call_original - subject.execute(shared_group) + subject.execute end end @@ -94,7 +94,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do let(:user) { group_user } it 'create proper authorizations' do - subject.execute(shared_group) + subject.execute expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project)).to be_truthy @@ -106,7 +106,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do let(:user) { parent_group_user } it 'create proper authorizations' do - subject.execute(shared_group) + subject.execute expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project)).to be_falsey @@ -118,7 +118,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do let(:user) { child_group_user } it 'create proper authorizations' do - subject.execute(shared_group) + subject.execute expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project)).to be_falsey @@ -127,4 +127,28 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do end end end + + context 'sharing outside the hierarchy is disabled' do + before do + shared_group_parent.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true) + end + + it 'prevents sharing with a group outside the hierarchy' do + result = subject.execute + + expect(group.reload.shared_group_links.count).to eq(0) + expect(result[:status]).to eq(:error) + expect(result[:http_status]).to eq(404) + end + + it 'allows sharing with a group within the hierarchy' do + sibling_group = create(:group, :private, parent: shared_group_parent) + sibling_group.add_guest(group_user) + + result = described_class.new(shared_group, sibling_group, user, opts).execute + + expect(sibling_group.reload.shared_group_links.count).to eq(1) + expect(result[:status]).to eq(:success) + end + end end diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index d2e0399c6c4..9465fe7f0d6 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -174,13 +174,19 @@ RSpec.describe WebHookService do context 'execution logging' do let(:hook_log) { project_hook.web_hook_logs.last } + def run_service + service_instance.execute + ::WebHooks::LogExecutionWorker.drain + project_hook.reload + end + context 'with success' do before do stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: 'Success') end it 'log successful execution' do - service_instance.execute + run_service expect(hook_log.trigger).to eq('push_hooks') expect(hook_log.url).to eq(project_hook.url) @@ -191,12 +197,16 @@ RSpec.describe WebHookService do expect(hook_log.internal_error_message).to be_nil end + it 'does not log in the service itself' do + expect { service_instance.execute }.not_to change(::WebHookLog, :count) + end + it 'does not increment the failure count' do - expect { service_instance.execute }.not_to change(project_hook, :recent_failures) + expect { run_service }.not_to change(project_hook, :recent_failures) end it 'does not change the disabled_until attribute' do - expect { service_instance.execute }.not_to change(project_hook, :disabled_until) + expect { run_service }.not_to change(project_hook, :disabled_until) end context 'when the hook had previously failed' do @@ -205,7 +215,7 @@ RSpec.describe WebHookService do end it 'resets the failure count' do - expect { service_instance.execute }.to change(project_hook, :recent_failures).to(0) + expect { run_service }.to change(project_hook, :recent_failures).to(0) end end end @@ -216,7 +226,7 @@ RSpec.describe WebHookService do end it 'logs failed execution' do - service_instance.execute + run_service expect(hook_log).to have_attributes( trigger: eq('push_hooks'), @@ -230,17 +240,17 @@ RSpec.describe WebHookService do end it 'increments the failure count' do - expect { service_instance.execute }.to change(project_hook, :recent_failures).by(1) + expect { run_service }.to change(project_hook, :recent_failures).by(1) end it 'does not change the disabled_until attribute' do - expect { service_instance.execute }.not_to change(project_hook, :disabled_until) + expect { run_service }.not_to change(project_hook, :disabled_until) end it 'does not allow the failure count to overflow' do project_hook.update!(recent_failures: 32767) - expect { service_instance.execute }.not_to change(project_hook, :recent_failures) + expect { run_service }.not_to change(project_hook, :recent_failures) end context 'when the web_hooks_disable_failed FF is disabled' do @@ -252,7 +262,7 @@ RSpec.describe WebHookService do it 'does not allow the failure count to overflow' do project_hook.update!(recent_failures: 32767) - expect { service_instance.execute }.not_to change(project_hook, :recent_failures) + expect { run_service }.not_to change(project_hook, :recent_failures) end end end @@ -263,7 +273,7 @@ RSpec.describe WebHookService do end it 'log failed execution' do - service_instance.execute + run_service expect(hook_log.trigger).to eq('push_hooks') expect(hook_log.url).to eq(project_hook.url) @@ -275,17 +285,15 @@ RSpec.describe WebHookService do end it 'does not increment the failure count' do - expect { service_instance.execute }.not_to change(project_hook, :recent_failures) + expect { run_service }.not_to change(project_hook, :recent_failures) end it 'backs off' do - expect(project_hook).to receive(:backoff!).and_call_original - - expect { service_instance.execute }.to change(project_hook, :disabled_until) + expect { run_service }.to change(project_hook, :disabled_until) end it 'increases the backoff count' do - expect { service_instance.execute }.to change(project_hook, :backoff_count).by(1) + expect { run_service }.to change(project_hook, :backoff_count).by(1) end context 'when the previous cool-off was near the maximum' do @@ -294,7 +302,7 @@ RSpec.describe WebHookService do end it 'sets the disabled_until attribute' do - expect { service_instance.execute }.to change(project_hook, :disabled_until).to(1.day.from_now) + expect { run_service }.to change(project_hook, :disabled_until).to(1.day.from_now) end end @@ -304,7 +312,7 @@ RSpec.describe WebHookService do end it 'sets the disabled_until attribute' do - expect { service_instance.execute }.to change(project_hook, :disabled_until).to(1.day.from_now) + expect { run_service }.to change(project_hook, :disabled_until).to(1.day.from_now) end end end @@ -312,7 +320,7 @@ RSpec.describe WebHookService do context 'with unsafe response body' do before do stub_full_request(project_hook.url, method: :post).to_return(status: 200, body: "\xBB") - service_instance.execute + run_service end it 'log successful execution' do diff --git a/spec/workers/web_hook_worker_spec.rb b/spec/workers/web_hook_worker_spec.rb index 066f4f67cb9..548cf4c717a 100644 --- a/spec/workers/web_hook_worker_spec.rb +++ b/spec/workers/web_hook_worker_spec.rb @@ -10,7 +10,7 @@ RSpec.describe WebHookWorker do describe '#perform' do it 'delegates to WebHookService' do - expect_next(WebHookService, project_hook, data.with_indifferent_access, hook_name).to receive(:execute) + expect_next(WebHookService, project_hook, data.with_indifferent_access, hook_name, anything).to receive(:execute) subject.perform(project_hook.id, data, hook_name) end |