Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-08 12:20:17 +00:00
parent 60028378dd
commit 6728ed6fe2
76 changed files with 1725 additions and 67 deletions

View file

@ -9,3 +9,4 @@
/sitespeed-result/
/fixtures/**/*.graphql
spec/fixtures/**/*.graphql
**/contracts/consumer/

View file

@ -16,7 +16,6 @@ Database/MultipleDatabases:
- lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table.rb
- lib/gitlab/database.rb
- lib/gitlab/health_checks/db_check.rb
- lib/gitlab/import_export/group/relation_tree_restorer.rb
- lib/gitlab/seeder.rb
- spec/db/schema_spec.rb
- spec/initializers/database_config_spec.rb

View file

@ -431,7 +431,7 @@ export default {
class="js-ide-edit-blob"
data-qa-selector="edit_in_ide_button"
>
{{ __('Edit in Web IDE') }}
{{ __('Open in Web IDE') }}
</gl-dropdown-item>
</template>

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
module Repositories
class KeepAroundRefsCreatedEvent < ::Gitlab::EventStore::Event
def schema
{
'type' => 'object',
'properties' => {
'project_id' => { 'type' => 'integer' }
}
}
end
end
end

View file

@ -37,14 +37,14 @@ class WebHook < ApplicationRecord
!temporarily_disabled? && !permanently_disabled?
end
def temporarily_disabled?
return false unless web_hooks_disable_failed?
def temporarily_disabled?(ignore_flag: false)
return false unless ignore_flag || web_hooks_disable_failed?
disabled_until.present? && disabled_until >= Time.current
end
def permanently_disabled?
return false unless web_hooks_disable_failed?
def permanently_disabled?(ignore_flag: false)
return false unless ignore_flag || web_hooks_disable_failed?
recent_failures > FAILURE_THRESHOLD
end
@ -106,6 +106,13 @@ class WebHook < ApplicationRecord
save(validate: false)
end
def active_state(ignore_flag: false)
return :permanently_disabled if permanently_disabled?(ignore_flag: ignore_flag)
return :temporarily_disabled if temporarily_disabled?(ignore_flag: ignore_flag)
:enabled
end
# @return [Boolean] Whether or not the WebHook is currently throttled.
def rate_limited?
return false unless rate_limit

View file

@ -194,6 +194,10 @@ class ProjectPolicy < BasePolicy
condition(:"#{f}_disabled", score: 32) { !access_allowed_to?(f.to_sym) }
end
condition(:project_runner_registration_allowed) do
Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?('project')
end
# `:read_project` may be prevented in EE, but `:read_project_for_iids` should
# not.
rule { guest | admin }.enable :read_project_for_iids
@ -230,6 +234,8 @@ class ProjectPolicy < BasePolicy
enable :set_emails_disabled
enable :set_show_default_award_emojis
enable :set_warn_about_potentially_unwanted_characters
enable :register_project_runners
end
rule { can?(:guest_access) }.policy do
@ -453,6 +459,7 @@ class ProjectPolicy < BasePolicy
enable :update_freeze_period
enable :destroy_freeze_period
enable :admin_feature_flags_client
enable :register_project_runners
enable :update_runners_registration_token
enable :admin_project_google_cloud
end
@ -727,6 +734,10 @@ class ProjectPolicy < BasePolicy
enable :access_security_and_compliance
end
rule { ~admin & ~project_runner_registration_allowed }.policy do
prevent :register_project_runners
end
private
def user_is_user?

View file

@ -23,11 +23,11 @@ module Issues
def header_to_value_hash
{
'Title' => 'title',
'Description' => 'description',
'Issue ID' => 'iid',
'URL' => -> (issue) { issue_url(issue) },
'Title' => 'title',
'State' => -> (issue) { issue.closed? ? 'Closed' : 'Open' },
'Description' => 'description',
'Author' => 'author_name',
'Author Username' => -> (issue) { issue.author&.username },
'Assignee' => -> (issue) { issue.assignees.map(&:name).join(', ') },

View file

@ -13,11 +13,11 @@ module MergeRequests
def header_to_value_hash
{
'Title' => 'title',
'Description' => 'description',
'MR IID' => 'iid',
'URL' => -> (merge_request) { merge_request_url(merge_request) },
'Title' => 'title',
'State' => 'state',
'Description' => 'description',
'Source Branch' => 'source_branch',
'Target Branch' => 'target_branch',
'Source Project ID' => 'source_project_id',

View file

@ -12,8 +12,9 @@ module WebHooks
def initialize(hook:, log_data:, response_category:)
@hook = hook
@log_data = log_data
@log_data = log_data.transform_keys(&:to_sym)
@response_category = response_category
@prev_state = hook.active_state(ignore_flag: true)
end
def execute
@ -24,7 +25,7 @@ module WebHooks
private
def log_execution
WebHookLog.create!(web_hook: hook, **log_data.transform_keys(&:to_sym))
WebHookLog.create!(web_hook: hook, **log_data)
end
# Perform this operation within an `Gitlab::ExclusiveLease` lock to make it
@ -41,11 +42,36 @@ module WebHooks
when :failed
hook.failed!
end
log_state_change
end
rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
raise if raise_lock_error?
end
def log_state_change
new_state = hook.active_state(ignore_flag: true)
return if @prev_state == new_state
Gitlab::AuthLogger.info(
message: 'WebHook change active_state',
# identification
hook_id: hook.id,
hook_type: hook.type,
project_id: hook.project_id,
group_id: hook.group_id,
# relevant data
prev_state: @prev_state,
new_state: new_state,
duration: log_data[:execution_duration],
response_status: log_data[:response_status],
recent_hook_failures: hook.recent_failures,
# context
**Gitlab::ApplicationContext.current
)
end
def lock_name
"web_hooks:update_hook_failure_state:#{hook.id}"
end

View file

@ -6,4 +6,4 @@
.content_list
.loading
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md')

View file

@ -1,4 +1,2 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
.loading-container.text-center.prepend-top-20
.gl-spinner.gl-spinner-md

View file

@ -6,4 +6,4 @@
.content_list
.loading
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md')

View file

@ -4,5 +4,4 @@
%ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
.js-groups-list-holder
.loading-container.text-center.prepend-top-20
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md', css_class: 'gl-mt-6')

View file

@ -1,3 +1,17 @@
- if Feature.enabled?(:runner_list_group_view_vue_ui, @group, default_enabled: :yaml)
.gl-card.gl-px-8.gl-py-6.gl-line-height-20
.gl-card-body.gl-display-flex{ :class => "gl-p-0!" }
.gl-banner-illustration
= image_tag('illustrations/rocket-launch-md.svg', alt: s_('Runners|Rocket launch illustration'))
.gl-banner-content
%h1.gl-banner-title
= s_('Runners|New group runners view')
%p
= s_('Runners|The new view gives you more space and better visibility into your fleet of runners.')
%a.btn.btn-confirm.btn-md.gl-button{ :href => group_runners_path(@group) }
%span.gl-button-text
= s_('Runners|Take me there!')
= render 'shared/runners/runner_description'
%hr

View file

@ -9,5 +9,5 @@
#ide.ide-loading{ data: ide_data }
.text-center
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md')
%h2.clgray= _('Loading the GitLab IDE...')

View file

@ -11,4 +11,4 @@
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
.loading
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md')

View file

@ -83,7 +83,7 @@
.mr-loading-status
.loading.hide
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'lg')
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch

View file

@ -16,5 +16,4 @@
- if @commit
.network-graph.gl-bg-white.gl-overflow-scroll.gl-overflow-x-hidden{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
.text-center.gl-mt-3
.gl-spinner.gl-spinner-md
= gl_loading_icon(size: 'md', css_class: 'gl-mt-3')

View file

@ -2,7 +2,7 @@
= _('Specific runners')
.bs-callout.help-callout
- if valid_runner_registrars.include?('project')
- if can?(current_user, :register_project_runners, @project)
= _('These runners are specific to this project.')
- if params[:ci_runner_templates]
%hr

View file

@ -4,7 +4,7 @@
%strong.fly-out-top-item-name
= sidebar_menu.title
- if sidebar_menu.has_pill?
%span.badge.badge-pill.count.fly-out-badge{ **sidebar_menu.pill_html_options }
= gl_badge_tag({ variant: :info, size: :sm }, { class: "count fly-out-badge #{sidebar_menu.pill_html_options[:class]}" }) do
= number_with_delimiter(sidebar_menu.pill_count)
- if sidebar_menu.has_renderable_items?

View file

@ -1,7 +1,7 @@
---
name: issues_full_text_search
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71913
rollout_issue_url:
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/354784
milestone: '14.5'
type: development
group: group::project management

View file

@ -0,0 +1,8 @@
---
name: source_editor_toolbar
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82304
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/354748
milestone: '14.9'
type: development
group: group::editor
default_enabled: false

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddCommentToDeploymentApprovals < Gitlab::Database::Migration[1.0]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20220303191047_add_text_limit_to_deployment_approvals_comment
def change
add_column :deployment_approvals, :comment, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddTextLimitToDeploymentApprovalsComment < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :deployment_approvals, :comment, 255
end
def down
remove_text_limit :deployment_approvals, :comment
end
end

View file

@ -0,0 +1 @@
f8fa8b83da24bf98c97447a2940c8ca801532c80395b6a65c11f83515f811652

View file

@ -0,0 +1 @@
19566152e16a92263dd5dcfd66299e3b9d8b82acd4edb4bba21f6b9b06fc8070

View file

@ -14264,7 +14264,9 @@ CREATE TABLE deployment_approvals (
user_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
status smallint NOT NULL
status smallint NOT NULL,
comment text,
CONSTRAINT check_e2eb6a17d8 CHECK ((char_length(comment) <= 255))
);
CREATE SEQUENCE deployment_approvals_id_seq

View file

@ -465,9 +465,10 @@ POST /projects/:id/deployments/:deployment_id/approval
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `deployment_id` | integer | yes | The ID of the deployment. |
| `status` | string | yes | The status of the approval (either `approved` or `rejected`). |
| `comment` | string | no | A comment to go with the approval |
```shell
curl --data "status=approved" \
curl --data "status=approved&comment=Looks good to me" \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/1/approval"
```
@ -484,6 +485,7 @@ Example response:
"web_url": "http://localhost:3000/root"
},
"status": "approved",
"created_at": "2022-02-24T20:22:30.097Z"
"created_at": "2022-02-24T20:22:30.097Z",
"comment":"Looks good to me"
}
```

View file

@ -89,7 +89,7 @@ Using the [Deployments API](../../api/deployments.md#approve-or-reject-a-blocked
Example:
```shell
curl --data "status=approved" \
curl --data "status=approved&comment=Looks good to me" \
--header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments/1/approval"
```

View file

@ -47,10 +47,7 @@ To implement tracking for HAML or Vue templates, add a [`data-track` attribute](
The following example shows `data-track-*` attributes assigned to a button:
```haml
%button.btn{ data: { track: { action: "click_button", label: "template_preview", property: "my-template" } } }
// or
// %button.btn{ data: { track_action: "click_button", track_label: "template_preview", track_property: "my-template" } }
%button.btn{ data: { track_action: "click_button", track_label: "template_preview", track_property: "my-template" } }
```
```html

View file

@ -12,16 +12,16 @@ each security partner:
<!-- vale gitlab.Spelling = NO -->
- [Accurics](https://readme.accurics.com/1409/)
- [Anchore](https://docs.anchore.com/current/docs/using/integration/ci_cd/gitlab/)
- [Bridgecrew](https://docs.bridgecrew.io/docs/integrate-with-gitlab-self-managed)
- [Checkmarx](https://checkmarx.atlassian.net/wiki/spaces/SD/pages/1929937052/GitLab+Integration)
- [Deepfactor](https://docs.deepfactor.io/hc/en-us/articles/1500008981941)
- [GrammaTech](https://www.grammatech.com/codesonar-gitlab-integration)
- [Indeni](https://cloudrail.app/doc/integrate-with-ci-cd/gitlab-instructions/)
- [Indeni](https://docs.cloudrail.app/#/integrations/gitlab)
- [JScrambler](https://docs.jscrambler.com/code-integrity/documentation/gitlab-ci-integration)
- [Semgrep](https://semgrep.dev/for/gitlab)
- [StackHawk](https://docs.stackhawk.com/continuous-integration/gitlab.html)
- [Tenable](https://docs.tenable.com/tenableio/Content/ContainerSecurity/GetStarted.htm)
- [Venafi](https://marketplace.venafi.com/details/gitlab-ci-cd/)
- [Veracode](https://community.veracode.com/s/knowledgeitem/gitlab-ci-MCEKSYPRWL35BRTGOVI55SK5RI4A)
- [WhiteSource](https://www.whitesourcesoftware.com/gitlab/)

View file

@ -9,9 +9,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To enable the Twitter OmniAuth provider you must register your application with
Twitter. Twitter generates a client ID and secret key for you to use.
1. Sign in to [Twitter Application Management](https://developer.twitter.com/apps).
1. Sign in to [Twitter Application Management](https://apps.twitter.com).
1. Select "Create new app"
1. Select "Create new app".
1. Fill in the application details.
- Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or

View file

@ -408,7 +408,7 @@ If you are using [EGit](https://www.eclipse.org/egit/), you can [add your SSH ke
## Use SSH on Microsoft Windows
If you're running Windows 10, you can either use the [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install)
with [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2) which
with [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install#update-to-wsl-2) which
has both `git` and `ssh` preinstalled, or install [Git for Windows](https://gitforwindows.org) to
use SSH through Powershell.

View file

@ -26,7 +26,7 @@ used for the build.
Specify either:
- The CI/CD variable `BUILDPACK_URL` according to [`pack`'s specifications](https://buildpacks.io/docs/app-developer-guide/specific-buildpacks/).
- The CI/CD variable `BUILDPACK_URL` according to [`pack`'s specifications](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
- A [`project.toml` project descriptor](https://buildpacks.io/docs/app-developer-guide/using-project-descriptor/) with the buildpacks you would like to include.
### Custom buildpacks with Herokuish

View file

@ -112,7 +112,7 @@ Herokuish, with the following caveats:
converted to a Cloud Native Buildpack using Heroku's
[`cnb-shim`](https://github.com/heroku/cnb-shim).
- `BUILDPACK_URL` must be in a format
[supported by `pack`](https://buildpacks.io/docs/app-developer-guide/specific-buildpacks/).
[supported by `pack`](https://buildpacks.io/docs/app-developer-guide/specify-buildpacks/).
- The `/bin/herokuish` command is not present in the built image, and prefixing
commands with `/bin/herokuish procfile exec` is no longer required (nor possible).
Instead, custom commands should be prefixed with `/cnb/lifecycle/launcher`

View file

@ -190,7 +190,7 @@ By doing this, you minimize the length of time during which you have to apply bu
After announcing a release branch, only add serious bug fixes to the branch.
If possible, first merge these bug fixes into `main`, and then cherry-pick them into the release branch.
If you start by merging into the release branch, you might forget to cherry-pick them into `main`, and then you'd encounter the same bug in subsequent releases.
Merging into `main` and then cherry-picking into release is called an "upstream first" policy, which is also practiced by [Google](https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first) and [Red Hat](https://www.redhat.com/en/blog/a-community-for-using-openstack-with-red-hat-rdo).
Merging into `main` and then cherry-picking into release is called an "upstream first" policy, which is also practiced by [Google](https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first/) and [Red Hat](https://www.redhat.com/en/blog/a-community-for-using-openstack-with-red-hat-rdo).
Every time you include a bug fix in a release branch, increase the patch version (to comply with [Semantic Versioning](https://semver.org/)) by setting a new tag.
Some projects also have a stable branch that points to the same commit as the latest released branch.
In this flow, it is not common to have a production branch (or Git flow `main` branch).

View file

@ -40,7 +40,7 @@ To view Code Review Analytics:
## Use cases
This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#delaney-development-team-lead)
This feature is designed for [development team leaders](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
and others who want to understand broad code review dynamics, and identify patterns to explain them.
You can use Code Review Analytics to:

View file

@ -980,7 +980,7 @@ podAnnotations:
The only information to be changed here is the profile name which is `profile-one`
in this example. Refer to the
[AppArmor tutorial](https://kubernetes.io/docs/tutorials/clusters/apparmor/#securing-a-pod)
[AppArmor tutorial](https://kubernetes.io/docs/tutorials/security/apparmor/#securing-a-pod)
for more information on how AppArmor is integrated in Kubernetes.
#### Using PodSecurityPolicy in your deployments
@ -1017,7 +1017,7 @@ securityPolicies:
```
This example creates a single policy named `example` with the provided specification,
and enables [AppArmor annotations](https://kubernetes.io/docs/tutorials/clusters/apparmor/#podsecuritypolicy-annotations) on it.
and enables [AppArmor annotations](https://kubernetes.io/docs/tutorials/security/apparmor/#podsecuritypolicy-annotations) on it.
Support for installing the AppArmor managed application is provided by the
GitLab Container Security group. If you run into unknown issues,

View file

@ -80,7 +80,7 @@ provided can manage resources in the `database.crossplane.io` API group:
## Configure Crossplane with a cloud provider
See [Configure Your Cloud Provider Account](https://crossplane.github.io/docs/v0.4/cloud-providers.html)
See [Configure Your Cloud Provider Account](https://crossplane.github.io/docs/v1.6/)
to configure the installed cloud provider stack with a user account.
The Secret, and the Provider resource referencing the Secret, must be

View file

@ -113,9 +113,9 @@ To use this integration:
`gitlab-managed-apps` namespace.
1. The `Service` resource must be called `elastic-stack-elasticsearch-master`
and expose the Elasticsearch API on port `9200`.
1. The logs are expected to be [Filebeat container logs](https://www.elastic.co/guide/en/beats/filebeat/7.x/filebeat-input-container.html)
following the [7.x log structure](https://www.elastic.co/guide/en/beats/filebeat/7.x/exported-fields-log.html)
and include [Kubernetes metadata](https://www.elastic.co/guide/en/beats/filebeat/7.x/add-kubernetes-metadata.html).
1. The logs are expected to be [Filebeat container logs](https://www.elastic.co/guide/en/beats/filebeat/7.16/filebeat-input-container.html)
following the [7.x log structure](https://www.elastic.co/guide/en/beats/filebeat/7.16/exported-fields-log.html)
and include [Kubernetes metadata](https://www.elastic.co/guide/en/beats/filebeat/7.16/add-kubernetes-metadata.html).
You can manage your Elastic Stack however you like, but as an example, you can
use [this Elastic Stack chart](https://gitlab.com/gitlab-org/charts/elastic-stack) to get up and

View file

@ -120,7 +120,7 @@ you want to manage with the Cluster Management Project.
## Backup and uninstall cert-manager v0.10
1. Follow the [official docs](https://docs.cert-manager.io/en/release-0.10/tasks/backup-restore-crds.html) on how to
1. Follow the [official docs](https://cert-manager.io/docs/tutorials/backup/) on how to
backup your cert-manager v0.10 data.
1. Uninstall cert-manager by editing the setting all the occurrences of `installed: true` to `installed: false` in the
`applications/cert-manager/helmfile.yaml` file.

View file

@ -52,7 +52,7 @@ export, after which the email is prepared.
## Sorting
Exported issues are always sorted by `Issue ID`.
Exported issues are always sorted by `Title`.
## Format
@ -63,11 +63,11 @@ the values:
| Column | Description |
|------------------------------------------|-----------------------------------------------------------|
| Title | Issue `title` |
| Description | Issue `description` |
| Issue ID | Issue `iid` |
| URL | A link to the issue on GitLab |
| Title | Issue `title` |
| State | `Open` or `Closed` |
| Description | Issue `description` |
| Author | Full name of the issue author |
| Author Username | Username of the author, with the `@` symbol omitted |
| Assignee | Full name of the issue assignee |
@ -85,6 +85,10 @@ the values:
| [Epic](../../group/epics/index.md) ID | ID of the parent epic, introduced in 12.7 |
| [Epic](../../group/epics/index.md) Title | Title of the parent epic, introduced in 12.7 |
In GitLab 14.7 and earlier, the first two columns were `Issue ID` and `URL`,
which [caused an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/34769)
when importing back into GitLab.
## Limitations
- Export Issues to CSV is not available at the Group's Issues List.

View file

@ -18,11 +18,11 @@ The following table shows what attributes will be present in the CSV.
| Column | Description |
|--------------------|--------------------------------------------------------------|
| Title | Merge request title |
| Description | Merge request description |
| MR ID | MR `iid` |
| URL | A link to the merge request on GitLab |
| Title | Merge request title |
| State | Opened, Closed, Locked, or Merged |
| Description | Merge request description |
| Source Branch | Source branch |
| Target Branch | Target branch |
| Source Project ID | ID of the source project |
@ -39,6 +39,10 @@ The following table shows what attributes will be present in the CSV.
| Created At (UTC) | Formatted as `YYYY-MM-DD HH:MM:SS` |
| Updated At (UTC) | Formatted as `YYYY-MM-DD HH:MM:SS` |
In GitLab 14.7 and earlier, the first two columns were `MR ID` and `URL`,
which [caused an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/34769)
when importing back into GitLab.
## Limitations
- Export merge requests to CSV is not available at the Group's merge request list.

View file

@ -17,16 +17,16 @@ You can also open the Web IDE when viewing a file, from the repository file list
and from merge requests:
- *When viewing a file, or the repository file list* -
1. In the upper right corner of the page, select **Edit in Web IDE** if it is visible.
1. If **Edit in Web IDE** is not visible:
1. In the upper right corner of the page, select **Open in Web IDE** if it is visible.
1. If **Open in Web IDE** is not visible:
1. Select the **(angle-down)** next to **Edit** or **Gitpod**, depending on your configuration.
1. Select **Edit in Web IDE** from the list to display it as the editing option.
1. Select **Edit in Web IDE** to open the editor.
1. Select **Open in Web IDE** from the list to display it as the editing option.
1. Select **Open in Web IDE** to open the editor.
- *When viewing a merge request* -
1. Go to your merge request, and select the **Overview** tab.
1. Scroll to the widgets section, after the merge request description.
1. Select **Edit in Web IDE** if it is visible.
1. If **Edit in Web IDE** is not visible:
1. Select **Open in Web IDE** if it is visible.
1. If **Open in Web IDE** is not visible:
1. Select the **(angle-down)** next to **Open in Gitpod**.
1. Select **Open in Web IDE** from the list to display it as the editing option.
1. Select **Open in Web IDE** to open the editor.
@ -221,7 +221,7 @@ different branch.
To edit Markdown files in the Web IDE:
1. Go to your repository, and navigate to the Markdown page you want to edit.
1. Select **Edit in Web IDE**, and GitLab loads the page in a tab in the editor.
1. Select **Open in Web IDE**, and GitLab loads the page in a tab in the editor.
1. Make your changes to the file. GitLab supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown).
1. When your changes are complete, select **Commit** in the left sidebar.
1. Add a commit message, select the branch you want to commit to, and select **Commit**.

View file

@ -195,6 +195,16 @@ module Gitlab
MAX_TIMESTAMP_VALUE > timestamp ? timestamp : MAX_TIMESTAMP_VALUE.dup
end
def self.all_uncached(&block)
# Calls to #uncached only disable caching for the current connection. Since the load balancer
# can potentially upgrade from read to read-write mode (using a different connection), we specify
# up-front that we'll explicitly use the primary for the duration of the operation.
Gitlab::Database::LoadBalancing::Session.current.use_primary do
base_models = database_base_models.values
base_models.reduce(block) { |blk, model| -> { model.uncached(&blk) } }.call
end
end
def self.allow_cross_joins_across_databases(url:)
# this method is implemented in:
# spec/support/database/prevent_cross_joins.rb

View file

@ -58,6 +58,7 @@ module Gitlab
push_frontend_feature_flag(:new_header_search, default_enabled: :yaml)
push_frontend_feature_flag(:bootstrap_confirmation_modals, default_enabled: :yaml)
push_frontend_feature_flag(:sandboxed_mermaid, default_enabled: :yaml)
push_frontend_feature_flag(:source_editor_toolbar, default_enabled: :yaml)
end
# Exposes the state of a feature flag to the frontend code.

View file

@ -29,7 +29,7 @@ module Gitlab
end
def restore
ActiveRecord::Base.uncached do
Gitlab::Database.all_uncached do
ActiveRecord::Base.no_touching do
update_params!

View file

@ -13229,9 +13229,6 @@ msgstr ""
msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Edit in Web IDE"
msgstr ""
msgid "Edit in pipeline editor"
msgstr ""
@ -31671,6 +31668,9 @@ msgstr ""
msgid "Runners|Never contacted"
msgstr ""
msgid "Runners|New group runners view"
msgstr ""
msgid "Runners|New registration token generated!"
msgstr ""
@ -31737,6 +31737,9 @@ msgstr ""
msgid "Runners|Revision"
msgstr ""
msgid "Runners|Rocket launch illustration"
msgstr ""
msgid "Runners|Runner"
msgstr ""
@ -31803,6 +31806,12 @@ msgstr ""
msgid "Runners|Tags"
msgstr ""
msgid "Runners|Take me there!"
msgstr ""
msgid "Runners|The new view gives you more space and better visibility into your fleet of runners."
msgstr ""
msgid "Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
msgstr ""

View file

@ -35,6 +35,8 @@ gem 'confiner', '~> 0.2'
gem 'chemlab', '~> 0.9'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
gem "pact", "~> 1.12"
gem 'deprecation_toolkit', '~> 1.5.1', require: false
group :development do

View file

@ -28,6 +28,7 @@ GEM
require_all (>= 2, < 4)
uuid (>= 2.3, < 3)
ast (2.4.2)
awesome_print (1.9.2)
binding_ninja (0.2.3)
builder (3.2.4)
byebug (9.1.0)
@ -91,6 +92,8 @@ GEM
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
filelock (1.1.1)
find_a_port (1.0.1)
fog-core (2.1.0)
builder
excon (~> 0.58)
@ -173,6 +176,7 @@ GEM
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb-client (1.17.0)
json (2.6.1)
jwt (2.3.0)
knapsack (4.0.0)
rake
@ -205,6 +209,29 @@ GEM
sawyer (~> 0.8.0, >= 0.5.3)
oj (3.13.11)
os (1.1.4)
pact (1.59.0)
pact-mock_service (~> 3.0, >= 3.3.1)
pact-support (~> 1.15)
rack-test (>= 0.6.3, < 2.0.0)
rspec (~> 3.0)
term-ansicolor (~> 1.0)
thor (>= 0.20, < 2.0)
webrick (~> 1.3)
pact-mock_service (3.6.2)
filelock (~> 1.1)
find_a_port (~> 1.0.1)
json
pact-support (~> 1.12, >= 1.12.0)
rack (~> 2.0)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
thor (>= 0.19, < 2.0)
webrick (~> 1.3)
pact-support (1.15.1)
awesome_print (~> 1.1)
randexp (~> 0.1.7)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
parallel (1.19.2)
parallel_tests (2.29.0)
parallel
@ -228,6 +255,7 @@ GEM
rack (>= 1.0, < 3)
rainbow (3.0.0)
rake (13.0.6)
randexp (0.1.7)
regexp_parser (2.1.1)
representable (3.1.1)
declarative (< 0.1.0)
@ -282,12 +310,18 @@ GEM
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
slack-notifier (2.4.0)
sync (0.5.0)
systemu (2.6.5)
table_print (1.5.7)
term-ansicolor (1.7.1)
tins (~> 1.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
thor (1.2.1)
thread_safe (0.3.6)
timecop (0.9.1)
tins (1.31.0)
sync
trailblazer-option (0.1.2)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
@ -337,6 +371,7 @@ DEPENDENCIES
influxdb-client (~> 1.17)
knapsack (~> 4.0)
octokit (~> 4.21)
pact (~> 1.12)
parallel (~> 1.19)
parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1)

32
qa/bin/contract Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rake'
host = ARGV.shift
ENV['CONTRACT_HOST'] ||= host
list = []
loop do
keyword = ARGV.shift
case keyword
when '--mr'
ENV['CONTRACT_MR'] ||= ARGV.shift
list.push 'test:merge_request'
else
break
end
end
app = Rake.application
Dir.chdir('contracts/provider') do
app.init
app.add_import 'Rakefile'
app.load_rakefile
list.each do |element|
app[element].invoke
end
end

2
qa/contracts/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
logs/
consumer/node_modules

View file

@ -0,0 +1 @@
14.17.5

View file

@ -0,0 +1,42 @@
'use strict';
const axios = require('axios');
exports.getMetadata = (endpoint) => {
const url = endpoint.url;
return axios
.request({
method: 'GET',
baseURL: url,
url: '/diffs_metadata.json',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};
exports.getDiscussions = (endpoint) => {
const url = endpoint.url;
return axios
.request({
method: 'GET',
baseURL: url,
url: '/discussions.json',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};
exports.getDiffs = (endpoint) => {
const url = endpoint.url;
return axios
.request({
method: 'GET',
baseURL: url,
url: '/diffs_batch.json?page=0',
headers: { Accept: '*/*' },
})
.then((response) => response.data);
};

View file

@ -0,0 +1,89 @@
'use strict';
const { Matchers } = require('@pact-foundation/pact');
const body = {
diff_files: Matchers.eachLike({
content_sha: Matchers.string('b0c94059db75b2473d616d4b1fde1a77533355a3'),
submodule: Matchers.boolean(false),
edit_path: Matchers.string('/gitlab-qa-bot/...'),
ide_edit_path: Matchers.string('/gitlab-qa-bot/...'),
old_path_html: Matchers.string('Gemfile'),
new_path_html: Matchers.string('Gemfile'),
blob: {
id: Matchers.string('855071bb3928d140764885964f7be1bb3e582495'),
path: Matchers.string('Gemfile'),
name: Matchers.string('Gemfile'),
mode: Matchers.string('1234567'),
readable_text: Matchers.boolean(true),
icon: Matchers.string('doc-text'),
},
can_modify_blob: Matchers.boolean(false),
file_identifier_hash: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
file_hash: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
file_path: Matchers.string('Gemfile'),
old_path: Matchers.string('Gemfile'),
new_path: Matchers.string('Gemfile'),
new_file: Matchers.boolean(false),
renamed_file: Matchers.boolean(false),
deleted_file: Matchers.boolean(false),
diff_refs: {
base_sha: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
start_sha: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
head_sha: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
},
mode_changed: Matchers.boolean(false),
a_mode: Matchers.string('123456'),
b_mode: Matchers.string('123456'),
viewer: {
name: Matchers.string('text'),
collapsed: Matchers.boolean(false),
},
old_size: Matchers.integer(2288),
new_size: Matchers.integer(2288),
added_lines: Matchers.integer(1),
removed_lines: Matchers.integer(1),
load_collapsed_diff_url: Matchers.string('/gitlab-qa-bot/...'),
view_path: Matchers.string('/gitlab-qa-bot/...'),
context_lines_path: Matchers.string('/gitlab-qa-bot/...'),
highlighted_diff_lines: Matchers.eachLike({
// The following values can also be null which is not supported
//line_code: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579_1_1'),
//old_line: Matchers.integer(1),
//new_line: Matchers.integer(1),
text: Matchers.string('source'),
rich_text: Matchers.string('<span></span>'),
can_receive_suggestion: Matchers.boolean(true),
}),
is_fully_expanded: Matchers.boolean(false),
}),
pagination: {
total_pages: Matchers.integer(1),
},
};
const Diffs = {
body: Matchers.extractPayload(body),
success: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
},
request: {
uponReceiving: 'a request for diff lines',
withRequest: {
method: 'GET',
path: '/diffs_batch.json',
headers: {
Accept: '*/*',
},
query: 'page=0',
},
},
};
exports.Diffs = Diffs;

View file

@ -0,0 +1,85 @@
'use strict';
const { Matchers } = require('@pact-foundation/pact');
const body = Matchers.eachLike({
id: Matchers.string('fd73763cbcbf7b29eb8765d969a38f7d735e222a'),
reply_id: Matchers.string('fd73763cbcbf7b29eb8765d969a38f7d735e222a'),
project_id: Matchers.integer(6954442),
confidential: Matchers.boolean(false),
diff_discussion: Matchers.boolean(false),
expanded: Matchers.boolean(false),
for_commit: Matchers.boolean(false),
individual_note: Matchers.boolean(true),
resolvable: Matchers.boolean(false),
resolved_by_push: Matchers.boolean(false),
notes: Matchers.eachLike({
id: Matchers.string('76489845'),
author: {
id: Matchers.integer(1675733),
username: Matchers.string('gitlab-qa-bot'),
name: Matchers.string('gitlab-qa-bot'),
state: Matchers.string('active'),
avatar_url: Matchers.string(
'https://secure.gravatar.com/avatar/8355ad0f2761367fae6b9c4fe80994b9?s=80&d=identicon',
),
show_status: Matchers.boolean(false),
path: Matchers.string('/gitlab-qa-bot'),
},
created_at: Matchers.iso8601DateTimeWithMillis('2022-02-22T07:06:55.038Z'),
updated_at: Matchers.iso8601DateTimeWithMillis('2022-02-22T07:06:55.038Z'),
system: Matchers.boolean(false),
noteable_id: Matchers.integer(8333422),
noteable_type: Matchers.string('MergeRequest'),
resolvable: Matchers.boolean(false),
resolved: Matchers.boolean(true),
confidential: Matchers.boolean(false),
noteable_iid: Matchers.integer(1),
note: Matchers.string('This is a test comment'),
note_html: Matchers.string(
'<p data-sourcepos="1:1-1:22" dir="auto">This is a test comment</p>',
),
current_user: {
can_edit: Matchers.boolean(true),
can_award_emoji: Matchers.boolean(true),
can_resolve: Matchers.boolean(false),
can_resolve_discussion: Matchers.boolean(false),
},
is_noteable_author: Matchers.boolean(true),
discussion_id: Matchers.string('fd73763cbcbf7b29eb8765d969a38f7d735e222a'),
emoji_awardable: Matchers.boolean(true),
report_abuse_path: Matchers.string('/gitlab-qa-bot/...'),
noteable_note_url: Matchers.string('https://staging.gitlab.com/gitlab-qa-bot/...'),
cached_markdown_version: Matchers.integer(1900552),
human_access: Matchers.string('Maintainer'),
is_contributor: Matchers.boolean(false),
project_name: Matchers.string('contract-testing'),
path: Matchers.string('/gitlab-qa-bot/...'),
}),
resolved: Matchers.boolean(true),
});
const Discussions = {
body: Matchers.extractPayload(body),
success: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
},
request: {
uponReceiving: 'a request for discussions',
withRequest: {
method: 'GET',
path: '/discussions.json',
headers: {
Accept: '*/*',
},
},
},
};
exports.Discussions = Discussions;

View file

@ -0,0 +1,96 @@
'use strict';
const { Matchers } = require('@pact-foundation/pact');
const body = {
real_size: Matchers.string('1'),
size: Matchers.integer(1),
branch_name: Matchers.string('testing-branch-1'),
source_branch_exists: Matchers.boolean(true),
target_branch_name: Matchers.string('master'),
merge_request_diff: {
created_at: Matchers.iso8601DateTimeWithMillis('2022-02-17T11:47:08.804Z'),
commits_count: Matchers.integer(1),
latest: Matchers.boolean(true),
short_commit_sha: Matchers.string('aee1ffec'),
base_version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773',
),
head_version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true',
),
version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773',
),
compare_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f',
),
},
latest_diff: Matchers.boolean(true),
latest_version_path: Matchers.string('/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs'),
added_lines: Matchers.integer(1),
removed_lines: Matchers.integer(1),
render_overflow_warning: Matchers.boolean(false),
email_patch_path: Matchers.string('/gitlab-qa-bot/contract-testing/-/merge_requests/1.patch'),
plain_diff_path: Matchers.string('/gitlab-qa-bot/contract-testing/-/merge_requests/1.diff'),
merge_request_diffs: Matchers.eachLike({
commits_count: Matchers.integer(1),
latest: Matchers.boolean(true),
short_commit_sha: Matchers.string('aee1ffec'),
base_version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773',
),
head_version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true',
),
version_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773',
),
compare_path: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f',
),
}),
definition_path_prefix: Matchers.string(
'/gitlab-qa-bot/contract-testing/-/blob/aee1ffec2299c0cfb17c8821e931339b73a3759f',
),
diff_files: Matchers.eachLike({
added_lines: Matchers.integer(1),
removed_lines: Matchers.integer(1),
new_path: Matchers.string('Gemfile'),
old_path: Matchers.string('Gemfile'),
new_file: Matchers.boolean(false),
deleted_file: Matchers.boolean(false),
submodule: Matchers.boolean(false),
file_identifier_hash: Matchers.string('67d82b8716a5b6c52c7abf0b2cd99c7594ed3587'),
file_hash: Matchers.string('de3150c01c3a946a6168173c4116741379fe3579'),
}),
has_conflicts: Matchers.boolean(false),
can_merge: Matchers.boolean(false),
project_path: Matchers.string('gitlab-qa-bot/contract-testing'),
project_name: Matchers.string('contract-testing'),
};
const Metadata = {
body: Matchers.extractPayload(body),
success: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: body,
},
request: {
uponReceiving: 'a request for Metadata',
withRequest: {
method: 'GET',
path: '/diffs_metadata.json',
headers: {
Accept: '*/*',
},
},
},
};
exports.Metadata = Metadata;

View file

@ -0,0 +1,17 @@
{
"name": "consumer",
"version": "1.0.0",
"description": "consumer side contract testing",
"license": "MIT",
"repository": "https://gitlab.com/gitlab-org/gitlab.git",
"dependencies": {
"@pact-foundation/pact": "^9.17.2",
"axios": "^0.26.0",
"jest": "^27.5.1",
"jest-pact": "^0.9.1",
"prettier": "^2.5.1"
},
"scripts": {
"test": "jest specs/ --runInBand"
}
}

View file

@ -0,0 +1,35 @@
'use strict';
const { pactWith } = require('jest-pact');
const { Diffs } = require('../fixtures/diffs.fixture');
const { getDiffs } = require('../endpoints/merge_request');
pactWith(
{
consumer: 'Merge Request Page',
provider: 'Merge Request Diffs Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
describe('Diffs Endpoint', () => {
beforeEach(() => {
const interaction = {
...Diffs.request,
willRespondWith: Diffs.success,
};
return provider.addInteraction(interaction);
});
it('return a successful body', () => {
return getDiffs({
url: provider.mockService.baseUrl,
}).then((diffs) => {
expect(diffs).toEqual(Diffs.body);
});
});
});
},
);

View file

@ -0,0 +1,35 @@
'use strict';
const { pactWith } = require('jest-pact');
const { Discussions } = require('../fixtures/discussions.fixture');
const { getDiscussions } = require('../endpoints/merge_request');
pactWith(
{
consumer: 'Merge Request Page',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
describe('Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
...Discussions.request,
willRespondWith: Discussions.success,
};
return provider.addInteraction(interaction);
});
it('return a successful body', () => {
return getDiscussions({
url: provider.mockService.baseUrl,
}).then((discussions) => {
expect(discussions).toEqual(Discussions.body);
});
});
});
},
);

View file

@ -0,0 +1,35 @@
'use strict';
const { pactWith } = require('jest-pact');
const { Metadata } = require('../fixtures/metadata.fixture');
const { getMetadata } = require('../endpoints/merge_request');
pactWith(
{
consumer: 'Merge Request Page',
provider: 'Merge Request Metadata Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
describe('Metadata Endpoint', () => {
beforeEach(() => {
const interaction = {
...Metadata.request,
willRespondWith: Metadata.success,
};
return provider.addInteraction(interaction);
});
it('return a successful body', () => {
return getMetadata({
url: provider.mockService.baseUrl,
}).then((metadata) => {
expect(metadata).toEqual(Metadata.body);
});
});
});
},
);

View file

@ -0,0 +1,228 @@
{
"consumer": {
"name": "Merge Request Page"
},
"provider": {
"name": "Merge Request Diffs Endpoint"
},
"interactions": [
{
"description": "a request for diff lines",
"request": {
"method": "GET",
"path": "/diffs_batch.json",
"query": "page=0",
"headers": {
"Accept": "*/*"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"diff_files": [
{
"content_sha": "b0c94059db75b2473d616d4b1fde1a77533355a3",
"submodule": false,
"edit_path": "/gitlab-qa-bot/...",
"ide_edit_path": "/gitlab-qa-bot/...",
"old_path_html": "Gemfile",
"new_path_html": "Gemfile",
"blob": {
"id": "855071bb3928d140764885964f7be1bb3e582495",
"path": "Gemfile",
"name": "Gemfile",
"mode": "1234567",
"readable_text": true,
"icon": "doc-text"
},
"can_modify_blob": false,
"file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
"file_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
"file_path": "Gemfile",
"old_path": "Gemfile",
"new_path": "Gemfile",
"new_file": false,
"renamed_file": false,
"deleted_file": false,
"diff_refs": {
"base_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
"start_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
"head_sha": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587"
},
"mode_changed": false,
"a_mode": "123456",
"b_mode": "123456",
"viewer": {
"name": "text",
"collapsed": false
},
"old_size": 2288,
"new_size": 2288,
"added_lines": 1,
"removed_lines": 1,
"load_collapsed_diff_url": "/gitlab-qa-bot/...",
"view_path": "/gitlab-qa-bot/...",
"context_lines_path": "/gitlab-qa-bot/...",
"highlighted_diff_lines": [
{
"text": "source",
"rich_text": "<span></span>",
"can_receive_suggestion": true
}
],
"is_fully_expanded": false
}
],
"pagination": {
"total_pages": 1
}
},
"matchingRules": {
"$.body.diff_files": {
"min": 1
},
"$.body.diff_files[*].*": {
"match": "type"
},
"$.body.diff_files[*].content_sha": {
"match": "type"
},
"$.body.diff_files[*].submodule": {
"match": "type"
},
"$.body.diff_files[*].edit_path": {
"match": "type"
},
"$.body.diff_files[*].ide_edit_path": {
"match": "type"
},
"$.body.diff_files[*].old_path_html": {
"match": "type"
},
"$.body.diff_files[*].new_path_html": {
"match": "type"
},
"$.body.diff_files[*].blob.id": {
"match": "type"
},
"$.body.diff_files[*].blob.path": {
"match": "type"
},
"$.body.diff_files[*].blob.name": {
"match": "type"
},
"$.body.diff_files[*].blob.mode": {
"match": "type"
},
"$.body.diff_files[*].blob.readable_text": {
"match": "type"
},
"$.body.diff_files[*].blob.icon": {
"match": "type"
},
"$.body.diff_files[*].can_modify_blob": {
"match": "type"
},
"$.body.diff_files[*].file_identifier_hash": {
"match": "type"
},
"$.body.diff_files[*].file_hash": {
"match": "type"
},
"$.body.diff_files[*].file_path": {
"match": "type"
},
"$.body.diff_files[*].old_path": {
"match": "type"
},
"$.body.diff_files[*].new_path": {
"match": "type"
},
"$.body.diff_files[*].new_file": {
"match": "type"
},
"$.body.diff_files[*].renamed_file": {
"match": "type"
},
"$.body.diff_files[*].deleted_file": {
"match": "type"
},
"$.body.diff_files[*].diff_refs.base_sha": {
"match": "type"
},
"$.body.diff_files[*].diff_refs.start_sha": {
"match": "type"
},
"$.body.diff_files[*].diff_refs.head_sha": {
"match": "type"
},
"$.body.diff_files[*].mode_changed": {
"match": "type"
},
"$.body.diff_files[*].a_mode": {
"match": "type"
},
"$.body.diff_files[*].b_mode": {
"match": "type"
},
"$.body.diff_files[*].viewer.name": {
"match": "type"
},
"$.body.diff_files[*].viewer.collapsed": {
"match": "type"
},
"$.body.diff_files[*].old_size": {
"match": "type"
},
"$.body.diff_files[*].new_size": {
"match": "type"
},
"$.body.diff_files[*].added_lines": {
"match": "type"
},
"$.body.diff_files[*].removed_lines": {
"match": "type"
},
"$.body.diff_files[*].load_collapsed_diff_url": {
"match": "type"
},
"$.body.diff_files[*].view_path": {
"match": "type"
},
"$.body.diff_files[*].context_lines_path": {
"match": "type"
},
"$.body.diff_files[*].highlighted_diff_lines": {
"min": 1
},
"$.body.diff_files[*].highlighted_diff_lines[*].*": {
"match": "type"
},
"$.body.diff_files[*].highlighted_diff_lines[*].text": {
"match": "type"
},
"$.body.diff_files[*].highlighted_diff_lines[*].rich_text": {
"match": "type"
},
"$.body.diff_files[*].highlighted_diff_lines[*].can_receive_suggestion": {
"match": "type"
},
"$.body.diff_files[*].is_fully_expanded": {
"match": "type"
},
"$.body.pagination.total_pages": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View file

@ -0,0 +1,235 @@
{
"consumer": {
"name": "Merge Request Page"
},
"provider": {
"name": "Merge Request Discussions Endpoint"
},
"interactions": [
{
"description": "a request for discussions",
"request": {
"method": "GET",
"path": "/discussions.json",
"headers": {
"Accept": "*/*"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": [
{
"id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
"reply_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
"project_id": 6954442,
"confidential": false,
"diff_discussion": false,
"expanded": false,
"for_commit": false,
"individual_note": true,
"resolvable": false,
"resolved_by_push": false,
"notes": [
{
"id": "76489845",
"author": {
"id": 1675733,
"username": "gitlab-qa-bot",
"name": "gitlab-qa-bot",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/8355ad0f2761367fae6b9c4fe80994b9?s=80&d=identicon",
"show_status": false,
"path": "/gitlab-qa-bot"
},
"created_at": "2022-02-22T07:06:55.038Z",
"updated_at": "2022-02-22T07:06:55.038Z",
"system": false,
"noteable_id": 8333422,
"noteable_type": "MergeRequest",
"resolvable": false,
"resolved": true,
"confidential": false,
"noteable_iid": 1,
"note": "This is a test comment",
"note_html": "<p data-sourcepos=\"1:1-1:22\" dir=\"auto\">This is a test comment</p>",
"current_user": {
"can_edit": true,
"can_award_emoji": true,
"can_resolve": false,
"can_resolve_discussion": false
},
"is_noteable_author": true,
"discussion_id": "fd73763cbcbf7b29eb8765d969a38f7d735e222a",
"emoji_awardable": true,
"report_abuse_path": "/gitlab-qa-bot/...",
"noteable_note_url": "https://staging.gitlab.com/gitlab-qa-bot/...",
"cached_markdown_version": 1900552,
"human_access": "Maintainer",
"is_contributor": false,
"project_name": "contract-testing",
"path": "/gitlab-qa-bot/..."
}
],
"resolved": true
}
],
"matchingRules": {
"$.body": {
"min": 1
},
"$.body[*].*": {
"match": "type"
},
"$.body[*].id": {
"match": "type"
},
"$.body[*].reply_id": {
"match": "type"
},
"$.body[*].project_id": {
"match": "type"
},
"$.body[*].confidential": {
"match": "type"
},
"$.body[*].diff_discussion": {
"match": "type"
},
"$.body[*].expanded": {
"match": "type"
},
"$.body[*].for_commit": {
"match": "type"
},
"$.body[*].individual_note": {
"match": "type"
},
"$.body[*].resolvable": {
"match": "type"
},
"$.body[*].resolved_by_push": {
"match": "type"
},
"$.body[*].notes": {
"min": 1
},
"$.body[*].notes[*].*": {
"match": "type"
},
"$.body[*].notes[*].id": {
"match": "type"
},
"$.body[*].notes[*].author.id": {
"match": "type"
},
"$.body[*].notes[*].author.username": {
"match": "type"
},
"$.body[*].notes[*].author.name": {
"match": "type"
},
"$.body[*].notes[*].author.state": {
"match": "type"
},
"$.body[*].notes[*].author.avatar_url": {
"match": "type"
},
"$.body[*].notes[*].author.show_status": {
"match": "type"
},
"$.body[*].notes[*].author.path": {
"match": "type"
},
"$.body[*].notes[*].created_at": {
"match": "regex",
"regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
},
"$.body[*].notes[*].updated_at": {
"match": "regex",
"regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
},
"$.body[*].notes[*].system": {
"match": "type"
},
"$.body[*].notes[*].noteable_id": {
"match": "type"
},
"$.body[*].notes[*].noteable_type": {
"match": "type"
},
"$.body[*].notes[*].resolvable": {
"match": "type"
},
"$.body[*].notes[*].resolved": {
"match": "type"
},
"$.body[*].notes[*].confidential": {
"match": "type"
},
"$.body[*].notes[*].noteable_iid": {
"match": "type"
},
"$.body[*].notes[*].note": {
"match": "type"
},
"$.body[*].notes[*].note_html": {
"match": "type"
},
"$.body[*].notes[*].current_user.can_edit": {
"match": "type"
},
"$.body[*].notes[*].current_user.can_award_emoji": {
"match": "type"
},
"$.body[*].notes[*].current_user.can_resolve": {
"match": "type"
},
"$.body[*].notes[*].current_user.can_resolve_discussion": {
"match": "type"
},
"$.body[*].notes[*].is_noteable_author": {
"match": "type"
},
"$.body[*].notes[*].discussion_id": {
"match": "type"
},
"$.body[*].notes[*].emoji_awardable": {
"match": "type"
},
"$.body[*].notes[*].report_abuse_path": {
"match": "type"
},
"$.body[*].notes[*].noteable_note_url": {
"match": "type"
},
"$.body[*].notes[*].cached_markdown_version": {
"match": "type"
},
"$.body[*].notes[*].human_access": {
"match": "type"
},
"$.body[*].notes[*].is_contributor": {
"match": "type"
},
"$.body[*].notes[*].project_name": {
"match": "type"
},
"$.body[*].notes[*].path": {
"match": "type"
},
"$.body[*].resolved": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View file

@ -0,0 +1,222 @@
{
"consumer": {
"name": "Merge Request Page"
},
"provider": {
"name": "Merge Request Metadata Endpoint"
},
"interactions": [
{
"description": "a request for Metadata",
"request": {
"method": "GET",
"path": "/diffs_metadata.json",
"headers": {
"Accept": "*/*"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"real_size": "1",
"size": 1,
"branch_name": "testing-branch-1",
"source_branch_exists": true,
"target_branch_name": "master",
"merge_request_diff": {
"created_at": "2022-02-17T11:47:08.804Z",
"commits_count": 1,
"latest": true,
"short_commit_sha": "aee1ffec",
"base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
"head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
"version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
"compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
},
"latest_diff": true,
"latest_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs",
"added_lines": 1,
"removed_lines": 1,
"render_overflow_warning": false,
"email_patch_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.patch",
"plain_diff_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1.diff",
"merge_request_diffs": [
{
"commits_count": 1,
"latest": true,
"short_commit_sha": "aee1ffec",
"base_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
"head_version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_head=true",
"version_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773",
"compare_path": "/gitlab-qa-bot/contract-testing/-/merge_requests/1/diffs?diff_id=10581773&start_sha=aee1ffec2299c0cfb17c8821e931339b73a3759f"
}
],
"definition_path_prefix": "/gitlab-qa-bot/contract-testing/-/blob/aee1ffec2299c0cfb17c8821e931339b73a3759f",
"diff_files": [
{
"added_lines": 1,
"removed_lines": 1,
"new_path": "Gemfile",
"old_path": "Gemfile",
"new_file": false,
"deleted_file": false,
"submodule": false,
"file_identifier_hash": "67d82b8716a5b6c52c7abf0b2cd99c7594ed3587",
"file_hash": "de3150c01c3a946a6168173c4116741379fe3579"
}
],
"has_conflicts": false,
"can_merge": false,
"project_path": "gitlab-qa-bot/contract-testing",
"project_name": "contract-testing"
},
"matchingRules": {
"$.body.real_size": {
"match": "type"
},
"$.body.size": {
"match": "type"
},
"$.body.branch_name": {
"match": "type"
},
"$.body.source_branch_exists": {
"match": "type"
},
"$.body.target_branch_name": {
"match": "type"
},
"$.body.merge_request_diff.created_at": {
"match": "regex",
"regex": "^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d(:?[0-5]\\d)?|Z)$"
},
"$.body.merge_request_diff.commits_count": {
"match": "type"
},
"$.body.merge_request_diff.latest": {
"match": "type"
},
"$.body.merge_request_diff.short_commit_sha": {
"match": "type"
},
"$.body.merge_request_diff.base_version_path": {
"match": "type"
},
"$.body.merge_request_diff.head_version_path": {
"match": "type"
},
"$.body.merge_request_diff.version_path": {
"match": "type"
},
"$.body.merge_request_diff.compare_path": {
"match": "type"
},
"$.body.latest_diff": {
"match": "type"
},
"$.body.latest_version_path": {
"match": "type"
},
"$.body.added_lines": {
"match": "type"
},
"$.body.removed_lines": {
"match": "type"
},
"$.body.render_overflow_warning": {
"match": "type"
},
"$.body.email_patch_path": {
"match": "type"
},
"$.body.plain_diff_path": {
"match": "type"
},
"$.body.merge_request_diffs": {
"min": 1
},
"$.body.merge_request_diffs[*].*": {
"match": "type"
},
"$.body.merge_request_diffs[*].commits_count": {
"match": "type"
},
"$.body.merge_request_diffs[*].latest": {
"match": "type"
},
"$.body.merge_request_diffs[*].short_commit_sha": {
"match": "type"
},
"$.body.merge_request_diffs[*].base_version_path": {
"match": "type"
},
"$.body.merge_request_diffs[*].head_version_path": {
"match": "type"
},
"$.body.merge_request_diffs[*].version_path": {
"match": "type"
},
"$.body.merge_request_diffs[*].compare_path": {
"match": "type"
},
"$.body.definition_path_prefix": {
"match": "type"
},
"$.body.diff_files": {
"min": 1
},
"$.body.diff_files[*].*": {
"match": "type"
},
"$.body.diff_files[*].added_lines": {
"match": "type"
},
"$.body.diff_files[*].removed_lines": {
"match": "type"
},
"$.body.diff_files[*].new_path": {
"match": "type"
},
"$.body.diff_files[*].old_path": {
"match": "type"
},
"$.body.diff_files[*].new_file": {
"match": "type"
},
"$.body.diff_files[*].deleted_file": {
"match": "type"
},
"$.body.diff_files[*].submodule": {
"match": "type"
},
"$.body.diff_files[*].file_identifier_hash": {
"match": "type"
},
"$.body.diff_files[*].file_hash": {
"match": "type"
},
"$.body.has_conflicts": {
"match": "type"
},
"$.body.can_merge": {
"match": "type"
},
"$.body.project_path": {
"match": "type"
},
"$.body.project_name": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'pact/tasks/verification_task'
Pact::VerificationTask.new(:metadata) do |pact|
pact.uri '../contracts/merge_request_page-merge_request_metadata_endpoint.json', pact_helper: './spec/metadata_helper.rb'
end
Pact::VerificationTask.new(:discussions) do |pact|
pact.uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json', pact_helper: './spec/discussions_helper.rb'
end
Pact::VerificationTask.new(:diffs) do |pact|
pact.uri '../contracts/merge_request_page-merge_request_diffs_endpoint.json', pact_helper: './spec/diffs_helper.rb'
end
task 'test:merge_request' => ['pact:verify:metadata', 'pact:verify:discussions', 'pact:verify:diffs']

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'faraday'
module Environments
class Base
attr_writer :base_url, :merge_request
def call(env)
@payload
end
def http(endpoint)
Faraday.default_adapter = :net_http
response = Faraday.get(@base_url + endpoint)
@payload = [response.status, response.headers, [response.body]]
self
end
def merge_request(endpoint)
if endpoint.include? '.json'
http(@merge_request + endpoint)
end
end
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
require_relative './base'
module Environments
class Local < Base
def initialize
@base_url = ENV['CONTRACT_HOST']
@merge_request = ENV['CONTRACT_MR']
end
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative '../environments/local'
module DiffsHelper
local = Environments::Local.new
Pact.service_provider "Merge Request Diffs Endpoint" do
app { local.merge_request('/diffs_batch.json?page=0') }
honours_pact_with 'Merge Request Page' do
pact_uri '../contracts/merge_request_page-merge_request_diffs_endpoint.json'
end
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative '../environments/local'
module DiscussionsHelper
local = Environments::Local.new
Pact.service_provider "Merge Request Discussions Endpoint" do
app { local.merge_request('/discussions.json') }
honours_pact_with 'Merge Request Page' do
pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json'
end
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative '../environments/local'
module MetadataHelper
local = Environments::Local.new
Pact.service_provider "Merge Request Metadata Endpoint" do
app { local.merge_request('/diffs_metadata.json') }
honours_pact_with 'Merge Request Page' do
pact_uri '../contracts/merge_request_page-merge_request_metadata_endpoint.json'
end
end
end

View file

@ -13,6 +13,24 @@ RSpec.describe 'Group CI/CD settings' do
sign_in(user)
end
describe 'new group runners view banner' do
it 'displays banner' do
visit group_settings_ci_cd_path(group)
expect(page).to have_content(s_('Runners|New group runners view'))
expect(page).to have_link(href: group_runners_path(group))
end
it 'does not display banner' do
stub_feature_flags(runner_list_group_view_vue_ui: false)
visit group_settings_ci_cd_path(group)
expect(page).not_to have_content(s_('Runners|New group runners view'))
expect(page).not_to have_link(href: group_runners_path(group))
end
end
describe 'runners registration token' do
let!(:token) { group.runners_token }

View file

@ -279,6 +279,46 @@ RSpec.describe Gitlab::Database do
end
end
describe '.all_uncached' do
let(:base_model) do
Class.new do
def self.uncached
@uncached = true
yield
end
end
end
let(:model1) { Class.new(base_model) }
let(:model2) { Class.new(base_model) }
before do
allow(described_class).to receive(:database_base_models)
.and_return({ model1: model1, model2: model2 }.with_indifferent_access)
end
it 'wraps the given block in uncached calls for each primary connection', :aggregate_failures do
expect(model1.instance_variable_get(:@uncached)).to be_nil
expect(model2.instance_variable_get(:@uncached)).to be_nil
expect(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary).and_yield
expect(model2).to receive(:uncached).and_call_original
expect(model1).to receive(:uncached).and_call_original
yielded_to_block = false
described_class.all_uncached do
expect(model1.instance_variable_get(:@uncached)).to be(true)
expect(model2.instance_variable_get(:@uncached)).to be(true)
yielded_to_block = true
end
expect(yielded_to_block).to be(true)
end
end
describe '.read_only?' do
it 'returns false' do
expect(described_class.read_only?).to eq(false)

View file

@ -432,6 +432,12 @@ RSpec.describe WebHook do
expect(hook).not_to be_temporarily_disabled
end
it 'can ignore the feature flag' do
stub_feature_flags(web_hooks_disable_failed: false)
expect(hook).to be_temporarily_disabled(ignore_flag: true)
end
end
end
@ -454,6 +460,12 @@ RSpec.describe WebHook do
expect(hook).not_to be_permanently_disabled
end
it 'can ignore the feature flag' do
stub_feature_flags(web_hooks_disable_failed: false)
expect(hook).to be_permanently_disabled(ignore_flag: true)
end
end
end

View file

@ -1755,4 +1755,100 @@ RSpec.describe ProjectPolicy do
end
end
end
describe 'register_project_runners' do
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
context 'with runner_registration_control FF disabled' do
before do
stub_feature_flags(runner_registration_control: false)
end
it { is_expected.to be_allowed(:register_project_runners) }
end
context 'with runner_registration_control FF enabled' do
before do
stub_feature_flags(runner_registration_control: true)
end
it { is_expected.to be_allowed(:register_project_runners) }
context 'with project runner registration disabled' do
before do
stub_application_setting(valid_runner_registrars: ['group'])
end
it { is_expected.to be_allowed(:register_project_runners) }
end
end
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:register_project_runners) }
end
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:register_project_runners) }
context 'with runner_registration_control FF disabled' do
before do
stub_feature_flags(runner_registration_control: false)
end
it { is_expected.to be_allowed(:register_project_runners) }
end
context 'with runner_registration_control FF enabled' do
before do
stub_feature_flags(runner_registration_control: true)
end
it { is_expected.to be_allowed(:register_project_runners) }
context 'with project runner registration disabled' do
before do
stub_application_setting(valid_runner_registrars: ['group'])
end
it { is_expected.to be_disallowed(:register_project_runners) }
end
end
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:register_project_runners) }
end
context 'with reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:register_project_runners) }
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:register_project_runners) }
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(:register_project_runners) }
end
context 'with anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:register_project_runners) }
end
end
end

View file

@ -95,12 +95,37 @@ RSpec.describe WebHooks::LogExecutionService do
it 'resets the failure count' do
expect { service.execute }.to change(project_hook, :recent_failures).to(0)
end
it 'sends a message to AuthLogger if the hook as not previously enabled' do
project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD + 1)
expect(Gitlab::AuthLogger).to receive(:info).with include(
message: 'WebHook change active_state',
# identification
hook_id: project_hook.id,
hook_type: project_hook.type,
project_id: project_hook.project_id,
group_id: nil,
# relevant data
prev_state: :permanently_disabled,
new_state: :enabled,
duration: 1.2,
response_status: '200',
recent_hook_failures: 0
)
service.execute
end
end
end
context 'when response_category is :failed' do
let(:response_category) { :failed }
before do
data[:response_status] = '400'
end
it 'increments the failure count' do
expect { service.execute }.to change(project_hook, :recent_failures).by(1)
end
@ -127,11 +152,36 @@ RSpec.describe WebHooks::LogExecutionService do
expect { service.execute }.not_to change(project_hook, :recent_failures)
end
end
it 'sends a message to AuthLogger if the state would change' do
project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD)
expect(Gitlab::AuthLogger).to receive(:info).with include(
message: 'WebHook change active_state',
# identification
hook_id: project_hook.id,
hook_type: project_hook.type,
project_id: project_hook.project_id,
group_id: nil,
# relevant data
prev_state: :enabled,
new_state: :permanently_disabled,
duration: (be > 0),
response_status: data[:response_status],
recent_hook_failures: ::WebHook::FAILURE_THRESHOLD + 1
)
service.execute
end
end
context 'when response_category is :error' do
let(:response_category) { :error }
before do
data[:response_status] = '500'
end
it 'does not increment the failure count' do
expect { service.execute }.not_to change(project_hook, :recent_failures)
end
@ -144,6 +194,25 @@ RSpec.describe WebHooks::LogExecutionService do
expect { service.execute }.to change(project_hook, :backoff_count).by(1)
end
it 'sends a message to AuthLogger if the state would change' do
expect(Gitlab::AuthLogger).to receive(:info).with include(
message: 'WebHook change active_state',
# identification
hook_id: project_hook.id,
hook_type: project_hook.type,
project_id: project_hook.project_id,
group_id: nil,
# relevant data
prev_state: :enabled,
new_state: :temporarily_disabled,
duration: (be > 0),
response_status: data[:response_status],
recent_hook_failures: 0
)
service.execute
end
context 'when the previous cool-off was near the maximum' do
before do
project_hook.update!(disabled_until: 5.minutes.ago, backoff_count: 8)

View file

@ -11,12 +11,14 @@ RSpec.describe 'projects/runners/specific_runners.html.haml' do
@project = project
@assignable_runners = []
@project_runners = []
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:reset_registration_token_namespace_project_settings_ci_cd_path).and_return('banana_url')
end
context 'when project runner registration is allowed' do
before do
stub_application_setting(valid_runner_registrars: ['project'])
allow(view).to receive(:can?).with(user, :register_project_runners, project).and_return(true)
end
it 'enables the Remove project button for a project' do
@ -32,7 +34,7 @@ RSpec.describe 'projects/runners/specific_runners.html.haml' do
stub_application_setting(valid_runner_registrars: ['group'])
end
it 'does not enable the the Remove project button for a project' do
it 'does not enable the Remove project button for a project' do
render 'projects/runners/specific_runners', project: project
expect(rendered).to have_content 'Please contact an admin to register runners.'