Commit 26411d8f authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents d59718db cd09e9a6
import $ from 'jquery';
import { deprecatedCreateFlash as flash } from './flash';
import axios from './lib/utils/axios_utils';
import { __ } from './locale';
/**
* In each pipelines table we have a mini pipeline graph for each pipeline.
*
* When we click in a pipeline stage, we need to make an API call to get the
* builds list to render in a dropdown.
*
* The container should be the table element.
*
* The stage icon clicked needs to have the following HTML structure:
* <div class="dropdown">
* <button class="dropdown js-builds-dropdown-button" data-toggle="dropdown"></button>
* <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div>
*/
export default class MiniPipelineGraph {
constructor(opts = {}) {
this.container = opts.container || '';
this.dropdownListSelector = '.js-builds-dropdown-container';
this.getBuildsList = this.getBuildsList.bind(this);
}
/**
* Adds the event listener when the dropdown is opened.
* All dropdown events are fired at the .dropdown-menu's parent element.
*/
bindEvents() {
$(document)
.off('shown.bs.dropdown', this.container)
.on('shown.bs.dropdown', this.container, this.getBuildsList);
}
/**
* When the user right clicks or cmd/ctrl + click in the job name
* the dropdown should not be closed and the link should open in another tab,
* so we stop propagation of the click event inside the dropdown.
*
* Since this component is rendered multiple times per page we need to guarantee we only
* target the click event of this component.
*/
stopDropdownClickPropagation() {
$(document).on(
'click',
`${this.container} .js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item`,
(e) => {
e.stopPropagation();
},
);
}
/**
* For the clicked stage, renders the given data in the dropdown list.
*
* @param {HTMLElement} stageContainer
* @param {Object} data
*/
renderBuildsList(stageContainer, data) {
const dropdownContainer = stageContainer.parentElement.querySelector(
`${this.dropdownListSelector} .js-builds-dropdown-list ul`,
);
dropdownContainer.innerHTML = data;
}
/**
* For the clicked stage, gets the list of builds.
*
* All dropdown events have a relatedTarget property,
* whose value is the toggling anchor element.
*
* @param {Object} e bootstrap dropdown event
* @return {Promise}
*/
getBuildsList(e) {
const button = e.relatedTarget;
const endpoint = button.dataset.stageEndpoint;
this.renderBuildsList(button, '');
this.toggleLoading(button);
axios
.get(endpoint)
.then(({ data }) => {
this.toggleLoading(button);
this.renderBuildsList(button, data.html);
this.stopDropdownClickPropagation();
})
.catch(() => {
this.toggleLoading(button);
if ($(button).parent().hasClass('open')) {
$(button).dropdown('toggle');
}
flash(__('An error occurred while fetching the builds.'), 'alert');
});
}
/**
* Toggles the visibility of the loading icon.
*
* @param {HTMLElement} stageContainer
* @return {type}
*/
toggleLoading(stageContainer) {
stageContainer.parentElement
.querySelector(`${this.dropdownListSelector} .js-builds-dropdown-loading`)
.classList.toggle('hidden');
}
}
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
}; };
</script> </script>
<template> <template>
<div data-testid="widget-mini-pipeline-graph"> <div data-testid="pipeline-mini-graph">
<div <div
v-for="stage in stages" v-for="stage in stages"
:key="stage.name" :key="stage.name"
......
import { fetchCommitMergeRequests } from '~/commit_merge_requests'; import { fetchCommitMergeRequests } from '~/commit_merge_requests';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph'; import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph';
import { initDetailsButton } from './init_details_button'; import { initDetailsButton } from './init_details_button';
import { loadBranches } from './load_branches'; import { loadBranches } from './load_branches';
export const initCommitBoxInfo = (containerSelector = '.js-commit-box-info') => { export const initCommitBoxInfo = () => {
const containerEl = document.querySelector(containerSelector);
// Display commit related branches // Display commit related branches
loadBranches(containerEl); loadBranches();
// Related merge requests to this commit // Related merge requests to this commit
fetchCommitMergeRequests(); fetchCommitMergeRequests();
// Display pipeline mini graph for this commit // Display pipeline mini graph for this commit
// Feature flag ci_commit_pipeline_mini_graph_vue initCommitPipelineMiniGraph();
if (gon.features.ciCommitPipelineMiniGraphVue) {
initCommitPipelineMiniGraph();
} else {
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
}
initDetailsButton(); initDetailsButton();
}; };
...@@ -2,7 +2,8 @@ import axios from 'axios'; ...@@ -2,7 +2,8 @@ import axios from 'axios';
import { sanitize } from '~/lib/dompurify'; import { sanitize } from '~/lib/dompurify';
import { __ } from '~/locale'; import { __ } from '~/locale';
export const loadBranches = (containerEl) => { export const loadBranches = (containerSelector = '.js-commit-box-info') => {
const containerEl = document.querySelector(containerSelector);
if (!containerEl) { if (!containerEl) {
return; return;
} }
......
...@@ -54,12 +54,7 @@ export default { ...@@ -54,12 +54,7 @@ export default {
:file-name="fileName" :file-name="fileName"
:editor-options="{ readOnly: true }" :editor-options="{ readOnly: true }"
/> />
<div <div v-else class="file-content code js-syntax-highlight" :class="$options.userColorScheme">
v-else
class="file-content code js-syntax-highlight"
data-qa-selector="file_content"
:class="$options.userColorScheme"
>
<div class="line-numbers"> <div class="line-numbers">
<a <a
v-for="line in lineNumbers" v-for="line in lineNumbers"
......
...@@ -60,3 +60,11 @@ pre { ...@@ -60,3 +60,11 @@ pre {
a[href]::after { a[href]::after {
content: none !important; content: none !important;
} }
.with-performance-bar .layout-page {
margin-top: 0;
}
.content-wrapper {
margin-top: 0;
}
...@@ -19,11 +19,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -19,11 +19,6 @@ class Projects::CommitController < Projects::ApplicationController
before_action :define_commit_box_vars, only: [:show, :pipelines] before_action :define_commit_box_vars, only: [:show, :pipelines]
before_action :define_note_vars, only: [:show, :diff_for_path, :diff_files] before_action :define_note_vars, only: [:show, :diff_for_path, :diff_files]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
before_action only: [:show, :pipelines] do
push_frontend_feature_flag(:ci_commit_pipeline_mini_graph_vue, @project, default_enabled: :yaml)
end
before_action do before_action do
push_frontend_feature_flag(:pick_into_project) push_frontend_feature_flag(:pick_into_project)
end end
...@@ -214,7 +209,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -214,7 +209,6 @@ class Projects::CommitController < Projects::ApplicationController
def define_commit_box_vars def define_commit_box_vars
@last_pipeline = @commit.last_pipeline @last_pipeline = @commit.last_pipeline
return unless ::Gitlab::Ci::Features.ci_commit_pipeline_mini_graph_vue_enabled?(@project)
return unless @commit.last_pipeline return unless @commit.last_pipeline
@last_pipeline_stages = StageSerializer.new(project: @project, current_user: @current_user).represent(@last_pipeline.stages) @last_pipeline_stages = StageSerializer.new(project: @project, current_user: @current_user).represent(@last_pipeline.stages)
......
...@@ -152,15 +152,6 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -152,15 +152,6 @@ class Projects::PipelinesController < Projects::ApplicationController
.represent(@stage, details: true, retried: params[:retried]) .represent(@stage, details: true, retried: params[:retried])
end end
# TODO: This endpoint is used by mini-pipeline-graph
# TODO: This endpoint should be migrated to `stage.json`
def stage_ajax
@stage = pipeline.legacy_stage(params[:stage])
return not_found unless @stage
render json: { html: view_to_html_string('projects/pipelines/_stage') }
end
def retry def retry
pipeline.retry_failed(current_user) pipeline.retry_failed(current_user)
......
...@@ -45,6 +45,8 @@ module Types ...@@ -45,6 +45,8 @@ module Types
description: 'Artifacts generated by the job.' description: 'Artifacts generated by the job.'
field :short_sha, type: GraphQL::STRING_TYPE, null: false, field :short_sha, type: GraphQL::STRING_TYPE, null: false,
description: 'Short SHA1 ID of the commit.' description: 'Short SHA1 ID of the commit.'
field :scheduling_type, GraphQL::STRING_TYPE, null: true,
description: 'Type of pipeline scheduling. Value is `dag` if the pipeline uses the `needs` keyword, and `stage` otherwise.'
def pipeline def pipeline
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
......
...@@ -118,6 +118,10 @@ module Types ...@@ -118,6 +118,10 @@ module Types
field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?, field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?,
description: 'Indicates if the pipeline is active.' description: 'Indicates if the pipeline is active.'
field :uses_needs, GraphQL::BOOLEAN_TYPE, null: true,
method: :uses_needs?,
description: 'Indicates if the pipeline has jobs with `needs` dependencies.'
def detailed_status def detailed_status
object.detailed_status(current_user) object.detailed_status(current_user)
end end
......
...@@ -131,8 +131,6 @@ module Ci ...@@ -131,8 +131,6 @@ module Ci
update_project_statistics project_statistics_name: :build_artifacts_size update_project_statistics project_statistics_name: :build_artifacts_size
scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) } scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) }
scope :with_files_stored_locally, -> { where(file_store: ::JobArtifactUploader::Store::LOCAL) }
scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) } scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) } scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) }
......
...@@ -446,6 +446,10 @@ module Ci ...@@ -446,6 +446,10 @@ module Ci
@auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines') @auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines')
end end
def uses_needs?
builds.where(scheduling_type: :dag).any?
end
def stages_count def stages_count
statuses.select(:stage).distinct.count statuses.select(:stage).distinct.count
end end
......
...@@ -4,8 +4,10 @@ module Ci ...@@ -4,8 +4,10 @@ module Ci
module Artifactable module Artifactable
extend ActiveSupport::Concern extend ActiveSupport::Concern
NotSupportedAdapterError = Class.new(StandardError) include ObjectStorable
STORE_COLUMN = :file_store
NotSupportedAdapterError = Class.new(StandardError)
FILE_FORMAT_ADAPTERS = { FILE_FORMAT_ADAPTERS = {
gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream, gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream,
raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
...@@ -20,6 +22,7 @@ module Ci ...@@ -20,6 +22,7 @@ module Ci
scope :expired_before, -> (timestamp) { where(arel_table[:expire_at].lt(timestamp)) } scope :expired_before, -> (timestamp) { where(arel_table[:expire_at].lt(timestamp)) }
scope :expired, -> (limit) { expired_before(Time.current).limit(limit) } scope :expired, -> (limit) { expired_before(Time.current).limit(limit) }
scope :project_id_in, ->(ids) { where(project_id: ids) }
end end
def each_blob(&blk) def each_blob(&blk)
......
...@@ -46,9 +46,7 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity ...@@ -46,9 +46,7 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
end end
end end
expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { presenter(mr).can_read_pipeline? } do |merge_request, options|
Feature.enabled?(:merge_request_cached_pipeline_serializer, mr.project) && presenter(mr).can_read_pipeline?
} do |merge_request, options|
MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options) MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options)
end end
......
...@@ -19,12 +19,6 @@ class MergeRequestPollWidgetEntity < Grape::Entity ...@@ -19,12 +19,6 @@ class MergeRequestPollWidgetEntity < Grape::Entity
# User entities # User entities
expose :merge_user, using: UserEntity expose :merge_user, using: UserEntity
expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) {
Feature.disabled?(:merge_request_cached_pipeline_serializer, mr.project) && presenter(mr).can_read_pipeline?
} do |merge_request, options|
MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options)
end
expose :merge_pipeline, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)} do |merge_request, options| expose :merge_pipeline, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)} do |merge_request, options|
MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options) MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options)
end end
......
-# Renders the content of each li in the dropdown
- subject = local_assigns.fetch(:subject)
- status = subject.detailed_status(current_user)
- klass = "ci-status-icon ci-status-icon-#{status.group}"
- tooltip = "#{subject.name} - #{status.status_tooltip}"
- if status.has_details?
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item d-flex', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= sprite_icon(status.icon)
%span.gl-text-truncate.mw-70p.gl-pl-2= subject.name
- else
.menu-item.mini-pipeline-graph-dropdown-item.d-flex{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
%span{ class: klass }= sprite_icon(status.icon)
%span.gl-text-truncate.mw-70p.gl-pl-2= subject.name
- if status.has_action?
= link_to status.action_path, class: "gl-button ci-action-icon-container ci-action-icon-wrapper js-ci-action-icon", method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
= sprite_icon(status.action_icon, css_class: "icon-action-#{status.action_icon}")
...@@ -59,11 +59,8 @@ ...@@ -59,11 +59,8 @@
- if @last_pipeline.stages_count.nonzero? - if @last_pipeline.stages_count.nonzero?
#{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) } #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
.mr-widget-pipeline-graph .mr-widget-pipeline-graph
- if ::Gitlab::Ci::Features.ci_commit_pipeline_mini_graph_vue_enabled?(@project) .stage-cell
.stage-cell .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe } }
.js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe } }
- else
= render 'shared/mini_pipeline_graph', pipeline: @last_pipeline, klass: 'js-commit-pipeline-graph'
- if @last_pipeline.duration - if @last_pipeline.duration
in in
= time_interval_in_words @last_pipeline.duration = time_interval_in_words @last_pipeline.duration
......
- grouped_statuses = @stage.statuses.latest_ordered.group_by(&:status)
- Ci::HasStatus::ORDERED_STATUSES.each do |ordered_status|
- grouped_statuses.fetch(ordered_status, []).each do |status|
%li
= render 'ci/status/dropdown_graph_badge', subject: status
.stage-cell
- pipeline.legacy_stages.each do |stage|
- if stage.status
- detailed_status = stage.detailed_status(current_user)
- icon_status = "#{detailed_status.icon}_borderless"
.stage-container.mt-0.ml-1.dropdown{ class: klass }
%button.mini-pipeline-graph-dropdown-toggle.has-tooltip.js-builds-dropdown-button{ class: "ci-status-icon-#{detailed_status.group}", type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_ajax_project_pipeline_path(pipeline.project, pipeline, stage: stage.name) } }
= sprite_icon(icon_status)
%ul.dropdown-menu.mini-pipeline-graph-dropdown-menu.js-builds-dropdown-container
%li.js-builds-dropdown-list.scrollable-menu
%ul
%li.js-builds-dropdown-loading.hidden
.loading-container.text-center
%span.spinner{ 'aria-label': _('Loading') }
...@@ -1307,6 +1307,14 @@ ...@@ -1307,6 +1307,14 @@
:weight: 5 :weight: 5
:idempotent: :idempotent:
:tags: [] :tags: []
- :name: pipeline_processing:ci_initial_pipeline_process
:feature_category: :continuous_integration
:has_external_dependencies:
:urgency: :high
:resource_boundary: :unknown
:weight: 5
:idempotent: true
:tags: []
- :name: pipeline_processing:ci_resource_groups_assign_resource_from_resource_group - :name: pipeline_processing:ci_resource_groups_assign_resource_from_resource_group
:feature_category: :continuous_delivery :feature_category: :continuous_delivery
:has_external_dependencies: :has_external_dependencies:
......
# frozen_string_literal: true
module Ci
class InitialPipelineProcessWorker
include ApplicationWorker
include PipelineQueue
queue_namespace :pipeline_processing
feature_category :continuous_integration
urgency :high
loggable_arguments 1
idempotent!
def perform(pipeline_id)
Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
Ci::ProcessPipelineService
.new(pipeline)
.execute
end
end
end
end
---
title: Update mini pipeline appearance in commit page to match other mini pipelines
in the application
merge_request: 56510
author:
type: changed
---
title: Move to btn-confirm from btn-success in pipelines quotas page
merge_request: 57861
author: Yogi (@yo)
type: other
---
title: Remove top margin for print layout
merge_request: 57824
author:
type: changed
---
title: Move pipelines calculation from widget.json to cached_widget.json
merge_request: 57822
author:
type: performance
---
title: Exposes schedulingType on CiJobType and adds usesNeeds to PipelineType
merge_request: 57398
author:
type: added
--- ---
name: ci_commit_pipeline_mini_graph_vue name: ci_async_initial_pipeline_processing
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55363 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57589
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323356 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326217
milestone: '13.10' milestone: '13.11'
type: development type: development
group: group::pipeline authoring group: group::continuous integration
default_enabled: true default_enabled: false
---
name: merge_request_cached_pipeline_serializer
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38273
rollout_issue_url:
milestone: '13.5'
type: development
group: group::code review
default_enabled: false
...@@ -12,7 +12,6 @@ resources :pipelines, only: [:index, :new, :create, :show, :destroy] do ...@@ -12,7 +12,6 @@ resources :pipelines, only: [:index, :new, :create, :show, :destroy] do
member do member do
get :stage get :stage
get :stage_ajax
post :cancel post :cancel
post :retry post :retry
get :builds get :builds
......
...@@ -1220,6 +1220,7 @@ An edge in a connection. ...@@ -1220,6 +1220,7 @@ An edge in a connection.
| `pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. | | `pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. |
| `queuedAt` | [`Time`](#time) | When the job was enqueued and marked as pending. | | `queuedAt` | [`Time`](#time) | When the job was enqueued and marked as pending. |
| `scheduledAt` | [`Time`](#time) | Schedule for the build. | | `scheduledAt` | [`Time`](#time) | Schedule for the build. |
| `schedulingType` | [`String`](#string) | Type of pipeline scheduling. Value is `dag` if the pipeline uses the `needs` keyword, and `stage` otherwise. |
| `shortSha` | [`String!`](#string) | Short SHA1 ID of the commit. | | `shortSha` | [`String!`](#string) | Short SHA1 ID of the commit. |
| `stage` | [`CiStage`](#cistage) | Stage of the job. | | `stage` | [`CiStage`](#cistage) | Stage of the job. |
| `startedAt` | [`Time`](#time) | When the job was started. | | `startedAt` | [`Time`](#time) | When the job was started. |
...@@ -4622,6 +4623,7 @@ Information about pagination in a connection. ...@@ -4622,6 +4623,7 @@ Information about pagination in a connection.
| `upstream` | [`Pipeline`](#pipeline) | Pipeline that triggered the pipeline. | | `upstream` | [`Pipeline`](#pipeline) | Pipeline that triggered the pipeline. |
| `user` | [`User`](#user) | Pipeline user. | | `user` | [`User`](#user) | Pipeline user. |
| `userPermissions` | [`PipelinePermissions!`](#pipelinepermissions) | Permissions for the current user on the resource. | | `userPermissions` | [`PipelinePermissions!`](#pipelinepermissions) | Permissions for the current user on the resource. |
| `usesNeeds` | [`Boolean`](#boolean) | Indicates if the pipeline has jobs with `needs` dependencies. |
| `warnings` | [`Boolean!`](#boolean) | Indicates if a pipeline has warnings. | | `warnings` | [`Boolean!`](#boolean) | Indicates if a pipeline has warnings. |
### `PipelineAnalytics` ### `PipelineAnalytics`
......
...@@ -15,7 +15,7 @@ points to. Examples for badges can be the [pipeline status](../../ci/pipelines/s ...@@ -15,7 +15,7 @@ points to. Examples for badges can be the [pipeline status](../../ci/pipelines/s
[test coverage](../../ci/pipelines/settings.md#test-coverage-report-badge), or ways to contact the [test coverage](../../ci/pipelines/settings.md#test-coverage-report-badge), or ways to contact the
project maintainers. project maintainers.
![Badges on Project overview page](img/project_overview_badges.png) ![Badges on Project overview page](img/project_overview_badges_v13_10.png)
## Project badges ## Project badges
......
...@@ -23,9 +23,6 @@ module EE ...@@ -23,9 +23,6 @@ module EE
API_FUZZING_REPORT_TYPES = %w[api_fuzzing].freeze API_FUZZING_REPORT_TYPES = %w[api_fuzzing].freeze
BROWSER_PERFORMANCE_REPORT_FILE_TYPES = %w[browser_performance performance].freeze BROWSER_PERFORMANCE_REPORT_FILE_TYPES = %w[browser_performance performance].freeze
scope :project_id_in, ->(ids) { where(project_id: ids) }
scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
scope :security_reports, -> (file_types: SECURITY_REPORT_FILE_TYPES) do scope :security_reports, -> (file_types: SECURITY_REPORT_FILE_TYPES) do
requested_file_types = *file_types requested_file_types = *file_types
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= link_to s_('UsageQuota|Buy additional minutes'), = link_to s_('UsageQuota|Buy additional minutes'),
EE::SUBSCRIPTIONS_MORE_MINUTES_URL, EE::SUBSCRIPTIONS_MORE_MINUTES_URL,
target: '_blank', target: '_blank',
class: 'gl-button btn btn-success float-right', class: 'gl-button btn btn-confirm float-right',
data: { track_event: 'click_buy_ci_minutes', track_label: namespace.actual_plan_name, track_property: 'pipeline_quota_page' } data: { track_event: 'click_buy_ci_minutes', track_label: namespace.actual_plan_name, track_property: 'pipeline_quota_page' }
.row .row
......
---
title: Remove key parameter check when validating actor in personal access token API
merge_request: 57675
author:
type: fixed
...@@ -31,7 +31,7 @@ module EE ...@@ -31,7 +31,7 @@ module EE
actor.update_last_used_at! actor.update_last_used_at!
user = actor.user user = actor.user
error_message = validate_actor_key(actor, params[:key_id]) error_message = validate_actor(actor)
return { success: false, message: error_message } if error_message return { success: false, message: error_message } if error_message
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Ci::CreatePipelineService do RSpec.describe Ci::CreatePipelineService do
subject(:execute) { service.execute(:push) }
let_it_be(:downstream_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'some')) } let_it_be(:downstream_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'some')) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:user) { project.owner } let(:user) { project.owner }
...@@ -30,29 +28,34 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -30,29 +28,34 @@ RSpec.describe Ci::CreatePipelineService do
end end
it 'persists pipeline' do it 'persists pipeline' do
expect(execute).to be_persisted pipeline = create_pipeline!
expect(pipeline).to be_persisted
end end
it 'persists both jobs' do it 'persists both jobs' do
expect { execute }.to change(Ci::Build, :count).from(0).to(1) expect { create_pipeline! }.to change(Ci::Build, :count).from(0).to(1)
.and change(Ci::Bridge, :count).from(0).to(1) .and change(Ci::Bridge, :count).from(0).to(1)
end end
it 'persists bridge needs' do it 'persists bridge needs' do
job = execute.builds.first pipeline = create_pipeline!
bridge = execute.stages.last.bridges.first job = pipeline.builds.first
bridge = pipeline.bridges.first
expect(bridge.needs.first.name).to eq(job.name) expect(bridge.needs.first.name).to eq(job.name)
end end
it 'persists bridge target project' do it 'persists bridge target project' do
bridge = execute.stages.last.bridges.first pipeline = create_pipeline!
bridge = pipeline.bridges.first
expect(bridge.downstream_project).to eq downstream_project expect(bridge.downstream_project).to eq downstream_project
end end
it "sets scheduling_type of bridge_dag_job as 'dag'" do it "sets scheduling_type of bridge_dag_job as 'dag'" do
bridge = execute.stages.last.bridges.first pipeline = create_pipeline!
bridge = pipeline.bridges.first
expect(bridge.scheduling_type).to eq('dag') expect(bridge.scheduling_type).to eq('dag')
end end
...@@ -71,12 +74,14 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -71,12 +74,14 @@ RSpec.describe Ci::CreatePipelineService do
end end
it 'creates a pipeline with regular_job and bridge_dag_job pending' do it 'creates a pipeline with regular_job and bridge_dag_job pending' do
processables = execute.processables pipeline = create_pipeline!
processables = pipeline.processables
Ci::InitialPipelineProcessWorker.new.perform(pipeline.id)
regular_job = processables.find { |processable| processable.name == 'regular_job' } regular_job = processables.find { |processable| processable.name == 'regular_job' }
bridge_dag_job = processables.find { |processable| processable.name == 'bridge_dag_job' } bridge_dag_job = processables.find { |processable| processable.name == 'bridge_dag_job' }
expect(execute).to be_persisted expect(pipeline).to be_persisted
expect(regular_job.status).to eq('pending') expect(regular_job.status).to eq('pending')
expect(bridge_dag_job.status).to eq('pending') expect(bridge_dag_job.status).to eq('pending')
end end
...@@ -110,11 +115,16 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -110,11 +115,16 @@ RSpec.describe Ci::CreatePipelineService do
end end
it 'has dependencies and variables', :aggregate_failures do it 'has dependencies and variables', :aggregate_failures do
job = execute.builds.first pipeline = create_pipeline!
job = pipeline.builds.first
expect(job).to be_present expect(job).to be_present
expect(job.all_dependencies).to include(dependency) expect(job.all_dependencies).to include(dependency)
expect(job.scoped_variables.to_hash).to include(dependency_variable.key => dependency_variable.value) expect(job.scoped_variables.to_hash).to include(dependency_variable.key => dependency_variable.value)
end end
end end
def create_pipeline!
service.execute(:push)
end
end end
...@@ -31,8 +31,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -31,8 +31,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it 'enqueues a new pipeline' do it 'enqueues a new pipeline' do
pipeline = create_pipeline! pipeline = create_pipeline!
expect(pipeline).to be_persisted expect(pipeline).to be_created_successfully
expect(pipeline).to be_pending
end end
end end
......
...@@ -173,11 +173,6 @@ RSpec.describe Ci::RunDastScanService do ...@@ -173,11 +173,6 @@ RSpec.describe Ci::RunDastScanService do
expect(build.yaml_variables).to contain_exactly(*expected_variables) expect(build.yaml_variables).to contain_exactly(*expected_variables)
end end
it 'enqueues a build' do
build = pipeline.builds.first
expect(build.queued_at).not_to be_nil
end
context 'when the pipeline fails to save' do context 'when the pipeline fails to save' do
before do before do
allow_any_instance_of(Ci::Pipeline).to receive(:created_successfully?).and_return(false) allow_any_instance_of(Ci::Pipeline).to receive(:created_successfully?).and_return(false)
......
...@@ -109,9 +109,7 @@ module API ...@@ -109,9 +109,7 @@ module API
end end
end end
def validate_actor_key(actor, key_id) def validate_actor(actor)
return 'Could not find a user without a key' unless key_id
return 'Could not find the given key' unless actor.key return 'Could not find the given key' unless actor.key
'Could not find a user for the given key' unless actor.user 'Could not find a user for the given key' unless actor.user
...@@ -206,7 +204,7 @@ module API ...@@ -206,7 +204,7 @@ module API
actor.update_last_used_at! actor.update_last_used_at!
user = actor.user user = actor.user
error_message = validate_actor_key(actor, params[:key_id]) error_message = validate_actor(actor)
if params[:user_id] && user.nil? if params[:user_id] && user.nil?
break { success: false, message: 'Could not find the given user' } break { success: false, message: 'Could not find the given user' }
...@@ -235,7 +233,7 @@ module API ...@@ -235,7 +233,7 @@ module API
actor.update_last_used_at! actor.update_last_used_at!
user = actor.user user = actor.user
error_message = validate_actor_key(actor, params[:key_id]) error_message = validate_actor(actor)
break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' } if actor.key.is_a?(DeployKey) break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' } if actor.key.is_a?(DeployKey)
...@@ -308,7 +306,7 @@ module API ...@@ -308,7 +306,7 @@ module API
actor.update_last_used_at! actor.update_last_used_at!
user = actor.user user = actor.user
error_message = validate_actor_key(actor, params[:key_id]) error_message = validate_actor(actor)
if error_message if error_message
{ success: false, message: error_message } { success: false, message: error_message }
......
...@@ -64,10 +64,6 @@ module Gitlab ...@@ -64,10 +64,6 @@ module Gitlab
::Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml) ::Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml)
end end
def self.ci_commit_pipeline_mini_graph_vue_enabled?(project)
::Feature.enabled?(:ci_commit_pipeline_mini_graph_vue, project, default_enabled: :yaml)
end
def self.remove_duplicate_artifact_exposure_paths?(project) def self.remove_duplicate_artifact_exposure_paths?(project)
::Feature.enabled?(:remove_duplicate_artifact_exposure_paths, project, default_enabled: :yaml) ::Feature.enabled?(:remove_duplicate_artifact_exposure_paths, project, default_enabled: :yaml)
end end
......
...@@ -8,9 +8,11 @@ module Gitlab ...@@ -8,9 +8,11 @@ module Gitlab
# After pipeline has been successfully created we can start processing it. # After pipeline has been successfully created we can start processing it.
class Process < Chain::Base class Process < Chain::Base
def perform! def perform!
::Ci::ProcessPipelineService if ::Feature.enabled?(:ci_async_initial_pipeline_processing, project, default_enabled: :yaml)
.new(@pipeline) ::Ci::InitialPipelineProcessWorker.perform_async(pipeline.id)
.execute else
::Ci::ProcessPipelineService.new(pipeline).execute
end
end end
def break? def break?
......
...@@ -3449,9 +3449,6 @@ msgstr "" ...@@ -3449,9 +3449,6 @@ msgstr ""
msgid "An error occurred while fetching the board lists. Please try again." msgid "An error occurred while fetching the board lists. Please try again."
msgstr "" msgstr ""
msgid "An error occurred while fetching the builds."
msgstr ""
msgid "An error occurred while fetching the job log." msgid "An error occurred while fetching the job log."
msgstr "" msgstr ""
......
...@@ -25,10 +25,6 @@ module QA ...@@ -25,10 +25,6 @@ module QA
element :file_title_content element :file_title_content
end end
base.view 'app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue' do
element :file_content
end
base.view 'app/assets/javascripts/blob/components/blob_content.vue' do base.view 'app/assets/javascripts/blob/components/blob_content.vue' do
element :file_content element :file_content
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
RSpec.describe 'Create', :smoke do RSpec.describe 'Create' do # convert back to a smoke test once proved to be stable
describe 'Personal snippet creation' do describe 'Personal snippet creation' do
it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1704' do it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1704' do
Flow::Login.sign_in Flow::Login.sign_in
......
...@@ -628,44 +628,6 @@ RSpec.describe Projects::PipelinesController do ...@@ -628,44 +628,6 @@ RSpec.describe Projects::PipelinesController do
end end
end end
describe 'GET stages_ajax.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when accessing existing stage' do
before do
create(:ci_build, pipeline: pipeline, stage: 'build')
get_stage_ajax('build')
end
it 'returns html source for stage dropdown' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template('projects/pipelines/_stage')
expect(json_response).to include('html')
end
end
context 'when accessing unknown stage' do
before do
get_stage_ajax('test')
end
it 'responds with not found' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
def get_stage_ajax(name)
get :stage_ajax, params: {
namespace_id: project.namespace,
project_id: project,
id: pipeline.id,
stage: name
},
format: :json
end
end
describe 'GET status.json' do describe 'GET status.json' do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
let(:status) { pipeline.detailed_status(double('user')) } let(:status) { pipeline.detailed_status(double('user')) }
...@@ -721,7 +683,7 @@ RSpec.describe Projects::PipelinesController do ...@@ -721,7 +683,7 @@ RSpec.describe Projects::PipelinesController do
pipeline = project.ci_pipelines.last pipeline = project.ci_pipelines.last
expected_redirect_path = Gitlab::Routing.url_helpers.project_pipeline_path(project, pipeline) expected_redirect_path = Gitlab::Routing.url_helpers.project_pipeline_path(project, pipeline)
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(response).to redirect_to(expected_redirect_path) expect(response).to redirect_to(expected_redirect_path)
end end
end end
......
...@@ -62,7 +62,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -62,7 +62,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 it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 2) 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-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}")
end end
end end
...@@ -154,7 +154,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -154,7 +154,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when detached merge request pipeline succeeds' do context 'when detached merge request pipeline succeeds' do
before do before do
detached_merge_request_pipeline.succeed! detached_merge_request_pipeline.reload.succeed!
wait_for_requests wait_for_requests
end end
...@@ -168,7 +168,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -168,7 +168,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when branch pipeline succeeds' do context 'when branch pipeline succeeds' do
before do before do
click_link 'Overview' click_link 'Overview'
push_pipeline.succeed! push_pipeline.reload.succeed!
wait_for_requests wait_for_requests
end end
...@@ -197,7 +197,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -197,7 +197,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees a branch pipeline in pipeline tab' do it 'sees a branch pipeline in pipeline tab' do
page.within('.ci-table') do page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 1) 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-url-link"]')).to have_content("##{push_pipeline.id}")
end end
end end
...@@ -341,7 +341,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -341,7 +341,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when the previous pipeline failed in the fork project' do context 'when the previous pipeline failed in the fork project' do
before do before do
detached_merge_request_pipeline.drop! detached_merge_request_pipeline.reload.drop!
end end
context 'when the parent project enables pipeline must succeed' do context 'when the parent project enables pipeline must succeed' do
...@@ -376,7 +376,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -376,7 +376,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when detached merge request pipeline succeeds' do context 'when detached merge request pipeline succeeds' do
before do before do
detached_merge_request_pipeline.succeed! detached_merge_request_pipeline.reload.succeed!
wait_for_requests wait_for_requests
end end
...@@ -389,7 +389,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', ...@@ -389,7 +389,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
context 'when branch pipeline succeeds' do context 'when branch pipeline succeeds' do
before do before do
push_pipeline.succeed! push_pipeline.reload.succeed!
wait_for_requests wait_for_requests
end end
......
...@@ -23,7 +23,7 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do ...@@ -23,7 +23,7 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
end end
it 'displays a mini pipeline graph' do it 'displays a mini pipeline graph' do
expect(page).to have_selector('.mr-widget-pipeline-graph') expect(page).to have_selector('[data-testid="pipeline-mini-graph"]')
end end
context 'as json' do context 'as json' do
......
...@@ -16,46 +16,28 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do ...@@ -16,46 +16,28 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
let(:build) { create(:ci_build, pipeline: pipeline, status: :running) } let(:build) { create(:ci_build, pipeline: pipeline, status: :running) }
shared_examples 'shows ci icon and mini pipeline' do before do
before do build.run
build.run visit project_commit_path(project, project.commit.id)
visit project_commit_path(project, project.commit.id) end
end
it 'display icon with status' do
expect(page).to have_selector('.ci-status-icon-running')
end
it 'displays a mini pipeline graph' do
expect(page).to have_selector('.mr-widget-pipeline-graph')
first('.mini-pipeline-graph-dropdown-toggle').click
wait_for_requests
page.within '.js-builds-dropdown-list' do
expect(page).to have_selector('.ci-status-icon-running')
expect(page).to have_content(build.stage)
end
build.drop it 'display icon with status' do
end expect(page).to have_selector('.ci-status-icon-running')
end end
context 'when ci_commit_pipeline_mini_graph_vue is disabled' do it 'displays a mini pipeline graph' do
before do expect(page).to have_selector('[data-testid="pipeline-mini-graph"]')
stub_feature_flags(ci_commit_pipeline_mini_graph_vue: false)
end
it_behaves_like 'shows ci icon and mini pipeline' first('.mini-pipeline-graph-dropdown-toggle').click
end
context 'when ci_commit_pipeline_mini_graph_vue is enabled' do wait_for_requests
before do
stub_feature_flags(ci_commit_pipeline_mini_graph_vue: true) page.within '.js-builds-dropdown-list' do
expect(page).to have_selector('.ci-status-icon-running')
expect(page).to have_content(build.stage)
end end
it_behaves_like 'shows ci icon and mini pipeline' build.drop
end end
end end
...@@ -65,7 +47,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do ...@@ -65,7 +47,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
end end
it 'does not display a mini pipeline graph' do it 'does not display a mini pipeline graph' do
expect(page).not_to have_selector('.mr-widget-pipeline-graph') expect(page).not_to have_selector('[data-testid="pipeline-mini-graph"]')
end end
end end
end end
...@@ -534,7 +534,7 @@ RSpec.describe 'Pipelines', :js do ...@@ -534,7 +534,7 @@ RSpec.describe 'Pipelines', :js do
end end
it 'renders a mini pipeline graph' do it 'renders a mini pipeline graph' do
expect(page).to have_selector('[data-testid="widget-mini-pipeline-graph"]') expect(page).to have_selector('[data-testid="pipeline-mini-graph"]')
expect(page).to have_selector(dropdown_selector) expect(page).to have_selector(dropdown_selector)
end end
......
<div class="js-builds-dropdown-tests dropdown dropdown" data-testid="widget-mini-pipeline-graph">
<button class="js-builds-dropdown-button" data-toggle="dropdown" data-stage-endpoint="foobar">
Dropdown
</button>
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
<li class="js-builds-dropdown-list scrollable-menu">
<ul></ul>
</li>
<li class="js-builds-dropdown-loading hidden">
<span class="gl-spinner"></span>
</li>
</ul>
</div>
import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
describe('Mini Pipeline Graph Dropdown', () => {
beforeEach(() => {
loadFixtures('static/mini_dropdown_graph.html');
});
describe('When is initialized', () => {
it('should initialize without errors when no options are given', () => {
const miniPipelineGraph = new MiniPipelineGraph();
expect(miniPipelineGraph.dropdownListSelector).toEqual('.js-builds-dropdown-container');
});
it('should set the container as the given prop', () => {
const container = '.foo';
const miniPipelineGraph = new MiniPipelineGraph({ container });
expect(miniPipelineGraph.container).toEqual(container);
});
});
describe('When dropdown is clicked', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
it('should call getBuildsList', () => {
const getBuildsListSpy = jest
.spyOn(MiniPipelineGraph.prototype, 'getBuildsList')
.mockImplementation(() => {});
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
expect(getBuildsListSpy).toHaveBeenCalled();
});
it('should make a request to the endpoint provided in the html', () => {
const ajaxSpy = jest.spyOn(axios, 'get');
mock.onGet('foobar').reply(200, {
html: '',
});
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
expect(ajaxSpy.mock.calls[0][0]).toEqual('foobar');
});
it('should not close when user uses cmd/ctrl + click', (done) => {
mock.onGet('foobar').reply(200, {
html: `<li>
<a class="mini-pipeline-graph-dropdown-item" href="#">
<span class="ci-status-icon ci-status-icon-failed"></span>
<span>build</span>
</a>
<a class="ci-action-icon-wrapper js-ci-action-icon" href="#"></a>
</li>`,
});
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
waitForPromises()
.then(() => {
document.querySelector('a.mini-pipeline-graph-dropdown-item').click();
})
.then(waitForPromises)
.then(() => {
expect($('.js-builds-dropdown-list').is(':visible')).toEqual(true);
})
.then(done)
.catch(done.fail);
});
it('should close the dropdown when request returns an error', (done) => {
mock.onGet('foobar').networkError();
new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
setImmediate(() => {
expect($('.js-builds-dropdown-tests .dropdown').hasClass('open')).toEqual(false);
done();
});
});
});
});
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { setHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { loadBranches } from '~/projects/commit_box/info/load_branches'; import { loadBranches } from '~/projects/commit_box/info/load_branches';
...@@ -9,32 +10,38 @@ const mockBranchesRes = ...@@ -9,32 +10,38 @@ const mockBranchesRes =
describe('~/projects/commit_box/info/load_branches', () => { describe('~/projects/commit_box/info/load_branches', () => {
let mock; let mock;
let el;
const getElInnerHtml = () => document.querySelector('.js-commit-box-info').innerHTML;
beforeEach(() => { beforeEach(() => {
setHTMLFixture(`
<div class="js-commit-box-info" data-commit-path="${mockCommitPath}">
<div class="commit-info branches">
<span class="spinner"/>
</div>
</div>`);
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet(mockCommitPath).reply(200, mockBranchesRes); mock.onGet(mockCommitPath).reply(200, mockBranchesRes);
el = document.createElement('div');
el.dataset.commitPath = mockCommitPath;
el.innerHTML = '<div class="commit-info branches"><span class="spinner"/></div>';
}); });
it('loads and renders branches info', async () => { it('loads and renders branches info', async () => {
loadBranches(el); loadBranches();
await waitForPromises(); await waitForPromises();
expect(el.innerHTML).toBe(`<div class="commit-info branches">${mockBranchesRes}</div>`); expect(getElInnerHtml()).toMatchInterpolatedText(
`<div class="commit-info branches">${mockBranchesRes}</div>`,
);
}); });
it('does not load when no container is provided', async () => { it('does not load when no container is provided', async () => {
loadBranches(null); loadBranches('.js-another-class');
await waitForPromises(); await waitForPromises();
expect(mock.history.get).toHaveLength(0); expect(mock.history.get).toHaveLength(0);
}); });
describe('when braches request returns unsafe content', () => { describe('when branches request returns unsafe content', () => {
beforeEach(() => { beforeEach(() => {
mock mock
.onGet(mockCommitPath) .onGet(mockCommitPath)
...@@ -42,25 +49,25 @@ describe('~/projects/commit_box/info/load_branches', () => { ...@@ -42,25 +49,25 @@ describe('~/projects/commit_box/info/load_branches', () => {
}); });
it('displays sanitized html', async () => { it('displays sanitized html', async () => {
loadBranches(el); loadBranches();
await waitForPromises(); await waitForPromises();
expect(el.innerHTML).toBe( expect(getElInnerHtml()).toMatchInterpolatedText(
'<div class="commit-info branches"><a href="/-/commits/master">master</a></div>', '<div class="commit-info branches"><a href="/-/commits/master">master</a></div>',
); );
}); });
}); });
describe('when braches request fails', () => { describe('when branches request fails', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet(mockCommitPath).reply(500, 'Error!'); mock.onGet(mockCommitPath).reply(500, 'Error!');
}); });
it('attempts to load and renders an error', async () => { it('attempts to load and renders an error', async () => {
loadBranches(el); loadBranches();
await waitForPromises(); await waitForPromises();
expect(el.innerHTML).toBe( expect(getElInnerHtml()).toMatchInterpolatedText(
'<div class="commit-info branches">Failed to load branches. Please try again.</div>', '<div class="commit-info branches">Failed to load branches. Please try again.</div>',
); );
}); });
......
...@@ -4,7 +4,6 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = ` ...@@ -4,7 +4,6 @@ exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = `
<div> <div>
<div <div
class="file-content code js-syntax-highlight" class="file-content code js-syntax-highlight"
data-qa-selector="file_content"
> >
<div <div
class="line-numbers" class="line-numbers"
......
...@@ -20,7 +20,7 @@ RSpec.describe Types::Ci::JobType do ...@@ -20,7 +20,7 @@ RSpec.describe Types::Ci::JobType do
pipeline pipeline
queued_at queued_at
scheduledAt scheduledAt
scheduledAt schedulingType
shortSha shortSha
stage stage
started_at started_at
......
...@@ -11,8 +11,8 @@ RSpec.describe Types::Ci::PipelineType do ...@@ -11,8 +11,8 @@ RSpec.describe Types::Ci::PipelineType do
expected_fields = %w[ expected_fields = %w[
id iid sha before_sha status detailed_status config_source duration id iid sha before_sha status detailed_status config_source duration
coverage created_at updated_at started_at finished_at committed_at coverage created_at updated_at started_at finished_at committed_at
stages user retryable cancelable jobs job source_job downstream stages user retryable cancelable jobs source_job job downstream
upstream path project active user_permissions warnings commit_path upstream path project active user_permissions warnings commit_path uses_needs
] ]
if Gitlab.ee? if Gitlab.ee?
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::Pipeline::Process do
let_it_be(:project) { build(:project) }
let_it_be(:user) { build(:user) }
let_it_be(:pipeline) { build(:ci_pipeline, project: project, id: 42) }
let_it_be(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
describe '#perform!' do
subject(:perform) { step.perform! }
it 'schedules a job to process the pipeline' do
expect(Ci::InitialPipelineProcessWorker)
.to receive(:perform_async)
.with(42)
perform
end
context 'with async processing disabled' do
before do
stub_feature_flags(ci_async_initial_pipeline_processing: false)
end
it 'processes pipeline inline' do
expect(::Ci::ProcessPipelineService)
.to receive(:new)
.with(pipeline)
.and_call_original
perform
end
end
end
describe '#break?' do
it { expect(step.break?).to be_falsey }
end
end
...@@ -3782,6 +3782,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do ...@@ -3782,6 +3782,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end end
end end
describe '#uses_needs?' do
let_it_be(:pipeline) { create(:ci_pipeline) }
context 'when the scheduling type is `dag`' do
it 'returns true' do
create(:ci_build, pipeline: pipeline, scheduling_type: :dag)
expect(pipeline.uses_needs?).to eq(true)
end
end
context 'when the scheduling type is nil or stage' do
it 'returns false' do
create(:ci_build, pipeline: pipeline, scheduling_type: :stage)
expect(pipeline.uses_needs?).to eq(false)
end
end
end
describe '#total_size' do describe '#total_size' do
let(:pipeline) { create(:ci_pipeline) } let(:pipeline) { create(:ci_pipeline) }
let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) } let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
......
...@@ -72,5 +72,33 @@ RSpec.describe Ci::Artifactable do ...@@ -72,5 +72,33 @@ RSpec.describe Ci::Artifactable do
expect(Ci::JobArtifact.expired(1).order_id_asc).to eq([recently_expired_artifact]) expect(Ci::JobArtifact.expired(1).order_id_asc).to eq([recently_expired_artifact])
end end
end end
describe '.with_files_stored_locally' do
it 'returns artifacts stored locally' do
expect(Ci::JobArtifact.with_files_stored_locally).to contain_exactly(recently_expired_artifact, later_expired_artifact, not_expired_artifact)
end
end
describe '.with_files_stored_remotely' do
let(:remote_artifact) { create(:ci_job_artifact, :remote_store) }
before do
stub_artifacts_object_storage
end
it 'returns artifacts stored remotely' do
expect(Ci::JobArtifact.with_files_stored_remotely).to contain_exactly(remote_artifact)
end
end
describe '.project_id_in' do
context 'when artifacts belongs to projects' do
let(:project_ids) { [recently_expired_artifact.project.id, not_expired_artifact.project.id, non_existing_record_id] }
it 'returns artifacts belonging to projects' do
expect(Ci::JobArtifact.project_id_in(project_ids)).to contain_exactly(recently_expired_artifact, not_expired_artifact)
end
end
end
end end
end end
...@@ -49,8 +49,6 @@ RSpec.describe API::Triggers do ...@@ -49,8 +49,6 @@ RSpec.describe API::Triggers do
expect(response).to have_gitlab_http_status(:created) expect(response).to have_gitlab_http_status(:created)
expect(json_response).to include('id' => pipeline.id) expect(json_response).to include('id' => pipeline.id)
pipeline.builds.reload
expect(pipeline.builds.pending.size).to eq(2)
expect(pipeline.builds.size).to eq(5) expect(pipeline.builds.size).to eq(5)
end end
......
...@@ -248,14 +248,6 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do ...@@ -248,14 +248,6 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
expect(subject[:pipeline]).to eq(pipeline_payload) expect(subject[:pipeline]).to eq(pipeline_payload)
end end
context 'when merge_request_cached_pipeline_serializer is disabled' do
it 'does not return pipeline' do
stub_feature_flags(merge_request_cached_pipeline_serializer: false)
expect(subject[:pipeline]).to be_nil
end
end
end end
context 'when user does not have access to pipelines' do context 'when user does not have access to pipelines' do
......
...@@ -226,19 +226,6 @@ RSpec.describe MergeRequestPollWidgetEntity do ...@@ -226,19 +226,6 @@ RSpec.describe MergeRequestPollWidgetEntity do
expect(subject[:pipeline]).to be_nil expect(subject[:pipeline]).to be_nil
end end
context 'when merge_request_cached_pipeline_serializer is disabled' do
it 'returns detailed info about pipeline' do
stub_feature_flags(merge_request_cached_pipeline_serializer: false)
pipeline_payload =
MergeRequests::PipelineEntity
.represent(pipeline, request: req)
.as_json
expect(subject[:pipeline]).to eq(pipeline_payload)
end
end
it 'returns ci_status' do it 'returns ci_status' do
expect(subject[:ci_status]).to eq('pending') expect(subject[:ci_status]).to eq('pending')
end end
......
...@@ -112,7 +112,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do ...@@ -112,7 +112,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'updates bridge status when downstream pipeline gets processed' do it 'updates bridge status when downstream pipeline gets processed' do
pipeline = service.execute(bridge) pipeline = service.execute(bridge)
expect(pipeline.reload).to be_pending expect(pipeline.reload).to be_created
expect(bridge.reload).to be_success expect(bridge.reload).to be_success
end end
...@@ -227,7 +227,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do ...@@ -227,7 +227,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
it 'updates bridge status when downstream pipeline gets processed' do it 'updates bridge status when downstream pipeline gets processed' do
pipeline = service.execute(bridge) pipeline = service.execute(bridge)
expect(pipeline.reload).to be_pending expect(pipeline.reload).to be_created
expect(bridge.reload).to be_success expect(bridge.reload).to be_success
end end
......
...@@ -40,6 +40,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -40,6 +40,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it 'creates bridge job with resource group' do it 'creates bridge job with resource group' do
pipeline = create_pipeline! pipeline = create_pipeline!
Ci::InitialPipelineProcessWorker.new.perform(pipeline.id)
test = pipeline.statuses.find_by(name: 'instrumentation_test') test = pipeline.statuses.find_by(name: 'instrumentation_test')
expect(pipeline).to be_created_successfully expect(pipeline).to be_created_successfully
......
...@@ -202,7 +202,7 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -202,7 +202,7 @@ RSpec.describe Ci::CreatePipelineService do
YAML YAML
end end
it 'creates a pipeline with build_a and test_b pending; deploy_b manual' do it 'creates a pipeline with build_a and test_b pending; deploy_b manual', :sidekiq_inline do
processables = pipeline.processables processables = pipeline.processables
build_a = processables.find { |processable| processable.name == 'build_a' } build_a = processables.find { |processable| processable.name == 'build_a' }
......
...@@ -91,6 +91,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -91,6 +91,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it 'creates bridge job with resource group', :aggregate_failures do it 'creates bridge job with resource group', :aggregate_failures do
pipeline = create_pipeline! pipeline = create_pipeline!
Ci::InitialPipelineProcessWorker.new.perform(pipeline.id)
test = pipeline.statuses.find_by(name: 'instrumentation_test') test = pipeline.statuses.find_by(name: 'instrumentation_test')
expect(pipeline).to be_created_successfully expect(pipeline).to be_created_successfully
......
...@@ -230,8 +230,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -230,8 +230,8 @@ RSpec.describe Ci::CreatePipelineService do
end end
context 'matching the first rule in the list' do context 'matching the first rule in the list' do
it 'saves a pending pipeline' do it 'saves a created pipeline' do
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
end end
end end
...@@ -239,8 +239,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -239,8 +239,8 @@ RSpec.describe Ci::CreatePipelineService do
context 'matching the last rule in the list' do context 'matching the last rule in the list' do
let(:ref) { 'refs/heads/feature' } let(:ref) { 'refs/heads/feature' }
it 'saves a pending pipeline' do it 'saves a created pipeline' do
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
end end
end end
...@@ -280,8 +280,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -280,8 +280,8 @@ RSpec.describe Ci::CreatePipelineService do
end end
context 'matching the first rule in the list' do context 'matching the first rule in the list' do
it 'saves a pending pipeline' do it 'saves a created pipeline' do
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
end end
end end
...@@ -305,8 +305,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -305,8 +305,8 @@ RSpec.describe Ci::CreatePipelineService do
context 'with partial match' do context 'with partial match' do
let(:ref) { 'refs/heads/feature' } let(:ref) { 'refs/heads/feature' }
it 'saves a pending pipeline' do it 'saves a created pipeline' do
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
end end
end end
...@@ -349,8 +349,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -349,8 +349,8 @@ RSpec.describe Ci::CreatePipelineService do
context 'where workflow passes and the job passes' do context 'where workflow passes and the job passes' do
let(:ref) { 'refs/heads/feature' } let(:ref) { 'refs/heads/feature' }
it 'saves a pending pipeline' do it 'saves a created pipeline' do
expect(pipeline).to be_pending expect(pipeline).to be_created
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
end end
end end
......
...@@ -63,7 +63,7 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -63,7 +63,7 @@ RSpec.describe Ci::CreatePipelineService do
expect(pipeline).to be_push expect(pipeline).to be_push
expect(pipeline).to eq(project.ci_pipelines.last) expect(pipeline).to eq(project.ci_pipelines.last)
expect(pipeline).to have_attributes(user: user) expect(pipeline).to have_attributes(user: user)
expect(pipeline).to have_attributes(status: 'pending') expect(pipeline).to have_attributes(status: 'created')
expect(pipeline.iid).not_to be_nil expect(pipeline.iid).not_to be_nil
expect(pipeline.repository_source?).to be true expect(pipeline.repository_source?).to be true
expect(pipeline.builds.first).to be_kind_of(Ci::Build) expect(pipeline.builds.first).to be_kind_of(Ci::Build)
...@@ -253,7 +253,7 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -253,7 +253,7 @@ RSpec.describe Ci::CreatePipelineService do
pipeline pipeline
pipeline_on_previous_commit pipeline_on_previous_commit
expect(pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil) expect(pipeline.reload).to have_attributes(status: 'created', auto_canceled_by_id: nil)
end end
it 'auto cancel pending non-HEAD pipelines', :sidekiq_might_not_need_inline do it 'auto cancel pending non-HEAD pipelines', :sidekiq_might_not_need_inline do
...@@ -263,8 +263,8 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -263,8 +263,8 @@ RSpec.describe Ci::CreatePipelineService do
expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id) expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: pipeline.id)
end end
it 'cancels running outdated pipelines', :sidekiq_might_not_need_inline do it 'cancels running outdated pipelines', :sidekiq_inline do
pipeline_on_previous_commit.run pipeline_on_previous_commit.reload.run
head_pipeline = execute_service head_pipeline = execute_service
expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: head_pipeline.id) expect(pipeline_on_previous_commit.reload).to have_attributes(status: 'canceled', auto_canceled_by_id: head_pipeline.id)
...@@ -278,13 +278,13 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -278,13 +278,13 @@ RSpec.describe Ci::CreatePipelineService do
end end
it 'does not cancel pipelines from the other branches' do it 'does not cancel pipelines from the other branches' do
pending_pipeline = execute_service( new_pipeline = execute_service(
ref: 'refs/heads/feature', ref: 'refs/heads/feature',
after: previous_commit_sha_from_ref('feature') after: previous_commit_sha_from_ref('feature')
) )
pipeline pipeline
expect(pending_pipeline.reload).to have_attributes(status: 'pending', auto_canceled_by_id: nil) expect(new_pipeline.reload).to have_attributes(status: 'created', auto_canceled_by_id: nil)
end end
context 'when the interruptible attribute is' do context 'when the interruptible attribute is' do
...@@ -465,12 +465,12 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -465,12 +465,12 @@ RSpec.describe Ci::CreatePipelineService do
project.update!(auto_cancel_pending_pipelines: 'disabled') project.update!(auto_cancel_pending_pipelines: 'disabled')
end end
it 'does not auto cancel pending non-HEAD pipelines' do it 'does not auto cancel created non-HEAD pipelines' do
pipeline_on_previous_commit pipeline_on_previous_commit
pipeline pipeline
expect(pipeline_on_previous_commit.reload) expect(pipeline_on_previous_commit.reload)
.to have_attributes(status: 'pending', auto_canceled_by_id: nil) .to have_attributes(status: 'created', auto_canceled_by_id: nil)
end end
end end
...@@ -770,7 +770,7 @@ RSpec.describe Ci::CreatePipelineService do ...@@ -770,7 +770,7 @@ RSpec.describe Ci::CreatePipelineService do
stub_ci_pipeline_yaml_file(config) stub_ci_pipeline_yaml_file(config)
end end
it 'does not create a new pipeline' do it 'does not create a new pipeline', :sidekiq_inline do
result = execute_service result = execute_service
expect(result).to be_persisted expect(result).to be_persisted
......
...@@ -34,7 +34,7 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do ...@@ -34,7 +34,7 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do
expect(subject).to eq(project.ci_pipelines.last) expect(subject).to eq(project.ci_pipelines.last)
expect(subject.external_pull_request).to eq(pull_request) expect(subject.external_pull_request).to eq(pull_request)
expect(subject.user).to eq(user) expect(subject.user).to eq(user)
expect(subject.status).to eq('pending') expect(subject.status).to eq('created')
expect(subject.ref).to eq(pull_request.source_branch) expect(subject.ref).to eq(pull_request.source_branch)
expect(subject.sha).to eq(pull_request.source_sha) expect(subject.sha).to eq(pull_request.source_sha)
expect(subject.source_sha).to eq(pull_request.source_sha) expect(subject.source_sha).to eq(pull_request.source_sha)
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'actor key validations' do RSpec.shared_examples 'actor key validations' do
context 'key id is not provided' do
let(:key_id) { nil }
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find a user without a key')
end
end
context 'key does not exist' do context 'key does not exist' do
let(:key_id) { non_existing_record_id } let(:key_id) { non_existing_record_id }
......
...@@ -34,14 +34,6 @@ RSpec.describe 'projects/commit/_commit_box.html.haml' do ...@@ -34,14 +34,6 @@ RSpec.describe 'projects/commit/_commit_box.html.haml' do
expect(rendered).to have_selector('.js-commit-pipeline-mini-graph') expect(rendered).to have_selector('.js-commit-pipeline-mini-graph')
end end
it 'shows pipeline stages in haml when feature flag is disabled' do
stub_feature_flags(ci_commit_pipeline_mini_graph_vue: false)
render
expect(rendered).to have_selector('.js-commit-pipeline-graph')
end
end end
context 'when there are multiple pipelines for a commit' do context 'when there are multiple pipelines for a commit' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'projects/pipelines/_stage' do
let(:project) { create(:project, :repository) }
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) { build(:ci_stage, pipeline: pipeline) }
before do
assign :stage, stage
end
context 'when there are only latest builds present' do
before do
create(:ci_build, name: 'test:build',
stage: stage.name,
pipeline: pipeline)
end
it 'shows the builds in the stage' do
render
expect(rendered).to have_text 'test:build'
end
end
context 'when build belongs to different stage' do
before do
create(:ci_build, name: 'test:build',
stage: 'other:stage',
pipeline: pipeline)
end
it 'does not render build' do
render
expect(rendered).not_to have_text 'test:build'
end
end
context 'when there are retried builds present' do
before do
create(:ci_build, name: 'test:build', stage: stage.name, pipeline: pipeline, retried: true)
create(:ci_build, name: 'test:build', stage: stage.name, pipeline: pipeline)
end
it 'shows only latest builds' do
render
expect(rendered).to have_text 'test:build', count: 1
end
end
context 'when there are multiple builds' do
before do
Ci::HasStatus::AVAILABLE_STATUSES.each do |status|
create_build(status)
end
end
it 'shows them in order' do
render
expect(rendered).to have_text(Ci::HasStatus::ORDERED_STATUSES.join(" "))
end
def create_build(status)
create(:ci_build, name: status, status: status,
pipeline: pipeline, stage: stage.name)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::InitialPipelineProcessWorker do
describe '#perform' do
let_it_be(:pipeline) { create(:ci_pipeline, :with_job, status: :created) }
include_examples 'an idempotent worker' do
let(:job_args) { pipeline.id }
it 'marks the pipeline as pending' do
expect(pipeline).to be_created
subject
expect(pipeline.reload).to be_pending
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment