Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-13 21:09:31 +00:00
parent feb61d56e7
commit c19dce027b
140 changed files with 2086 additions and 174 deletions

View File

@ -2,6 +2,16 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 13.5.4 (2020-11-13)
### Fixed (4 changes)
- Fix Vue Labels Select dropdown keyboard scroll. !43874
- Hashed Storage: make migration and rollback resilient to exceptions. !46178
- Fix compliance framework database migration on CE instances. !46761
- Resolve problem when namespace_settings were not created for groups created via admin panel. !46875
## 13.5.3 (2020-11-03)
### Fixed (3 changes)

View File

@ -66,7 +66,7 @@ export default {
<template>
<div class="js-file-title file-title-flex-parent">
<blob-filepath :blob="blob">
<template #filepathPrepend>
<template #filepath-prepend>
<slot name="prepend"></slot>
</template>
</blob-filepath>

View File

@ -26,7 +26,7 @@ export default {
</script>
<template>
<div class="file-header-content d-flex align-items-center lh-100">
<slot name="filepathPrepend"></slot>
<slot name="filepath-prepend"></slot>
<template v-if="blob.path">
<file-icon :file-name="blob.path" :size="18" aria-hidden="true" css-classes="mr-2" />

View File

@ -343,7 +343,7 @@ export default {
>
<span v-else class="js-cluster-application-title">{{ title }}</span>
</strong>
<slot name="installedVia"></slot>
<slot name="installed-via"></slot>
<div>
<slot name="description"></slot>
</div>

View File

@ -549,8 +549,8 @@ export default {
@set="setKnativeDomain"
/>
</template>
<template v-if="cloudRun" #installedVia>
<span data-testid="installedVia">
<template v-if="cloudRun" #installed-via>
<span data-testid="installed-via">
<gl-sprintf
:message="s__('ClusterIntegration|installed via %{linkStart}Cloud Run%{linkEnd}')"
>

View File

@ -0,0 +1,5 @@
import setupToggleButtons from '~/toggle_buttons';
export default () => {
setupToggleButtons(document.querySelector('.js-dependency-proxy-toggle-area'));
};

View File

@ -210,7 +210,7 @@ export default {
:class="{ 'gl-bg-blue-50': isDiscussionActive }"
@error="$emit('update-note-error', $event)"
>
<template v-if="discussion.resolvable" #resolveDiscussion>
<template v-if="discussion.resolvable" #resolve-discussion>
<button
v-gl-tooltip
:class="{ 'is-active': discussion.resolved }"
@ -224,7 +224,7 @@ export default {
<gl-loading-icon v-else inline />
</button>
</template>
<template v-if="discussion.resolved" #resolvedStatus>
<template v-if="discussion.resolved" #resolved-status>
<p class="gl-text-gray-500 gl-font-sm gl-m-0 gl-mt-5" data-testid="resolved-message">
{{ __('Resolved by') }}
<gl-link
@ -277,7 +277,7 @@ export default {
@submit-form="mutate"
@cancel-form="hideForm"
>
<template v-if="discussion.resolvable" #resolveCheckbox>
<template v-if="discussion.resolvable" #resolve-checkbox>
<label data-testid="resolve-checkbox">
<input v-model="shouldChangeResolvedStatus" type="checkbox" />
{{ resolveCheckboxText }}

View File

@ -108,7 +108,7 @@ export default {
</span>
</div>
<div class="gl-display-flex gl-align-items-baseline">
<slot name="resolveDiscussion"></slot>
<slot name="resolve-discussion"></slot>
<button
v-if="isEditButtonVisible"
v-gl-tooltip
@ -127,7 +127,7 @@ export default {
class="note-text js-note-text md"
data-qa-selector="note_content"
></div>
<slot name="resolvedStatus"></slot>
<slot name="resolved-status"></slot>
</template>
<apollo-mutation
v-else

View File

@ -110,7 +110,7 @@ export default {
</textarea>
</template>
</markdown-field>
<slot name="resolveCheckbox"></slot>
<slot name="resolve-checkbox"></slot>
<div class="note-form-actions gl-display-flex gl-justify-content-space-between">
<gl-button
ref="submitButton"

View File

@ -207,6 +207,6 @@ export default {
/>
</gl-collapse>
</template>
<slot name="replyForm"></slot>
<slot name="reply-form"></slot>
</div>
</template>

View File

@ -383,7 +383,7 @@ export default {
@toggleResolvedComments="toggleResolvedComments"
@todoError="onTodoError"
>
<template #replyForm>
<template #reply-form>
<apollo-mutation
v-if="isAnnotating"
#default="{ mutate, loading }"

View File

@ -69,7 +69,7 @@ export default {
<div class="environments-container">
<gl-loading-icon v-if="isLoading" size="md" class="gl-mt-3" label="Loading environments" />
<slot name="emptyState"></slot>
<slot name="empty-state"></slot>
<div v-if="!isLoading && environments.length > 0" class="table-holder">
<environment-table

View File

@ -228,7 +228,7 @@ export default {
:deploy-boards-help-path="deployBoardsHelpPath"
@onChangePage="onChangePage"
>
<template v-if="!isLoading && state.environments.length === 0" #emptyState>
<template v-if="!isLoading && state.environments.length === 0" #empty-state>
<empty-state :help-path="helpPagePath" />
</template>
</container>

View File

@ -7,11 +7,14 @@ import { visitUrl, objectToQuery } from '~/lib/utils/url_utility';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
let eTagPoll;
const hasRedirectInError = e => e?.response?.data?.error?.redirect;
const redirectToUrlInError = e => visitUrl(e.response.data.error.redirect);
const tooManyRequests = e => e.response.status === httpStatusCodes.TOO_MANY_REQUESTS;
const pathWithParams = ({ path, ...params }) => {
const filteredParams = Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== ''),
@ -71,6 +74,14 @@ const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit })
if (hasRedirectInError(e)) {
redirectToUrlInError(e);
} else if (tooManyRequests(e)) {
createFlash(
sprintf(s__('ImportProjects|%{provider} rate limit exceeded. Try again later'), {
provider: capitalizeFirstCharacter(provider),
}),
);
commit(types.RECEIVE_REPOS_ERROR);
} else {
createFlash(
sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {

View File

@ -419,7 +419,7 @@ export default {
</template>
</gl-table>
</template>
<template #emtpy-state>
<template #empty-state>
<gl-empty-state
:title="emptyStateData.title"
:svg-path="emptyListSvgPath"

View File

@ -22,6 +22,7 @@ const httpStatusCodes = {
CONFLICT: 409,
GONE: 410,
UNPROCESSABLE_ENTITY: 422,
TOO_MANY_REQUESTS: 429,
INTERNAL_SERVER_ERROR: 500,
SERVICE_UNAVAILABLE: 503,
};

View File

@ -423,7 +423,7 @@ export default {
:prometheus-alerts-available="prometheusAlertsAvailable"
@timerangezoom="onTimeRangeZoom"
>
<template #topLeft>
<template #top-left>
<gl-button
ref="goBackBtn"
v-gl-tooltip

View File

@ -365,7 +365,7 @@ export default {
<template>
<div v-gl-resize-observer="onResize" class="prometheus-graph">
<div class="d-flex align-items-center">
<slot name="topLeft"></slot>
<slot name="top-left"></slot>
<h5
ref="graphTitle"
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate gl-mr-3"

View File

@ -0,0 +1,17 @@
import $ from 'jquery';
import initDependencyProxy from '~/dependency_proxy';
document.addEventListener('DOMContentLoaded', () => {
initDependencyProxy();
});
document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('form.edit_dependency_proxy_group_setting');
const toggleInput = $('input.js-project-feature-toggle-input');
if (form && toggleInput) {
toggleInput.on('trigger-change', () => {
form.submit();
});
}
});

View File

@ -138,7 +138,7 @@ export default {
href="#related-issues"
aria-hidden="true"
/>
<slot name="headerText">{{ __('Linked issues') }}</slot>
<slot name="header-text">{{ __('Linked issues') }}</slot>
<gl-link
v-if="hasHelpPath"
:href="helpPath"
@ -167,7 +167,7 @@ export default {
/>
</div>
</h3>
<slot name="headerActions"></slot>
<slot name="header-actions"></slot>
</div>
<div
class="linked-issues-card-body bg-gray-light"

View File

@ -134,7 +134,7 @@ export default {
class="mr-widget-section grouped-security-reports mr-report"
@toggleEvent="handleToggleEvent"
>
<template v-if="showViewFullReport" #actionButtons>
<template v-if="showViewFullReport" #action-buttons>
<gl-button
:href="testTabURL"
target="_blank"
@ -145,7 +145,7 @@ export default {
{{ s__('ciReport|View full report') }}
</gl-button>
</template>
<template v-if="hasRecentFailures(summary)" #subHeading>
<template v-if="hasRecentFailures(summary)" #sub-heading>
{{ recentFailuresText(summary) }}
</template>
<template #body>

View File

@ -181,10 +181,10 @@ export default {
<slot :name="slotName"></slot>
<popover v-if="hasPopover" :options="popoverOptions" class="gl-ml-2" />
</div>
<slot name="subHeading"></slot>
<slot name="sub-heading"></slot>
</div>
<slot name="actionButtons"></slot>
<slot name="action-buttons"></slot>
<button
v-if="isCollapsible"

View File

@ -308,6 +308,6 @@ export default {
@input="handlePageChange"
/>
<slot v-if="!showItems" name="emtpy-state"></slot>
<slot v-if="!showItems" name="empty-state"></slot>
</div>
</template>

View File

@ -147,7 +147,7 @@ export default {
class="card upload-dropzone-border upload-dropzone-overlay gl-w-full gl-h-full gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-3 gl-bg-white"
>
<div v-show="!isDragDataValid" class="mw-50 gl-text-center">
<slot name="invalidDragDataSlot">
<slot name="invalid-drag-data-slot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Oh no!') }}
</h3>
@ -159,7 +159,7 @@ export default {
</slot>
</div>
<div v-show="isDragDataValid" class="mw-50 gl-text-center">
<slot name="validDragDataSlot">
<slot name="valid-drag-data-slot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Incoming!') }}
</h3>

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module DependencyProxyAccess
extend ActiveSupport::Concern
included do
before_action :verify_dependency_proxy_enabled!
before_action :authorize_read_dependency_proxy!
end
private
def verify_dependency_proxy_enabled!
render_404 unless group.dependency_proxy_feature_available?
end
def authorize_read_dependency_proxy!
access_denied! unless can?(current_user, :read_dependency_proxy, group)
end
def authorize_admin_dependency_proxy!
access_denied! unless can?(current_user, :admin_dependency_proxy, group)
end
end

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
module Groups
class DependencyProxiesController < Groups::ApplicationController
include DependencyProxyAccess
before_action :authorize_admin_dependency_proxy!, only: :update
before_action :dependency_proxy
feature_category :package_registry
def show
@blobs_count = group.dependency_proxy_blobs.count
@blobs_total_size = group.dependency_proxy_blobs.total_size
end
def update
dependency_proxy.update(dependency_proxy_params)
redirect_to group_dependency_proxy_path(group)
end
private
def dependency_proxy
@dependency_proxy ||=
group.dependency_proxy_setting || group.create_dependency_proxy_setting
end
def dependency_proxy_params
params.require(:dependency_proxy_group_setting).permit(:enabled)
end
end
end

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
class Groups::DependencyProxyForContainersController < Groups::ApplicationController
include DependencyProxyAccess
include SendFileUpload
before_action :ensure_token_granted!
before_action :ensure_feature_enabled!
attr_reader :token
feature_category :package_registry
def manifest
result = DependencyProxy::PullManifestService.new(image, tag, token).execute
if result[:status] == :success
render json: result[:manifest]
else
render status: result[:http_status], json: result[:message]
end
end
def blob
result = DependencyProxy::FindOrCreateBlobService
.new(group, image, token, params[:sha]).execute
if result[:status] == :success
send_upload(result[:blob].file)
else
head result[:http_status]
end
end
private
def image
params[:image]
end
def tag
params[:tag]
end
def dependency_proxy
@dependency_proxy ||=
group.dependency_proxy_setting || group.create_dependency_proxy_setting
end
def ensure_feature_enabled!
render_404 unless dependency_proxy.enabled
end
def ensure_token_granted!
result = DependencyProxy::RequestTokenService.new(image).execute
if result[:status] == :success
@token = result[:token]
else
render status: result[:http_status], json: result[:message]
end
end
end

View File

@ -15,6 +15,7 @@ class Import::GithubController < Import::BaseController
rescue_from OAuthConfigMissingError, with: :missing_oauth_config
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
rescue_from Octokit::TooManyRequests, with: :provider_rate_limit
rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded
def new
if !ci_cd_only? && github_import_configured? && logged_in_with_provider?
@ -114,7 +115,7 @@ class Import::GithubController < Import::BaseController
def client_repos
@client_repos ||= if Feature.enabled?(:remove_legacy_github_client)
filtered(concatenated_repos)
concatenated_repos
else
filtered(client.repos)
end
@ -122,8 +123,15 @@ class Import::GithubController < Import::BaseController
def concatenated_repos
return [] unless client.respond_to?(:each_page)
return client.each_page(:repos).flat_map(&:objects) unless sanitized_filter_param
client.each_page(:repos).flat_map(&:objects)
client.search_repos_by_name(sanitized_filter_param).flat_map(&:objects).flat_map(&:items)
end
def sanitized_filter_param
super
@filter = @filter&.tr(' ', '')&.tr(':', '')
end
def oauth_client
@ -245,6 +253,10 @@ class Import::GithubController < Import::BaseController
def extra_import_params
{}
end
def rate_limit_threshold_exceeded
head :too_many_requests
end
end
Import::GithubController.prepend_if_ee('EE::Import::GithubController')

View File

@ -170,6 +170,10 @@ module GroupsHelper
group_container_registry_nav?
end
def group_dependency_proxy_nav?
@group.dependency_proxy_feature_available?
end
def group_packages_list_nav?
@group.packages_feature_enabled?
end

View File

@ -103,6 +103,10 @@ module Ci
)
end
scope :in_pipelines, ->(pipelines) do
where(pipeline: pipelines)
end
scope :with_existing_job_artifacts, ->(query) do
where('EXISTS (?)', ::Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').merge(query))
end

View File

@ -355,6 +355,14 @@ module Ci
end
end
def self.latest_running_for_ref(ref)
newest_first(ref: ref).running.take
end
def self.latest_failed_for_ref(ref)
newest_first(ref: ref).failed.take
end
# Returns a Hash containing the latest pipeline for every given
# commit.
#

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
module DependencyProxy
def self.table_name_prefix
'dependency_proxy_'
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class DependencyProxy::Blob < ApplicationRecord
include FileStoreMounter
belongs_to :group
validates :group, presence: true
validates :file, presence: true
validates :file_name, presence: true
mount_file_store_uploader DependencyProxy::FileUploader
def self.total_size
sum(:size)
end
def self.find_or_build(file_name)
find_or_initialize_by(file_name: file_name)
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class DependencyProxy::GroupSetting < ApplicationRecord
belongs_to :group
validates :group, presence: true
default_value_for :enabled, true
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
class DependencyProxy::Registry
AUTH_URL = 'https://auth.docker.io'.freeze
LIBRARY_URL = 'https://registry-1.docker.io/v2'.freeze
class << self
def auth_url(image)
"#{AUTH_URL}/token?service=registry.docker.io&scope=repository:#{image_path(image)}:pull"
end
def manifest_url(image, tag)
"#{LIBRARY_URL}/#{image_path(image)}/manifests/#{tag}"
end
def blob_url(image, blob_sha)
"#{LIBRARY_URL}/#{image_path(image)}/blobs/#{blob_sha}"
end
private
def image_path(image)
if image.include?('/')
image
else
"library/#{image}"
end
end
end
end

View File

@ -71,6 +71,9 @@ class Group < Namespace
has_many :group_deploy_tokens
has_many :deploy_tokens, through: :group_deploy_tokens
has_one :dependency_proxy_setting, class_name: 'DependencyProxy::GroupSetting'
has_many :dependency_proxy_blobs, class_name: 'DependencyProxy::Blob'
accepts_nested_attributes_for :variables, allow_destroy: true
validate :visibility_level_allowed_by_projects
@ -203,6 +206,10 @@ class Group < Namespace
::Gitlab.config.packages.enabled
end
def dependency_proxy_feature_available?
::Gitlab.config.dependency_proxy.enabled
end
def notification_email_for(user)
# Finds the closest notification_setting with a `notification_email`
notification_settings = notification_settings_for(user, hierarchy_order: :asc)

View File

@ -46,6 +46,10 @@ class GroupPolicy < BasePolicy
group_projects_for(user: @user, group: @subject, only_owned: false).any? { |p| p.design_management_enabled? }
end
condition(:dependency_proxy_available) do
@subject.dependency_proxy_feature_available?
end
desc "Deploy token with read_package_registry scope"
condition(:read_package_registry_deploy_token) do
@user.is_a?(DeployToken) && @user.groups.include?(@subject) && @user.read_package_registry
@ -193,6 +197,12 @@ class GroupPolicy < BasePolicy
enable :read_group
end
rule { can?(:read_group) & dependency_proxy_available }
.enable :read_dependency_proxy
rule { developer & dependency_proxy_available }
.enable :admin_dependency_proxy
rule { resource_access_token_available & can?(:admin_group) }.policy do
enable :admin_resource_access_tokens
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module DependencyProxy
class BaseService < ::BaseService
private
def registry
DependencyProxy::Registry
end
def auth_headers
{
Authorization: "Bearer #{@token}"
}
end
end
end

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
module DependencyProxy
class DownloadBlobService < DependencyProxy::BaseService
class DownloadError < StandardError
attr_reader :http_status
def initialize(message, http_status)
@http_status = http_status
super(message)
end
end
def initialize(image, blob_sha, token)
@image = image
@blob_sha = blob_sha
@token = token
@temp_file = Tempfile.new
end
def execute
File.open(@temp_file.path, "wb") do |file|
Gitlab::HTTP.get(blob_url, headers: auth_headers, stream_body: true) do |fragment|
if [301, 302, 307].include?(fragment.code)
# do nothing
elsif fragment.code == 200
file.write(fragment)
else
raise DownloadError.new('Non-success response code on downloading blob fragment', fragment.code)
end
end
end
success(file: @temp_file)
rescue DownloadError => exception
error(exception.message, exception.http_status)
rescue Timeout::Error => exception
error(exception.message, 599)
end
private
def blob_url
registry.blob_url(@image, @blob_sha)
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module DependencyProxy
class FindOrCreateBlobService < DependencyProxy::BaseService
def initialize(group, image, token, blob_sha)
@group = group
@image = image
@token = token
@blob_sha = blob_sha
end
def execute
file_name = @blob_sha.sub('sha256:', '') + '.gz'
blob = @group.dependency_proxy_blobs.find_or_build(file_name)
unless blob.persisted?
result = DependencyProxy::DownloadBlobService
.new(@image, @blob_sha, @token).execute
if result[:status] == :error
log_failure(result)
return error('Failed to download the blob', result[:http_status])
end
blob.file = result[:file]
blob.size = result[:file].size
blob.save!
end
success(blob: blob)
end
private
def log_failure(result)
log_error(
"Dependency proxy: Failed to download the blob." \
"Blob sha: #{@blob_sha}." \
"Error message: #{result[:message][0, 100]}" \
"HTTP status: #{result[:http_status]}"
)
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module DependencyProxy
class PullManifestService < DependencyProxy::BaseService
def initialize(image, tag, token)
@image = image
@tag = tag
@token = token
end
def execute
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers)
if response.success?
success(manifest: response.body)
else
error(response.body, response.code)
end
rescue Timeout::Error => exception
error(exception.message, 599)
end
private
def manifest_url
registry.manifest_url(@image, @tag)
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module DependencyProxy
class RequestTokenService < DependencyProxy::BaseService
def initialize(image)
@image = image
end
def execute
response = Gitlab::HTTP.get(auth_url)
if response.success?
success(token: Gitlab::Json.parse(response.body)['token'])
else
error('Expected 200 response code for an access token', response.code)
end
rescue Timeout::Error => exception
error(exception.message, 599)
rescue JSON::ParserError
error('Failed to parse a response body for an access token', 500)
end
private
def auth_url
registry.auth_url(@image)
end
end
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class DependencyProxy::FileUploader < GitlabUploader
include ObjectStorage::Concern
storage_options Gitlab.config.dependency_proxy
alias_method :upload, :model
def filename
model.file_name
end
def store_dir
dynamic_segment
end
private
def dynamic_segment
Gitlab::HashedPath.new('dependency_proxy', model.group_id, 'files', model.id, root_hash: model.group_id)
end
end

View File

@ -0,0 +1,12 @@
- proxy_url = "#{group_url(@group)}/dependency_proxy/containers"
%h5.prepend-top-20= _('Dependency proxy URL')
.row
.col-lg-8.col-md-12.input-group
= text_field_tag :url, "#{proxy_url}", class: 'js-dependency-proxy-url form-control', readonly: true
= clipboard_button(text: "#{proxy_url}", title: _("Copy %{proxy_url}") % { proxy_url: proxy_url })
.row
.col-12.help-block.gl-mt-3
= _('Contains %{count} blobs of images (%{size})') % { count: @blobs_count, size: number_to_human_size(@blobs_total_size) }

View File

@ -0,0 +1,28 @@
- page_title _("Dependency Proxy")
.settings-header
%h4= _('Dependency proxy')
%p
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('user/packages/dependency_proxy/index') }
= _('Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
- if @group.public?
- if can?(current_user, :admin_dependency_proxy, @group)
= form_for(@dependency_proxy, method: :put, url: group_dependency_proxy_path(@group)) do |f|
.form-group
%h5.prepend-top-20= _('Enable proxy')
.js-dependency-proxy-toggle-area
= render "shared/buttons/project_feature_toggle", is_checked: @dependency_proxy.enabled?, label: s_("DependencyProxy|Toggle Dependency Proxy") do
= f.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
- if @dependency_proxy.enabled
= render 'groups/dependency_proxies/url'
- else
- if @dependency_proxy.enabled
= render 'groups/dependency_proxies/url'
- else
.gl-alert.gl-alert-info
= sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
= _('Dependency proxy feature is limited to public groups for now.')

View File

@ -1,7 +1,7 @@
- packages_link = group_packages_list_nav? ? group_packages_path(@group) : group_container_registries_path(@group)
- if group_packages_nav?
= nav_link(controller: ['groups/packages', 'groups/registry/repositories']) do
= nav_link(controller: ['groups/packages', 'groups/registry/repositories', 'groups/dependency_proxies']) do
= link_to packages_link, title: _('Packages') do
.nav-icon-container
= sprite_icon('package')
@ -21,3 +21,7 @@
= nav_link(controller: 'groups/registry/repositories') do
= link_to group_container_registries_path(@group), title: _('Container Registry') do
%span= _('Container Registry')
- if group_dependency_proxy_nav?
= nav_link(controller: 'groups/dependency_proxies') do
= link_to group_dependency_proxy_path(@group), title: _('Dependency Proxy') do
%span= _('Dependency Proxy')

View File

@ -451,6 +451,14 @@
:weight: 1
:idempotent: true
:tags: []
- :name: dependency_proxy:purge_dependency_proxy_cache
:feature_category: :dependency_proxy
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: deployment:deployments_drop_older_deployments
:feature_category: :continuous_delivery
:has_external_dependencies:

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class PurgeDependencyProxyCacheWorker
include ApplicationWorker
include Gitlab::Allowable
idempotent!
queue_namespace :dependency_proxy
feature_category :dependency_proxy
def perform(current_user_id, group_id)
@current_user = User.find_by_id(current_user_id)
@group = Group.find_by_id(group_id)
return unless valid?
@group.dependency_proxy_blobs.destroy_all # rubocop:disable Cop/DestroyAll
end
private
def valid?
return unless @group
can?(@current_user, :admin_group, @group) && @group.dependency_proxy_feature_available?
end
end

View File

@ -1,5 +0,0 @@
---
title: Fix Vue Labels Select dropdown keyboard scroll
merge_request: 43874
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: "Hashed Storage: make migration and rollback resilient to exceptions"
merge_request: 46178
author:
type: fixed

View File

@ -1,6 +0,0 @@
---
title: Resolve problem when namespace_settings were not created for groups created
via admin panel
merge_request: 46875
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Dependency proxy feature is moved to GitLab core
merge_request: 47471
author:
type: changed

View File

@ -1,5 +0,0 @@
---
title: Fix compliance framework database migration on CE instances
merge_request: 46761
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Filter GitHub projects to import using GitHub Search API
merge_request: 47002
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Expand scope of coverage badge query to all successful builds
merge_request: 45321
author:
type: changed

View File

@ -362,18 +362,16 @@ Settings.packages['object_store'] = ObjectStoreSettings.legacy_parse(Settings.p
#
# Dependency Proxy
#
Gitlab.ee do
Settings['dependency_proxy'] ||= Settingslogic.new({})
Settings.dependency_proxy['enabled'] = true if Settings.dependency_proxy['enabled'].nil?
Settings.dependency_proxy['storage_path'] = Settings.absolute(Settings.dependency_proxy['storage_path'] || File.join(Settings.shared['path'], "dependency_proxy"))
Settings.dependency_proxy['object_store'] = ObjectStoreSettings.legacy_parse(Settings.dependency_proxy['object_store'])
Settings['dependency_proxy'] ||= Settingslogic.new({})
Settings.dependency_proxy['enabled'] = true if Settings.dependency_proxy['enabled'].nil?
Settings.dependency_proxy['storage_path'] = Settings.absolute(Settings.dependency_proxy['storage_path'] || File.join(Settings.shared['path'], "dependency_proxy"))
Settings.dependency_proxy['object_store'] = ObjectStoreSettings.legacy_parse(Settings.dependency_proxy['object_store'])
# For first iteration dependency proxy uses Rails server to download blobs.
# To ensure acceptable performance we only allow feature to be used with
# multithreaded web-server Puma. This will be removed once download logic is moved
# to GitLab workhorse
Settings.dependency_proxy['enabled'] = false unless Gitlab::Runtime.puma?
end
# For first iteration dependency proxy uses Rails server to download blobs.
# To ensure acceptable performance we only allow feature to be used with
# multithreaded web-server Puma. This will be removed once download logic is moved
# to GitLab workhorse
Settings.dependency_proxy['enabled'] = false unless Gitlab::Runtime.puma?
#
# Terraform state

View File

@ -107,6 +107,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
resources :container_registries, only: [:index, :show], controller: 'registry/repositories'
resource :dependency_proxy, only: [:show, :update]
end
scope(path: '*id',
@ -119,3 +120,14 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
delete '/', action: :destroy
end
end
# Dependency proxy for containers
# Because docker adds v2 prefix to URI this need to be outside of usual group routes
scope format: false do
get 'v2', to: proc { [200, {}, ['']] } # rubocop:disable Cop/PutGroupRoutesUnderScope
constraints image: Gitlab::PathRegex.container_image_regex, sha: Gitlab::PathRegex.container_image_blob_sha_regex do
get 'v2/*group_id/dependency_proxy/containers/*image/manifests/*tag' => 'groups/dependency_proxy_for_containers#manifest' # rubocop:todo Cop/PutGroupRoutesUnderScope
get 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha' => 'groups/dependency_proxy_for_containers#blob' # rubocop:todo Cop/PutGroupRoutesUnderScope
end
end

View File

@ -232,6 +232,7 @@ Laravel
LDAP
ldapsearch
Leiningen
Lefthook
Libravatar
liveness
Lograge

View File

@ -269,7 +269,7 @@ The following documentation relates to the DevOps **Package** stage:
| Package topics | Description |
|:----------------------------------------------------------------|:-------------------------------------------------------|
| [Container Registry](user/packages/container_registry/index.md) | The GitLab Container Registry enables every project in GitLab to have its own space to store [Docker](https://www.docker.com/) images. |
| [Dependency Proxy](user/packages/dependency_proxy/index.md) **(PREMIUM)** | The GitLab Dependency Proxy sets up a local proxy for frequently used upstream images/packages. |
| [Dependency Proxy](user/packages/dependency_proxy/index.md) | The GitLab Dependency Proxy sets up a local proxy for frequently used upstream images/packages. |
| [Package Registry](user/packages/package_registry/index.md) | Use GitLab as a private or public registry for a variety of common package managers, including [NPM](user/packages/npm_registry/index.md), [Maven](user/packages/maven_repository/index.md), [PyPI](user/packages/pypi_repository/index.md), and others. You can also store generic files. |
<div align="right">

View File

@ -180,6 +180,12 @@ the steps bellow.
Feature.enable(:repository_push_audit_event)
```
## Retention policy
On GitLab.com, Audit Event records become subject to deletion after 400 days, or when your license is downgraded to a tier that does not include access to Audit Events. Data that is subject to deletion will be deleted at GitLab's discretion, possibly without additional notice.
If you require a longer retention period, you should independently archive your Audit Event data, which you can retrieve through the [Audit Events API](../api/audit_events.md).
## Export to CSV **(PREMIUM ONLY)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.

View File

@ -143,7 +143,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Container Registry](packages/container_registry.md): Configure Container Registry with GitLab.
- [Package Registry](packages/index.md): Enable GitLab to act as an NPM Registry and a Maven Repository.
- [Dependency Proxy](packages/dependency_proxy.md): Configure the Dependency Proxy, a local proxy for frequently used upstream images/packages. **(PREMIUM ONLY)**
- [Dependency Proxy](packages/dependency_proxy.md): Configure the Dependency Proxy, a local proxy for frequently used upstream images/packages.
### Repository settings

View File

@ -4,9 +4,10 @@ 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 Dependency Proxy administration **(PREMIUM ONLY)**
# GitLab Dependency Proxy administration
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
GitLab can be utilized as a dependency proxy for a variety of common package managers.

View File

@ -2007,7 +2007,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -2007,7 +2007,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -866,7 +866,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -1742,7 +1742,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -2007,7 +2007,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -1741,7 +1741,7 @@ on what features you intend to use:
| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) **(PREMIUM ONLY)** | Yes |
| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
| [Pseudonymizer](../pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |

View File

@ -1,6 +1,6 @@
---
stage: Plan
group: Product Planning
group: Certify
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
---

View File

@ -4,11 +4,12 @@ group: unassigned
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
---
# Dependency Proxy API **(PREMIUM)**
# Dependency Proxy API
## Purge the dependency proxy for a group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
Deletes the cached blobs for a group. This endpoint requires group admin access.

View File

@ -14967,8 +14967,7 @@ type Project {
): ClusterAgentConnection
"""
Code coverages summary associated with the project. Available only when
feature flag `group_coverage_data_report` is enabled
Code coverage summary associated with the project
"""
codeCoverageSummary: CodeCoverageSummary

View File

@ -44149,7 +44149,7 @@
},
{
"name": "codeCoverageSummary",
"description": "Code coverages summary associated with the project. Available only when feature flag `group_coverage_data_report` is enabled",
"description": "Code coverage summary associated with the project",
"args": [
],

View File

@ -2257,7 +2257,7 @@ Autogenerated return type of PipelineRetry.
| `boards` | BoardConnection | Boards of the project |
| `clusterAgent` | ClusterAgent | Find a single cluster agent by name |
| `clusterAgents` | ClusterAgentConnection | Cluster agents associated with the project |
| `codeCoverageSummary` | CodeCoverageSummary | Code coverages summary associated with the project. Available only when feature flag `group_coverage_data_report` is enabled |
| `codeCoverageSummary` | CodeCoverageSummary | Code coverage summary associated with the project |
| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks associated with the project |
| `containerExpirationPolicy` | ContainerExpirationPolicy | The container expiration policy of the project |
| `containerRegistryEnabled` | Boolean | Indicates if the project stores Docker container images in a container registry |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -48,14 +48,16 @@ for changed files. This saves you time as you don't have to wait for the same er
by CI/CD.
Lefthook relies on a pre-push hook to prevent commits that violate its ruleset.
If you wish to override this behavior, pass the environment variable `LEFTHOOK=0`.
That is, `LEFTHOOK=0 git push`.
To override this behavior, pass the environment variable `LEFTHOOK=0`. That is,
`LEFTHOOK=0 git push`.
You can also:
- Define [local configuration](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#local-config).
- Skip [checks per tag on the fly](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#skip-some-tags-on-the-fly), e.g. `LEFTHOOK_EXCLUDE=frontend git push origin`.
- Run [hooks manually](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#run-githook-group-directly), e.g. `lefthook run pre-push`.
- Skip [checks per tag on the fly](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#skip-some-tags-on-the-fly).
For example, `LEFTHOOK_EXCLUDE=frontend git push origin`.
- Run [hooks manually](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#run-githook-group-directly).
For example, `lefthook run pre-push`.
## Ruby, Rails, RSpec

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -181,7 +181,9 @@ SAST and Dependency Scanning scanners must scan the files in the project directo
In order to be consistent with the official Container Scanning for GitLab,
scanners must scan the Docker image whose name and tag are given by
`CI_APPLICATION_REPOSITORY` and `CI_APPLICATION_TAG`, respectively.
`CI_APPLICATION_REPOSITORY` and `CI_APPLICATION_TAG`, respectively. If the `DOCKER_IMAGE`
variable is provided, then the `CI_APPLICATION_REPOSITORY` and `CI_APPLICATION_TAG` variables
are ignored, and the image specified in the `DOCKER_IMAGE` variable is scanned instead.
If not provided, `CI_APPLICATION_REPOSITORY` should default to
`$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG`, which is a combination of predefined CI variables.

View File

@ -325,39 +325,69 @@ In general, we use an `expect` statement to check that something _is_ as we expe
```ruby
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_job("a_job")
expect(pipeline).to have_job('a_job')
end
```
### Ensure `expect` checks for negation efficiently
### Create negatable matchers to speed `expect` checks
However, sometimes we want to check that something is _not_ as we _don't_ want it to be. In other
words, we want to make sure something is absent. In such a case we should use an appropriate
predicate method that returns quickly, rather than waiting for a state that won't appear.
It's most efficient to use a predicate method that returns immediately when there is no job, or waits
until it disappears:
words, we want to make sure something is absent. For unit tests and feature specs,
we commonly use `not_to`
because RSpec's built-in matchers are negatable, as are Capybara's, which means the following two statements are
equivalent.
```ruby
# Good
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_no_job("a_job")
except(page).not_to have_text('hidden')
except(page).to have_no_text('hidden')
```
Unfortunately, that's not automatically the case for the predicate methods that we add to our
[page objects](page_objects.md). We need to [create our own negatable matchers](https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/custom-matchers/define-a-custom-matcher#matcher-with-separate-logic-for-expect().to-and-expect().not-to).
The initial example uses the `have_job` matcher which is derived from the [`has_job?` predicate
method of the `Page::Project::Pipeline::Show` page object](https://gitlab.com/gitlab-org/gitlab/-/blob/87864b3047c23b4308f59c27a3757045944af447/qa/qa/page/project/pipeline/show.rb#L53).
To create a negatable matcher, we use `has_no_job?` for the negative case:
```ruby
RSpec::Matchers.define :have_job do |job_name|
match do |page_object|
page_object.has_job?(job_name)
end
match_when_negated do |page_object|
page_object.has_no_job?(job_name)
end
end
```
### Problematic alternatives
And then the two `expect` statements in the following example are equivalent:
Alternatively, if we want to check that a job doesn't exist it might be tempting to use `not_to`:
```ruby
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).not_to have_job('a_job')
expect(pipeline).to have_no_job('a_job')
end
```
[See this merge request for a real example of adding a custom matcher](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46302).
NOTE: **Note:**
We need to create custom negatable matchers only for the predicate methods we've added to the test framework, and only if we're using `not_to`. If we use `to have_no_*` a negatable matcher is not necessary.
### Why we need negatable matchers
Consider the following code, but assume that we _don't_ have a custom negatable matcher for `have_job`.
```ruby
# Bad
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).not_to have_job("a_job")
expect(pipeline).not_to have_job('a_job')
end
```
For this statement to pass, `have_job("a_job")` has to return `false` so that `not_to` can negate it.
The problem is that `have_job("a_job")` waits up to ten seconds for `"a job"` to appear before
For this statement to pass, `have_job('a_job')` has to return `false` so that `not_to` can negate it.
The problem is that `have_job('a_job')` waits up to ten seconds for `'a job'` to appear before
returning `false`. Under the expected condition this test will take ten seconds longer than it needs to.
Instead, we could force no wait:
@ -365,9 +395,13 @@ Instead, we could force no wait:
```ruby
# Not as bad but potentially flaky
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).not_to have_job("a_job", wait: 0)
expect(pipeline).not_to have_job('a_job', wait: 0)
end
```
The problem is that if `"a_job"` is present and we're waiting for it to disappear, this statement
will fail.
The problem is that if `'a_job'` is present and we're waiting for it to disappear, this statement will fail.
Neither problem is present if we create a custom negatable matcher because the `has_no_job?` predicate method
would be used, which would wait only as long as necessary for the job to disappear.
Lastly, negatable matchers are preferred over using matchers of the form `have_no_*` because it's a common and familiar practice to negate matchers using `not_to`. If we facilitate that practice by adding negatable matchers, we make it easier for subsequent test authors to write efficient tests.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -186,6 +186,7 @@ scanning by using the following environment variables:
| `CI_APPLICATION_REPOSITORY` | `$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG` | Docker repository URL for the image to be scanned. |
| `CI_APPLICATION_TAG` | `$CI_COMMIT_SHA` | Docker repository tag for the image to be scanned. |
| `CS_MAJOR_VERSION` | `3` | The major version of the Docker image tag. |
| `DOCKER_IMAGE` | `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` | The Docker image to be scanned. If set, this variable overrides the `$CI_APPLICATION_REPOSITORY` and `$CI_APPLICATION_TAG` variables. |
| `DOCKER_INSECURE` | `"false"` | Allow [Klar](https://github.com/optiopay/klar) to access secure Docker registries using HTTPS with bad (or self-signed) SSL certificates. |
| `DOCKER_PASSWORD` | `$CI_REGISTRY_PASSWORD` | Password for accessing a Docker registry requiring authentication. |
| `DOCKER_USER` | `$CI_REGISTRY_USER` | Username for accessing a Docker registry requiring authentication. |

View File

@ -412,11 +412,56 @@ spec:
## Example projects
The following example projects can help you get started with the Kubernetes Agent.
### Simple NGINX deployment
This basic GitOps example deploys NGINX:
- [Configuration repository](https://gitlab.com/gitlab-org/configure/examples/kubernetes-agent)
- [Manifest repository](https://gitlab.com/gitlab-org/configure/examples/gitops-project)
- [Install GitLab Runner](https://gitlab.com/gitlab-examples/install-runner-via-k8s-agent)
### Deploying GitLab Runner with the Agent
These instructions assume that the Agent is already set up as described in the
[Get started with GitOps](#get-started-with-gitops-and-the-gitlab-agent):
1. Check the possible
[Runner chart YAML values](https://gitlab.com/gitlab-org/charts/gitlab-runner/blob/master/values.yaml)
on the Runner chart documentation, and create a `runner-chart-values.yaml` file
with the configuration that fits your needs, such as:
```yaml
## The GitLab Server URL (with protocol) that want to register the runner against
## ref: https://docs.gitlab.com/runner/commands/README.html#gitlab-runner-register
##
gitlabUrl: https://gitlab.my.domain.com/
## The Registration Token for adding new Runners to the GitLab Server. This must
## be retrieved from your GitLab Instance.
## ref: https://docs.gitlab.com/ce/ci/runners/README.html
##
runnerRegistrationToken: "XXXXXXYYYYYYZZZZZZ"
## For RBAC support:
rbac:
create: true
## Run all containers with the privileged flag enabled
## This will allow the docker:dind image to run if you need to run Docker
## commands. Please read the docs before turning this on:
## ref: https://docs.gitlab.com/runner/executors/kubernetes.html#using-dockerdind
runners:
privileged: true
```
1. Create a single manifest file to install the Runner chart with your cluster agent:
```shell
helm template --namespace gitlab gitlab-runner -f runner-chart-values.yaml gitlab/gitlab-runner > manifest.yaml
```
1. Push your `manifest.yaml` to your manifest repository.
## Troubleshooting
@ -479,7 +524,7 @@ but KAS on the server side is not available via `wss`. To fix it, make sure the
same schemes are configured on both sides.
It's not possible to set the `grpc` scheme due to the issue
[It is not possible to configure KAS to work with grpc without directly editing GitLab KAS deployment](https://gitlab.com/gitlab-org/gitlab/-/issues/276888). To use `grpc` while the
[It is not possible to configure KAS to work with `grpc` without directly editing GitLab KAS deployment](https://gitlab.com/gitlab-org/gitlab/-/issues/276888). To use `grpc` while the
issue is in progress, directly edit the deployment with the
`kubectl edit deployment gitlab-kas` command, and change `--listen-websocket=true` to `--listen-websocket=false`. After running that command, you should be able to use
`grpc://gitlab-kas.<YOUR-NAMESPACE>:5005`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -17,7 +17,7 @@ for merging into production.
To access the Compliance Dashboard for a group, navigate to **{shield}** **Security & Compliance > Compliance** on the group's menu.
![Compliance Dashboard](img/compliance_dashboard_v13_3_1.png)
![Compliance Dashboard](img/compliance_dashboard_v13_6.png)
NOTE: **Note:**
The Compliance Dashboard shows only the latest MR on each project.
@ -63,7 +63,9 @@ This column has four states:
If you do not see the success icon in your Compliance dashboard; please review the above criteria for the Merge Requests
project to make sure it complies with the separation of duties described above.
## Chain of Custody report
## Chain of Custody report **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213364) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3.
The Chain of Custody report allows customers to export a list of merge commits within the group.
The data provides a comprehensive view with respect to merge commits. It includes the merge commit SHA,
@ -72,6 +74,13 @@ Depending on the merge strategy, the merge commit SHA can either be a merge comm
To download the Chain of Custody report, navigate to **{shield}** **Security & Compliance > Compliance** on the group's menu and click **List of all merge commits**
### Commit-specific Chain of Custody Report **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267629) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.6.
You can generate a commit-specific Chain of Custody report for a given commit SHA. To do so, select
the dropdown next to the **List of all merge commits** button at the top of the Compliance Dashboard.
NOTE: **Note:**
The Chain of Custody report download is a CSV file, with a maximum size of 15 MB.
The remaining records are truncated when this limit is reached.

View File

@ -798,7 +798,7 @@ With [GitLab Issue Analytics](issues_analytics/index.md), you can see a bar char
With [GitLab Repositories Analytics](repositories_analytics/index.md), you can download a CSV of the latest coverage data for all the projects in your group.
## Dependency Proxy **(PREMIUM)**
## Dependency Proxy
Use GitLab as a [dependency proxy](../packages/dependency_proxy/index.md) for upstream Docker images.

View File

@ -15,22 +15,12 @@ This feature might not be available to you. Check the **version history** note a
## Latest project test coverage list
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267624) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.6.
> - It's [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
> - It's disabled on GitLab.com
> - It can be enabled or disabled per-group.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-latest-project-test-coverage).
To see the latest code coverage for each project in your group:
1. Go to **Analytics > Repositories** in the group (not from a project).
1. In the **Latest test coverage results** section, use the **Select projects** dropdown to choose the projects you want to check.
### Enable or disable latest project test coverage
This feature comes with the `:group_coverage_data_report` feature flag disabled by default. It is disabled on GitLab.com.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) can enable it for your instance.
The group test coverage table can be enabled or disabled per-group.
## Download historic test coverage data
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215104) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.

View File

@ -4,9 +4,10 @@ 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
---
# Dependency Proxy **(PREMIUM)**
# Dependency Proxy
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
The GitLab Dependency Proxy is a local proxy you can use for your frequently-accessed
upstream images.

View File

@ -256,7 +256,7 @@ group.
| Share (invite) groups with groups | | | | | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
| Create/edit/delete iterations | | | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy | | | ✓ | ✓ | ✓ |
| Create and edit group wiki pages **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -188,17 +188,22 @@ To set your current status:
1. Set the desired emoji and/or status message.
1. Click **Set status**. Alternatively, you can click **Remove status** to remove your user status entirely.
![Busy status indicator](img/busy_status_indicator_v13_6.png)
or
1. Click your avatar.
1. Select **Profile**.
1. Click **Edit profile** (pencil icon).
1. Enter your status message in the **Your status** text field.
1. Alternatively, select the **Busy** checkbox ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259649) in GitLab 13.6}.
1. Click **Add status emoji** (smiley face), and select the desired emoji.
1. Click **Update profile settings**.
You can also set your current status [using the API](../../api/users.md#user-status).
If you previously selected the "Busy" checkbox, remember to deselect it when you become available again.
## Commit email
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/21598) in GitLab 11.4.

View File

@ -1,7 +1,7 @@
---
type: reference, howto
stage: Plan
group: Product Planning
group: Certify
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
---

View File

@ -1,6 +1,6 @@
---
stage: Plan
group: Product Planning
group: Certify
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
---

View File

@ -162,6 +162,7 @@ module API
mount ::API::CommitStatuses
mount ::API::ContainerRegistryEvent
mount ::API::ContainerRepositories
mount ::API::DependencyProxy
mount ::API::DeployKeys
mount ::API::DeployTokens
mount ::API::Deployments

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module API
class DependencyProxy < ::API::Base
helpers ::API::Helpers::PackagesHelpers
feature_category :dependency_proxy
helpers do
def obtain_new_purge_cache_lease
Gitlab::ExclusiveLease
.new("dependency_proxy:delete_group_blobs:#{user_group.id}",
timeout: 1.hour)
.try_obtain
end
end
before do
authorize! :admin_group, user_group
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Deletes all dependency_proxy_blobs for a group' do
detail 'This feature was introduced in GitLab 12.10'
end
delete ':id/dependency_proxy/cache' do
not_found! unless user_group.dependency_proxy_feature_available?
message = 'This request has already been made. It may take some time to purge the cache. You can run this at most once an hour for a given group'
render_api_error!(message, 409) unless obtain_new_purge_cache_lease
# rubocop:disable CodeReuse/Worker
PurgeDependencyProxyCacheWorker.perform_async(current_user.id, user_group.id)
# rubocop:enable CodeReuse/Worker
end
end
end
end

View File

@ -40,23 +40,35 @@ module Gitlab
private
def pipeline
@pipeline ||= @project.ci_pipelines.latest_successful_for_ref(@ref)
def successful_pipeline
@successful_pipeline ||= @project.ci_pipelines.latest_successful_for_ref(@ref)
end
def failed_pipeline
@failed_pipeline ||= @project.ci_pipelines.latest_failed_for_ref(@ref)
end
def running_pipeline
@running_pipeline ||= @project.ci_pipelines.latest_running_for_ref(@ref)
end
# rubocop: disable CodeReuse/ActiveRecord
def raw_coverage
return unless pipeline
latest =
if @job.present?
builds = ::Ci::Build
.in_pipelines([successful_pipeline, running_pipeline, failed_pipeline])
.latest
.success
.for_ref(@ref)
.by_name(@job)
if @job.blank?
pipeline.coverage
else
pipeline.builds
.find_by(name: @job)
.try(:coverage)
end
builds.max_by(&:created_at)
else
successful_pipeline
end
latest&.coverage
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View File

@ -18,6 +18,8 @@ module Gitlab
attr_reader :octokit
SEARCH_MAX_REQUESTS_PER_MINUTE = 30
# A single page of data and the corresponding page number.
Page = Struct.new(:objects, :number)
@ -28,6 +30,7 @@ module Gitlab
# rate limit at once. The threshold is put in place to not hit the limit
# in most cases.
RATE_LIMIT_THRESHOLD = 50
SEARCH_RATE_LIMIT_THRESHOLD = 3
# token - The GitHub API token to use.
#
@ -152,8 +155,26 @@ module Gitlab
end
end
def search_repos_by_name(name)
each_page(:search_repositories, search_query(str: name, type: :name))
end
def search_query(str:, type:, include_collaborations: true, include_orgs: true)
query = "#{str} in:#{type} is:public,private user:#{octokit.user.login}"
query = [query, collaborations_subquery].join(' ') if include_collaborations
query = [query, organizations_subquery].join(' ') if include_orgs
query
end
# Returns `true` if we're still allowed to perform API calls.
# Search API has rate limit of 30, use lowered threshold when search is used.
def requests_remaining?
if requests_limit == SEARCH_MAX_REQUESTS_PER_MINUTE
return remaining_requests > SEARCH_RATE_LIMIT_THRESHOLD
end
remaining_requests > RATE_LIMIT_THRESHOLD
end
@ -161,6 +182,10 @@ module Gitlab
octokit.rate_limit.remaining
end
def requests_limit
octokit.rate_limit.limit
end
def raise_or_wait_for_rate_limit
rate_limit_counter.increment
@ -221,6 +246,20 @@ module Gitlab
'The number of GitHub API calls performed when importing projects'
)
end
private
def collaborations_subquery
each_object(:repos, nil, { affiliation: 'collaborator' })
.map { |repo| "repo:#{repo.full_name}" }
.join(' ')
end
def organizations_subquery
each_object(:organizations)
.map { |org| "org:#{org.login}" }
.join(' ')
end
end
end
end

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