Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-02-16 21:12:25 +00:00
parent 5e65d4f6c6
commit ee52c3666e
51 changed files with 603 additions and 154 deletions

View File

@ -23,6 +23,7 @@ export function initIssueStatusSelect() {
return new Vue({
el,
name: 'StatusSelectRoot',
render: (createElement) => createElement(StatusSelect),
});
}

View File

@ -32,6 +32,7 @@ export function initCsvImportExportButtons() {
return new Vue({
el,
name: 'CsvImportExportButtonsRoot',
provide: {
showExportButton: parseBoolean(showExportButton),
showImportButton: parseBoolean(showImportButton),
@ -74,6 +75,7 @@ export function initIssuableByEmail() {
return new Vue({
el,
name: 'IssuableByEmailRoot',
provide: {
initialEmail,
issuableType,

View File

@ -30,6 +30,7 @@ export function mountJiraIssuesListApp() {
return new Vue({
el,
name: 'JiraIssuesImportStatusRoot',
apolloProvider,
render(createComponent) {
return createComponent(JiraIssuesImportStatusRoot, {
@ -119,6 +120,7 @@ export function mountIssuesListApp() {
return new Vue({
el,
name: 'IssuesListRoot',
apolloProvider,
provide: {
autocompleteAwardEmojisPath,

View File

@ -20,6 +20,7 @@ export function initTitleSuggestions() {
return new Vue({
el,
name: 'TitleSuggestionsRoot',
apolloProvider,
data() {
return {
@ -51,6 +52,7 @@ export function initTypePopover() {
return new Vue({
el,
name: 'TypePopoverRoot',
render: (createElement) => createElement(TypePopover),
});
}

View File

@ -95,7 +95,7 @@ export default {
this.renderGFM();
this.updateTaskStatusText();
if (this.workItemsEnabled && this.$el) {
if (this.workItemsEnabled) {
this.renderTaskActions();
}
},
@ -157,7 +157,12 @@ export default {
}
},
renderTaskActions() {
if (!this.$el?.querySelectorAll) {
return;
}
const taskListFields = this.$el.querySelectorAll('.task-list-item');
taskListFields.forEach((item, index) => {
const button = document.createElement('button');
button.classList.add(

View File

@ -160,6 +160,7 @@ export function initSentryErrorStackTrace() {
return new Vue({
el,
name: 'SentryErrorStackTraceRoot',
store: errorTrackingStore,
render: (createElement) =>
createElement(SentryErrorStackTrace, { props: { issueStackTracePath } }),

View File

@ -11,6 +11,7 @@ import ProjectLabelSubscription from './project_label_subscription';
export function initDeleteLabelModal(optionalProps = {}) {
new Vue({
name: 'DeleteLabelModalRoot',
render(h) {
return h(DeleteLabelModal, {
props: {
@ -65,6 +66,7 @@ export function initLabelIndex() {
return new Vue({
el: '#js-promote-label-modal',
name: 'PromoteLabelModal',
data() {
return {
modalProps: {

View File

@ -46,6 +46,7 @@ export function initPromoteMilestoneModal() {
return new Vue({
el: promoteMilestoneModal,
name: 'PromoteMilestoneModalRoot',
render(createElement) {
return createElement(PromoteMilestoneModal);
},
@ -80,6 +81,7 @@ export function initDeleteMilestoneModal() {
return new Vue({
el: '#js-delete-milestone-modal',
name: 'DeleteMilestoneModalRoot',
data() {
return {
modalProps: {

View File

@ -136,8 +136,8 @@ export default {
return __('Branch');
}
},
commitTitle() {
return this.pipeline?.commit?.title;
commitTitleText() {
return this.pipeline?.commit?.title || __("Can't find HEAD commit for this branch");
},
hasAuthor() {
return (
@ -159,27 +159,22 @@ export default {
<div class="pipeline-tags" data-testid="pipeline-url-table-cell">
<template v-if="rearrangePipelinesTable">
<div class="commit-title gl-mb-2" data-testid="commit-title-container">
<span v-if="commitTitle" class="gl-display-flex">
<tooltip-on-truncate :title="commitTitle" class="flex-truncate-child gl-flex-grow-1">
<span class="gl-display-flex">
<tooltip-on-truncate :title="commitTitleText" class="flex-truncate-child gl-flex-grow-1">
<gl-link
:href="commitUrl"
class="commit-row-message gl-text-gray-900"
:href="pipeline.path"
class="commit-row-message gl-text-blue-600!"
data-testid="commit-title"
>{{ commitTitle }}</gl-link
data-qa-selector="pipeline_url_link"
>{{ commitTitleText }}</gl-link
>
</tooltip-on-truncate>
</span>
<span v-else>{{ __("Can't find HEAD commit for this branch") }}</span>
</div>
<div class="gl-mb-2">
<gl-link
:href="pipeline.path"
class="gl-text-decoration-underline gl-text-blue-600!"
data-testid="pipeline-url-link"
data-qa-selector="pipeline_url_link"
>
<span class="gl-font-weight-bold gl-text-gray-500" data-testid="pipeline-identifier">
#{{ pipeline[pipelineKey] }}
</gl-link>
</span>
<!--Commit row-->
<div class="icon-container gl-display-inline-block">
<gl-icon

View File

@ -21,6 +21,7 @@ export default class SidebarMilestone {
// eslint-disable-next-line no-new
new Vue({
el,
name: 'SidebarMilestoneRoot',
components: {
timeTracker,
},

View File

@ -70,6 +70,7 @@ export default (selector) => {
// eslint-disable-next-line no-new
new Vue({
el: selector,
name: 'AlertDetailsRoot',
components: {
AlertDetails,
},

View File

@ -4,7 +4,7 @@ module Ci
class RunnersFinder < UnionFinder
include Gitlab::Allowable
ALLOWED_SORTS = %w[contacted_asc contacted_desc created_at_asc created_at_desc created_date].freeze
ALLOWED_SORTS = %w[contacted_asc contacted_desc created_at_asc created_at_desc created_date token_expires_at_asc token_expires_at_desc].freeze
DEFAULT_SORT = 'created_at_desc'
def initialize(current_user:, params:)

View File

@ -10,6 +10,8 @@ module Types
value 'CONTACTED_DESC', 'Ordered by contacted_at in descending order.', value: :contacted_desc
value 'CREATED_ASC', 'Ordered by created_at in ascending order.', value: :created_at_asc
value 'CREATED_DESC', 'Ordered by created_at in descending order.', value: :created_at_desc
value 'TOKEN_EXPIRES_AT_ASC', 'Ordered by token_expires_at in ascending order.', value: :token_expires_at_asc
value 'TOKEN_EXPIRES_AT_DESC', 'Ordered by token_expires_at in descending order.', value: :token_expires_at_desc
end
end
end

View File

@ -25,6 +25,9 @@ module Types
field :contacted_at, Types::TimeType, null: true,
description: 'Timestamp of last contact from this runner.',
method: :contacted_at
field :token_expires_at, Types::TimeType, null: true,
description: 'Runner token expiration time.',
method: :token_expires_at
field :maximum_timeout, GraphQL::Types::Int, null: true,
description: 'Maximum timeout (in seconds) for jobs processed by the runner.'
field :access_level, ::Types::Ci::RunnerAccessLevelEnum, null: false,

View File

@ -15,5 +15,6 @@ module Types
field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.'
field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.'
field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.'
field :dependency_proxy_size, GraphQL::Types::Float, null: false, description: 'Dependency Proxy sizes in bytes.'
end
end

View File

@ -223,13 +223,9 @@ To add methods of a module to an allowlist, use `delegator_override_with`. For e
```ruby
module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::TagHelper` inclusion as it overrides `Ci::Pipeline#tag`
```
Keep in mind that if you use `delegator_override_with`,
there is a high chance that you're doing **something wrong**.
Read the [Validate Accidental Overrides](#validate-accidental-overrides) for more information.

View File

@ -6,7 +6,7 @@ module AlertManagement
include ActionView::Helpers::UrlHelper
presents ::AlertManagement::Alert
delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
delegator_override_with Gitlab::Utils::StrongMemoize # This module inclusion is expected. See https://gitlab.com/gitlab-org/gitlab/-/issues/352884.
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"

View File

@ -4,7 +4,7 @@ module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
delegator_override_with Gitlab::Utils::StrongMemoize # This module inclusion is expected. See https://gitlab.com/gitlab-org/gitlab/-/issues/352884.
delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
# We use a class method here instead of a constant, allowing EE to redefine

View File

@ -13,7 +13,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Experiment::Dsl
delegator_override_with GitlabRoutingHelper # TODO: Remove `GitlabRoutingHelper` inclusion as it's duplicate
delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
delegator_override_with Gitlab::Utils::StrongMemoize # This module inclusion is expected. See https://gitlab.com/gitlab-org/gitlab/-/issues/352884.
presents ::Project, as: :project

View File

@ -1,8 +0,0 @@
---
name: grape_gitlab_json
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36472
rollout_issue_url:
milestone: '13.2'
type: development
group: group::source code
default_enabled: true

View File

@ -0,0 +1,16 @@
- name: "Deprecate feature flag PUSH_RULES_SUPERSEDE_CODE_OWNERS" # The name of the feature to be deprecated
announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-02-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
removal_date: "2022-05-22" # The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
breaking_change: true # If this deprecation is a breaking change, set this value to true
reporter: sarahwaldner # GitLab username of the person reporting the deprecation
body: | # Do not modify this line, instead modify the lines below.
The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede CODEOWNERS. The CODEOWNERS feature will no longer be available for access control.
# The following items are not published on the docs page, but may be used in the future.
stage: create # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/262019 # (optional) This is a link to the deprecation issue in GitLab
documentation_url: # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
class CreateJobArtifactStates < Gitlab::Database::Migration[1.0]
VERIFICATION_STATE_INDEX_NAME = "index_job_artifact_states_on_verification_state"
PENDING_VERIFICATION_INDEX_NAME = "index_job_artifact_states_pending_verification"
FAILED_VERIFICATION_INDEX_NAME = "index_job_artifact_states_failed_verification"
NEEDS_VERIFICATION_INDEX_NAME = "index_job_artifact_states_needs_verification"
enable_lock_retries!
def up
create_table :ci_job_artifact_states, id: false do |t|
t.datetime_with_timezone :verification_started_at
t.datetime_with_timezone :verification_retry_at
t.datetime_with_timezone :verified_at
t.references :job_artifact, primary_key: true, null: false, foreign_key: { on_delete: :cascade, to_table: :ci_job_artifacts }
t.integer :verification_state, default: 0, limit: 2, null: false
t.integer :verification_retry_count, limit: 2
t.binary :verification_checksum, using: 'verification_checksum::bytea'
t.text :verification_failure, limit: 255
t.index :verification_state, name: VERIFICATION_STATE_INDEX_NAME
t.index :verified_at, where: "(verification_state = 0)", order: { verified_at: 'ASC NULLS FIRST' }, name: PENDING_VERIFICATION_INDEX_NAME
t.index :verification_retry_at, where: "(verification_state = 3)", order: { verification_retry_at: 'ASC NULLS FIRST' }, name: FAILED_VERIFICATION_INDEX_NAME
t.index :verification_state, where: "(verification_state = 0 OR verification_state = 3)", name: NEEDS_VERIFICATION_INDEX_NAME
end
end
def down
drop_table :ci_job_artifact_states
end
end

View File

@ -0,0 +1 @@
d618c28360f7716807e9727566019e269963d85164cf2f306ec9692d3b037802

View File

@ -11863,6 +11863,27 @@ CREATE SEQUENCE ci_instance_variables_id_seq
ALTER SEQUENCE ci_instance_variables_id_seq OWNED BY ci_instance_variables.id;
CREATE TABLE ci_job_artifact_states (
verification_started_at timestamp with time zone,
verification_retry_at timestamp with time zone,
verified_at timestamp with time zone,
job_artifact_id bigint NOT NULL,
verification_state smallint DEFAULT 0 NOT NULL,
verification_retry_count smallint,
verification_checksum bytea,
verification_failure text,
CONSTRAINT check_df832b66ea CHECK ((char_length(verification_failure) <= 255))
);
CREATE SEQUENCE ci_job_artifact_states_job_artifact_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE ci_job_artifact_states_job_artifact_id_seq OWNED BY ci_job_artifact_states.job_artifact_id;
CREATE TABLE ci_job_artifacts (
project_id integer NOT NULL,
file_type integer NOT NULL,
@ -21661,6 +21682,8 @@ ALTER TABLE ONLY ci_group_variables ALTER COLUMN id SET DEFAULT nextval('ci_grou
ALTER TABLE ONLY ci_instance_variables ALTER COLUMN id SET DEFAULT nextval('ci_instance_variables_id_seq'::regclass);
ALTER TABLE ONLY ci_job_artifact_states ALTER COLUMN job_artifact_id SET DEFAULT nextval('ci_job_artifact_states_job_artifact_id_seq'::regclass);
ALTER TABLE ONLY ci_job_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_job_artifacts_id_seq'::regclass);
ALTER TABLE ONLY ci_job_token_project_scope_links ALTER COLUMN id SET DEFAULT nextval('ci_job_token_project_scope_links_id_seq'::regclass);
@ -23138,6 +23161,9 @@ ALTER TABLE ONLY ci_group_variables
ALTER TABLE ONLY ci_instance_variables
ADD CONSTRAINT ci_instance_variables_pkey PRIMARY KEY (id);
ALTER TABLE ONLY ci_job_artifact_states
ADD CONSTRAINT ci_job_artifact_states_pkey PRIMARY KEY (job_artifact_id);
ALTER TABLE ONLY ci_job_artifacts
ADD CONSTRAINT ci_job_artifacts_pkey PRIMARY KEY (id);
@ -25767,6 +25793,8 @@ CREATE UNIQUE INDEX index_ci_group_variables_on_group_id_and_key_and_environment
CREATE UNIQUE INDEX index_ci_instance_variables_on_key ON ci_instance_variables USING btree (key);
CREATE INDEX index_ci_job_artifact_states_on_job_artifact_id ON ci_job_artifact_states USING btree (job_artifact_id);
CREATE INDEX index_ci_job_artifacts_for_terraform_reports ON ci_job_artifacts USING btree (project_id, id) WHERE (file_type = 18);
CREATE INDEX index_ci_job_artifacts_id_for_terraform_reports ON ci_job_artifacts USING btree (id) WHERE (file_type = 18);
@ -26755,6 +26783,14 @@ CREATE INDEX index_jira_imports_on_user_id ON jira_imports USING btree (user_id)
CREATE INDEX index_jira_tracker_data_on_service_id ON jira_tracker_data USING btree (service_id);
CREATE INDEX index_job_artifact_states_failed_verification ON ci_job_artifact_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
CREATE INDEX index_job_artifact_states_needs_verification ON ci_job_artifact_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3));
CREATE INDEX index_job_artifact_states_on_verification_state ON ci_job_artifact_states USING btree (verification_state);
CREATE INDEX index_job_artifact_states_pending_verification ON ci_job_artifact_states USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
CREATE INDEX index_keys_on_expires_at_and_id ON keys USING btree (date(timezone('UTC'::text, expires_at)), id) WHERE (expiry_notification_delivered_at IS NULL);
CREATE UNIQUE INDEX index_keys_on_fingerprint ON keys USING btree (fingerprint);
@ -30968,6 +31004,9 @@ ALTER TABLE ONLY application_settings
ALTER TABLE ONLY clusters_kubernetes_namespaces
ADD CONSTRAINT fk_rails_7e7688ecaf FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_job_artifact_states
ADD CONSTRAINT fk_rails_80a9cba3b2 FOREIGN KEY (job_artifact_id) REFERENCES ci_job_artifacts(id) ON DELETE CASCADE;
ALTER TABLE ONLY approval_merge_request_rules_users
ADD CONSTRAINT fk_rails_80e6801803 FOREIGN KEY (approval_merge_request_rule_id) REFERENCES approval_merge_request_rules(id) ON DELETE CASCADE;

View File

@ -39,7 +39,7 @@ verification methods:
| Blobs | User uploads _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | LFS objects _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | LFS objects _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | CI job artifacts _(file system)_ | Geo with API | _Not implemented_ |
| Blobs | CI job artifacts _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | CI job artifacts _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | Archived CI build traces _(file system)_ | Geo with API | _Not implemented_ |
| Blobs | Archived CI build traces _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |

View File

@ -270,6 +270,16 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_pages_deployments_verification_total` | Gauge | 14.6 | Number of pages deployments verifications tried on secondary | `url` |
| `geo_pages_deployments_verified` | Gauge | 14.6 | Number of pages deployments verified on secondary | `url` |
| `geo_pages_deployments_verification_failed` | Gauge | 14.6 | Number of pages deployments verifications failed on secondary | `url` |
| `geo_job_artifacts` | Gauge | 14.8 | Number of job artifacts on primary | `url` |
| `geo_job_artifacts_checksum_total` | Gauge | 14.8 | Number of job artifacts tried to checksum on primary | `url` |
| `geo_job_artifacts_checksummed` | Gauge | 14.8 | Number of job artifacts successfully checksummed on primary | `url` |
| `geo_job_artifacts_checksum_failed` | Gauge | 14.8 | Number of job artifacts failed to calculate the checksum on primary | `url` |
| `geo_job_artifacts_synced` | Gauge | 14.8 | Number of syncable job artifacts synced on secondary | `url` |
| `geo_job_artifacts_failed` | Gauge | 14.8 | Number of syncable job artifacts failed to sync on secondary | `url` |
| `geo_job_artifacts_registry` | Gauge | 14.8 | Number of job artifacts in the registry | `url` |
| `geo_job_artifacts_verification_total` | Gauge | 14.8 | Number of job artifacts verifications tried on secondary | `url` |
| `geo_job_artifacts_verified` | Gauge | 14.8 | Number of job artifacts verified on secondary | `url` |
| `geo_job_artifacts_verification_failed` | Gauge | 14.8 | Number of job artifacts verifications failed on secondary | `url` |
| `limited_capacity_worker_running_jobs` | Gauge | 13.5 | Number of running jobs | `worker` |
| `limited_capacity_worker_max_running_jobs` | Gauge | 13.5 | Maximum number of running jobs | `worker` |
| `limited_capacity_worker_remaining_work_count` | Gauge | 13.5 | Number of jobs waiting to be enqueued | `worker` |

View File

@ -433,9 +433,9 @@ To disable the monitoring of Kubernetes:
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to
take effect.
### Troubleshooting
## Troubleshooting
## `/var/opt/gitlab/prometheus` consumes too much disk space
### `/var/opt/gitlab/prometheus` consumes too much disk space
If you are **not** using Prometheus monitoring:

View File

@ -1115,8 +1115,6 @@ than GitLab to prevent XSS attacks.
### Rate limits
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/631) in GitLab 14.5.
You can enforce rate limits to help minimize the risk of a Denial of Service (DoS) attack. GitLab Pages
uses a [token bucket algorithm](https://en.wikipedia.org/wiki/Token_bucket) to enforce rate limiting. By default,
requests that exceed the specified limits are reported but not rejected.
@ -1136,6 +1134,8 @@ Rate limits are enforced using the following:
#### Enable source-IP rate limits
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/631) in GitLab 14.5.
1. Set rate limits in `/etc/gitlab/gitlab.rb`:
```ruby
@ -1154,6 +1154,8 @@ Rate limits are enforced using the following:
#### Enable domain rate limits
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/630) in GitLab 14.7.
1. Set rate limits in `/etc/gitlab/gitlab.rb`:
```ruby

View File

@ -467,6 +467,19 @@ Example response:
"uploads_verified_count": null,
"uploads_verification_failed_count": null,
"uploads_verified_in_percentage": "0.00%",
"job_artifacts_count": 5,
"job_artifacts_checksum_total_count": 5,
"job_artifacts_checksummed_count": 5,
"job_artifacts_checksum_failed_count": 0,
"job_artifacts_synced_count": 5,
"job_artifacts_failed_count": 0,
"job_artifacts_registry_count": 5,
"job_artifacts_verification_total_count": 5,
"job_artifacts_verified_count": 5,
"job_artifacts_verification_failed_count": 0,
"job_artifacts_synced_in_percentage": "100.00%",
"job_artifacts_verified_in_percentage": "100.00%",
"job_artifacts_synced_missing_on_primary_count": 0,
},
{
"geo_node_id": 2,
@ -623,6 +636,19 @@ Example response:
"uploads_verified_count": null,
"uploads_verification_failed_count": null,
"uploads_verified_in_percentage": "0.00%",
"job_artifacts_count": 5,
"job_artifacts_checksum_total_count": 5,
"job_artifacts_checksummed_count": 5,
"job_artifacts_checksum_failed_count": 0,
"job_artifacts_synced_count": 5,
"job_artifacts_failed_count": 0,
"job_artifacts_registry_count": 5,
"job_artifacts_verification_total_count": 5,
"job_artifacts_verified_count": 5,
"job_artifacts_verification_failed_count": 0,
"job_artifacts_synced_in_percentage": "100.00%",
"job_artifacts_verified_in_percentage": "100.00%",
"job_artifacts_synced_missing_on_primary_count": 0,
}
]
```
@ -776,6 +802,19 @@ Example response:
"uploads_verified_count": null,
"uploads_verification_failed_count": null,
"uploads_verified_in_percentage": "0.00%",
"job_artifacts_count": 5,
"job_artifacts_checksum_total_count": 5,
"job_artifacts_checksummed_count": 5,
"job_artifacts_checksum_failed_count": 0,
"job_artifacts_synced_count": 5,
"job_artifacts_failed_count": 0,
"job_artifacts_registry_count": 5,
"job_artifacts_verification_total_count": 5,
"job_artifacts_verified_count": 5,
"job_artifacts_verification_failed_count": 0,
"job_artifacts_synced_in_percentage": "100.00%",
"job_artifacts_verified_in_percentage": "100.00%",
"job_artifacts_synced_missing_on_primary_count": 0,
}
```

View File

@ -6803,6 +6803,29 @@ The edge type for [`JiraProject`](#jiraproject).
| <a id="jiraprojectedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="jiraprojectedgenode"></a>`node` | [`JiraProject`](#jiraproject) | The item at the end of the edge. |
#### `JobArtifactRegistryConnection`
The connection type for [`JobArtifactRegistry`](#jobartifactregistry).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="jobartifactregistryconnectionedges"></a>`edges` | [`[JobArtifactRegistryEdge]`](#jobartifactregistryedge) | A list of edges. |
| <a id="jobartifactregistryconnectionnodes"></a>`nodes` | [`[JobArtifactRegistry]`](#jobartifactregistry) | A list of nodes. |
| <a id="jobartifactregistryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `JobArtifactRegistryEdge`
The edge type for [`JobArtifactRegistry`](#jobartifactregistry).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="jobartifactregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="jobartifactregistryedgenode"></a>`node` | [`JobArtifactRegistry`](#jobartifactregistry) | The item at the end of the edge. |
#### `JobNeedUnionConnection`
The connection type for [`JobNeedUnion`](#jobneedunion).
@ -9201,6 +9224,7 @@ Represents the total number of issues and their weights for a particular day.
| <a id="cirunnerrunnertype"></a>`runnerType` | [`CiRunnerType!`](#cirunnertype) | Type of the runner. |
| <a id="cirunnershortsha"></a>`shortSha` | [`String`](#string) | First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID. |
| <a id="cirunnertaglist"></a>`tagList` | [`[String!]`](#string) | Tags associated with the runner. |
| <a id="cirunnertokenexpiresat"></a>`tokenExpiresAt` | [`Time`](#time) | Runner token expiration time. |
| <a id="cirunneruserpermissions"></a>`userPermissions` | [`RunnerPermissions!`](#runnerpermissions) | Permissions for the current user on the resource. |
| <a id="cirunnerversion"></a>`version` | [`String`](#string) | Version of the runner. |
@ -10780,6 +10804,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="geonodegroupwikirepositoryregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. |
##### `GeoNode.jobArtifactRegistries`
Find Job Artifact registries on this Geo node Available only when feature flag `geo_job_artifact_replication` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Returns [`JobArtifactRegistryConnection`](#jobartifactregistryconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="geonodejobartifactregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. |
##### `GeoNode.lfsObjectRegistries`
Find LFS object registries on this Geo node.
@ -11970,6 +12010,23 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="jirauserjiradisplayname"></a>`jiraDisplayName` | [`String!`](#string) | Display name of the Jira user. |
| <a id="jirauserjiraemail"></a>`jiraEmail` | [`String`](#string) | Email of the Jira user, returned only for users with public emails. |
### `JobArtifactRegistry`
Represents the Geo replication and verification state of a job_artifact.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="jobartifactregistryartifactid"></a>`artifactId` | [`ID!`](#id) | ID of the Job Artifact. |
| <a id="jobartifactregistrycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp when the JobArtifactRegistry was created. |
| <a id="jobartifactregistryid"></a>`id` | [`ID!`](#id) | ID of the JobArtifactRegistry. |
| <a id="jobartifactregistrylastsyncfailure"></a>`lastSyncFailure` | [`String`](#string) | Error message during sync of the JobArtifactRegistry. |
| <a id="jobartifactregistrylastsyncedat"></a>`lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the JobArtifactRegistry. |
| <a id="jobartifactregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the JobArtifactRegistry should be resynced. |
| <a id="jobartifactregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the JobArtifactRegistry. |
| <a id="jobartifactregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the JobArtifactRegistry. |
### `JobPermissions`
#### Fields
@ -14940,6 +14997,7 @@ Counts of requirements by their state.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="rootstoragestatisticsbuildartifactssize"></a>`buildArtifactsSize` | [`Float!`](#float) | CI artifacts size in bytes. |
| <a id="rootstoragestatisticsdependencyproxysize"></a>`dependencyProxySize` | [`Float!`](#float) | Dependency Proxy sizes in bytes. |
| <a id="rootstoragestatisticslfsobjectssize"></a>`lfsObjectsSize` | [`Float!`](#float) | LFS objects size in bytes. |
| <a id="rootstoragestatisticspackagessize"></a>`packagesSize` | [`Float!`](#float) | Packages size in bytes. |
| <a id="rootstoragestatisticspipelineartifactssize"></a>`pipelineArtifactsSize` | [`Float!`](#float) | CI pipeline artifacts size in bytes. |
@ -16796,6 +16854,8 @@ Values for sorting runners.
| <a id="cirunnersortcontacted_desc"></a>`CONTACTED_DESC` | Ordered by contacted_at in descending order. |
| <a id="cirunnersortcreated_asc"></a>`CREATED_ASC` | Ordered by created_at in ascending order. |
| <a id="cirunnersortcreated_desc"></a>`CREATED_DESC` | Ordered by created_at in descending order. |
| <a id="cirunnersorttoken_expires_at_asc"></a>`TOKEN_EXPIRES_AT_ASC` | Ordered by token_expires_at in ascending order. |
| <a id="cirunnersorttoken_expires_at_desc"></a>`TOKEN_EXPIRES_AT_DESC` | Ordered by token_expires_at in descending order. |
### `CiRunnerStatus`

View File

@ -818,6 +818,18 @@ The following `geo:db:*` tasks will be replaced with their corresponding `db:*:g
**Planned removal milestone: 15.0 (2022-05-22)**
### Deprecate feature flag PUSH_RULES_SUPERSEDE_CODE_OWNERS
WARNING:
This feature will be changed or removed in 15.0
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
The feature flag `PUSH_RULES_SUPERSEDE_CODE_OWNERS` is being removed in GitLab 15.0. Upon its removal, push rules will supersede CODEOWNERS. The CODEOWNERS feature will no longer be available for access control.
**Planned removal milestone: 15.0 (2022-05-22)**
### Deprecate legacy Gitaly configuration methods
WARNING:

View File

@ -456,28 +456,24 @@ GitLab provides two methods of accomplishing this, each with advantages and disa
- [Compliance framework pipelines](../project/settings/#compliance-pipeline-configuration)
are recommended when:
- Scan execution enforcement is required for SAST IaC, Container Scanning, Dependency Scanning,
- Scan execution enforcement is required for SAST IaC, Dependency Scanning,
License Compliance, API Fuzzing, or Coverage-guided Fuzzing.
- Scan execution enforcement of SAST or Secret Detection when customization of the default scan
variables is required.
- Scan execution enforcement is required for scanners external to GitLab.
- Enforced execution is required for custom jobs other than security scans.
- [Scan execution policies](policies/#scan-execution-policies)
- [Scan execution policies](policies/scan-execution-policies.md)
are recommended when:
- Scan execution enforcement is required for DAST.
- Scan execution enforcement is required for SAST or Secret Detection with the default scan
variables.
- Scan execution enforcement is required for DAST, SAST, Secret Detection, or Container Scanning.
- Scans are required to run on a regular, scheduled cadence.
Additional details about the differences between the two solutions are outlined below:
| | Compliance Framework Pipelines | Scan Execution Policies |
| ------ | ------ | ------ |
| **Flexibility** | Supports anything that can be done in a CI file. | Limited to only the items for which GitLab has explicitly added support. DAST, SAST, and Secret Detection scans are supported. |
| **Flexibility** | Supports anything that can be done in a CI file. | Limited to only the items for which GitLab has explicitly added support. DAST, SAST, Secret Detection, and Container Scanning scans are supported. |
| **Usability** | Requires knowledge of CI YAML. | Follows a `rules` and `actions`-based YAML structure. |
| **Inclusion in CI pipeline** | The compliance pipeline is executed instead of the project's `gitlab-ci.yml` file. To include the project's `gitlab-ci.yml` file, use an `include` statement. Defined variables aren't allowed to be overwritten by the included project's YAML file. | Forced inclusion of a new job into the CI pipeline. DAST jobs that must be customized on a per-project basis can have project-level Site Profiles and Scan Profiles defined. To ensure separation of duties, these profiles are immutable when referenced in a scan execution policy. |
| **Inclusion in CI pipeline** | The compliance pipeline is executed instead of the project's `gitlab-ci.yml` file. To include the project's `gitlab-ci.yml` file, use an `include` statement. Defined variables aren't allowed to be overwritten by the included project's YAML file. | Forced inclusion of a new job into the CI pipeline. DAST jobs that must be customized on a per-project basis can have project-level Site Profiles and Scan Profiles defined. To ensure separation of duties, these profiles are immutable when referenced in a scan execution policy. All jobs can be customized as part of the security policy itself with the same variables that are normally available to the CI job. |
| **Schedulable** | Can be scheduled through a scheduled pipeline on the group. | Can be scheduled natively through the policy configuration itself. |
| **Separation of Duties** | Only group owners can create compliance framework labels. Only project owners can apply compliance framework labels to projects. The ability to make or approve changes to the compliance pipeline definition is limited to individuals who are explicitly given access to the project that contains the compliance pipeline. | Only project owners can define a linked security policy project. The ability to make or approve changes to security policies is limited to individuals who are explicitly given access to the security policy project. |
| **Ability to apply one standard to multiple projects** | The same compliance framework label can be applied to multiple projects inside a group. | The same security policy project can be used for multiple projects across GitLab with no requirement of being located in the same group. |

View File

@ -101,7 +101,7 @@ rule in the defined policy are met.
| `scan` | `string` | `dast`, `secret_detection`, `sast`, `container_scanning`, `cluster_image_scanning` | The action's type. |
| `site_profile` | `string` | Name of the selected [DAST site profile](../dast/index.md#site-profile). | The DAST site profile to execute the DAST scan. This field should only be set if `scan` type is `dast`. |
| `scanner_profile` | `string` or `null` | Name of the selected [DAST scanner profile](../dast/index.md#scanner-profile). | The DAST scanner profile to execute the DAST scan. This field should only be set if `scan` type is `dast`.|
| `variables` | `object` | | Set of variables applied and enforced for the selected scan. The object's key is the variable name with a value provided as a string. |
| `variables` | `object` | | A set of CI variables, supplied as an array of `key: value` pairs, to apply and enforce for the selected scan. The `key` is the variable name, with its `value` provided as a string. This parameter supports any variable that the GitLab CI job supports for the specified scan. |
Note the following:

View File

@ -93,7 +93,7 @@ executed in place of the local project's `gitlab-ci.yml` file. As part of this p
files are merged together any time the pipeline runs. Jobs and variables defined in the compliance
pipeline can't be changed by variables in the local project's `gitlab-ci.yml` file.
When used to enforce scan execution, this feature has some overlap with [scan execution policies](../../application_security/policies/#scan-execution-policies),
When used to enforce scan execution, this feature has some overlap with [scan execution policies](../../application_security/policies/scan-execution-policies.md),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
For details on the similarities and differences between these features, see
[Enforce scan execution](../../application_security/#enforce-scan-execution).

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# rubocop: disable Style/Documentation
class MigrateJobArtifactRegistryToSsf
def perform(*job_artifact_ids)
end
end
end
end
Gitlab::BackgroundMigration::MigrateJobArtifactRegistryToSsf.prepend_mod_with('Gitlab::BackgroundMigration::MigrateJobArtifactRegistryToSsf')

View File

@ -85,6 +85,7 @@ ci_instance_variables: :gitlab_ci
ci_job_artifacts: :gitlab_ci
ci_job_token_project_scope_links: :gitlab_ci
ci_job_variables: :gitlab_ci
ci_job_artifact_states: :gitlab_ci
ci_minutes_additional_packs: :gitlab_ci
ci_namespace_monthly_usages: :gitlab_ci
ci_namespace_mirrors: :gitlab_ci

View File

@ -180,9 +180,6 @@ module Gitlab
class GrapeFormatter
# Convert an object to JSON.
#
# This will default to the built-in Grape formatter if either :oj_json or :grape_gitlab_json
# flags are disabled.
#
# The `env` param is ignored because it's not needed in either our formatter or Grape's,
# but it is passed through for consistency.
#
@ -194,11 +191,7 @@ module Gitlab
def self.call(object, env = nil)
return object.to_s if object.is_a?(PrecompiledJson)
if Feature.enabled?(:grape_gitlab_json, default_enabled: true)
Gitlab::Json.dump(object)
else
Grape::Formatter::Json.call(object, env)
end
Gitlab::Json.dump(object)
end
end

View File

@ -109,7 +109,7 @@
"codesandbox-api": "0.0.23",
"compression-webpack-plugin": "^5.0.2",
"copy-webpack-plugin": "^6.4.1",
"core-js": "^3.21.0",
"core-js": "^3.21.1",
"cron-validator": "^1.1.1",
"cronstrue": "^1.122.0",
"cropper": "^2.3.0",

View File

@ -14,7 +14,7 @@ module QA
attribute :group do
Group.fabricate_via_api! do |resource|
resource.name = 'group-with-milestone'
resource.name = "group-with-milestone-#{SecureRandom.hex(4)}"
end
end

View File

@ -13,6 +13,7 @@ FactoryBot.define do
transient do
groups { [] }
projects { [] }
token_expires_at { nil }
end
after(:build) do |runner, evaluator|
@ -25,6 +26,10 @@ FactoryBot.define do
end
end
after(:create) do |runner, evaluator|
runner.update!(token_expires_at: evaluator.token_expires_at) if evaluator.token_expires_at
end
trait :online do
contacted_at { Time.now }
end

View File

@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-created', count: 2)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}")
expect(first('[data-testid="pipeline-identifier"]')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@ -101,16 +101,16 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 4)
expect(all('[data-testid="pipeline-url-link"]')[0])
expect(all('[data-testid="pipeline-identifier"]')[0])
.to have_content("##{detached_merge_request_pipeline_2.id}")
expect(all('[data-testid="pipeline-url-link"]')[1])
expect(all('[data-testid="pipeline-identifier"]')[1])
.to have_content("##{detached_merge_request_pipeline.id}")
expect(all('[data-testid="pipeline-url-link"]')[2])
expect(all('[data-testid="pipeline-identifier"]')[2])
.to have_content("##{push_pipeline_2.id}")
expect(all('[data-testid="pipeline-url-link"]')[3])
expect(all('[data-testid="pipeline-identifier"]')[3])
.to have_content("##{push_pipeline.id}")
end
end
@ -201,7 +201,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees a branch pipeline in pipeline tab' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-created', count: 1)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{push_pipeline.id}")
expect(first('[data-testid="pipeline-identifier"]')).to have_content("##{push_pipeline.id}")
end
end
@ -252,7 +252,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 2)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}")
expect(first('[data-testid="pipeline-identifier"]')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@ -295,16 +295,16 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 4)
expect(all('[data-testid="pipeline-url-link"]')[0])
expect(all('[data-testid="pipeline-identifier"]')[0])
.to have_content("##{detached_merge_request_pipeline_2.id}")
expect(all('[data-testid="pipeline-url-link"]')[1])
expect(all('[data-testid="pipeline-identifier"]')[1])
.to have_content("##{detached_merge_request_pipeline.id}")
expect(all('[data-testid="pipeline-url-link"]')[2])
expect(all('[data-testid="pipeline-identifier"]')[2])
.to have_content("##{push_pipeline_2.id}")
expect(all('[data-testid="pipeline-url-link"]')[3])
expect(all('[data-testid="pipeline-identifier"]')[3])
.to have_content("##{push_pipeline.id}")
end
end

View File

@ -134,7 +134,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
create_merge_request_pipeline
act_on_security_warning(action: 'Run pipeline')
check_pipeline(expected_project: parent_project)
check_pipeline(expected_project: parent_project, link_selector: 'pipeline-url-link')
check_head_pipeline(expected_project: parent_project)
end
@ -179,13 +179,13 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
click_button('Run pipeline')
end
def check_pipeline(expected_project:)
def check_pipeline(expected_project:, link_selector: 'commit-title')
page.within('.ci-table') do
expect(page).to have_selector('.commit', count: 2)
page.within(first('.commit')) do
page.within('.pipeline-tags') do
expect(page.find('[data-testid="pipeline-url-link"]')[:href]).to include(expected_project.full_path)
expect(page.find("[data-testid=#{link_selector}]")[:href]).to include(expected_project.full_path)
expect(page).to have_content('detached')
end
page.within('.pipeline-triggerer') do

View File

@ -711,7 +711,7 @@ RSpec.describe 'Pipelines', :js do
end
expect(page.find('[data-testid="pipeline-th"]')).to have_content 'Pipeline'
expect(page.find('[data-testid="pipeline-url-link"]')).to have_content "##{pipeline.iid}"
expect(page.find('[data-testid="pipeline-identifier"]')).to have_content "##{pipeline.iid}"
end
end
end

View File

@ -91,8 +91,8 @@ RSpec.describe Ci::RunnersFinder do
end
context 'sorting' do
let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago }
let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago }
let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago, token_expires_at: '2022-02-15 07:00' }
let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago, token_expires_at: '2022-02-15 06:00' }
let_it_be(:runner3) { create :ci_runner, created_at: '2018-07-12 09:00', contacted_at: 2.minutes.ago }
subject do
@ -142,6 +142,22 @@ RSpec.describe Ci::RunnersFinder do
is_expected.to eq [runner1, runner3, runner2]
end
end
context 'with sort param equal to token_expires_at_asc' do
let(:params) { { sort: 'token_expires_at_asc' } }
it 'sorts by contacted_at ascending' do
is_expected.to eq [runner2, runner1, runner3]
end
end
context 'with sort param equal to token_expires_at_desc' do
let(:params) { { sort: 'token_expires_at_desc' } }
it 'sorts by contacted_at descending' do
is_expected.to eq [runner3, runner1, runner2]
end
end
end
context 'by non admin user' do

View File

@ -209,57 +209,79 @@ describe('Description component', () => {
});
describe('with work items feature flag is enabled', () => {
beforeEach(async () => {
createComponent({
props: {
descriptionHtml: descriptionHtmlWithCheckboxes,
},
provide: {
glFeatures: {
workItems: true,
describe('empty description', () => {
beforeEach(async () => {
createComponent({
props: {
descriptionHtml: '',
},
},
provide: {
glFeatures: {
workItems: true,
},
},
});
await nextTick();
});
it('renders without error', () => {
expect(findTaskActionButtons()).toHaveLength(0);
});
await nextTick();
});
it('renders a list of hidden buttons corresponding to checkboxes in description HTML', () => {
expect(findTaskActionButtons()).toHaveLength(3);
});
describe('description with checkboxes', () => {
beforeEach(async () => {
createComponent({
props: {
descriptionHtml: descriptionHtmlWithCheckboxes,
},
provide: {
glFeatures: {
workItems: true,
},
},
});
await nextTick();
});
it('renders a list of popovers corresponding to checkboxes in description HTML', () => {
expect(findPopovers()).toHaveLength(3);
expect(findPopovers().at(0).props('target')).toBe(
findTaskActionButtons().at(0).attributes('id'),
);
});
it('renders a list of hidden buttons corresponding to checkboxes in description HTML', () => {
expect(findTaskActionButtons()).toHaveLength(3);
});
it('does not show a modal by default', () => {
expect(findModal().props('visible')).toBe(false);
});
it('renders a list of popovers corresponding to checkboxes in description HTML', () => {
expect(findPopovers()).toHaveLength(3);
expect(findPopovers().at(0).props('target')).toBe(
findTaskActionButtons().at(0).attributes('id'),
);
});
it('opens a modal when a button on popover is clicked and displays correct title', async () => {
findConvertToTaskButton().vm.$emit('click');
expect(showModal).toHaveBeenCalled();
await nextTick();
expect(findCreateWorkItem().props('initialTitle').trim()).toBe('todo 1');
});
it('does not show a modal by default', () => {
expect(findModal().props('visible')).toBe(false);
});
it('closes the modal on `closeCreateTaskModal` event', () => {
findConvertToTaskButton().vm.$emit('click');
findCreateWorkItem().vm.$emit('closeModal');
expect(hideModal).toHaveBeenCalled();
});
it('opens a modal when a button on popover is clicked and displays correct title', async () => {
findConvertToTaskButton().vm.$emit('click');
expect(showModal).toHaveBeenCalled();
await nextTick();
expect(findCreateWorkItem().props('initialTitle').trim()).toBe('todo 1');
});
it('updates description HTML on `onCreate` event', async () => {
const newTitle = 'New title';
findConvertToTaskButton().vm.$emit('click');
findCreateWorkItem().vm.$emit('onCreate', newTitle);
expect(hideModal).toHaveBeenCalled();
await nextTick();
it('closes the modal on `closeCreateTaskModal` event', () => {
findConvertToTaskButton().vm.$emit('click');
findCreateWorkItem().vm.$emit('closeModal');
expect(hideModal).toHaveBeenCalled();
});
expect(findTaskSvg().exists()).toBe(true);
expect(wrapper.text()).toContain(newTitle);
it('updates description HTML on `onCreate` event', async () => {
const newTitle = 'New title';
findConvertToTaskButton().vm.$emit('click');
findCreateWorkItem().vm.$emit('onCreate', newTitle);
expect(hideModal).toHaveBeenCalled();
await nextTick();
expect(findTaskSvg().exists()).toBe(true);
expect(wrapper.text()).toContain(newTitle);
});
});
});
});

View File

@ -676,7 +676,7 @@ export const mockPipeline = (projectPath) => {
short_id: 'fd6df5b3',
created_at: '2021-10-19T21:17:12.000+00:00',
parent_ids: ['7147906b84306e83cb3fec6582a25390b75713c6'],
title: 'Commit',
title: 'Commit Title',
message: 'Commit',
author_name: 'Administrator',
author_email: 'admin@example.com',
@ -1141,3 +1141,176 @@ export const mockPipelineBranch = () => {
viewType: 'root',
};
};
export const mockPipelineNoCommit = () => {
return {
pipeline: {
id: 268,
iid: 34,
user: {
id: 1,
username: 'root',
name: 'Administrator',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://gdk.test:3000/root',
show_status: false,
path: '/root',
},
active: false,
source: 'push',
created_at: '2022-01-14T17:40:27.866Z',
updated_at: '2022-01-14T18:02:35.850Z',
path: '/root/mr-widgets/-/pipelines/268',
flags: {
stuck: false,
auto_devops: false,
merge_request: false,
yaml_errors: false,
retryable: true,
cancelable: false,
failure_reason: false,
detached_merge_request_pipeline: false,
merge_request_pipeline: false,
merge_train_pipeline: false,
latest: true,
},
details: {
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
tooltip: 'passed',
has_details: true,
details_path: '/root/mr-widgets/-/pipelines/268',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
stages: [
{
name: 'validate',
title: 'validate: passed with warnings',
status: {
icon: 'status_warning',
text: 'passed',
label: 'passed with warnings',
group: 'success-with-warnings',
tooltip: 'passed',
has_details: true,
details_path: '/root/mr-widgets/-/pipelines/268#validate',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/mr-widgets/-/pipelines/268#validate',
dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=validate',
},
{
name: 'test',
title: 'test: passed',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/mr-widgets/-/pipelines/268#test',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/mr-widgets/-/pipelines/268#test',
dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=test',
},
{
name: 'build',
title: 'build: passed',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/mr-widgets/-/pipelines/268#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/mr-widgets/-/pipelines/268#build',
dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=build',
},
],
duration: 75,
finished_at: '2022-01-14T18:02:35.842Z',
name: 'Pipeline',
manual_actions: [],
scheduled_actions: [],
},
ref: {
name: 'update-ci',
path: '/root/mr-widgets/-/commits/update-ci',
tag: false,
branch: true,
merge_request: false,
},
retry_path: '/root/mr-widgets/-/pipelines/268/retry',
delete_path: '/root/mr-widgets/-/pipelines/268',
failed_builds: [
{
id: 1260,
name: 'fmt',
started: '2022-01-14T17:40:36.435Z',
complete: true,
archived: false,
build_path: '/root/mr-widgets/-/jobs/1260',
retry_path: '/root/mr-widgets/-/jobs/1260/retry',
playable: false,
scheduled: false,
created_at: '2022-01-14T17:40:27.879Z',
updated_at: '2022-01-14T17:40:42.129Z',
status: {
icon: 'status_warning',
text: 'failed',
label: 'failed (allowed to fail)',
group: 'failed-with-warnings',
tooltip: 'failed - (script failure) (allowed to fail)',
has_details: true,
details_path: '/root/mr-widgets/-/jobs/1260',
illustration: {
image:
'/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
size: 'svg-430',
title: 'This job does not have a trace.',
},
favicon:
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
action: {
icon: 'retry',
title: 'Retry',
path: '/root/mr-widgets/-/jobs/1260/retry',
method: 'post',
button_title: 'Retry this job',
},
},
recoverable: false,
},
],
project: {
id: 23,
name: 'mr-widgets',
full_path: '/root/mr-widgets',
full_name: 'Administrator / mr-widgets',
},
triggered_by: null,
triggered: [],
},
pipelineScheduleUrl: 'foo',
pipelineKey: 'id',
viewType: 'root',
};
};

View File

@ -1,7 +1,12 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import PipelineUrlComponent from '~/pipelines/components/pipelines_list/pipeline_url.vue';
import { mockPipeline, mockPipelineBranch, mockPipelineTag } from './mock_data';
import {
mockPipeline,
mockPipelineBranch,
mockPipelineTag,
mockPipelineNoCommit,
} from './mock_data';
const projectPath = 'test/test';
@ -26,7 +31,7 @@ describe('Pipeline Url Component', () => {
const findCommitIconType = () => wrapper.findByTestId('commit-icon-type');
const findCommitTitleContainer = () => wrapper.findByTestId('commit-title-container');
const findCommitTitle = (commitWrapper) => commitWrapper.find('[data-testid="commit-title"]');
const findCommitTitle = () => wrapper.findByTestId('commit-title');
const defaultProps = mockPipeline(projectPath);
@ -232,5 +237,33 @@ describe('Pipeline Url Component', () => {
expect(findCommitIconType().attributes('title')).toBe(expectedTitle);
},
);
describe('with commit', () => {
beforeEach(() => {
createComponent({}, true);
});
it('displays commit title with link to pipeline', () => {
expect(findCommitTitle().attributes('href')).toBe(defaultProps.pipeline.path);
});
it('displays commit title text', () => {
expect(findCommitTitle().text()).toBe(defaultProps.pipeline.commit.title);
});
});
describe('without commit', () => {
beforeEach(() => {
createComponent(mockPipelineNoCommit(), true);
});
it('displays cant find head commit text', () => {
expect(findCommitTitle().text()).toBe("Can't find HEAD commit for this branch");
});
it('displays link to pipeline', () => {
expect(findCommitTitle().attributes('href')).toBe(mockPipelineNoCommit().pipeline.path);
});
});
});
});

View File

@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
id description created_at contacted_at maximum_timeout access_level active paused status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count admin_url edit_admin_url user_permissions executor_name
groups projects jobs
groups projects jobs token_expires_at
]
expect(described_class).to include_graphql_fields(*expected_fields)

View File

@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['RootStorageStatistics'] do
it 'has all the required fields' do
expect(described_class).to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size,
:build_artifacts_size, :packages_size, :wiki_size, :snippets_size,
:pipeline_artifacts_size, :uploads_size)
:pipeline_artifacts_size, :uploads_size, :dependency_proxy_size)
end
specify { expect(described_class).to require_graphql_authorizations(:read_statistics) }

View File

@ -317,36 +317,14 @@ RSpec.describe Gitlab::Json do
let(:env) { {} }
let(:result) { "{\"test\":true}" }
context "grape_gitlab_json flag is enabled" do
before do
stub_feature_flags(grape_gitlab_json: true)
end
it "generates JSON" do
expect(subject).to eq(result)
end
it "uses Gitlab::Json" do
expect(Gitlab::Json).to receive(:dump).with(obj)
subject
end
it "generates JSON" do
expect(subject).to eq(result)
end
context "grape_gitlab_json flag is disabled" do
before do
stub_feature_flags(grape_gitlab_json: false)
end
it "uses Gitlab::Json" do
expect(Gitlab::Json).to receive(:dump).with(obj)
it "generates JSON" do
expect(subject).to eq(result)
end
it "uses Grape::Formatter::Json" do
expect(Grape::Formatter::Json).to receive(:call).with(obj, env)
subject
end
subject
end
context "precompiled JSON" do

View File

@ -3831,10 +3831,10 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
core-js@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.0.tgz#f479dbfc3dffb035a0827602dd056839a774aa71"
integrity sha512-YUdI3fFu4TF/2WykQ2xzSiTQdldLB4KVuL9WeAy5XONZYt5Cun/fpQvctoKbCgvPhmzADeesTk/j2Rdx77AcKQ==
core-js@^3.21.1:
version "3.21.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94"
integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==
core-js@~2.3.0:
version "2.3.0"