Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-05 12:08:47 +00:00
parent 2fe341d705
commit 5460c19548
122 changed files with 1725 additions and 281 deletions

View File

@ -72,7 +72,7 @@ review-deploy:
- download_chart
- date
- deploy || (display_deployment_debug && exit 1)
- disable_sign_ups
- disable_sign_ups || (delete_release && exit 1)
# When the job is manual, review-qa-smoke is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'

View File

@ -17,19 +17,11 @@ export default {
required: false,
default: null,
},
repositioning: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
isNewNote() {
return this.label === null;
},
pinStyle() {
return this.repositioning ? { ...this.position, cursor: 'move' } : this.position;
},
pinLabel() {
return this.isNewNote
? __('Comment form position')
@ -41,13 +33,13 @@ export default {
<template>
<button
:style="pinStyle"
:style="position"
:aria-label="pinLabel"
:class="{
'btn-transparent comment-indicator': isNewNote,
'btn-transparent comment-indicator gl-p-0': isNewNote,
'js-image-badge badge badge-pill': !isNewNote,
}"
class="design-pin gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-0"
class="gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-font-lg gl-outline-0!"
type="button"
@mousedown="$emit('mousedown', $event)"
@mouseup="$emit('mouseup', $event)"

View File

@ -266,7 +266,7 @@ export default {
type="button"
role="button"
:aria-label="$options.i18n.newCommentButtonLabel"
class="gl-absolute gl-w-full gl-h-full gl-p-0 gl-top-0 gl-left-0 gl-outline-0! btn-transparent design-detail-overlay-add-comment"
class="gl-absolute gl-w-full gl-h-full gl-p-0 gl-top-0 gl-left-0 gl-outline-0! btn-transparent gl-hover-cursor-crosshair"
data-qa-selector="design_image_button"
@mouseup="onAddCommentMouseup"
></button>
@ -276,7 +276,6 @@ export default {
v-if="resolvedDiscussionsExpanded || !note.resolved"
:key="note.id"
:label="note.index"
:repositioning="isMovingNote(note.id)"
:position="
isMovingNote(note.id) && movingNoteNewPosition
? getNotePositionStyle(movingNoteNewPosition)
@ -290,7 +289,6 @@ export default {
<design-note-pin
v-if="currentCommentForm"
:position="currentCommentPositionStyle"
:repositioning="isMovingCurrentComment"
@mousedown.stop="onNoteMousedown"
@mouseup.stop="onNoteMouseup"
/>

View File

@ -0,0 +1,19 @@
export function loadCSSFile(path) {
return new Promise(resolve => {
if (document.querySelector(`link[href="${path}"]`)) {
resolve();
} else {
const linkElement = document.createElement('link');
linkElement.type = 'text/css';
linkElement.rel = 'stylesheet';
// eslint-disable-next-line @gitlab/require-i18n-strings
linkElement.media = 'screen,print';
linkElement.onload = () => {
resolve();
};
linkElement.href = path;
document.head.appendChild(linkElement);
}
});
}

View File

@ -82,7 +82,7 @@ export const PACKAGE_REGISTRY_TABS = [
type: PackageType.NUGET,
},
{
title: s__('PackageRegistry|PyPi'),
title: s__('PackageRegistry|PyPI'),
type: PackageType.PYPI,
},
];

View File

@ -18,7 +18,7 @@ export const getPackageTypeLabel = packageType => {
case PackageType.NUGET:
return s__('PackageType|NuGet');
case PackageType.PYPI:
return s__('PackageType|PyPi');
return s__('PackageType|PyPI');
case PackageType.COMPOSER:
return s__('PackageType|Composer');

View File

@ -3,6 +3,7 @@
import $ from 'jquery';
import 'cropper';
import { isString } from 'lodash';
import { loadCSSFile } from '../lib/utils/css_utils';
(() => {
// Matches everything but the file name
@ -180,6 +181,9 @@ import { isString } from 'lodash';
}
}
const cropModal = document.querySelector('.modal-profile-crop');
if (cropModal) loadCSSFile(cropModal.dataset.cropperCssPath);
$.fn.glCrop = function(opts) {
return this.each(function() {
return $(this).data('glcrop', new GitLabCrop(this, opts));

View File

@ -6,7 +6,6 @@
@import '@gitlab/at.js/dist/css/jquery.atwho';
@import 'dropzone/dist/basic';
@import 'select2';
@import 'cropper';
// GitLab UI framework
@import 'framework';

View File

@ -1,3 +1,6 @@
$design-pin-diameter: 28px;
$t-gray-a-16-design-pin: rgba($black, 0.16);
.layout-page.design-detail-layout {
max-height: 100vh;
}
@ -9,8 +12,53 @@
top: 35px;
}
.design-pin {
transition: opacity $gl-transition-duration-medium $general-hover-transition-curve;
.badge.badge-pill {
display: flex;
height: $design-pin-diameter;
width: $design-pin-diameter;
box-sizing: content-box;
background-color: $purple-500;
color: $white;
font-weight: $gl-font-weight-bold;
border-radius: 50%;
z-index: 1;
padding: 0;
&.resolved {
background-color: $gray-500;
}
}
.comment-indicator {
border-radius: 50%;
}
.comment-indicator,
.frame .badge.badge-pill {
&:active {
cursor: grabbing;
}
}
/**
* Design pin that overlays the design
*/
.frame .badge.badge-pill {
box-shadow: 0 2px 4px $t-gray-a-08, 0 0 1px $t-gray-a-24;
border: $white 2px solid;
will-change: transform, box-shadow, opacity;
// NOTE: verbose transition property required for Safari
transition: transform $general-hover-transition-duration linear, box-shadow $general-hover-transition-duration linear, opacity $general-hover-transition-duration linear;
transform-origin: 0 0;
transform: translate(-50%, -50%);
&:hover {
transform: scale(1.2) translate(-50%, -50%);
}
&:active {
box-shadow: 0 0 4px $t-gray-a-16-design-pin, 0 4px 12px $t-gray-a-16-design-pin;
}
&.inactive {
@include gl-opacity-5;
@ -20,24 +68,6 @@
}
}
}
.badge.badge-pill {
display: flex;
height: 28px;
width: 28px;
background-color: $blue-400;
color: $white;
border: $white 1px solid;
border-radius: 50%;
&.resolved {
background-color: $gray-500;
}
}
.design-detail-overlay-add-comment {
cursor: crosshair;
}
}
.design-presentation-wrapper {
@ -105,8 +135,8 @@
border-left: 1px solid $gray-100;
position: absolute;
left: 28px;
top: -18px;
height: 18px;
top: -17px;
height: 17px;
}
.design-note {

View File

@ -0,0 +1,378 @@
/*!
* Cropper v2.3.0
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2016 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2016-02-22T02:13:13.332Z
*/
.cropper-container {
font-size: 0;
line-height: 0;
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
direction: ltr !important;
touch-action: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
.cropper-container img {
display: block;
width: 100%;
min-width: 0 !important;
max-width: none !important;
height: 100%;
min-height: 0 !important;
max-height: none !important;
image-orientation: 0deg !important;
}
.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.cropper-wrap-box {
overflow: hidden;
}
.cropper-drag-box {
opacity: 0;
background-color: #fff;
filter: alpha(opacity=0);
}
.cropper-modal {
opacity: 0.5;
background-color: #000;
filter: alpha(opacity=50);
}
.cropper-view-box {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
outline: 1px solid #39f;
outline-color: rgba(51, 153, 255, 0.75);
}
.cropper-dashed {
position: absolute;
display: block;
opacity: 0.5;
border: 0 dashed #eee;
filter: alpha(opacity=50);
}
.cropper-dashed.dashed-h {
top: 33.33333%;
left: 0;
width: 100%;
height: 33.33333%;
border-top-width: 1px;
border-bottom-width: 1px;
}
.cropper-dashed.dashed-v {
top: 0;
left: 33.33333%;
width: 33.33333%;
height: 100%;
border-right-width: 1px;
border-left-width: 1px;
}
.cropper-center {
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 0;
height: 0;
opacity: 0.75;
filter: alpha(opacity=75);
}
.cropper-center::before,
.cropper-center::after {
position: absolute;
display: block;
content: ' ';
background-color: #eee;
}
.cropper-center::before {
top: 0;
left: -3px;
width: 7px;
height: 1px;
}
.cropper-center::after {
top: -3px;
left: 0;
width: 1px;
height: 7px;
}
.cropper-face,
.cropper-line,
.cropper-point {
position: absolute;
display: block;
width: 100%;
height: 100%;
opacity: 0.1;
filter: alpha(opacity=10);
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
}
.cropper-line {
background-color: #39f;
}
.cropper-line.line-e {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
.cropper-line.line-n {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
.cropper-line.line-w {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
.cropper-line.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
.cropper-point {
width: 5px;
height: 5px;
opacity: 0.75;
background-color: #39f;
filter: alpha(opacity=75);
}
.cropper-point.point-e {
top: 50%;
right: -3px;
margin-top: -3px;
cursor: e-resize;
}
.cropper-point.point-n {
top: -3px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
.cropper-point.point-w {
top: 50%;
left: -3px;
margin-top: -3px;
cursor: w-resize;
}
.cropper-point.point-s {
bottom: -3px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
.cropper-point.point-ne {
top: -3px;
right: -3px;
cursor: ne-resize;
}
.cropper-point.point-nw {
top: -3px;
left: -3px;
cursor: nw-resize;
}
.cropper-point.point-sw {
bottom: -3px;
left: -3px;
cursor: sw-resize;
}
.cropper-point.point-se {
right: -3px;
bottom: -3px;
width: 20px;
height: 20px;
cursor: se-resize;
opacity: 1;
filter: alpha(opacity=100);
}
.cropper-point.point-se::before {
position: absolute;
right: -50%;
bottom: -50%;
display: block;
width: 200%;
height: 200%;
content: ' ';
opacity: 0;
background-color: #39f;
filter: alpha(opacity=0);
}
@media (min-width: 768px) {
.cropper-point.point-se {
width: 15px;
height: 15px;
}
}
@media (min-width: 992px) {
.cropper-point.point-se {
width: 10px;
height: 10px;
}
}
@media (min-width: 1200px) {
.cropper-point.point-se {
width: 5px;
height: 5px;
opacity: 0.75;
filter: alpha(opacity=75);
}
}
.cropper-invisible {
opacity: 0;
filter: alpha(opacity=0);
}
.cropper-bg {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}
.cropper-hide {
position: absolute;
display: block;
width: 0;
height: 0;
}
.cropper-hidden {
display: none !important;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
cursor: not-allowed;
}

View File

@ -2,6 +2,7 @@
class HelpController < ApplicationController
skip_before_action :authenticate_user!, unless: :public_visibility_restricted?
feature_category :not_owned
layout 'help'

View File

@ -11,6 +11,8 @@ class IdeController < ApplicationController
push_frontend_feature_flag(:schema_linting)
end
feature_category :web_ide
def index
Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count
end

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::AvailableNamespacesController < ApplicationController
feature_category :importers
def index
render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes)
end

View File

@ -4,6 +4,7 @@ class Import::BaseController < ApplicationController
include ActionView::Helpers::SanitizeHelper
before_action :import_rate_limit, only: [:create]
feature_category :importers
def status
respond_to do |format|

View File

@ -4,6 +4,8 @@ class Import::BulkImportsController < ApplicationController
before_action :ensure_group_import_enabled
before_action :verify_blocked_uri, only: :status
feature_category :importers
def configure
session[access_token_key] = params[access_token_key]&.strip
session[url_key] = params[url_key]

View File

@ -6,6 +6,8 @@ class Import::GitlabGroupsController < ApplicationController
before_action :ensure_group_import_enabled
before_action :import_rate_limit, only: %i[create]
feature_category :importers
def create
unless file_is_valid?(group_params[:file])
return redirect_back_or_default(options: { alert: s_('GroupImport|Unable to process group import file') })

View File

@ -12,6 +12,8 @@ class InvitesController < ApplicationController
respond_to :html
feature_category :authentication_and_authorization
def show
track_new_user_invite_experiment('opened')
accept if skip_invitation_prompt?

View File

@ -7,6 +7,8 @@ class JiraConnect::ApplicationController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :verify_atlassian_jwt!
feature_category :integrations
attr_reader :current_jira_installation
private

View File

@ -8,6 +8,8 @@ class JwtController < ApplicationController
# Add this before other actions, since we want to have the user or project
prepend_before_action :auth_user, :authenticate_project_or_user
feature_category :authentication_and_authorization
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService
}.freeze

View File

@ -3,6 +3,8 @@
class NotificationSettingsController < ApplicationController
before_action :authenticate_user!
feature_category :users
def create
return render_404 unless can_read?(resource)

View File

@ -8,6 +8,8 @@ class Oauth::Jira::AuthorizationsController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
feature_category :integrations
# 1. Rewire Jira OAuth initial request to our stablished OAuth authorization URL.
def new
session[:redirect_uri] = params['redirect_uri']

View File

@ -11,6 +11,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
feature_category :authentication_and_authorization
def handle_omniauth
omniauth_flow(Gitlab::Auth::OAuth)
end

View File

@ -7,6 +7,8 @@ module Registrations
before_action :check_experiment_enabled
before_action :ensure_namespace_path_param
feature_category :navigation
def update
current_user.experience_level = params[:experience_level]

View File

@ -15,6 +15,8 @@ class RegistrationsController < Devise::RegistrationsController
if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? }
before_action :load_recaptcha, only: :new
feature_category :authentication_and_authorization
def new
if experiment_enabled?(:signup_flow)
track_experiment_event(:terms_opt_in, 'start')

View File

@ -20,6 +20,8 @@ module Repositories
prepend_before_action :authenticate_user, :parse_repo_path
feature_category :source_code_management
private
def download_request?

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
class RunnerSetupController < ApplicationController
feature_category :continuous_integration
def platforms
render json: Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS)
end

View File

@ -25,6 +25,8 @@ class SearchController < ApplicationController
layout 'search'
feature_category :global_search
def show
@project = search_service.project
@group = search_service.group

View File

@ -3,6 +3,8 @@
class SentNotificationsController < ApplicationController
skip_before_action :authenticate_user!
feature_category :users
def unsubscribe
@sent_notification = SentNotification.for(params[:id])

View File

@ -49,6 +49,8 @@ class SessionsController < Devise::SessionsController
# token mismatch.
protect_from_forgery with: :exception, prepend: true, except: :destroy
feature_category :authentication_and_authorization
CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'
MAX_FAILED_LOGIN_ATTEMPTS = 5

View File

@ -4,6 +4,8 @@ class Snippets::ApplicationController < ApplicationController
include FindSnippet
include SnippetAuthorizations
feature_category :snippets
private
def authorize_read_snippet!

View File

@ -8,6 +8,8 @@ class Snippets::NotesController < ApplicationController
before_action :authorize_read_snippet!, only: [:show, :index]
before_action :authorize_create_note!, only: [:create]
feature_category :snippets
private
def note

View File

@ -25,6 +25,8 @@ class UploadsController < ApplicationController
before_action :authorize_create_access!, only: [:create, :authorize]
before_action :verify_workhorse_api!, only: [:authorize]
feature_category :not_owned
def uploader_class
PersonalFileUploader
end

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
class UserCalloutsController < ApplicationController
feature_category :navigation
def create
callout = ensure_callout

View File

@ -14,6 +14,8 @@ module Users
layout 'terms'
feature_category :users
def index
@redirect = redirect_path

View File

@ -21,6 +21,8 @@ class UsersController < ApplicationController
before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets]
feature_category :users
def show
respond_to do |format|
format.html

View File

@ -152,6 +152,13 @@ module Types
field :auto_merge_enabled, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if auto merge is enabled for the merge request'
field :approved_by, Types::UserType.connection_type, null: true,
description: 'Users who approved the merge request'
def approved_by
object.approved_by_users
end
def diff_stats(path: nil)
stats = Array.wrap(object.diff_stats&.to_a)

View File

@ -3,7 +3,7 @@
module Types
class PackageTypeEnum < BaseEnum
::Packages::Package.package_types.keys.each do |package_type|
value package_type.to_s.upcase, "Packages from the #{package_type} package manager", value: package_type.to_s
value package_type.to_s.upcase, "Packages from the #{package_type.capitalize} package manager", value: package_type.to_s
end
end
end

View File

@ -782,6 +782,11 @@ module Ci
end
end
def has_expired_locked_archive_artifacts?
locked_artifacts? &&
artifacts_expire_at.present? && artifacts_expire_at < Time.current
end
def has_expiring_archive_artifacts?
has_expiring_artifacts? && job_artifacts_archive.present?
end

View File

@ -27,6 +27,13 @@ module Ci
sha_attribute :source_sha
sha_attribute :target_sha
# Ci::CreatePipelineService returns Ci::Pipeline so this is the only place
# where we can pass additional information from the service. This accessor
# is used for storing the processed CI YAML contents for linting purposes.
# There is an open issue to address this:
# https://gitlab.com/gitlab-org/gitlab/-/issues/259010
attr_accessor :merged_yaml
belongs_to :project, inverse_of: :all_pipelines
belongs_to :user
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'

View File

@ -4,7 +4,7 @@ class Packages::Event < ApplicationRecord
belongs_to :package, optional: true
# FIXME: Remove debian: 9 from here when it's added to the types in package.rb model
EVENT_SCOPES = ::Packages::Package.package_types.merge(debian: 9, container: 1000, tag: 1001).freeze
EVENT_SCOPES = ::Packages::Package.package_types.merge(container: 1000, tag: 1001).freeze
enum event_scope: EVENT_SCOPES

View File

@ -47,7 +47,7 @@ class Packages::Package < ApplicationRecord
format: { with: Gitlab::Regex.generic_package_version_regex },
if: :generic?
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6, generic: 7, golang: 8 }
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6, generic: 7, golang: 8, debian: 9 }
scope :with_name, ->(name) { where(name: name) }
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# Display package version data acording to PyPi
# Display package version data acording to PyPI
# Simple API: https://warehouse.pypa.io/api-reference/legacy/#simple-project-api
module Packages
module Pypi
@ -12,7 +12,7 @@ module Packages
@project = project
end
# Returns the HTML body for PyPi simple API.
# Returns the HTML body for PyPI simple API.
# Basically a list of package download links for a specific
# package
def body

View File

@ -35,7 +35,7 @@ class BuildDetailsEntity < JobEntity
browse_project_job_artifacts_path(project, build)
end
expose :keep_path, if: -> (*) { (build.locked_artifacts? || build.has_expiring_archive_artifacts?) && can?(current_user, :update_build, build) } do |build|
expose :keep_path, if: -> (*) { (build.has_expired_locked_archive_artifacts? || build.has_expiring_archive_artifacts?) && can?(current_user, :update_build, build) } do |build|
keep_project_job_artifacts_path(project, build)
end

View File

@ -82,7 +82,9 @@ class PipelineEntity < Grape::Entity
end
expose :failed_builds, if: -> (*) { can_retry? }, using: JobEntity do |pipeline|
pipeline.failed_builds
pipeline.failed_builds.each do |build|
build.project = pipeline.project
end
end
private

View File

@ -2,12 +2,20 @@
module Ci
class BuildReportResultService
include Gitlab::Utils::UsageData
EVENT_NAME = 'i_testing_test_case_parsed'
def execute(build)
return unless build.has_test_reports?
test_suite = generate_test_suite_report(build)
track_test_cases(build, test_suite)
build.report_results.create!(
project_id: build.project_id,
data: tests_params(build)
data: tests_params(test_suite)
)
end
@ -17,9 +25,7 @@ module Ci
build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new)
end
def tests_params(build)
test_suite = generate_test_suite_report(build)
def tests_params(test_suite)
{
tests: {
name: test_suite.name,
@ -31,5 +37,20 @@ module Ci
}
}
end
def track_test_cases(build, test_suite)
return if Feature.disabled?(:track_unique_test_cases_parsed, build.project)
track_usage_event(EVENT_NAME, test_case_hashes(build, test_suite))
end
def test_case_hashes(build, test_suite)
[].tap do |hashes|
test_suite.each_test_case do |test_case|
key = "#{build.project_id}-#{test_suite.name}-#{test_case.key}"
hashes << Digest::SHA256.hexdigest(key)
end
end
end
end
end

View File

@ -13,6 +13,8 @@ module SystemNotes
def relate_issue(noteable_ref)
body = "marked this issue as related to #{noteable_ref.to_reference(noteable.project)}"
issue_activity_counter.track_issue_related_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'relate'))
end
@ -27,6 +29,8 @@ module SystemNotes
def unrelate_issue(noteable_ref)
body = "removed the relation with #{noteable_ref.to_reference(noteable.project)}"
issue_activity_counter.track_issue_unrelated_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'unrelate'))
end
@ -174,6 +178,8 @@ module SystemNotes
if noteable.is_a?(ExternalIssue)
noteable.project.external_issue_tracker.create_cross_reference_note(noteable, mentioner, author)
else
issue_activity_counter.track_issue_cross_referenced_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, noteable.project, author, body, action: 'cross_reference'))
end
end
@ -208,6 +214,8 @@ module SystemNotes
status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE
body = "marked the task **#{new_task.source}** as #{status_label}"
issue_activity_counter.track_issue_description_changed_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'task'))
end
@ -229,6 +237,8 @@ module SystemNotes
cross_reference = noteable_ref.to_reference(project)
body = "moved #{direction} #{cross_reference}"
issue_activity_counter.track_issue_moved_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
@ -299,6 +309,9 @@ module SystemNotes
# Returns the created Note object
def mark_duplicate_issue(canonical_issue)
body = "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}"
issue_activity_counter.track_issue_marked_as_duplicate_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
end
@ -322,6 +335,14 @@ module SystemNotes
action = noteable.discussion_locked? ? 'locked' : 'unlocked'
body = "#{action} this #{noteable.class.to_s.titleize.downcase}"
if noteable.is_a?(Issue)
if action == 'locked'
issue_activity_counter.track_issue_locked_action(author: author)
else
issue_activity_counter.track_issue_unlocked_action(author: author)
end
end
create_note(NoteSummary.new(noteable, project, author, body, action: action))
end

View File

@ -122,7 +122,7 @@
= f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
= link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'
.modal.modal-profile-crop
.modal.modal-profile-crop{ data: { cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css') } }
.modal-dialog
.modal-content
.modal-header

View File

@ -0,0 +1,5 @@
---
title: Update styling of design comment pins
merge_request: 39797
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add more issue change events to usage ping
merge_request: 43828
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Reduce cached SQL for JobsController#show
merge_request: 43559
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Allow get approvals on merge request by GraphQL in CE
author: Pavel Kuznetsov
merge_request: 43325
type: add

View File

@ -0,0 +1,5 @@
---
title: Create ComplianceManagement::Framework Model
merge_request: 43301
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add :default_branch_name column to namespace_settings
merge_request: 42778
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Track unique number of test cases parsed
merge_request: 41918
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix spelling of PyPI
merge_request: 44058
author: Peter Bittner (@bittner)
type: other

View File

@ -0,0 +1,5 @@
---
title: Show keep path for expired locked artifacts.
merge_request: 43866
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add project scoped CI lint API endpoint.
merge_request: 42998
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Loads cropper css only when needed
merge_request: 44137
author:
type: performance

View File

@ -194,6 +194,7 @@ module Gitlab
config.assets.precompile << "page_bundles/milestone.css"
config.assets.precompile << "page_bundles/todos.css"
config.assets.precompile << "page_bundles/xterm.css"
config.assets.precompile << "lazy_bundles/cropper.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "disable_animations.css"

View File

@ -0,0 +1,7 @@
---
name: track_unique_test_cases_parsed
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41918
rollout_issue_url:
group: group::testing
type: development
default_enabled: false

View File

@ -0,0 +1,7 @@
---
name: usage_data_i_testing_test_case_parsed
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41918
rollout_issue_url:
group: group::testing
type: development
default_enabled: true

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddDefaultBranchNameToNamespaceSettings < ActiveRecord::Migration[6.0]
DOWNTIME = false
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20200919204155_add_text_limit_to_namespace_settings_default_branch_name
#
def change
add_column :namespace_settings, :default_branch_name, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddTextLimitToNamespaceSettingsDefaultBranchName < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_text_limit :namespace_settings, :default_branch_name, 255
end
def down
# Down is required as `add_text_limit` is not reversible
#
remove_text_limit :namespace_settings, :default_branch_name
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
class AddComplianceFrameworkModel < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
unless table_exists?(:compliance_management_frameworks)
with_lock_retries do
create_table :compliance_management_frameworks do |t|
t.references :group, foreign_key: { to_table: :namespaces, on_delete: :cascade }, null: false, index: false
t.text :name, null: false
t.text :description, null: false
t.text :color, null: false
t.index [:group_id, :name], unique: true
end
end
end
add_text_limit :compliance_management_frameworks, :name, 255
add_text_limit :compliance_management_frameworks, :description, 255
add_text_limit :compliance_management_frameworks, :color, 10
end
def down
with_lock_retries do
drop_table :compliance_management_frameworks
end
end
end

View File

@ -0,0 +1 @@
f33c66297ca7848c576778dc275e42801f5f9d7cdcf8c4d2fb205d4eb9770937

View File

@ -0,0 +1 @@
097cb7a36fdc831045f3a33a047f8bda6474b8165ef5fc95dbdeea45d0ac04a3

View File

@ -0,0 +1 @@
a8450c6c21b1182afd06c88af18c0d9be92b0e7fdc73505c07d4ab3fddd39abf

View File

@ -11087,6 +11087,26 @@ CREATE SEQUENCE commit_user_mentions_id_seq
ALTER SEQUENCE commit_user_mentions_id_seq OWNED BY commit_user_mentions.id;
CREATE TABLE compliance_management_frameworks (
id bigint NOT NULL,
group_id bigint NOT NULL,
name text NOT NULL,
description text NOT NULL,
color text NOT NULL,
CONSTRAINT check_08cd34b2c2 CHECK ((char_length(color) <= 10)),
CONSTRAINT check_1617e0b87e CHECK ((char_length(description) <= 255)),
CONSTRAINT check_ab00bc2193 CHECK ((char_length(name) <= 255))
);
CREATE SEQUENCE compliance_management_frameworks_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE compliance_management_frameworks_id_seq OWNED BY compliance_management_frameworks.id;
CREATE TABLE container_expiration_policies (
project_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -13609,7 +13629,9 @@ CREATE TABLE namespace_settings (
updated_at timestamp with time zone NOT NULL,
namespace_id integer NOT NULL,
prevent_forking_outside_group boolean DEFAULT false NOT NULL,
allow_mfa_for_subgroups boolean DEFAULT true NOT NULL
allow_mfa_for_subgroups boolean DEFAULT true NOT NULL,
default_branch_name text,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255))
);
CREATE TABLE namespace_statistics (
@ -17344,6 +17366,8 @@ ALTER TABLE ONLY clusters_kubernetes_namespaces ALTER COLUMN id SET DEFAULT next
ALTER TABLE ONLY commit_user_mentions ALTER COLUMN id SET DEFAULT nextval('commit_user_mentions_id_seq'::regclass);
ALTER TABLE ONLY compliance_management_frameworks ALTER COLUMN id SET DEFAULT nextval('compliance_management_frameworks_id_seq'::regclass);
ALTER TABLE ONLY container_repositories ALTER COLUMN id SET DEFAULT nextval('container_repositories_id_seq'::regclass);
ALTER TABLE ONLY conversational_development_index_metrics ALTER COLUMN id SET DEFAULT nextval('conversational_development_index_metrics_id_seq'::regclass);
@ -18377,6 +18401,9 @@ ALTER TABLE ONLY clusters
ALTER TABLE ONLY commit_user_mentions
ADD CONSTRAINT commit_user_mentions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY compliance_management_frameworks
ADD CONSTRAINT compliance_management_frameworks_pkey PRIMARY KEY (id);
ALTER TABLE ONLY container_expiration_policies
ADD CONSTRAINT container_expiration_policies_pkey PRIMARY KEY (project_id);
@ -19445,6 +19472,8 @@ CREATE UNIQUE INDEX idx_jira_connect_subscriptions_on_installation_id_namespace_
CREATE INDEX idx_members_created_at_user_id_invite_token ON members USING btree (created_at) WHERE ((invite_token IS NOT NULL) AND (user_id IS NULL));
CREATE INDEX idx_merge_requests_on_id_and_merge_jid ON merge_requests USING btree (id, merge_jid) WHERE ((merge_jid IS NOT NULL) AND (state_id = 4));
CREATE INDEX idx_merge_requests_on_source_project_and_branch_state_opened ON merge_requests USING btree (source_project_id, source_branch) WHERE (state_id = 1);
CREATE INDEX idx_merge_requests_on_state_id_and_merge_status ON merge_requests USING btree (state_id, merge_status) WHERE ((state_id = 1) AND ((merge_status)::text = 'can_be_merged'::text));
@ -19987,6 +20016,8 @@ CREATE INDEX index_clusters_on_user_id ON clusters USING btree (user_id);
CREATE UNIQUE INDEX index_commit_user_mentions_on_note_id ON commit_user_mentions USING btree (note_id);
CREATE UNIQUE INDEX index_compliance_management_frameworks_on_group_id_and_name ON compliance_management_frameworks USING btree (group_id, name);
CREATE INDEX index_container_expiration_policies_on_next_run_at_and_enabled ON container_expiration_policies USING btree (next_run_at, enabled);
CREATE INDEX index_container_repositories_on_project_id ON container_repositories USING btree (project_id);
@ -23668,6 +23699,9 @@ ALTER TABLE ONLY requirements_management_test_reports
ALTER TABLE ONLY pool_repositories
ADD CONSTRAINT fk_rails_d2711daad4 FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE SET NULL;
ALTER TABLE ONLY compliance_management_frameworks
ADD CONSTRAINT fk_rails_d3240d6339 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY group_group_links
ADD CONSTRAINT fk_rails_d3a0488427 FOREIGN KEY (shared_group_id) REFERENCES namespaces(id) ON DELETE CASCADE;

View File

@ -181,7 +181,7 @@ successfully, you must replicate their data using some other means.
| [Maven Repository](../../../user/packages/maven_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [Conan Repository](../../../user/packages/conan_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [NuGet Repository](../../../user/packages/nuget_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [PyPi Repository](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [PyPI Repository](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [Composer Repository](../../../user/packages/composer_repository/index.md) | **Yes** (13.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_terraform_state_version_replication`, enabled by default |
| [External merge request diffs](../../merge_request_diffs.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/33817) | No | Via Object Storage provider if supported. Native Geo support (Beta). | |

View File

@ -317,7 +317,7 @@ disable enforcement. For more information, see the documentation on configuring
```
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
1. Run `sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml`
1. Run `sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml`
to confirm that Gitaly can perform callbacks to the GitLab internal API.
**For installations from source**
@ -364,7 +364,7 @@ disable enforcement. For more information, see the documentation on configuring
```
1. Save the files and [restart GitLab](../restart_gitlab.md#installations-from-source).
1. Run `sudo -u git /home/git/gitlab-shell/bin/check -config /home/git/gitlab-shell/config.yml`
1. Run `sudo -u git /home/git/gitaly/gitaly-hooks check /home/git/gitaly/config.toml`
to confirm that Gitaly can perform callbacks to the GitLab internal API.
### Configure Gitaly clients
@ -711,6 +711,15 @@ Gitaly Go process. Some examples of things that are implemented in `gitaly-ruby`
- RPCs that deal with wikis.
- RPCs that create commits on behalf of a user, such as merge commits.
We recommend:
- At least 300MB memory per worker.
- No more than one worker per core.
NOTE: **Note:**
`gitaly-ruby` is planned to be eventually removed. To track progress, see the
[Remove the Gitaly-Ruby sidecar](https://gitlab.com/groups/gitlab-org/-/epics/2862) epic.
### Configure number of `gitaly-ruby` workers
`gitaly-ruby` has much less capacity than Gitaly implemented in Go. If your Gitaly server has to handle lots of

View File

@ -873,10 +873,10 @@ Particular attention should be shown to:
gitlab-ctl reconfigure
```
1. Verify each `gitlab-shell` on each Gitaly node can reach GitLab. On each Gitaly node run:
1. Verify on each Gitaly node the Git Hooks can reach GitLab. On each Gitaly node run:
```shell
/opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
/opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
1. Verify that GitLab can reach Praefect:

View File

@ -14,7 +14,7 @@ The Packages feature allows GitLab to act as a repository for the following:
| Software repository | Description | Available in GitLab version |
| ------------------- | ----------- | --------------------------- |
| [PyPi Repository](../../user/packages/pypi_repository/index.md) | The GitLab PyPi Repository enables every project in GitLab to have its own space to store [PyPi](https://pypi.org/) packages. | 12.10+ |
| [PyPI Repository](../../user/packages/pypi_repository/index.md) | The GitLab PyPI Repository enables every project in GitLab to have its own space to store [PyPI](https://pypi.org/) packages. | 12.10+ |
| [Composer Repository](../../user/packages/composer_repository/index.md) | The GitLab Composer Repository enables every project in GitLab to have its own space to store [Composer](https://getcomposer.org/) packages. | 13.1+ |
| [NuGet Repository](../../user/packages/nuget_repository/index.md) | The GitLab NuGet Repository enables every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.8+ |
| [Conan Repository](../../user/packages/conan_repository/index.md) | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.4+ |

View File

@ -1879,7 +1879,7 @@ On each node perform the following:
1. Optionally, from the Gitaly servers, confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
NOTE: **Note:**

View File

@ -1879,7 +1879,7 @@ On each node perform the following:
1. Optionally, from the Gitaly servers, confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
NOTE: **Note:**

View File

@ -466,7 +466,7 @@ To configure the Gitaly server:
1. Confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
### Gitaly TLS support

View File

@ -1223,7 +1223,7 @@ On each node:
1. Confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
1. Verify the GitLab services are running:

View File

@ -1879,7 +1879,7 @@ On each node perform the following:
1. Optionally, from the Gitaly servers, confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
NOTE: **Note:**

View File

@ -1222,7 +1222,7 @@ On each node:
1. Confirm that Gitaly can perform callbacks to the internal API:
```shell
sudo /opt/gitlab/embedded/service/gitlab-shell/bin/check -config /opt/gitlab/embedded/service/gitlab-shell/config.yml
sudo /opt/gitlab/embedded/bin/gitaly-hooks check /var/opt/gitlab/gitaly/config.toml
```
1. Verify the GitLab services are running:

View File

@ -11619,6 +11619,7 @@ type Mutation {
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2")
updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateBoard(input: UpdateBoardInput!): UpdateBoardPayload
updateBoardEpicUserPreferences(input: UpdateBoardEpicUserPreferencesInput!): UpdateBoardEpicUserPreferencesPayload
updateBoardList(input: UpdateBoardListInput!): UpdateBoardListPayload
updateContainerExpirationPolicy(input: UpdateContainerExpirationPolicyInput!): UpdateContainerExpirationPolicyPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
@ -12257,42 +12258,47 @@ type PackageFileRegistryEdge {
enum PackageTypeEnum {
"""
Packages from the composer package manager
Packages from the Composer package manager
"""
COMPOSER
"""
Packages from the conan package manager
Packages from the Conan package manager
"""
CONAN
"""
Packages from the generic package manager
Packages from the Debian package manager
"""
DEBIAN
"""
Packages from the Generic package manager
"""
GENERIC
"""
Packages from the golang package manager
Packages from the Golang package manager
"""
GOLANG
"""
Packages from the maven package manager
Packages from the Maven package manager
"""
MAVEN
"""
Packages from the npm package manager
Packages from the Npm package manager
"""
NPM
"""
Packages from the nuget package manager
Packages from the Nuget package manager
"""
NUGET
"""
Packages from the pypi package manager
Packages from the Pypi package manager
"""
PYPI
}
@ -18470,6 +18476,51 @@ type UpdateAlertStatusPayload {
todo: Todo
}
"""
Autogenerated input type of UpdateBoardEpicUserPreferences
"""
input UpdateBoardEpicUserPreferencesInput {
"""
The board global ID
"""
boardId: BoardID!
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Whether the epic should be collapsed in the board
"""
collapsed: Boolean!
"""
ID of an epic to set preferences for
"""
epicId: EpicID!
}
"""
Autogenerated return type of UpdateBoardEpicUserPreferences
"""
type UpdateBoardEpicUserPreferencesPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
User preferences for the epic in the board after mutation
"""
epicUserPreferences: BoardEpicUserPreferences
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
}
"""
Autogenerated input type of UpdateBoard
"""

View File

@ -34109,6 +34109,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateBoardEpicUserPreferences",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UpdateBoardEpicUserPreferencesInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "UpdateBoardEpicUserPreferencesPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateBoardList",
"description": null,
@ -36318,49 +36345,55 @@
"enumValues": [
{
"name": "MAVEN",
"description": "Packages from the maven package manager",
"description": "Packages from the Maven package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NPM",
"description": "Packages from the npm package manager",
"description": "Packages from the Npm package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CONAN",
"description": "Packages from the conan package manager",
"description": "Packages from the Conan package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NUGET",
"description": "Packages from the nuget package manager",
"description": "Packages from the Nuget package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "PYPI",
"description": "Packages from the pypi package manager",
"description": "Packages from the Pypi package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "COMPOSER",
"description": "Packages from the composer package manager",
"description": "Packages from the Composer package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "GENERIC",
"description": "Packages from the generic package manager",
"description": "Packages from the Generic package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "GOLANG",
"description": "Packages from the golang package manager",
"description": "Packages from the Golang package manager",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DEBIAN",
"description": "Packages from the Debian package manager",
"isDeprecated": false,
"deprecationReason": null
}
@ -54019,6 +54052,136 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateBoardEpicUserPreferencesInput",
"description": "Autogenerated input type of UpdateBoardEpicUserPreferences",
"fields": null,
"inputFields": [
{
"name": "boardId",
"description": "The board global ID",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "BoardID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "epicId",
"description": "ID of an epic to set preferences for",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "EpicID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "collapsed",
"description": "Whether the epic should be collapsed in the board",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "UpdateBoardEpicUserPreferencesPayload",
"description": "Autogenerated return type of UpdateBoardEpicUserPreferences",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "epicUserPreferences",
"description": "User preferences for the epic in the board after mutation",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "BoardEpicUserPreferences",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UpdateBoardInput",

View File

@ -2623,6 +2623,16 @@ Autogenerated return type of UpdateAlertStatus.
| `issue` | Issue | The issue created after mutation |
| `todo` | Todo | The todo after mutation |
### UpdateBoardEpicUserPreferencesPayload
Autogenerated return type of UpdateBoardEpicUserPreferences.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `epicUserPreferences` | BoardEpicUserPreferences | User preferences for the epic in the board after mutation |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
### UpdateBoardListPayload
Autogenerated return type of UpdateBoardList.
@ -3416,14 +3426,15 @@ Values for sorting projects.
| Value | Description |
| ----- | ----------- |
| `COMPOSER` | Packages from the composer package manager |
| `CONAN` | Packages from the conan package manager |
| `GENERIC` | Packages from the generic package manager |
| `GOLANG` | Packages from the golang package manager |
| `MAVEN` | Packages from the maven package manager |
| `NPM` | Packages from the npm package manager |
| `NUGET` | Packages from the nuget package manager |
| `PYPI` | Packages from the pypi package manager |
| `COMPOSER` | Packages from the Composer package manager |
| `CONAN` | Packages from the Conan package manager |
| `DEBIAN` | Packages from the Debian package manager |
| `GENERIC` | Packages from the Generic package manager |
| `GOLANG` | Packages from the Golang package manager |
| `MAVEN` | Packages from the Maven package manager |
| `NPM` | Packages from the Npm package manager |
| `NUGET` | Packages from the Nuget package manager |
| `PYPI` | Packages from the Pypi package manager |
### PipelineConfigSourceEnum

View File

@ -94,3 +94,51 @@ Example response:
"merged_config": "---\n:another_test:\n :stage: test\n :script: echo 2\n:test:\n :stage: test\n :script: echo 1\n"
}
```
## Validate a project's CI configuration
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231352) in GitLab 13.5.
Checks if a project's latest (`HEAD` of the project's default branch)
`.gitlab-ci.yml` configuration is valid. This endpoint uses all namespace
specific data available, including variables, local includes, and so on.
```plaintext
GET /projects/:id/ci/lint
```
| Attribute | Type | Required | Description |
| ---------- | ------- | -------- | -------- |
| `dry_run` | boolean | no | Run pipeline creation simulation, or only do static check. |
Example request:
```shell
curl "https://gitlab.example.com/api/v4/projects/:id/ci/lint"
```
Example responses:
- Valid config:
```json
{
"valid": true,
"merged_yaml": "---\n:test_job:\n :script: echo 1\n",
"errors": [],
"warnings": []
}
```
- Invalid config:
```json
{
"valid": false,
"merged_yaml": "---\n:test_job:\n :script: echo 1\n",
"errors": [
"jobs config should contain at least one visible job"
],
"warnings": []
}
```

View File

@ -223,6 +223,7 @@ Example response:
- GitLab Geo
- GitLab Shell's `bin/check`
- Gitaly
## Get new 2FA recovery codes using an SSH key

View File

@ -441,7 +441,7 @@ The following are some available Rake tasks:
| [`sudo gitlab-rake gitlab:elastic:index`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Enables Elasticsearch indexing and run `gitlab:elastic:create_empty_index`, `gitlab:elastic:clear_index_status`, `gitlab:elastic:index_projects`, and `gitlab:elastic:index_snippets`. |
| [`sudo gitlab-rake gitlab:elastic:index_projects`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Iterates over all projects and queues Sidekiq jobs to index them in the background. |
| [`sudo gitlab-rake gitlab:elastic:index_projects_status`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Determines the overall status of the indexing. It is done by counting the total number of indexed projects, dividing by a count of the total number of projects, then multiplying by 100. |
| [`sudo gitlab-rake gitlab:elastic:clear_index_status`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Deletes all instances of IndexStatus for all projects. |
| [`sudo gitlab-rake gitlab:elastic:clear_index_status`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Deletes all instances of IndexStatus for all projects. Note that this command will result in a complete wipe of the index, and it should be used with caution. |
| [`sudo gitlab-rake gitlab:elastic:create_empty_index[<TARGET_NAME>]`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Generates an empty index and assigns an alias for it on the Elasticsearch side only if it doesn't already exist. |
| [`sudo gitlab-rake gitlab:elastic:delete_index[<TARGET_NAME>]`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Removes the GitLab index and alias (if exists) on the Elasticsearch instance. |
| [`sudo gitlab-rake gitlab:elastic:recreate_index[<TARGET_NAME>]`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Wrapper task for `gitlab:elastic:delete_index[<TARGET_NAME>]` and `gitlab:elastic:create_empty_index[<TARGET_NAME>]`. |

View File

@ -914,7 +914,7 @@ restore:
sudo gitlab-backup restore BACKUP=11493107454_2018_04_25_10.6.4-ce
```
Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:backup:create` instead.
Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:backup:restore` instead.
CAUTION: **Warning:**
`gitlab-rake gitlab:backup:restore` doesn't set the correct file system

View File

@ -4,14 +4,14 @@ group: Package
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# GitLab PyPi Repository
# GitLab PyPI Repository
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208747) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) to GitLab Core in 13.3.
With the GitLab PyPi Repository, every project can have its own space to store PyPi packages.
With the GitLab PyPI Repository, every project can have its own space to store PyPI packages.
The GitLab PyPi Repository works with:
The GitLab PyPI Repository works with:
- [pip](https://pypi.org/project/pip/)
- [twine](https://pypi.org/project/twine/)
@ -20,13 +20,13 @@ The GitLab PyPi Repository works with:
You need a recent version of [pip](https://pypi.org/project/pip/) and [twine](https://pypi.org/project/twine/).
## Enabling the PyPi Repository
## Enabling the PyPI Repository
NOTE: **Note:**
This option is available only if your GitLab administrator has
[enabled support for the Package Registry](../../../administration/packages/index.md).
After the PyPi Repository is enabled, it is available for all new projects
After the PyPI Repository is enabled, it is available for all new projects
by default. To enable it for existing projects, or if you want to disable it:
1. Navigate to your project's **Settings > General > Visibility, project features, permissions**.
@ -37,8 +37,8 @@ You should then be able to see the **Packages & Registries** section on the left
## Getting started
This section covers creating a new example PyPi package to upload. This is a
quickstart to test out the **GitLab PyPi Registry**. If you already understand how
This section covers creating a new example PyPI package to upload. This is a
quickstart to test out the **GitLab PyPI Registry**. If you already understand how
to build and publish your own packages, move on to the [next section](#adding-the-gitlab-pypi-repository-as-a-source).
### Create a project
@ -152,10 +152,10 @@ And confirm your output matches the below:
mypypipackage-0.0.1-py3-none-any.whl mypypipackage-0.0.1.tar.gz
```
Our package is now all set up and ready to be uploaded to the **GitLab PyPi
Our package is now all set up and ready to be uploaded to the **GitLab PyPI
Package Registry**. Before we do so, we next need to set up authentication.
## Adding the GitLab PyPi Repository as a source
## Adding the GitLab PyPI Repository as a source
### Authenticating with a personal access token
@ -256,7 +256,7 @@ TWINE_PASSWORD=<personal_access_token or deploy_token> TWINE_USERNAME=<username
```
If you did not follow the guide above, then you need to ensure your package
has been properly built and you [created a PyPi package with `setuptools`](https://packaging.python.org/tutorials/packaging-projects/).
has been properly built and you [created a PyPI package with `setuptools`](https://packaging.python.org/tutorials/packaging-projects/).
You can then upload your package using the following command:

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module API
module Entities
module Ci
module Lint
class Result < Grape::Entity
expose :valid?, as: :valid
expose :errors
expose :warnings
expose :merged_yaml
end
end
end
end
end

View File

@ -25,5 +25,24 @@ module API
end
end
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Validation of .gitlab-ci.yml content' do
detail 'This feature was introduced in GitLab 13.5.'
end
params do
optional :dry_run, type: Boolean, default: false, desc: 'Run pipeline creation simulation, or only do static check.'
end
get ':id/ci/lint' do
authorize! :download_code, user_project
content = user_project.repository.gitlab_ci_yml_for(user_project.commit.id, user_project.ci_config_path_or_default)
result = Gitlab::Ci::Lint
.new(project: user_project, current_user: current_user)
.validate(content, dry_run: params[:dry_run])
present result, with: Entities::Ci::Lint::Result, current_user: current_user
end
end
end
end

View File

@ -4,10 +4,11 @@ module Gitlab
module Ci
class Lint
class Result
attr_reader :jobs, :errors, :warnings
attr_reader :jobs, :merged_yaml, :errors, :warnings
def initialize(jobs:, errors:, warnings:)
def initialize(jobs:, merged_yaml:, errors:, warnings:)
@jobs = jobs
@merged_yaml = merged_yaml
@errors = errors
@warnings = warnings
end
@ -39,6 +40,7 @@ module Gitlab
Result.new(
jobs: dry_run_convert_to_jobs(pipeline.stages),
merged_yaml: pipeline.merged_yaml,
errors: pipeline.error_messages.map(&:content),
warnings: pipeline.warning_messages(limit: ::Gitlab::Ci::Warnings::MAX_LIMIT).map(&:content)
)
@ -54,6 +56,7 @@ module Gitlab
Result.new(
jobs: static_validation_convert_to_jobs(result),
merged_yaml: result.merged_yaml,
errors: result.errors,
warnings: result.warnings.take(::Gitlab::Ci::Warnings::MAX_LIMIT) # rubocop: disable CodeReuse/ActiveRecord
)

View File

@ -28,6 +28,8 @@ module Gitlab
error(result.errors.first, config_error: true)
end
@pipeline.merged_yaml = result.merged_yaml
rescue => ex
Gitlab::ErrorTracking.track_exception(ex,
project_id: project.id,

View File

@ -23,7 +23,7 @@ module Gitlab
@attachment = params.fetch(:attachment, nil)
@job = params.fetch(:job, nil)
@key = sanitize_key_name("#{classname}_#{name}")
@key = hash_key("#{classname}_#{name}")
end
def has_attachment?
@ -42,8 +42,8 @@ module Gitlab
private
def sanitize_key_name(key)
key.gsub(/[^0-9A-Za-z]/, '-')
def hash_key(key)
Digest::SHA256.hexdigest(key)
end
end
end

View File

@ -12,18 +12,24 @@ module Gitlab
def initialize(name = nil)
@name = name
@test_cases = {}
@all_test_cases = []
@total_time = 0.0
@duplicate_cases = []
end
def add_test_case(test_case)
@duplicate_cases << test_case if existing_key?(test_case)
@test_cases[test_case.status] ||= {}
@test_cases[test_case.status][test_case.key] = test_case
@total_time += test_case.execution_time
end
def each_test_case
@test_cases.each do |status, test_cases|
test_cases.values.each do |test_case|
yield test_case
end
end
end
# rubocop: disable CodeReuse/ActiveRecord
def total_count
return 0 if suite_error
@ -86,10 +92,6 @@ module Gitlab
private
def existing_key?(test_case)
@test_cases[test_case.status]&.key?(test_case.key)
end
def sort_by_status
@test_cases = @test_cases.sort_by { |status, _| Gitlab::Ci::Reports::TestCase::STATUS_TYPES.index(status) }.to_h
end

View File

@ -3,6 +3,7 @@
module Gitlab
module Redis
class HLL
BATCH_SIZE = 300
KEY_REGEX = %r{\A(\w|-|:)*\{\w*\}(\w|-|:)*\z}.freeze
KeyFormatError = Class.new(StandardError)
@ -29,17 +30,24 @@ module Gitlab
# 2020-216-{project_action}
# i_{analytics}_dev_ops_score-2020-32
def add(key:, value:, expiry:)
unless KEY_REGEX.match?(key)
raise KeyFormatError.new("Invalid key format. #{key} key should have changeable parts in curly braces. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands")
end
validate_key!(key)
Gitlab::Redis::SharedState.with do |redis|
redis.multi do |multi|
multi.pfadd(key, value)
Array.wrap(value).each_slice(BATCH_SIZE) { |batch| multi.pfadd(key, batch) }
multi.expire(key, expiry)
end
end
end
private
def validate_key!(key)
return if KEY_REGEX.match?(key)
raise KeyFormatError.new("Invalid key format. #{key} key should have changeable parts in curly braces. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands")
end
end
end
end

View File

@ -17,6 +17,13 @@ module Gitlab
ISSUE_REOPENED = 'g_project_management_issue_reopened'
ISSUE_TITLE_CHANGED = 'g_project_management_issue_title_changed'
ISSUE_WEIGHT_CHANGED = 'g_project_management_issue_weight_changed'
ISSUE_CROSS_REFERENCED = 'g_project_management_issue_cross_referenced'
ISSUE_MOVED = 'g_project_management_issue_moved'
ISSUE_RELATED = 'g_project_management_issue_related'
ISSUE_UNRELATED = 'g_project_management_issue_unrelated'
ISSUE_MARKED_AS_DUPLICATE = 'g_project_management_issue_marked_as_duplicate'
ISSUE_LOCKED = 'g_project_management_issue_locked'
ISSUE_UNLOCKED = 'g_project_management_issue_unlocked'
class << self
def track_issue_created_action(author:, time: Time.zone.now)
@ -67,6 +74,34 @@ module Gitlab
track_unique_action(ISSUE_WEIGHT_CHANGED, author, time)
end
def track_issue_cross_referenced_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_CROSS_REFERENCED, author, time)
end
def track_issue_moved_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MOVED, author, time)
end
def track_issue_related_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_RELATED, author, time)
end
def track_issue_unrelated_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_UNRELATED, author, time)
end
def track_issue_marked_as_duplicate_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author, time)
end
def track_issue_locked_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_LOCKED, author, time)
end
def track_issue_unlocked_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_UNLOCKED, author, time)
end
private
def track_unique_action(action, author, time)

View File

@ -185,6 +185,11 @@
redis_slot: incident_management
category: incident_management
aggregation: weekly
# Testing category
- name: i_testing_test_case_parsed
category: testing
redis_slot: testing
aggregation: weekly
# Project Management group
- name: g_project_management_issue_title_changed
category: issues_edit
@ -234,3 +239,31 @@
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_cross_referenced
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_moved
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_related
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_unrelated
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_marked_as_duplicate
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_locked
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_unlocked
category: issues_edit
redis_slot: project_management
aggregation: daily

View File

@ -18305,7 +18305,7 @@ msgstr ""
msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}"
msgstr ""
msgid "PackageRegistry|PyPi"
msgid "PackageRegistry|PyPI"
msgstr ""
msgid "PackageRegistry|Recipe: %{recipe}"
@ -18389,7 +18389,7 @@ msgstr ""
msgid "PackageType|NuGet"
msgstr ""
msgid "PackageType|PyPi"
msgid "PackageType|PyPI"
msgstr ""
msgid "Packages"

View File

@ -137,7 +137,7 @@ function run_task() {
local ruby_cmd="${1}"
local task_runner_pod=$(get_pod "task-runner")
kubectl exec -it --namespace "${namespace}" "${task_runner_pod}" -- gitlab-rails runner "${ruby_cmd}"
kubectl exec --namespace "${namespace}" "${task_runner_pod}" -- gitlab-rails runner "${ruby_cmd}"
}
function disable_sign_ups() {
@ -150,7 +150,7 @@ function disable_sign_ups() {
# Create the root token
local ruby_cmd="token = User.find_by_username('root').personal_access_tokens.create(scopes: [:api], name: 'Token to disable sign-ups'); token.set_token('${REVIEW_APPS_ROOT_TOKEN}'); begin; token.save!; rescue(ActiveRecord::RecordNotUnique); end"
run_task "${ruby_cmd}"
retry "run_task \"${ruby_cmd}\""
# Disable sign-ups
local signup_enabled=$(retry 'curl --silent --show-error --request PUT --header "PRIVATE-TOKEN: ${REVIEW_APPS_ROOT_TOKEN}" "${CI_ENVIRONMENT_URL}/api/v4/application/settings?signup_enabled=false" | jq ".signup_enabled"')

View File

@ -24,19 +24,19 @@ RSpec.describe "Every controller" do
let_it_be(:routes_without_category) do
controller_actions.map do |controller, action|
next if controller.feature_category_for_action(action)
next unless controller.to_s.start_with?('B', 'C', 'D', 'E', 'F', 'Projects::MergeRequestsController')
next unless controller.to_s.start_with?('B', 'C', 'D', 'E', 'F',
'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'Q', 'R',
'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z',
'Projects::MergeRequestsController')
"#{controller}##{action}"
end.compact
end
it "has feature categories" do
routes_without_category.map { |x| x.split('#') }.group_by(&:first).each do |controller, actions|
puts controller
puts actions.map { |x| ":#{x.last}" }.sort.join(', ')
puts ''
end
expect(routes_without_category).to be_empty, "#{routes_without_category} did not have a category"
end

View File

@ -197,16 +197,40 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'with not expiry date' do
let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
it 'exposes needed information' do
get_show_json
context 'when artifacts are unlocked' do
before do
job.pipeline.unlocked!
end
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
expect(json_response['artifact']['download_path']).to match(%r{artifacts/download})
expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse})
expect(json_response['artifact']).not_to have_key('keep_path')
expect(json_response['artifact']).not_to have_key('expired')
expect(json_response['artifact']).not_to have_key('expired_at')
it 'exposes needed information' do
get_show_json
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
expect(json_response['artifact']['download_path']).to match(%r{artifacts/download})
expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse})
expect(json_response['artifact']).not_to have_key('keep_path')
expect(json_response['artifact']).not_to have_key('expired')
expect(json_response['artifact']).not_to have_key('expired_at')
end
end
context 'when artifacts are locked' do
before do
job.pipeline.artifacts_locked!
end
it 'exposes needed information' do
get_show_json
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
expect(json_response['artifact']['download_path']).to match(%r{artifacts/download})
expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse})
expect(json_response['artifact']).not_to have_key('keep_path')
expect(json_response['artifact']).not_to have_key('expired')
expect(json_response['artifact']).not_to have_key('expired_at')
end
end
end

View File

@ -21,6 +21,10 @@ FactoryBot.define do
end
end
factory :debian_package do
package_type { :debian }
end
factory :npm_package do
sequence(:name) { |n| "@#{project.root_namespace.path}/package-#{n}"}
version { '1.0.0' }

Some files were not shown because too many files have changed in this diff Show More