Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-23 21:09:13 +00:00
parent 35272ed523
commit 145fb28542
30 changed files with 5700 additions and 97 deletions

View File

@ -63,7 +63,6 @@ export default {
data-testid="preview-item"
@click="onClickDraft(draft)"
>
{{ isOnLatestDiff(draft) }}
<preview-item :draft="draft" :is-last="isLast(index)" />
</gl-dropdown-item>
</gl-dropdown>

View File

@ -136,6 +136,7 @@ export default {
v-for="group in groups"
:key="group.id"
:name="group.name"
data-qa-selector="group_select_dropdown_item"
@click="selectGroup(group)"
>
<gl-avatar-labeled

View File

@ -371,51 +371,3 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
/* stylelint-disable property-no-vendor-prefix */
-webkit-backdrop-filter: blur(2px); // still required by Safari
}
/*
* The styles from here to END-#1825 will be moved to @gitlab/ui by
* https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1825
*/
.gl-lg-mx-12 {
@include media-breakpoint-up(lg) {
margin-left: $gl-spacing-scale-12;
margin-right: $gl-spacing-scale-12;
}
}
.gl-lg-ml-12 {
@include media-breakpoint-up(lg) {
margin-left: $gl-spacing-scale-12;
}
}
.gl-lg-mr-12 {
@include media-breakpoint-up(lg) {
margin-right: $gl-spacing-scale-12;
}
}
.gl-lg-ml-10 {
@include media-breakpoint-up(lg) {
margin-left: $gl-spacing-scale-10;
}
}
.gl-lg-mr-10 {
@include media-breakpoint-up(lg) {
margin-right: $gl-spacing-scale-10;
}
}
.gl-lg-w-30p {
@include gl-media-breakpoint-up(lg) {
width: 30%;
}
}
.gl-lg-w-40p {
@include gl-media-breakpoint-up(lg) {
width: 40%;
}
}
/* END-#1825 */

View File

@ -4,8 +4,8 @@ module Clusters
class AgentsFinder
include FinderMethods
def initialize(project, current_user, params: {})
@project = project
def initialize(object, current_user, params: {})
@object = object
@current_user = current_user
@params = params
end
@ -13,7 +13,7 @@ module Clusters
def execute
return ::Clusters::Agent.none unless can_read_cluster_agents?
agents = project.cluster_agents
agents = object.cluster_agents
agents = agents.with_name(params[:name]) if params[:name].present?
agents.ordered_by_name
@ -21,10 +21,10 @@ module Clusters
private
attr_reader :project, :current_user, :params
attr_reader :object, :current_user, :params
def can_read_cluster_agents?
current_user.can?(:read_cluster, project)
current_user&.can?(:read_cluster, object)
end
end
end

View File

@ -15,12 +15,10 @@ module Resolvers
description: 'Name of the cluster agent.'
end
alias_method :project, :object
def resolve_with_lookahead(**args)
apply_lookahead(
::Clusters::AgentsFinder
.new(project, current_user, params: args)
.new(object, current_user, params: args)
.execute
)
end

View File

@ -53,3 +53,5 @@ module Clusters
end
end
end
Clusters::Agent.prepend_mod_with('Clusters::Agent')

View File

@ -474,6 +474,7 @@ class Project < ApplicationRecord
delegate :forward_deployment_enabled, :forward_deployment_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true
delegate :opt_in_jwt, :opt_in_jwt=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true
delegate :separated_caches, :separated_caches=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :runner_token_expiration_interval, :runner_token_expiration_interval=, :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval_human_readable=, to: :ci_cd_settings, allow_nil: true

View File

@ -0,0 +1,8 @@
---
name: operational_vulnerabilities_filters
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90845
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365986
milestone: '15.2'
type: development
group: group::container security
default_enabled: false

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddToggleOptInJwt < Gitlab::Database::Migration[2.0]
def change
add_column :project_ci_cd_settings, :opt_in_jwt, :boolean, default: false, null: false
end
end

View File

@ -0,0 +1 @@
a18f38b1f25ab3344c3ce8dcefc4579370c79e70354dc1665dbb8026892e1e37

View File

@ -19202,7 +19202,8 @@ CREATE TABLE project_ci_cd_settings (
restrict_user_defined_variables boolean DEFAULT false NOT NULL,
job_token_scope_enabled boolean DEFAULT false NOT NULL,
runner_token_expiration_interval integer,
separated_caches boolean DEFAULT true NOT NULL
separated_caches boolean DEFAULT true NOT NULL,
opt_in_jwt boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE project_ci_cd_settings_id_seq

View File

@ -611,6 +611,13 @@ user.skip_reconfirmation!
### Disable 2fa for single user
**In GitLab 13.5 and later:**
Use the code under [Disable 2FA | For a single user](../../security/two_factor_authentication.md#for-a-single-user) so that the target user
is notified that 2FA has been disabled.
**In GitLab 13.4 and earlier:**
```ruby
user = User.find_by_username('<username>')
user.disable_two_factor!

View File

@ -11694,6 +11694,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupallowstalerunnerpruning"></a>`allowStaleRunnerPruning` | [`Boolean!`](#boolean) | Indicates whether to regularly prune stale group runners. Defaults to false. |
| <a id="groupautodevopsenabled"></a>`autoDevopsEnabled` | [`Boolean`](#boolean) | Indicates whether Auto DevOps is enabled for all projects within this group. |
| <a id="groupavatarurl"></a>`avatarUrl` | [`String`](#string) | Avatar URL of the group. |
| <a id="groupclusteragents"></a>`clusterAgents` | [`ClusterAgentConnection`](#clusteragentconnection) | Cluster agents associated with projects in the group and its subgroups. (see [Connections](#connections)) |
| <a id="groupcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the group. |
| <a id="groupcontainslockedprojects"></a>`containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. |
| <a id="groupcrossprojectpipelineavailable"></a>`crossProjectPipelineAvailable` | [`Boolean!`](#boolean) | Indicates if the cross_project_pipeline feature is available for the namespace. |
@ -12573,6 +12574,7 @@ A block of time for which a participant is on-call.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="instancesecuritydashboardclusteragents"></a>`clusterAgents` | [`ClusterAgentConnection`](#clusteragentconnection) | Cluster agents associated with projects selected in the Instance Security Dashboard. (see [Connections](#connections)) |
| <a id="instancesecuritydashboardvulnerabilitygrades"></a>`vulnerabilityGrades` | [`[VulnerableProjectsByGrade!]!`](#vulnerableprojectsbygrade) | Represents vulnerable project counts for each grade. |
| <a id="instancesecuritydashboardvulnerabilityscanners"></a>`vulnerabilityScanners` | [`VulnerabilityScannerConnection`](#vulnerabilityscannerconnection) | Vulnerability scanners reported on the vulnerabilities from projects selected in Instance Security Dashboard. (see [Connections](#connections)) |

View File

@ -1,32 +1,11 @@
---
stage: Secure, Protect
group: all
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
type: index, dev, reference
redirect_to: '../index.md'
remove_date: '2022-09-23'
---
# Application Security development documentation
This document was moved to [another location](../index.md).
Development guides that are specific to the stages that work on Application Security features are listed here.
Please go to [Application Security](../../user/application_security/index.md) if you are looking for documentation on how to use those features.
## Namespaces
Application Security code in the Rails monolith is organized into the following namespaces, which generally follows
the feature categories in the [Secure](https://about.gitlab.com/stages-devops-lifecycle/secure/) and [Protect](https://about.gitlab.com/stages-devops-lifecycle/protect/) stages.
- `AppSec`: shared code.
- `AppSec::ContainerScanning`: Container Scanning code.
- `AppSec::Dast`: DAST code.
- `AppSec::DependencyScanning`: Dependency Scanning code.
- `AppSec::Fuzzing::API`: API Fuzzing code.
- `AppSec::Fuzzing::Coverage`: Coverage Fuzzing code.
- `AppSec::Fuzzing`: Shared fuzzing code.
- `AppSec::LicenseCompliance`: License Compliance code.
- `AppSec::Sast`: SAST code.
- `AppSec::SecretDetection`: Secret Detection code.
- `AppSec::VulnMgmt`: Vulnerability Management code.
Most AppSec code does not conform to these namespace guidelines. When developing, make an effort
to move existing code into the appropriate namespace whenever possible.
<!-- This redirect file can be deleted after <2022-09-23>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -174,7 +174,6 @@ See [database guidelines](database/index.md).
## Domain-specific guides
- [CI/CD development documentation](cicd/index.md)
- [AppSec development documentation](appsec/index.md)
## Technical Reference by Group

View File

@ -41,7 +41,7 @@ Please consider using a virtual machine to run GitLab.
### Redis versions
GitLab 13.0 and later requires Redis version 4.0 or higher.
GitLab 13.0 and later requires Redis version 5.0 or higher.
Redis version 6.0 or higher is recommended, as this is what ships with
[Omnibus GitLab](https://docs.gitlab.com/omnibus/) packages starting with GitLab 13.9.

View File

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Scan execution policies **(ULTIMATE)**
> - Group-level security policies were [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4425) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `group_level_security_policies`. Disabled by default.
> - Group-level security policies were [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4425) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `group_level_security_policies`. Enabled by default.
Group, sub-group, or project owners can use scan execution policies to require that security scans run on a specified
schedule or with the project (or multiple projects if the policy is defined at a group or sub-group level) pipeline. Required scans are injected into the CI pipeline as new jobs

View File

@ -104,6 +104,7 @@ module API
expose :ci_forward_deployment_enabled
expose :ci_job_token_scope_enabled
expose :ci_separated_caches
expose :ci_opt_in_jwt
expose :public_builds, as: :public_jobs
expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
project.build_allow_git_fetch ? 'fetch' : 'clone'

View File

@ -4,12 +4,12 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
contracts = File.expand_path('../../spec/contracts', __dir__)
contracts = File.expand_path('../../../spec/contracts', __dir__)
provider = File.expand_path('provider', contracts)
# rubocop:disable Rails/RakeEnvironment
namespace :contracts do
namespace :mr do
namespace :merge_requests do
Pact::VerificationTask.new(:diffs_batch) do |pact|
pact.uri(
"#{contracts}/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
@ -33,14 +33,11 @@ namespace :contracts do
end
desc 'Run all merge request contract tests'
task 'test:merge_request', :contract_mr do |_t, arg|
raise(ArgumentError, 'Merge request contract tests require contract_mr to be set') unless arg[:contract_mr]
ENV['CONTRACT_MR'] = arg[:contract_mr]
errors = %w[metadata discussions diffs].each_with_object([]) do |task, err|
task 'test:merge_requests', :contract_mr do |_t, arg|
errors = %w[diffs_batch diffs_metadata discussions].each_with_object([]) do |task, err|
Rake::Task["contracts:mr:pact:verify:#{task}"].execute
rescue StandardError, SystemExit
err << "contracts:mr:pact:verify:#{task}"
err << "contracts:merge_requests:pact:verify:#{task}"
end
raise StandardError, "Errors in tasks #{errors.join(', ')}" unless errors.empty?

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
return if Rails.env.production?
require 'pact/tasks/verification_task'
contracts = File.expand_path('../../../spec/contracts', __dir__)
provider = File.expand_path('provider', contracts)
# rubocop:disable Rails/RakeEnvironment
namespace :contracts do
namespace :pipelines do
Pact::VerificationTask.new(:get_list_project_pipelines) do |pact|
pact.uri(
"#{contracts}/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json",
pact_helper: "#{provider}/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb"
)
end
desc 'Run all pipeline contract tests'
task 'test:pipelines', :contract_mr do |_t, arg|
errors = %w[get_list_project_pipelines].each_with_object([]) do |task, err|
Rake::Task["contracts:pipelines:pact:verify:#{task}"].execute
rescue StandardError, SystemExit
err << "contracts:pipelines:pact:verify:#{task}"
end
raise StandardError, "Errors in tasks #{errors.join(', ')}" unless errors.empty?
end
end
end
# rubocop:enable Rails/RakeEnvironment

View File

@ -17,6 +17,7 @@ module QA
base.view 'app/assets/javascripts/invite_members/components/group_select.vue' do
element :group_select_dropdown_search_field
element :group_select_dropdown_item
end
base.view 'app/assets/javascripts/invite_members/components/members_token_select.vue' do
@ -59,6 +60,8 @@ module QA
within_element(:invite_members_modal_content) do
click_button 'Select a group'
Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) }
# Helps stabilize race condition with concurrent group API calls while searching
# TODO: Replace with `fill_element :group_select_dropdown_search_field, group_name` when this bug is resolved: https://gitlab.com/gitlab-org/gitlab/-/issues/349379
send_keys_to_element(:group_select_dropdown_search_field, group_name)

View File

@ -0,0 +1,16 @@
import { request } from 'axios';
export function getProjectPipelines(endpoint) {
const { url } = endpoint;
return request({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/pipelines.json',
headers: { Accept: '*/*' },
params: {
scope: 'all',
page: 1,
},
}).then((response) => response.data);
}

View File

@ -0,0 +1,243 @@
/* eslint-disable @gitlab/require-i18n-strings */
import { Matchers } from '@pact-foundation/pact';
import {
URL,
URL_PATH,
PIPELINE_GROUPS,
PIPELINE_LABELS,
PIPELINE_SOURCES,
PIPELINE_STATUSES,
PIPELINE_TEXTS,
USER_STATES,
} from '../../../helpers/common_regex_patterns';
const body = {
pipelines: Matchers.eachLike({
id: Matchers.integer(564173401),
iid: Matchers.integer(8197225),
user: {
id: Matchers.integer(1781152),
username: Matchers.string('gitlab-bot'),
name: Matchers.string('🤖 GitLab Bot 🤖'),
state: Matchers.term({
matcher: USER_STATES,
generate: 'active',
}),
avatar_url: Matchers.term({
matcher: URL,
generate: 'https://gitlab.com/uploads/-/system/user/avatar/1516152/avatar.png',
}),
web_url: Matchers.term({
matcher: URL,
generate: 'https://gitlab.com/gitlab-bot',
}),
show_status: Matchers.boolean(false),
path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-bot',
}),
},
active: Matchers.boolean(true),
source: Matchers.term({
matcher: PIPELINE_SOURCES,
generate: 'schedule',
}),
created_at: Matchers.iso8601DateTimeWithMillis('2022-06-11T00:05:21.558Z'),
updated_at: Matchers.iso8601DateTimeWithMillis('2022-06-11T00:05:34.258Z'),
path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/pipelines/561224401',
}),
flags: {
stuck: Matchers.boolean(false),
auto_devops: Matchers.boolean(false),
merge_request: Matchers.boolean(false),
yaml_errors: Matchers.boolean(false),
retryable: Matchers.boolean(false),
cancelable: Matchers.boolean(false),
failure_reason: Matchers.boolean(false),
detached_merge_request_pipeline: Matchers.boolean(false),
merge_request_pipeline: Matchers.boolean(false),
merge_train_pipeline: Matchers.boolean(false),
latest: Matchers.boolean(true),
},
details: {
status: {
icon: Matchers.term({
matcher: PIPELINE_STATUSES,
generate: 'status_running',
}),
text: Matchers.term({
matcher: PIPELINE_TEXTS,
generate: 'running',
}),
label: Matchers.term({
matcher: PIPELINE_LABELS,
generate: 'running',
}),
group: Matchers.term({
matcher: PIPELINE_GROUPS,
generate: 'running',
}),
tooltip: Matchers.term({
matcher: PIPELINE_LABELS,
generate: 'passed',
}),
has_details: Matchers.boolean(true),
details_path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/pipelines/566374401',
}),
illustration: null,
favicon: Matchers.term({
matcher: URL_PATH,
generate: '/assets/ci_favicons/favicon_status_running.png',
}),
},
stages: Matchers.eachLike({
name: Matchers.string('sync'),
title: Matchers.string('sync: passed'),
status: {
icon: Matchers.term({
matcher: PIPELINE_STATUSES,
generate: 'status_success',
}),
text: Matchers.term({
matcher: PIPELINE_TEXTS,
generate: 'passed',
}),
label: Matchers.term({
matcher: PIPELINE_LABELS,
generate: 'passed',
}),
group: Matchers.term({
matcher: PIPELINE_GROUPS,
generate: 'success',
}),
tooltip: Matchers.term({
matcher: PIPELINE_LABELS,
generate: 'passed',
}),
has_details: Matchers.boolean(true),
details_path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/pipelines/561174401#sync',
}),
illustration: null,
favicon: Matchers.term({
matcher: URL_PATH,
generate: '/assets/ci_favicons/favicon_status_success.png',
}),
},
path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/pipelines/561124401#sync',
}),
dropdown_path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/pipelines/561174401/stage.json?stage=sync',
}),
}),
duration: Matchers.integer(25),
finished_at: Matchers.iso8601DateTimeWithMillis('2022-06-11T00:55:21.558Z'),
name: Matchers.string('Pipeline'),
manual_actions: Matchers.eachLike({
name: Matchers.string('review-docs-deploy'),
playable: Matchers.boolean(true),
scheduled: Matchers.boolean(false),
}),
scheduled_actions: Matchers.eachLike({
name: Matchers.string('review-docs-schedule'),
playable: Matchers.boolean(true),
scheduled: Matchers.boolean(false),
}),
},
ref: {
name: Matchers.string('master'),
path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/commits/master',
}),
tag: Matchers.boolean(false),
branch: Matchers.boolean(true),
merge_request: Matchers.boolean(false),
},
commit: {
id: Matchers.string('e6d797385144b955c6d4ecfa00e9656dc33efd2b'),
short_id: Matchers.string('e6d79738'),
created_at: Matchers.iso8601DateTimeWithMillis('2022-06-10T22:02:10.000+00:00'),
parent_ids: Matchers.eachLike(Matchers.string('3b0e053a24958174eaa7e3b183c7263432890d1c')),
title: Matchers.string("Merge branch 'ee-test' into 'master'"),
message: Matchers.string("Merge branch 'ee-test' into 'master'\nThis is a test."),
author_name: Matchers.string('John Doe'),
author_email: Matchers.email('jdoe@gitlab.com'),
authored_date: Matchers.iso8601DateTimeWithMillis('2022-06-10T22:02:10.000+00:00'),
committer_name: Matchers.string('John Doe'),
committer_email: Matchers.email('jdoe@gitlab.com'),
committed_date: Matchers.iso8601DateTimeWithMillis('2022-06-10T22:02:10.000+00:00'),
trailers: {},
web_url: Matchers.term({
matcher: URL,
generate: 'https://gitlab.com/gitlab-org/gitlab/-/commit/f559253c514d9ab707c66e',
}),
author: null,
author_gravatar_url: Matchers.term({
matcher: URL,
generate:
'https://secure.gravatar.com/avatar/d85e45af29611ac2c1395e3c3d6ec5d6?s=80\u0026d=identicon',
}),
commit_url: Matchers.term({
matcher: URL,
generate:
'https://gitlab.com/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a',
}),
commit_path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a',
}),
},
project: {
id: Matchers.integer(253964),
name: Matchers.string('GitLab'),
full_path: Matchers.term({
matcher: URL_PATH,
generate: '/gitlab-org/gitlab',
}),
full_name: Matchers.string('GitLab.org / GitLab'),
},
triggered_by: null,
triggered: [],
}),
count: {
all: Matchers.string('1,000+'),
},
};
const ProjectPipelines = {
body: Matchers.extractPayload(body),
success: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body,
},
request: {
uponReceiving: 'a request for a list of project pipelines',
withRequest: {
method: 'GET',
path: '/gitlab-org/gitlab-qa/-/pipelines.json',
headers: {
Accept: '*/*',
},
query: 'scope=all&page=1',
},
},
};
export { ProjectPipelines };
/* eslint-enable @gitlab/require-i18n-strings */

View File

@ -0,0 +1,20 @@
/**
* Important note: These regex patterns need to use Ruby format because the underlying Pact mock service is written in Ruby.
*/
export const URL = '^(http|https)://[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(/.*)?$';
export const URL_PATH = '^/[a-zA-Z0-9#-=?_]+$';
// Pipelines
export const PIPELINE_GROUPS =
'^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$';
export const PIPELINE_LABELS =
'^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$';
export const PIPELINE_SOURCES =
'^(push|web|trigger|schedule|api|external|pipeline|chat|webide|merge_request_event|external_pull_request_event|parent_pipeline|ondemand_dast_scan|ondemand_dast_validation)$';
export const PIPELINE_STATUSES =
'^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$';
export const PIPELINE_TEXTS =
'^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$';
// Users
export const USER_STATES = '^(active|blocked)$';

View File

@ -0,0 +1,44 @@
/* eslint-disable @gitlab/require-i18n-strings */
import { pactWith } from 'jest-pact';
import { ProjectPipelines } from '../../../fixtures/project/pipeline/get_list_project_pipelines.fixture';
import { getProjectPipelines } from '../../../endpoints/project/pipelines';
const CONSUMER_NAME = 'Pipelines#index';
const CONSUMER_LOG = '../logs/consumer.log';
const CONTRACT_DIR = '../contracts/project/pipeline/index';
const PROVIDER_NAME = 'GET List project pipelines';
// API endpoint: /pipelines.json
pactWith(
{
consumer: CONSUMER_NAME,
provider: PROVIDER_NAME,
log: CONSUMER_LOG,
dir: CONTRACT_DIR,
},
(provider) => {
describe(PROVIDER_NAME, () => {
beforeEach(() => {
const interaction = {
state: 'a few pipelines for a project exists',
...ProjectPipelines.request,
willRespondWith: ProjectPipelines.success,
};
provider.addInteraction(interaction);
});
it('returns a successful body', () => {
return getProjectPipelines({
url: provider.mockService.baseUrl,
}).then((pipelines) => {
expect(pipelines).toEqual(ProjectPipelines.body);
});
});
});
},
);
/* eslint-enable @gitlab/require-i18n-strings */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,472 @@
{
"consumer": {
"name": "Pipelines#index"
},
"provider": {
"name": "GET List project pipelines"
},
"interactions": [
{
"description": "a request for a list of project pipelines",
"providerState": "a few pipelines for a project exists",
"request": {
"method": "GET",
"path": "/gitlab-org/gitlab-qa/-/pipelines.json",
"query": "scope=all&page=1",
"headers": {
"Accept": "*/*"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"pipelines": [
{
"id": 564173401,
"iid": 8197225,
"user": {
"id": 1781152,
"username": "gitlab-bot",
"name": "🤖 GitLab Bot 🤖",
"state": "active",
"avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/1516152/avatar.png",
"web_url": "https://gitlab.com/gitlab-bot",
"show_status": false,
"path": "/gitlab-bot"
},
"active": true,
"source": "schedule",
"created_at": "2022-06-11T00:05:21.558Z",
"updated_at": "2022-06-11T00:05:34.258Z",
"path": "/gitlab-org/gitlab/-/pipelines/561224401",
"flags": {
"stuck": false,
"auto_devops": false,
"merge_request": false,
"yaml_errors": false,
"retryable": false,
"cancelable": false,
"failure_reason": false,
"detached_merge_request_pipeline": false,
"merge_request_pipeline": false,
"merge_train_pipeline": false,
"latest": true
},
"details": {
"status": {
"icon": "status_running",
"text": "running",
"label": "running",
"group": "running",
"tooltip": "passed",
"has_details": true,
"details_path": "/gitlab-org/gitlab/-/pipelines/566374401",
"illustration": null,
"favicon": "/assets/ci_favicons/favicon_status_running.png"
},
"stages": [
{
"name": "sync",
"title": "sync: passed",
"status": {
"icon": "status_success",
"text": "passed",
"label": "passed",
"group": "success",
"tooltip": "passed",
"has_details": true,
"details_path": "/gitlab-org/gitlab/-/pipelines/561174401#sync",
"illustration": null,
"favicon": "/assets/ci_favicons/favicon_status_success.png"
},
"path": "/gitlab-org/gitlab/-/pipelines/561124401#sync",
"dropdown_path": "/gitlab-org/gitlab/-/pipelines/561174401/stage.json?stage=sync"
}
],
"duration": 25,
"finished_at": "2022-06-11T00:55:21.558Z",
"name": "Pipeline",
"manual_actions": [
{
"name": "review-docs-deploy",
"playable": true,
"scheduled": false
}
],
"scheduled_actions": [
{
"name": "review-docs-schedule",
"playable": true,
"scheduled": false
}
]
},
"ref": {
"name": "master",
"path": "/gitlab-org/gitlab/-/commits/master",
"tag": false,
"branch": true,
"merge_request": false
},
"commit": {
"id": "e6d797385144b955c6d4ecfa00e9656dc33efd2b",
"short_id": "e6d79738",
"created_at": "2022-06-10T22:02:10.000+00:00",
"parent_ids": [
"3b0e053a24958174eaa7e3b183c7263432890d1c"
],
"title": "Merge branch 'ee-test' into 'master'",
"message": "Merge branch 'ee-test' into 'master'\nThis is a test.",
"author_name": "John Doe",
"author_email": "jdoe@gitlab.com",
"authored_date": "2022-06-10T22:02:10.000+00:00",
"committer_name": "John Doe",
"committer_email": "jdoe@gitlab.com",
"committed_date": "2022-06-10T22:02:10.000+00:00",
"trailers": {
},
"web_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/f559253c514d9ab707c66e",
"author": null,
"author_gravatar_url": "https://secure.gravatar.com/avatar/d85e45af29611ac2c1395e3c3d6ec5d6?s=80&d=identicon",
"commit_url": "https://gitlab.com/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a",
"commit_path": "/gitlab-org/gitlab/-/commit/dc7522f559253c514d9ab707c66e7a1026abca5a"
},
"project": {
"id": 253964,
"name": "GitLab",
"full_path": "/gitlab-org/gitlab",
"full_name": "GitLab.org / GitLab"
},
"triggered_by": null,
"triggered": [
]
}
],
"count": {
"all": "1,000+"
}
},
"matchingRules": {
"$.body.pipelines": {
"min": 1
},
"$.body.pipelines[*].*": {
"match": "type"
},
"$.body.pipelines[*].id": {
"match": "type"
},
"$.body.pipelines[*].iid": {
"match": "type"
},
"$.body.pipelines[*].user.id": {
"match": "type"
},
"$.body.pipelines[*].user.username": {
"match": "type"
},
"$.body.pipelines[*].user.name": {
"match": "type"
},
"$.body.pipelines[*].user.state": {
"match": "regex",
"regex": "^(active|blocked)$"
},
"$.body.pipelines[*].user.avatar_url": {
"match": "regex",
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
},
"$.body.pipelines[*].user.web_url": {
"match": "regex",
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
},
"$.body.pipelines[*].user.show_status": {
"match": "type"
},
"$.body.pipelines[*].user.path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].active": {
"match": "type"
},
"$.body.pipelines[*].source": {
"match": "regex",
"regex": "^(push|web|trigger|schedule|api|external|pipeline|chat|webide|merge_request_event|external_pull_request_event|parent_pipeline|ondemand_dast_scan|ondemand_dast_validation)$"
},
"$.body.pipelines[*].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.pipelines[*].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.pipelines[*].path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].flags.stuck": {
"match": "type"
},
"$.body.pipelines[*].flags.auto_devops": {
"match": "type"
},
"$.body.pipelines[*].flags.merge_request": {
"match": "type"
},
"$.body.pipelines[*].flags.yaml_errors": {
"match": "type"
},
"$.body.pipelines[*].flags.retryable": {
"match": "type"
},
"$.body.pipelines[*].flags.cancelable": {
"match": "type"
},
"$.body.pipelines[*].flags.failure_reason": {
"match": "type"
},
"$.body.pipelines[*].flags.detached_merge_request_pipeline": {
"match": "type"
},
"$.body.pipelines[*].flags.merge_request_pipeline": {
"match": "type"
},
"$.body.pipelines[*].flags.merge_train_pipeline": {
"match": "type"
},
"$.body.pipelines[*].flags.latest": {
"match": "type"
},
"$.body.pipelines[*].details.status.icon": {
"match": "regex",
"regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
},
"$.body.pipelines[*].details.status.text": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
},
"$.body.pipelines[*].details.status.label": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
},
"$.body.pipelines[*].details.status.group": {
"match": "regex",
"regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
},
"$.body.pipelines[*].details.status.tooltip": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
},
"$.body.pipelines[*].details.status.has_details": {
"match": "type"
},
"$.body.pipelines[*].details.status.details_path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.status.favicon": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.stages": {
"min": 1
},
"$.body.pipelines[*].details.stages[*].*": {
"match": "type"
},
"$.body.pipelines[*].details.stages[*].name": {
"match": "type"
},
"$.body.pipelines[*].details.stages[*].title": {
"match": "type"
},
"$.body.pipelines[*].details.stages[*].status.icon": {
"match": "regex",
"regex": "^status_(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|warning)$"
},
"$.body.pipelines[*].details.stages[*].status.text": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual|passed|pending|preparing|running|skipped|waiting)$"
},
"$.body.pipelines[*].details.stages[*].status.label": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
},
"$.body.pipelines[*].details.stages[*].status.group": {
"match": "regex",
"regex": "^(canceled|created|failed|manual|pending|preparing|running|scheduled|skipped|success|success_warning|waiting-for-resource)$"
},
"$.body.pipelines[*].details.stages[*].status.tooltip": {
"match": "regex",
"regex": "^(canceled|created|delayed|failed|manual action|passed|pending|preparing|running|skipped|passed with warnings|waiting for resource)$"
},
"$.body.pipelines[*].details.stages[*].status.has_details": {
"match": "type"
},
"$.body.pipelines[*].details.stages[*].status.details_path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.stages[*].status.favicon": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.stages[*].path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.stages[*].dropdown_path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].details.duration": {
"match": "type"
},
"$.body.pipelines[*].details.finished_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.pipelines[*].details.name": {
"match": "type"
},
"$.body.pipelines[*].details.manual_actions": {
"min": 1
},
"$.body.pipelines[*].details.manual_actions[*].*": {
"match": "type"
},
"$.body.pipelines[*].details.manual_actions[*].name": {
"match": "type"
},
"$.body.pipelines[*].details.manual_actions[*].playable": {
"match": "type"
},
"$.body.pipelines[*].details.manual_actions[*].scheduled": {
"match": "type"
},
"$.body.pipelines[*].details.scheduled_actions": {
"min": 1
},
"$.body.pipelines[*].details.scheduled_actions[*].*": {
"match": "type"
},
"$.body.pipelines[*].details.scheduled_actions[*].name": {
"match": "type"
},
"$.body.pipelines[*].details.scheduled_actions[*].playable": {
"match": "type"
},
"$.body.pipelines[*].details.scheduled_actions[*].scheduled": {
"match": "type"
},
"$.body.pipelines[*].ref.name": {
"match": "type"
},
"$.body.pipelines[*].ref.path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].ref.tag": {
"match": "type"
},
"$.body.pipelines[*].ref.branch": {
"match": "type"
},
"$.body.pipelines[*].ref.merge_request": {
"match": "type"
},
"$.body.pipelines[*].commit.id": {
"match": "type"
},
"$.body.pipelines[*].commit.short_id": {
"match": "type"
},
"$.body.pipelines[*].commit.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.pipelines[*].commit.parent_ids": {
"min": 1
},
"$.body.pipelines[*].commit.parent_ids[*].*": {
"match": "type"
},
"$.body.pipelines[*].commit.parent_ids[*]": {
"match": "type"
},
"$.body.pipelines[*].commit.title": {
"match": "type"
},
"$.body.pipelines[*].commit.message": {
"match": "type"
},
"$.body.pipelines[*].commit.author_name": {
"match": "type"
},
"$.body.pipelines[*].commit.author_email": {
"match": "regex",
"regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
},
"$.body.pipelines[*].commit.authored_date": {
"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.pipelines[*].commit.committer_name": {
"match": "type"
},
"$.body.pipelines[*].commit.committer_email": {
"match": "regex",
"regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$"
},
"$.body.pipelines[*].commit.committed_date": {
"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.pipelines[*].commit.web_url": {
"match": "regex",
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
},
"$.body.pipelines[*].commit.author_gravatar_url": {
"match": "regex",
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
},
"$.body.pipelines[*].commit.commit_url": {
"match": "regex",
"regex": "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*.[a-z]{2,5}(:[0-9]{1,5})?(\\/.*)?$"
},
"$.body.pipelines[*].commit.commit_path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].project.id": {
"match": "type"
},
"$.body.pipelines[*].project.name": {
"match": "type"
},
"$.body.pipelines[*].project.full_path": {
"match": "regex",
"regex": "^\\/[a-zA-Z0-9#-=?_]+$"
},
"$.body.pipelines[*].project.full_name": {
"match": "type"
},
"$.body.count.all": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
require_relative '../../../spec_helper'
require_relative '../../../states/project/pipeline/pipelines_state'
module Provider
module GetListProjectPipelinesHelper
Pact.service_provider "GET List project pipelines" do
app { Environments::Test.app }
honours_pact_with 'Pipelines#index' do
pact_uri '../contracts/project/project/pipeline/index/pipelines#index-get_list_project_pipelines.json'
end
end
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
Pact.provider_states_for "Pipelines#index" do
provider_state "a few pipelines for a project exists" do
set_up do
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
namespace = create(:namespace, name: 'gitlab-org')
project = create(:project, :repository, name: 'gitlab-qa', namespace: namespace, creator: user)
scheduled_job = create(:ci_build, :scheduled)
manual_job = create(:ci_build, :manual)
project.add_maintainer(user)
create(
:ci_pipeline,
:with_job,
:success,
project: project,
user: user,
duration: 10,
finished_at: '2022-06-01T02:47:31.432Z',
builds: [scheduled_job, manual_job]
)
end
end
end

View File

@ -100,6 +100,7 @@ ci_cd_settings:
forward_deployment_enabled: ci_forward_deployment_enabled
job_token_scope_enabled: ci_job_token_scope_enabled
separated_caches: ci_separated_caches
opt_in_jwt: ci_opt_in_jwt
build_import_state: # import_state
unexposed_attributes: