Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-22 18:10:13 +00:00
parent 5f5f492fe2
commit 0a5e00b691
90 changed files with 776 additions and 912 deletions

View File

@ -684,8 +684,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
- spec/models/terraform/state_spec.rb
- spec/models/u2f_registration_spec.rb
- spec/models/user_spec.rb
- spec/models/wiki_page/meta_spec.rb
- spec/models/wiki_page_spec.rb
- spec/requests/api/api_spec.rb
- spec/requests/api/award_emoji_spec.rb
- spec/requests/api/branches_spec.rb

View File

@ -1 +1 @@
c2c12e3152bfc6c899b5ad08974659ac7b450232
ffbce774bce90b5a65f5b235afe492a7266aa82f

View File

@ -0,0 +1,60 @@
<script>
import { GlEmptyState } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
export const i18n = {
noTestsButton: s__('TestReports|Learn more about pipeline test reports'),
noTestsDescription: s__('TestReports|No test cases were found in the test report.'),
noTestsTitle: s__('TestReports|There are no tests to display'),
noReportsButton: s__('TestReports|Learn how to upload pipeline test reports'),
noReportsDescription: s__(
'TestReports|You can configure your job to use unit test reports, and GitLab displays a report here and in the related merge request.',
),
noReportsTitle: s__('TestReports|There are no test reports for this pipeline'),
};
export default {
i18n,
components: {
GlEmptyState,
},
inject: {
emptyStateImagePath: {
default: '',
},
hasTestReport: {
default: false,
},
},
computed: {
emptyStateText() {
if (this.hasTestReport) {
return {
button: this.$options.i18n.noTestsButton,
description: this.$options.i18n.noTestsDescription,
title: this.$options.i18n.noTestsTitle,
};
}
return {
button: this.$options.i18n.noReportsButton,
description: this.$options.i18n.noReportsDescription,
title: this.$options.i18n.noReportsTitle,
};
},
testReportDocPath() {
return helpPagePath('ci/unit_test_reports');
},
},
};
</script>
<template>
<gl-empty-state
:title="emptyStateText.title"
:description="emptyStateText.description"
:svg-path="emptyStateImagePath"
:primary-button-link="testReportDocPath"
:primary-button-text="emptyStateText.button"
/>
</template>

View File

@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
import TestSummaryTable from './test_summary_table.vue';
@ -8,11 +9,17 @@ import TestSummaryTable from './test_summary_table.vue';
export default {
name: 'TestReports',
components: {
EmptyState,
GlLoadingIcon,
TestSuiteTable,
TestSummary,
TestSummaryTable,
},
inject: {
hasTestReport: {
default: false,
},
},
computed: {
...mapState(['isLoading', 'selectedSuiteIndex', 'testReports']),
...mapGetters(['getSelectedSuite']),
@ -25,7 +32,9 @@ export default {
},
},
created() {
this.fetchSummary();
if (this.hasTestReport) {
this.fetchSummary();
}
},
methods: {
...mapActions([
@ -83,11 +92,5 @@ export default {
</transition>
</div>
<div v-else>
<div class="row gl-mt-3">
<div class="col-12">
<p data-testid="no-tests-to-show">{{ s__('TestReports|There are no tests to show.') }}</p>
</div>
</div>
</div>
<empty-state v-else />
</template>

View File

@ -1,5 +1,6 @@
import Vue from 'vue';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { parseBoolean } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import Translate from '~/vue_shared/translate';
import PipelineGraphLegacy from './components/graph/graph_component_legacy.vue';
@ -63,7 +64,8 @@ const createLegacyPipelinesDetailApp = (mediator) => {
const createTestDetails = () => {
const el = document.querySelector(SELECTORS.PIPELINE_TESTS);
const { blobPath, summaryEndpoint, suiteEndpoint } = el?.dataset || {};
const { blobPath, emptyStateImagePath, hasTestReport, summaryEndpoint, suiteEndpoint } =
el?.dataset || {};
const testReportsStore = createTestReportsStore({
blobPath,
summaryEndpoint,
@ -76,6 +78,10 @@ const createTestDetails = () => {
components: {
TestReports,
},
provide: {
emptyStateImagePath,
hasTestReport: parseBoolean(hasTestReport),
},
store: testReportsStore,
render(createElement) {
return createElement('test-reports');

View File

@ -7,12 +7,7 @@ export const setNotification = (appEl) => {
const notificationEl = document.querySelector('.header-help');
let notificationCountEl = notificationEl.querySelector('.js-whats-new-notification-count');
const legacyStorageKey = 'display-whats-new-notification-13.10';
const localStoragePairs = [
[legacyStorageKey, false],
[STORAGE_KEY, versionDigest],
];
if (localStoragePairs.some((pair) => localStorage.getItem(pair[0]) === pair[1].toString())) {
if (localStorage.getItem(STORAGE_KEY) === versionDigest) {
notificationEl.classList.remove('with-notifications');
if (notificationCountEl) {
notificationCountEl.parentElement.removeChild(notificationCountEl);

View File

@ -36,23 +36,11 @@ module Packages
def packages_with_path
matching_packages = base.only_maven_packages_with_path(@path, use_cte: @group.present?)
if group_level_improvements?
matching_packages = matching_packages.order_by_package_file if @order_by_package_file
else
matching_packages = matching_packages.order_by_package_file if versionless_package?(matching_packages)
end
matching_packages = matching_packages.order_by_package_file if @order_by_package_file
matching_packages
end
def versionless_package?(matching_packages)
return if matching_packages.empty?
# if one matching package is versionless, they all are.
matching_packages.first&.version.nil?
end
# Produces a query that retrieves packages from a single project.
def packages_for_a_single_project
@project.packages
@ -61,11 +49,7 @@ module Packages
# Produces a query that retrieves packages from multiple projects that
# the current user can view within a group.
def packages_for_multiple_projects
if group_level_improvements?
packages_visible_to_user(@current_user, within_group: @group)
else
::Packages::Package.for_projects(projects_visible_to_current_user)
end
packages_visible_to_user(@current_user, within_group: @group)
end
# Returns the projects that the current user can view within a group.
@ -73,12 +57,6 @@ module Packages
@group.all_projects
.public_or_visible_to_user(@current_user)
end
def group_level_improvements?
strong_memoize(:group_level_improvements) do
Feature.enabled?(:maven_packages_group_level_improvements, default_enabled: :yaml)
end
end
end
end
end

View File

@ -8,10 +8,7 @@ module Types
argument :not, NegatedBoardIssueInputType,
required: false,
prepare: ->(negated_args, ctx) { negated_args.to_h },
description: <<~MD
List of negated arguments.
Warning: this argument is experimental and a subject to change in future.
MD
description: 'List of negated arguments.'
argument :search, GraphQL::STRING_TYPE,
required: false,

View File

@ -10,9 +10,10 @@ class MembersPreloader
def preload_all
ActiveRecord::Associations::Preloader.new.preload(members, :user)
ActiveRecord::Associations::Preloader.new.preload(members, :source)
ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status)
ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations)
ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :webauthn_registrations)
ActiveRecord::Associations::Preloader.new.preload(members, :created_by)
ActiveRecord::Associations::Preloader.new.preload(members, user: :status)
ActiveRecord::Associations::Preloader.new.preload(members, user: :u2f_registrations)
ActiveRecord::Associations::Preloader.new.preload(members, user: :webauthn_registrations)
end
end

View File

@ -74,6 +74,7 @@ class Packages::Package < ApplicationRecord
enum status: { default: 0, hidden: 1, processing: 2, error: 3 }
scope :for_projects, ->(project_ids) { where(project_id: project_ids) }
scope :with_name, ->(name) { where(name: name) }
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }
scope :with_normalized_pypi_name, ->(name) { where("LOWER(regexp_replace(name, '[-_.]+', '-', 'g')) = ?", name.downcase) }
@ -137,14 +138,6 @@ class Packages::Package < ApplicationRecord
after_commit :update_composer_cache, on: :destroy, if: -> { composer? }
def self.for_projects(projects)
unless Feature.enabled?(:maven_packages_group_level_improvements, default_enabled: :yaml)
return none unless projects.any?
end
where(project_id: projects)
end
def self.only_maven_packages_with_path(path, use_cte: false)
if use_cte && Feature.enabled?(:maven_metadata_by_path_with_optimization_fence, default_enabled: :yaml)
# This is an optimization fence which assumes that looking up the Metadatum record by path (globally)

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
class FlowdockService < Service
include ActionView::Helpers::UrlHelper
prop_accessor :token
validates :token, presence: true, if: :activated?
@ -9,7 +10,12 @@ class FlowdockService < Service
end
def description
s_('FlowdockService|Flowdock is a collaboration web app for technical teams.')
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
@ -18,7 +24,7 @@ class FlowdockService < Service
def fields
[
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|Flowdock Git source token'), required: true }
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end

View File

@ -3,12 +3,14 @@
require 'hangouts_chat'
class HangoutsChatService < ChatNotificationService
include ActionView::Helpers::UrlHelper
def title
'Hangouts Chat'
'Google Chat'
end
def description
'Receive event notifications in Google Hangouts Chat'
'Send notifications from GitLab to a room in Google Chat.'
end
def self.to_param
@ -16,13 +18,8 @@ class HangoutsChatService < ChatNotificationService
end
def help
'This service sends notifications about projects events to Google Hangouts Chat room.<br />
To set up this service:
<ol>
<li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li>
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
<li>Select events below to enable notifications.</li>
</ol>'
docs_link = link_to _('How do I set up a Google Chat webhook?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/hangouts_chat'), target: '_blank', rel: 'noopener noreferrer'
s_('Before enabling this integration, create a webhook for the room in Google Chat where you want to receive notifications from this project. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def event_field(event)
@ -42,7 +39,7 @@ class HangoutsChatService < ChatNotificationService
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
{ type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
]

View File

@ -30,7 +30,8 @@ class UserCallout < ApplicationRecord
new_user_signups_cap_reached: 26, # EE-only
unfinished_tag_cleanup_callout: 27,
eoa_bronze_plan_banner: 28, # EE-only
pipeline_needs_banner: 29
pipeline_needs_banner: 29,
pipeline_needs_hover_tip: 30
}
validates :user, presence: true

View File

@ -4,7 +4,7 @@
- if @broadcast_message.message.present?
= render_broadcast_message(@broadcast_message)
- else
Your message here
= _('Your message here')
.d-flex.justify-content-center
.broadcast-message.broadcast-notification-message.preview.js-broadcast-notification-message-preview.mt-2{ class: ('hidden' unless @broadcast_message.notification? ) }
= sprite_icon('bullhorn', css_class: 'vertical-align-text-top')
@ -12,7 +12,7 @@
- if @broadcast_message.message.present?
= render_broadcast_message(@broadcast_message)
- else
Your message here
= _('Your message here')
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form js-quick-submit js-requires-input'} do |f|
= form_errors(@broadcast_message)
@ -55,7 +55,7 @@
= _('Allow users to dismiss the broadcast message')
.form-group.row.js-toggle-colors-container.toggle-colors.hide
.col-sm-2.col-form-label
= f.label :font, "Font Color"
= f.label :font, _("Font Color")
.col-sm-10
= f.color_field :font, class: "form-control gl-form-input text-font-color"
.form-group.row
@ -77,6 +77,6 @@
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions
- if @broadcast_message.persisted?
= f.submit "Update broadcast message", class: "btn gl-button btn-confirm"
= f.submit _("Update broadcast message"), class: "btn gl-button btn-confirm"
- else
= f.submit "Add broadcast message", class: "btn gl-button btn-confirm"
= f.submit _("Add broadcast message"), class: "btn gl-button btn-confirm"

View File

@ -83,5 +83,7 @@
#js-tab-tests.tab-pane
#js-pipeline-tests-detail{ data: { summary_endpoint: summary_project_pipeline_tests_path(@project, @pipeline, format: :json),
suite_endpoint: project_pipeline_test_path(@project, @pipeline, suite_name: 'suite', format: :json),
blob_path: project_blob_path(@project, @pipeline.sha) } }
blob_path: project_blob_path(@project, @pipeline.sha),
has_test_report: @pipeline.has_reports?(Ci::JobArtifact.test_reports).to_s,
empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg') } }
= render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project

View File

@ -0,0 +1,5 @@
---
title: Add link to documentation in empty pipeline test reports
merge_request: 59812
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Remove the enabled by default feature flag for maven group level improvements
merge_request: 59748
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Externalize strings in broadcast_messages/_form.html.haml
merge_request: 58143
author: nuwe1
type: other

View File

@ -0,0 +1,5 @@
---
title: Add cascading namespace setting database migration helper
merge_request: 58940
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix EmptyLineAfterFinalLetItBe offenses in spec/models/wiki_page
merge_request: 58388
author: Huzaifa Iftikhar @huzaifaiftikhar
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Review UI text and docs - Google Chat
merge_request: 59518
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Review UI text and docs for Flowdock integration
merge_request: 59388
author:
type: other

View File

@ -1,8 +0,0 @@
---
name: maven_packages_group_level_improvements
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57600
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326099
milestone: '13.11'
type: development
group: group::package
default_enabled: true

View File

@ -0,0 +1,117 @@
- title: GitLab Kubernetes Agent available on GitLab.com
body: |
The GitLab Kubernetes Agent is finally available on GitLab.com. By using the Agent, you can benefit from fast, pull-based deployments to your cluster, while GitLab.com manages the necessary server-side components of the Agent. The GitLab Kubernetes Agent is the core building block of GitLab's Kubernetes integrations. The Agent-based integration today supports pull-based deployments and Network Security policy integration and alerts, and will soon receive support for push-based deployments too.
Unlike the legacy, certificate-based Kubernetes integration, the GitLab Kubernetes Agent does not require opening up your cluster towards GitLab and allows fine-tuned RBAC controls around GitLab's capabilities within your clusters.
stage: Configure
self-managed: true
gitlab-com: true
packages: [Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/clusters/agent/
image_url: https://img.youtube.com/vi/4Sh5bghbAjY/hqdefault.jpg
published_at: 2021-04-22
release: 13.11
- title: Compliance pipeline configurations
body: |
We are thrilled to announce that it is now possible to define enforceable pipelines that will run for any project assigned a corresponding [compliance framework](https://docs.gitlab.com/ee/user/project/settings/#compliance-framework).
For teams looking to implement compliance requirements in the pipeline workflow, they can now enforce even more separation of duties by setting up a single pipeline definition for a specific compliance framework. All projects using that framework will include the predefined pipeline automatically. Users extend, but cannot modify, the pipeline configuration in the downstream projects, ensuring that compliance steps are run the same way every time.
This saves security and compliance teams time by eliminating the need to manually copy a pipeline configuration to every project that needs it and then monitoring to prevent edits or removal. It also helps development teams follow policies without requiring them to become experts in compliance.
Check out the [video walkthrough](https://www.youtube.com/embed/upLJ_equomw) to see its setup and implementation!
stage: Manage
self-managed: true
gitlab-com: true
packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/project/settings/#compliance-pipeline-configuration
image_url: https://img.youtube.com/vi/upLJ_equomw/hqdefault.jpg
published_at: 2021-04-22
release: 13.11
- title: On-call schedule management
body: |
Software services do not get "turned off" at the end of the business day. Your customers expect 24/7 availability. When things go wrong, you need a team (or multiple teams!) that can quickly and effectively respond to service outages.
Being on-call can be a stressful job. To better manage stress and burn-out, most teams rotate this on-call responsibility. GitLab's **on-call schedule management** allows you and your team to create and manage schedules for on-call responsibilities. Alerts received in GitLab through an HTTP endpoint are routed to the on-call engineer in the schedule for that specific project.
stage: Monitor
self-managed: true
gitlab-com: true
packages: [Premium, Ultimate]
url: https://docs.gitlab.com/ee/operations/incident_management/oncall_schedules.html
image_url: https://img.youtube.com/vi/QXfCQ24-Ufo/hqdefault.jpg
published_at: 2021-04-22
release: 13.11
- title: Use multiple caches in the same job
body: |
GitLab CI/CD provides a caching mechanism that saves precious development time when your jobs are running. Previously, it was impossible to configure multiple cache keys in the same job. This limitation may have caused you to use artifacts for caching, or use duplicate jobs with different cache paths. In this release, we provide the ability to configure multiple cache keys in a single job which will help you increase your pipeline performance.
stage: Verify
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/ci/yaml/README.html#multiple-caches
image_url: https://about.gitlab.com/images/13_11/cache.png
published_at: 2021-04-22
release: 13.11
- title: Group SAML Enforcement for Git activity
body: |
GitLab group maintainers can now enhance their group security by enforcing Group SAML for Git activity. Security-minded organizations want all GitLab activity to be protected and governed by their SAML Identity Provider. Currently, SAML SSO enforcement only applies to activity in the GitLab UI. Git CLI operations do not require an active SAML SSO session. When Git Group SAML SSO enforcement is enabled, users must have an active web SAML session to perform Git operations in the CLI.
stage: Manage
self-managed: false
gitlab-com: true
packages: [Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/group/saml_sso/#sso-enforcement
image_url: https://about.gitlab.com/images/sdlc-icons/manage.svg
published_at: 2021-04-22
release: 13.11
- title: Cherry pick commits from fork to parent
body: |
With GitLab 13.11, if you are a project member, you can now cherry-pick commits from downstream forks back into your project. We've added a new **Pick into project** section to the cherry-pick dialog, shown when you select **Options > Cherry-pick** on a commit's details page.
Your community of contributors can contribute to your project, and your team no longer needs to manually download a fork's `.patch` file to pull in good changes from stale or unmaintained forks.
Future enhancements include [cherry-picking commits from fork to fork](https://gitlab.com/gitlab-org/gitlab/-/issues/326771).
stage: Create
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/merge_requests/cherry_pick_changes.html#cherry-pick-into-a-project
image_url: https://about.gitlab.com/images/13_11/cherry_pick_commits_from_fork_to_parent.png
published_at: 2021-04-22
release: 13.11
- title: Improvements to Jira Connect application configuration
body: |
When configuring the [GitLab.com for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) application, you can now filter the available namespaces when linking them to your account, simplifying configuration for users with access to a large number of namespaces.
stage: Create
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/integration/jira/connect-app.html
image_url: https://about.gitlab.com/images/13_11/jira-connect-app-search.png
published_at: 2021-04-22
release: 13.11
- title: Search within a settings page
body: |
Finding the exact location of a GitLab setting can be challenging. Even if you know generally where to look, many of the settings views have multiple sections and dozens of individual configuration options.
In this release, you can now use the search field in group, project, admin, and user settings to quickly pinpoint your desired configuration. Your search criteria will filter the current page down to display only relevant settings and even highlight occurrences of your search term on the page.
In the future iterations, we are looking to extend this functionality to [search across all settings views](https://gitlab.com/groups/gitlab-org/-/epics/5198).
stage: Create
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/search/#search-settings
image_url: https://about.gitlab.com/images/13_11/search-settings.gif
published_at: 2021-04-22
release: 13.11
- title: Deploy GitLab on OpenShift and Kubernetes with the GitLab Operator (beta)
body: |
GitLab is working to offer full support for OpenShift. To accomplish this, we have released the MVP [GitLab Operator](https://gitlab.com/gitlab-org/gl-openshift/gitlab-operator/-/tree/master/doc). The operator aims to manage the full lifecycle of GitLab instances on Kubernetes and OpenShift container platforms. Currently, this is a [beta release](https://gitlab.com/groups/gitlab-org/-/epics/3444) and it is **not recommended for production use**. The next steps will be to make the operator generally available (GA). In the future the operator will become the recommended installation method for Kubernetes and OpenShift, although the GitLab Helm chart will still be supported. We welcome you to try this operator and [provide feedback on our issue tracker](https://gitlab.com/gitlab-org/gl-openshift/gitlab-operator/-/issues/131).
stage: Enablement
self-managed: true
gitlab-com: true
packages: [Free, Premium, Ultimate]
url: https://gitlab.com/gitlab-org/gl-openshift/gitlab-operator/-/tree/master/doc
image_url: https://about.gitlab.com/images/13_11/gitlab-operator.png
published_at: 2021-04-22
release: 13.11

View File

@ -1,237 +1,8 @@
---
stage: Create
group: Source Code
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, howto
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/git_annex.html'
redirect_to: 'index.md'
---
# Git annex
This document was moved to [another location](index.md).
WARNING:
[Git Annex support was removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1648)
in GitLab 9.0. Read through the [migration guide from git-annex to Git LFS](../topics/git/lfs/migrate_from_git_annex_to_git_lfs.md).
The biggest limitation of Git, compared to some older centralized version
control systems has been the maximum size of the repositories.
The general recommendation is to not have Git repositories larger than 1GB to
preserve performance. Although GitLab has no limit (some repositories in GitLab
are over 50GB!), we subscribe to the advice to keep repositories as small as
you can.
Not being able to version control large binaries is a big problem for many
larger organizations.
Videos, photos, audio, compiled binaries, and many other types of files are too
large. As a workaround, people keep artwork-in-progress in a Dropbox folder and
only check in the final result. This results in using outdated files, not
having a complete history, and increases the risk of losing work.
This problem is solved in GitLab Enterprise Edition by integrating the
[git-annex](https://git-annex.branchable.com/) application.
`git-annex` allows managing large binaries with Git without checking the
contents into Git.
You check-in only a symlink that contains the SHA-1 of the large binary. If you
need the large binary, you can sync it from the GitLab server over `rsync`, a
very fast file copying tool.
## GitLab git-annex Configuration
`git-annex` is disabled by default in GitLab. Below are the
configuration options required to enable it.
### Requirements
`git-annex` needs to be installed both on the server and the client-side.
For Debian-like systems (for example, Debian and Ubuntu) this can be achieved by running:
```shell
sudo apt-get update && sudo apt-get install git-annex
```
For RedHat-like systems (for example, CentOS and RHEL) this can be achieved by running:
```shell
sudo yum install epel-release && sudo yum install git-annex
```
### Configuration for Omnibus packages
For Omnibus GitLab packages, only one configuration setting is needed.
The Omnibus package internally sets the correct options in all locations.
1. In `/etc/gitlab/gitlab.rb` add the following line:
```ruby
gitlab_shell['git_annex_enabled'] = true
```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
### Configuration for installations from source
There are 2 settings to enable git-annex on your GitLab server.
One is located in `config/gitlab.yml` of the GitLab repository and the other
one is located in `config.yml` of GitLab Shell.
1. In `config/gitlab.yml` add or edit the following lines:
```yaml
gitlab_shell:
git_annex_enabled: true
```
1. In `config.yml` of GitLab Shell add or edit the following lines:
```yaml
git_annex_enabled: true
```
1. Save the files and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
## Using GitLab git-annex
NOTE:
Your Git remotes must be using the SSH protocol, not HTTP(S).
Here is an example workflow of uploading a very large file and then checking it
into your Git repository:
```shell
git clone git@example.com:group/project.git
git annex init 'My Laptop' # initialize the annex project and give an optional description
cp ~/tmp/debian.iso ./ # copy a large file into the current directory
git annex add debian.iso # add the large file to git annex
git commit -am "Add Debian iso" # commit the file metadata
git annex sync --content # sync the Git repo and large file to the GitLab server
```
The output should look like this:
```plaintext
commit
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
ok
pull origin
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
From example.com:group/project
497842b..5162f80 git-annex -> origin/git-annex
ok
(merging origin/git-annex into git-annex...)
(recording state in git...)
copy debian.iso (checking origin...) (to origin...)
SHA256E-s26214400--8092b3d482fb1b7a5cf28c43bc1425c8f2d380e86869c0686c49aa7b0f086ab2.iso
26,214,400 100% 638.88kB/s 0:00:40 (xfr#1, to-chk=0/1)
ok
pull origin
ok
(recording state in git...)
push origin
Counting objects: 15, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (15/15), 1.64 KiB | 0 bytes/s, done.
Total 15 (delta 1), reused 0 (delta 0)
To example.com:group/project.git
* [new branch] git-annex -> synced/git-annex
* [new branch] master -> synced/master
ok
```
Your files can be found in the `master` branch, but more branches are created
by the `annex sync` command.
Git Annex creates a new directory at `.git/annex/` and records the
tracked files in the `.git/config` file. The files you assign to be tracked
with `git-annex` don't affect the existing `.git/config` records. The files
are turned into symbolic links that point to data in `.git/annex/objects/`.
The `debian.iso` file in the example contain the symbolic link:
```plaintext
.git/annex/objects/ZW/1k/SHA256E-s82701--6384039733b5035b559efd5a2e25a493ab6e09aabfd5162cc03f6f0ec238429d.png/SHA256E-s82701--6384039733b5035b559efd5a2e25a493ab6e09aabfd5162cc03f6f0ec238429d.iso
```
Use `git annex info` to retrieve the information about the local copy of your
repository.
---
You can download a single large file with these commands:
```shell
git clone git@gitlab.example.com:group/project.git
git annex sync # sync Git branches but not the large file
git annex get debian.iso # download the large file
```
To download all files:
```shell
git clone git@gitlab.example.com:group/project.git
git annex sync --content # sync Git branches and download all the large files
```
By using `git-annex` without GitLab, anyone that can access the server can also
access the files of all projects. GitLab Annex ensures that you can only
access files of projects you have access to (developer, maintainer, or owner role).
## How it works
Internally GitLab uses [GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell) to handle SSH access and this was a great
integration point for `git-annex`.
There is a setting in GitLab Shell so you can disable GitLab Annex support
if you want to.
## Troubleshooting tips
Differences in the version of `git-annex` on the GitLab server and on local machines
can cause `git-annex` to raise unpredicted warnings and errors.
Consult the [Annex upgrade page](https://git-annex.branchable.com/upgrades/) for more information about
the differences between versions. You can find out which version is installed
on your server by navigating to `https://pkgs.org/download/git-annex` and
searching for your distribution.
Although there is no general guide for `git-annex` errors, there are a few tips
on how to go around the warnings.
### `git-annex-shell: Not a git-annex or gcrypt repository`
This warning can appear on the initial `git annex sync --content` and is caused
by differences in `git-annex-shell`. You can read more about it
[in this git-annex issue](https://git-annex.branchable.com/forum/Error_from_git-annex-shell_on_creation_of_gcrypt_special_remote/).
One important thing to note is that despite the warning, the `sync` succeeds
and the files are pushed to the GitLab repository.
If you get hit by this, you can run the following command inside the repository
that the warning was raised:
```shell
git config remote.origin.annex-ignore false
```
Consecutive runs of `git annex sync --content` **should not** produce this
warning and the output should look like this:
```plaintext
commit ok
pull origin
ok
pull origin
ok
push origin
```
<!-- This redirect file can be deleted after <2021-07-22>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View File

@ -190,7 +190,6 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Git LFS configuration](lfs/index.md): Learn how to configure LFS for GitLab.
- [Housekeeping](housekeeping.md): Keep your Git repositories tidy and fast.
- [Configuring Git Protocol v2](git_protocol.md): Git protocol version 2 support.
- [Manage large files with `git-annex` (Deprecated)](git_annex.md)
## Monitoring GitLab

View File

@ -13783,6 +13783,7 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumnew_user_signups_cap_reached"></a>`NEW_USER_SIGNUPS_CAP_REACHED` | Callout feature name for new_user_signups_cap_reached. |
| <a id="usercalloutfeaturenameenumpersonal_access_token_expiry"></a>`PERSONAL_ACCESS_TOKEN_EXPIRY` | Callout feature name for personal_access_token_expiry. |
| <a id="usercalloutfeaturenameenumpipeline_needs_banner"></a>`PIPELINE_NEEDS_BANNER` | Callout feature name for pipeline_needs_banner. |
| <a id="usercalloutfeaturenameenumpipeline_needs_hover_tip"></a>`PIPELINE_NEEDS_HOVER_TIP` | Callout feature name for pipeline_needs_hover_tip. |
| <a id="usercalloutfeaturenameenumregistration_enabled_callout"></a>`REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
| <a id="usercalloutfeaturenameenumservice_templates_deprecated_callout"></a>`SERVICE_TEMPLATES_DEPRECATED_CALLOUT` | Callout feature name for service_templates_deprecated_callout. |
| <a id="usercalloutfeaturenameenumsuggest_pipeline"></a>`SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
@ -14653,7 +14654,7 @@ Field that are available while modifying the custom mapping attributes for an HT
| <a id="boardissueinputlabelname"></a>`labelName` | [`[String]`](#string) | Filter by label name. |
| <a id="boardissueinputmilestonetitle"></a>`milestoneTitle` | [`String`](#string) | Filter by milestone title. |
| <a id="boardissueinputmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| <a id="boardissueinputnot"></a>`not` | [`NegatedBoardIssueInput`](#negatedboardissueinput) | List of negated arguments. Warning: this argument is experimental and a subject to change in future. |
| <a id="boardissueinputnot"></a>`not` | [`NegatedBoardIssueInput`](#negatedboardissueinput) | List of negated arguments. |
| <a id="boardissueinputreleasetag"></a>`releaseTag` | [`String`](#string) | Filter by release tag. |
| <a id="boardissueinputsearch"></a>`search` | [`String`](#string) | Search query for issue title or description. |
| <a id="boardissueinputweight"></a>`weight` | [`String`](#string) | Filter by weight. |
@ -14745,7 +14746,7 @@ Input type for DastSiteProfile authentication.
| <a id="epicfiltersauthorusername"></a>`authorUsername` | [`String`](#string) | Filter by author username. |
| <a id="epicfilterslabelname"></a>`labelName` | [`[String]`](#string) | Filter by label name. |
| <a id="epicfiltersmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| <a id="epicfiltersnot"></a>`not` | [`NegatedEpicBoardIssueInput`](#negatedepicboardissueinput) | Negated epic arguments. Warning: this argument is experimental and a subject to change in the future. |
| <a id="epicfiltersnot"></a>`not` | [`NegatedEpicBoardIssueInput`](#negatedepicboardissueinput) | Negated epic arguments. |
| <a id="epicfilterssearch"></a>`search` | [`String`](#string) | Search query for epic title or description. |
### `EpicTreeNodeFieldsInputType`

View File

@ -572,7 +572,9 @@ GET /projects/:id/services/external-wiki
## Flowdock
Flowdock is a collaboration web app for technical teams.
Flowdock is a ChatOps application for collaboration in software engineering
companies. You can send notifications from GitLab events to Flowdock flows.
For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
### Create/Edit Flowdock service

View File

@ -207,6 +207,7 @@ In these cases, use the following workflow:
- [Newlines style guide](newlines_styleguide.md)
- [Image scaling guide](image_scaling.md)
- [Export to CSV](export_csv.md)
- [Cascading Settings](cascading_settings.md)
## Performance guides

View File

@ -0,0 +1,96 @@
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Cascading Settings
> Introduced in [GitLab 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/321724).
The cascading settings framework allows groups to essentially inherit settings
values from ancestors (parent group on up the group hierarchy) and from
instance-level application settings. The framework also allows settings values
to be enforced on groups lower in the hierarchy.
Cascading settings can currently only be defined within `NamespaceSetting`, though
the framework may be extended to other objects in the future.
## Add a new cascading setting
Settings are not cascading by default. To define a cascading setting, take the following steps:
1. In the `NamespaceSetting` model, define the new attribute using the `cascading_attr`
helper method. You can use an array to define multiple attributes on a single line.
```ruby
class NamespaceSetting
include CascadingNamespaceSettingAttribute
cascading_attr :delayed_project_removal
end
```
1. Create the database columns.
You can use the following database migration helper for a completely new setting.
The helper creates four columns, two each in `namespace_settings` and
`application_settings`.
```ruby
class AddDelayedProjectRemovalCascadingSetting < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
def up
add_cascading_namespace_setting :delayed_project_removal, :boolean, default: false, null: false
end
def down
remove_cascading_namespace_setting :delayed_project_removal
end
end
```
Existing settings being converted to a cascading setting will require individual
migrations to add columns and change existing columns. Use the specifications
below to create migrations as required:
1. Columns in `namespace_settings` table:
- `delayed_project_removal`: No default value. Null values allowed. Use any column type.
- `lock_delayed_project_removal`: Boolean column. Default value is false. Null values not allowed.
1. Columns in `application_settings` table:
- `delayed_project_removal`: Type matching for the column created in `namespace_settings`.
Set default value as desired. Null values not allowed.
- `lock_delayed_project_removal`: Boolean column. Default value is false. Null values not allowed.
## Convenience methods
By defining an attribute using the `cascading_attr` method, a number of convenience
methods are automatically defined.
**Definition:**
```ruby
cascading_attr :delayed_project_removal
```
**Convenience Methods Available:**
- `delayed_project_removal`
- `delayed_project_removal=`
- `delayed_project_removal_locked?`
- `delayed_project_removal_locked_by_ancestor?`
- `delayed_project_removal_locked_by_application_setting?`
- `delayed_project_removal?` (Boolean attributes only)
- `delayed_project_removal_locked_ancestor` - (Returns locked namespace settings object [namespace_id])
The attribute reader method (`delayed_project_removal`) returns the correct
cascaded value using the following criteria:
1. Returns the dirty value, if the attribute has changed. This allows standard
Rails validators to be used on the attribute, though `nil` values *must* be allowed.
1. Return locked ancestor value.
1. Return locked instance-level application settings value.
1. Return this namespace's attribute, if not nil.
1. Return value from nearest ancestor where value is not nil.
1. Return instance-level application setting.

View File

@ -48,6 +48,12 @@ For all of the above, please include `--why "Reason"` and `--who "My Name"` so t
More detailed information on how the gem and its commands work is available in the [License Finder README](https://github.com/pivotal/LicenseFinder).
## Encryption keys
If your license was created in your local development or staging environment for Customers Portal or License App, an environment variable called `GITLAB_LICENSE_MODE` with the value `test` needs to be set to use the correct decryption key.
Those projects are set to use a test license encryption key by default.
## Additional information
Please see the [Open Source](https://about.gitlab.com/handbook/engineering/open-source/#using-open-source-libraries) page for more information on licensing.

View File

@ -492,39 +492,32 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
Example event:
```yaml
- name: i_compliance_credential_inventory
category: compliance
redis_slot: compliance
expiry: 42 # 6 weeks
- name: users_creating_epics
category: epics_usage
redis_slot: users
aggregation: weekly
feature_flag: usage_data_i_compliance_credential_inventory
feature_flag: track_epics_activity
```
Keys:
- `name`: unique event name.
Name format `<prefix>_<redis_slot>_name`.
Name format for Redis HLL events `<name>_<redis_slot>`.
Use one of the following prefixes for the event's name:
- `g_` for group, as an event which is tracked for group.
- `p_` for project, as an event which is tracked for project.
- `i_` for instance, as an event which is tracked for instance.
- `a_` for events encompassing all `g_`, `p_`, `i_`.
- `o_` for other.
[See Metric name](metrics_dictionary.md#metric-name) for a complete guide on metric naming suggestion.
Consider including in the event's name the Redis slot to be able to count totals for a specific category.
Example names: `i_compliance_credential_inventory`, `g_analytics_contribution`.
Example names: `users_creating_epics`, `users_triggering_security_scans`.
- `category`: event category. Used for getting total counts for events in a category, for easier
access to a group of events.
- `redis_slot`: optional Redis slot; default value: event name. Used if needed to calculate totals
for a group of metrics. Ensure keys are in the same slot. For example:
`i_compliance_credential_inventory` with `redis_slot: 'compliance'` builds Redis key
`i_{compliance}_credential_inventory-2020-34`. If `redis_slot` is not defined the Redis key will
be `{i_compliance_credential_inventory}-2020-34`.
`users_creating_epics` with `redis_slot: 'users'` builds Redis key
`{users}_creating_epics-2020-34`. If `redis_slot` is not defined the Redis key will
be `{users_creating_epics}-2020-34`.
- `expiry`: expiry time in days. Default: 29 days for daily aggregation and 6 weeks for weekly
aggregation.
- `aggregation`: may be set to a `:daily` or `:weekly` key. Defines how counting data is stored in Redis.
@ -581,7 +574,7 @@ Use one of the following methods to track events:
user: current_user, subject: user_group
).execute
increment_unique_values('i_list_repositories', current_user.id)
increment_unique_values('users_listing_repositories', current_user.id)
present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
end
@ -655,15 +648,15 @@ Use one of the following methods to track events:
Trigger events in rails console by using `track_event` method
```ruby
Gitlab::UsageDataCounters::HLLRedisCounter.track_event('g_compliance_audit_events', values: 1)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event('g_compliance_audit_events', values: [2, 3])
Gitlab::UsageDataCounters::HLLRedisCounter.track_event('users_viewing_compliance_audit_events', values: 1)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event('users_viewing_compliance_audit_events', values: [2, 3])
```
Next, get the unique events for the current week.
```ruby
# Get unique events for metric for current_week
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: 'g_compliance_audit_events',
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: 'users_viewing_compliance_audit_events',
start_date: Date.current.beginning_of_week, end_date: Date.current.next_week)
```
@ -824,7 +817,6 @@ We return fallback values in these cases:
Add the metric in one of the top level keys
- `license`: for license related metrics.
- `settings`: for settings related metrics.
- `counts_weekly`: for counters that have data for the most recent 7 days.
- `counts_monthly`: for counters that have data for the most recent 28 days.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -101,5 +101,4 @@ The following relate to Git Large File Storage:
- [Removing objects from LFS](lfs/index.md#removing-objects-from-lfs)
- [GitLab Git LFS user documentation](lfs/index.md)
- [GitLab Git LFS admin documentation](../../administration/lfs/index.md)
- [Git Annex to Git LFS migration guide](lfs/migrate_from_git_annex_to_git_lfs.md)
- [Towards a production quality open source Git LFS server](https://about.gitlab.com/blog/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/)

View File

@ -1,248 +1,8 @@
---
stage: Create
group: Source Code
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, howto
redirect_to: 'index.md'
---
# Migration guide from Git Annex to Git LFS **(FREE)**
This document was moved to [another location](index.md).
WARNING:
Git Annex support [has been removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1648) in GitLab Enterprise
Edition 9.0 (2017/03/22).
Both [Git Annex](http://git-annex.branchable.com/) and [Git LFS](https://git-lfs.github.com/) are tools to manage large files in Git.
## History
Git Annex [was introduced in GitLab Enterprise Edition 7.8](https://about.gitlab.com/blog/2015/02/17/gitlab-annex-solves-the-problem-of-versioning-large-binaries-with-git/), at a time
where Git LFS didn't yet exist. A few months later, GitLab brought support for
Git LFS in [GitLab 8.2](https://about.gitlab.com/blog/2015/11/23/announcing-git-lfs-support-in-gitlab/) and is available for both Community and
Enterprise editions.
## Differences between Git Annex and Git LFS
Some items below are general differences between the two protocols and some are
ones that GitLab developed.
- Git Annex works only through SSH, whereas Git LFS works both with SSH and HTTPS
(SSH support was added in GitLab 8.12).
- Annex files are stored in a sub-directory of the normal repositories, whereas
LFS files are stored outside of the repositories in a place you can define.
- Git Annex requires a more complex setup, but has much more options than Git
LFS. You can compare the commands each one offers by running `man git-annex`
and `man git-lfs`.
- Annex files cannot be browsed directly in the GitLab interface, whereas LFS
files can.
## Migration steps
Git Annex files are stored in a sub-directory of the normal repositories
(`.git/annex/objects`) and LFS files are stored outside of the repositories.
The two aren't compatible as they are using a different scheme. Therefore, the
migration has to be done manually per repository.
There are basically two steps you need to take in order to migrate from Git
Annex to Git LFS.
### TL; DR
If you know what you are doing and want to skip the reading, this is what you
need to do (we assume you have [git-annex enabled](../../../administration/git_annex.md#using-gitlab-git-annex) in your
repository and that you have made backups in case something goes wrong).
Fire up a terminal, navigate to your Git repository and:
1. Disable `git-annex`:
```shell
git annex sync --content
git annex direct
git annex uninit
git annex indirect
```
1. Enable `git-lfs`:
```shell
git lfs install
git lfs track <files>
git add .
git commit -m "commit message"
git push
```
### Disabling Git Annex in your repository
Before changing anything, make sure you have a backup of your repository first.
There are a couple of ways to do that, but you can clone it to another
local path and maybe push it to GitLab if you want a remote backup as well.
A guide on
[how to back up a **git-annex** repository to an external hard drive](https://www.thomas-krenn.com/en/wiki/Git-annex_Repository_on_an_External_Hard_Drive) is also available.
Because Annex files are stored as objects with symlinks and cannot be directly
modified, we need to first remove those symlinks.
NOTE:
Make sure the you read about the [`direct` mode](https://git-annex.branchable.com/direct_mode/) as it contains
information that may fit in your use case. The `annex direct` command is
deprecated in Git Annex version 6, so you may need to upgrade your repository
if the server also has Git Annex 6 installed. Read more in the
[Git Annex troubleshooting tips](../../../administration/git_annex.md#troubleshooting-tips) section.
1. Backup your repository
```shell
cd repository
git annex sync --content
cd ..
git clone repository repository-backup
cd repository-backup
git annex get
cd ..
```
1. Use `annex direct`:
```shell
cd repository
git annex direct
```
The output should be similar to this:
```shell
commit
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
ok
direct debian.iso ok
direct ok
```
1. Disable Git Annex with [`annex uninit`](https://git-annex.branchable.com/git-annex-uninit/):
```shell
git annex uninit
```
The output should be similar to this:
```shell
unannex debian.iso ok
Deleted branch git-annex (was 2534d2c).
```
This command runs `unannex` on every file in the repository, leaving the original files.
1. Switch back to `indirect` mode:
```shell
git annex indirect
```
The output should be similar to this:
```shell
(merging origin/git-annex into git-annex...)
(recording state in git...)
commit (recording state in git...)
ok
(recording state in git...)
[master fac3194] commit before switching to indirect mode
1 file changed, 1 deletion(-)
delete mode 120000 alpine-virt-3.4.4-x86_64.iso
ok
indirect ok
ok
```
---
At this point, you have two options. Either add, commit and push the files
directly back to GitLab or switch to Git LFS. The LFS switch is described in
the next section.
### Enabling Git LFS in your repository
Git LFS is enabled by default on all GitLab products (GitLab CE, GitLab EE,
GitLab.com), therefore, you don't need to do anything server-side.
1. First, make sure you have `git-lfs` installed locally:
```shell
git lfs help
```
If the terminal doesn't prompt you with a full response on `git-lfs` commands,
[install the Git LFS client](https://git-lfs.github.com/) first.
1. Inside the repository, run the following command to initiate LFS:
```shell
git lfs install
```
1. Enable `git-lfs` for the group of files you want to track. You
can track specific files, all files containing the same extension, or an
entire directory:
```shell
git lfs track images/01.png # per file
git lfs track **/*.png # per extension
git lfs track images/ # per directory
```
After this, run `git status` to see the `.gitattributes` added
to your repository. It collects all file patterns that you chose to track via
`git-lfs`.
1. Add the files, commit and push them to GitLab:
```shell
git add .
git commit -m "commit message"
git push
```
If your remote is set up with HTTP, you are asked to enter your login
credentials. If you have [2FA enabled](../../../user/profile/account/two_factor_authentication.md), make sure to use a
[personal access token](../../../user/profile/account/two_factor_authentication.md#personal-access-tokens)
instead of your password.
## Removing the Git Annex branches
After the migration finishes successfully, you can remove all `git-annex`
related branches from your repository.
On GitLab, navigate to your project's **Repository > Branches** and delete all
branches created by Git Annex: `git-annex`, and all under `synced/`.
![repository branches](img/git-annex-branches.png)
You can also do this on the command line with:
```shell
git branch -d synced/master
git branch -d synced/git-annex
git push origin :synced/master
git push origin :synced/git-annex
git push origin :git-annex
git remote prune origin
```
If there are still some Annex objects inside your repository (`.git/annex/`)
or references inside `.git/config`, run `annex uninit` again:
```shell
git annex uninit
```
## Further Reading
- (Blog Post) [Getting Started with Git FLS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
- (Blog Post) [Announcing LFS Support in GitLab](https://about.gitlab.com/blog/2015/11/23/announcing-git-lfs-support-in-gitlab/)
- (Blog Post) [GitLab Annex Solves the Problem of Versioning Large Binaries with Git](https://about.gitlab.com/blog/2015/02/17/gitlab-annex-solves-the-problem-of-versioning-large-binaries-with-git/)
- [Git Annex](../../../administration/git_annex.md)
- [Git LFS](index.md)
<!-- This redirect file can be deleted after <2021-07-22>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View File

@ -174,7 +174,6 @@ but commented out to help encourage others to add to it in the future. -->
## References
- [Getting Started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
- [Migrate from Git Annex to Git LFS](migrate_from_git_annex_to_git_lfs.md)
- [GitLab Git LFS user documentation](index.md)
- [GitLab Git LFS administrator documentation](../../../administration/lfs/index.md)
- Alternative method to [migrate an existing repository to Git LFS](https://github.com/git-lfs/git-lfs/wiki/Tutorial#migrating-existing-repository-data-to-lfs)

View File

@ -27,8 +27,6 @@ of top-performing instances based on [usage ping data](../settings/usage_statist
collected. Your score is compared to the lead score of each feature and then expressed as a percentage at the bottom of said feature.
Your overall **DevOps Score** is an average of your feature scores. You can use this score to compare your DevOps status to other organizations.
![DevOps Report](img/dev_ops_report_v13_4.png)
The page also provides helpful links to articles and GitLab docs, to help you
improve your scores.
@ -58,8 +56,6 @@ DevOps Adoption allows you to:
- Identify specific groups that are lagging in their adoption of GitLab so you can help them along in their DevOps journey.
- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
![DevOps Report](img/dev_ops_adoption_v13_9.png)
### Disable or enable DevOps Adoption
DevOps Adoption is deployed behind a feature flag that is **disabled by default**.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -16,9 +16,7 @@ section.
By default, the navigation bar has the GitLab logo, but this can be customized with
any image desired. It is optimized for images 28px high (any width), but any image can be
used (less than 1MB) and it is automatically resized.
![Navigation bar header logo screenshot](img/appearance_header_logo_v12_3.png)
used (less than 1 MB) and it is automatically resized.
After you select and upload an image, click **Update appearance settings** at the bottom
of the page to activate it in the GitLab instance.
@ -34,8 +32,6 @@ By default, the favicon (used by the browser as the tab icon, as well as the CI
uses the GitLab logo, but this can be customized with any icon desired. It must be a
32x32 `.png` or `.ico` image.
![favicon screenshot](img/appearance_favicon_v12_3.png)
After you select and upload an icon, click **Update appearance settings** at the bottom
of the page to activate it in the GitLab instance.
@ -52,8 +48,6 @@ Limited [Markdown](../markdown.md) is supported, such as bold, italics, and link
example. Other Markdown features, including lists, images, and quotes are not supported
as the header and footer messages can only be a single line.
![header and footer screenshot](img/appearance_header_footer_v12_3.png)
If desired, you can select **Enable header and footer in emails** to have the text of
the header and footer added to all emails sent by the GitLab instance.
@ -63,16 +57,12 @@ to activate it in the GitLab instance.
## Sign in / Sign up pages
You can replace the default message on the sign in / sign up page with your own message
and logo. You can make full use of [Markdown](../markdown.md) in the description:
![sign in message screenshot](img/appearance_sign_in_v12_3.png)
and logo. You can make full use of [Markdown](../markdown.md) in the description.
The optimal size for the logo is 640x360px, but any image can be used (below 1MB)
and it is resized automatically. The logo image appears between the title and
the description, on the left of the sign-up page.
![sign in message preview screenshot](img/appearance_sign_in_preview_v12_3.png)
After you add a message, click **Update appearance settings** at the bottom of the page
to activate it in the GitLab instance. You can also click on the **Sign-in page** button,
to review the saved appearance settings:
@ -85,8 +75,6 @@ You can add also add a [customized help message](settings/help_page.md) below th
You can add a new project guidelines message to the **New project page** within GitLab.
You can make full use of [Markdown](../markdown.md) in the description:
![new project message screenshot](img/appearance_new_project_v12_3.png)
The message is displayed below the **New Project** message, on the left side
of the **New project page**.
@ -94,8 +82,6 @@ After you add a message, click **Update appearance settings** at the bottom of t
to activate it in the GitLab instance. You can also click on the **New project page**
button, which brings you to the new project page so you can review the change.
![new project message preview screenshot](img/appearance_new_project_preview_v12_3.png)
## Libravatar
[Libravatar](https://www.libravatar.org) is supported by GitLab for avatar images, but you must

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -18,13 +18,8 @@ You can add a help message, which is shown on the GitLab `/help` page (e.g.,
1. Navigate to **Admin Area > Settings > Preferences**, then expand **Help page**.
1. Under **Help page text**, fill in the information you wish to display on `/help`.
![help page help message](img/help_page_help_page_text_v12_3.png)
1. Save your changes. You can now see the message on `/help`.
![help message on help page example](img/help_page_help_page_text_ex_v12_3.png)
## Adding a help message to the login page **(STARTER)**
You can add a help message, which is shown on the GitLab login page in a new section

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -15,7 +15,7 @@ To see user cohorts, go to **Admin Area > Overview > Users**.
How do you interpret the user cohorts table? Let's review an example with the
following user cohorts:
![User cohort example](img/cohorts_v13_9.png)
![User cohort example](img/cohorts_v13_9_a.png)
For the cohort of March 2020, three users were added to this server and have
been active since this month. One month later (April 2020), two users are still

View File

@ -6,14 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Feature highlight
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/16379) in GitLab 10.5
Feature highlights are represented by a pulsing blue dot. Hovering over the dot
displays more information.
They are used to emphasize a certain feature and make something more visible to the user.
displays more information. They're used to emphasize certain features and
highlight helpful information to the user.
You can dismiss any feature highlight permanently by clicking the "Got it" link
at the bottom of the modal window. There isn't a way to restore the feature highlight
after it has been dismissed.
![Clusters feature highlight](img/feature_highlight_example.png)
You can dismiss any feature highlight permanently by clicking the **Got it** link
at the bottom of the information window. You cannot restore a feature highlight
after you dismiss it.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@ -86,10 +86,11 @@ There are several types of users in GitLab:
## User activity
You can follow or unfollow other users from their [user profiles](profile/index.md#access-your-user-profile).
To see their activity in the top-level Activity view, select Follow or Unfollow, and select
the Followed Users tab:
To view a user's activity in a top-level Activity view:
![Follow users](img/activity_followed_users_v13_9.png)
1. From a user's profile, select **Follow**.
1. In the GitLab menu, select **Activity**.
1. Select the **Followed users** tab.
## Projects

View File

@ -143,6 +143,10 @@ But you have the option to [invite](members/share_project_with_groups.md)
the Subgroup Y to the Project A so that their members also become eligible
Code Owners:
NOTE:
If you do not invite Subgroup Y to Project A, but make them Code Owners, their approval
of the merge request becomes optional.
![Invite subgroup members to become eligible Code Owners](img/code_owners_invite_members_v13_4.png)
After being invited, any member (`@user`) of the group or subgroup can be set

View File

@ -62,9 +62,16 @@ For this association to succeed, each GitHub author and assignee in the reposito
must meet one of the following conditions prior to the import:
- Have previously logged in to a GitLab account using the GitHub icon.
- Have a GitHub account with a publicly visible
[primary email address](https://docs.github.com/en/rest/reference/users#get-a-user)
on their profile that matches their GitLab account's primary or secondary email address.
- Have a GitHub account with a [public-facing email address](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address)
that matches their GitLab account's email address.
NOTE:
GitLab content imports that use GitHub accounts require that the GitHub public-facing
email address is populated so that all comments and contributions are properly mapped
to the same user in GitLab. GitHub Enterprise (on premise) does not require this field
to be populated to use the product, so you may need to add it on existing GitHub Enterprise
accounts for imported content to be properly mapped to the user in the new system.
Refer to GitHub documentation for instructions on how to add that address.
If a user referenced in the project is not found in the GitLab database, the project creator (typically the user
that initiated the import process) is set as the author/assignee, but a note on the issue mentioning the original

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -20,9 +20,7 @@ This feature is [also available at the group level](../../group/insights/index.m
## View your project's Insights
You can access your project's Insights by clicking the **Analytics > Insights**
link in the left sidebar:
![Insights sidebar link](img/insights_sidebar_link_v12_8.png)
link in the left sidebar.
## Configure your Insights

View File

@ -4,32 +4,50 @@ group: Ecosystem
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Hangouts Chat service **(FREE)**
# Google Chat integration **(FREE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/43756) in GitLab 11.2.
The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
Integrate your project to send notifications from GitLab to a
room of your choice in [Google Chat](https://chat.google.com/) (former Google
Hangouts).
## On Hangouts Chat
## How it works
1. Open the chat room in which you want to see the notifications.
1. From the chat room menu, select **Configure Webhooks**.
1. Click on **ADD WEBHOOK** and fill in the name of the bot to post the messages. Optionally define an avatar.
1. Click **SAVE** and copy the **Webhook URL** of your webhook.
To enable this integration, first you need to create a webhook for the room in
Google Chat where you want to receive the nofications from your project.
See also [the Hangouts Chat documentation for configuring incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks)
After that, enable the integration in GitLab and choose the events you want to
be notified about in your Google Chat room.
## On GitLab
For every selected event in your project, GitLab acts like a bot sending
notifications to Google Chat:
When you have the **Webhook URL** for your Hangouts Chat room webhook, you can set up the GitLab service.
![Google Chat integration illustration](img/google_chat_integration_v13_11.png)
1. Navigate to the [Integrations page](overview.md#accessing-integrations) in your project's settings, i.e. **Project > Settings > Integrations**.
1. Select the **Hangouts Chat** integration to configure it.
1. Ensure that the **Active** toggle is enabled.
1. Check the checkboxes corresponding to the GitLab events you want to receive.
1. Paste the **Webhook URL** that you copied from the Hangouts Chat configuration step.
1. Configure the remaining options and click `Save changes`.
## In Google Chat
Your Hangouts Chat room now starts receiving GitLab event notifications as configured.
Select a room and create a webhook:
![Hangouts Chat configuration](img/hangouts_chat_configuration.png)
1. Enter the room where you want to receive notifications from GitLab.
1. Open the room dropdown menu on the top-left and select **Manage webhooks**.
1. Enter the name for your webhook, for example "GitLab integration".
1. (Optional) Add an avatar for your bot.
1. Select **Save**.
1. Copy the webhook URL.
For further details, see [the Google Chat documentation for configuring webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks).
## In GitLab
Enable the Google Chat integration in GitLab:
1. In your project, go to **Settings > Integrations** and select **Google Chat**.
1. Scroll down to the end of the page where you find a **Webhook** field.
1. Enter the webhook URL you copied from Google Chat.
1. Select the events you want to be notified about in your Google Chat room.
1. (Optional) Select **Test settings** to verify the connection.
1. Select **Save changes**.
To test the integration, make a change based on the events you selected and
see the notification in your Google Chat room.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -39,9 +39,9 @@ Click on the service links to see further configuration instructions and details
| [Emails on push](emails_on_push.md) | Send commits and diff of each push by email. | **{dotted-circle}** No |
| [EWM](ewm.md) | Use IBM Engineering Workflow Management as the issue tracker. | **{dotted-circle}** No |
| [External wiki](../wiki/index.md#link-an-external-wiki) | Link an external wiki. | **{dotted-circle}** No |
| Flowdock | Use Flowdock with GitLab. | **{dotted-circle}** No |
| [Flowdock](../../../api/services.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No |
| [GitHub](github.md) | Obtain statuses for commits and pull requests. | **{dotted-circle}** No |
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications. | **{dotted-circle}** No |
| [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat.| **{dotted-circle}** No |
| [Irker (IRC gateway)](irker.md) | Send IRC messages. | **{dotted-circle}** No |
| [Jenkins](../../../integration/jenkins.md) | Run CI/CD pipelines with Jenkins. | **{check-circle}** Yes |
| JetBrains TeamCity CI | Run CI/CD pipelines with TeamCity. | **{check-circle}** Yes |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@ -28,22 +28,14 @@ This is where the group sharing feature can be of use.
To share 'Project Acme' with the 'Engineering' group:
1. For 'Project Acme' use the left navigation menu to go to **Members**.
![share project with groups](img/share_project_with_groups_tab_v13_9.png)
1. Select the **Invite group** tab.
1. Add the 'Engineering' group with the maximum access level of your choice.
1. Optionally, select an expiring date.
1. Click **Invite**.
1. After sharing 'Project Acme' with 'Engineering':
- The group is listed in the **Groups** tab.
!['Engineering' group is listed in Groups tab](img/project_groups_tab_v13_9.png)
- The project is listed on the group dashboard.
!['Project Acme' is listed as a shared project for 'Engineering'](img/other_group_sees_shared_project_v13_8.png)
Note that you can only share a project with:
- groups for which you have an explicitly defined membership

View File

@ -13,7 +13,5 @@ time_frame: <%= time_frame %>
data_source:
distribution:
<%= distribution %>
# tier:
# - free
# - premium
# - ultimate
tier:
<%= tier %>

View File

@ -88,11 +88,8 @@ module API
end
def fetch_package(file_name:, project: nil, group: nil)
order_by_package_file = false
if Feature.enabled?(:maven_packages_group_level_improvements, default_enabled: :yaml)
order_by_package_file = file_name.include?(::Packages::Maven::Metadata.filename) &&
!params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
end
order_by_package_file = file_name.include?(::Packages::Maven::Metadata.filename) &&
!params[:path].include?(::Packages::Maven::FindOrCreatePackageService::SNAPSHOT_TERM)
::Packages::Maven::PackageFinder.new(
params[:path],

View File

@ -53,9 +53,11 @@ module Gitlab
end
def distribution
value = ['- ce']
value << '- ee' if ee?
value.join("\n")
(ee? ? ['- ee'] : ['- ce', '- ee']).join("\n")
end
def tier
(ee? ? ['#- premium', '- ultimate'] : ['- free', '- premium', '- ultimate']).join("\n")
end
def milestone

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
module Gitlab
module Database
module MigrationHelpers
module CascadingNamespaceSettings
include Gitlab::Database::MigrationHelpers
# Creates the four required columns that constitutes a single cascading
# namespace settings attribute. This helper is only appropriate if the
# setting is not already present as a non-cascading attribute.
#
# Creates the `setting_name` column along with the `lock_setting_name`
# column in both `namespace_settings` and `application_settings`.
#
# This helper is not reversible and must be defined in conjunction with
# `remove_cascading_namespace_setting` in separate up and down directions.
#
# setting_name - The name of the cascading attribute - same as defined
# in `NamespaceSetting` with the `cascading_attr` method.
# type - The column type for the setting itself (:boolean, :integer, etc.)
# options - Standard Rails column options hash. Accepts keys such as
# `null` and `default`.
#
# `null` and `default` options will only be applied to the `application_settings`
# column. In most cases, a non-null default value should be specified.
def add_cascading_namespace_setting(setting_name, type, **options)
lock_column_name = "lock_#{setting_name}".to_sym
check_cascading_namespace_setting_consistency(setting_name, lock_column_name)
namespace_options = options.merge(null: true, default: nil)
with_lock_retries do
add_column(:namespace_settings, setting_name, type, namespace_options)
add_column(:namespace_settings, lock_column_name, :boolean, default: false, null: false)
end
add_column(:application_settings, setting_name, type, options)
add_column(:application_settings, lock_column_name, :boolean, default: false, null: false)
end
def remove_cascading_namespace_setting(setting_name)
lock_column_name = "lock_#{setting_name}".to_sym
with_lock_retries do
remove_column(:namespace_settings, setting_name) if column_exists?(:namespace_settings, setting_name)
remove_column(:namespace_settings, lock_column_name) if column_exists?(:namespace_settings, lock_column_name)
end
remove_column(:application_settings, setting_name) if column_exists?(:application_settings, setting_name)
remove_column(:application_settings, lock_column_name) if column_exists?(:application_settings, lock_column_name)
end
private
def check_cascading_namespace_setting_consistency(setting_name, lock_name)
existing_columns = []
%w(namespace_settings application_settings).each do |table|
existing_columns << "#{table}.#{setting_name}" if column_exists?(table.to_sym, setting_name)
existing_columns << "#{table}.#{lock_name}" if column_exists?(table.to_sym, lock_name)
end
return if existing_columns.empty?
raise <<~ERROR
One or more cascading namespace columns already exist. `add_cascading_namespace_setting` helper
can only be used for new settings, when none of the required columns already exist.
Existing columns: #{existing_columns.join(', ')}
ERROR
end
end
end
end
end

View File

@ -1937,6 +1937,9 @@ msgstr ""
msgid "Add bold text"
msgstr ""
msgid "Add broadcast message"
msgstr ""
msgid "Add child epic to an epic"
msgstr ""
@ -4935,6 +4938,9 @@ msgstr ""
msgid "Be careful. Renaming a project's repository can have unintended side effects."
msgstr ""
msgid "Before enabling this integration, create a webhook for the room in Google Chat where you want to receive notifications from this project. %{docs_link}"
msgstr ""
msgid "Before inserting code, be sure to read the comment that separated each code group."
msgstr ""
@ -13930,10 +13936,13 @@ msgstr ""
msgid "Flags"
msgstr ""
msgid "FlowdockService|Flowdock Git source token"
msgid "FlowdockService|1b609b52537..."
msgstr ""
msgid "FlowdockService|Flowdock is a collaboration web app for technical teams."
msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows."
msgstr ""
msgid "FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}"
msgstr ""
msgid "Focus filter bar"
@ -16082,6 +16091,9 @@ msgstr ""
msgid "How do I mirror repositories?"
msgstr ""
msgid "How do I set up a Google Chat webhook?"
msgstr ""
msgid "How do I set up this service?"
msgstr ""
@ -31166,16 +31178,28 @@ msgstr ""
msgid "TestReports|Jobs"
msgstr ""
msgid "TestReports|Learn how to upload pipeline test reports"
msgstr ""
msgid "TestReports|Learn more about pipeline test reports"
msgstr ""
msgid "TestReports|No test cases were found in the test report."
msgstr ""
msgid "TestReports|Tests"
msgstr ""
msgid "TestReports|There are no test cases to display."
msgstr ""
msgid "TestReports|There are no test reports for this pipeline"
msgstr ""
msgid "TestReports|There are no test suites to show."
msgstr ""
msgid "TestReports|There are no tests to show."
msgid "TestReports|There are no tests to display"
msgstr ""
msgid "TestReports|There was an error fetching the summary."
@ -31184,6 +31208,9 @@ msgstr ""
msgid "TestReports|There was an error fetching the test suite."
msgstr ""
msgid "TestReports|You can configure your job to use unit test reports, and GitLab displays a report here and in the related merge request."
msgstr ""
msgid "Tests"
msgstr ""
@ -33888,6 +33915,9 @@ msgstr ""
msgid "Update approvers"
msgstr ""
msgid "Update broadcast message"
msgstr ""
msgid "Update failed"
msgstr ""

View File

@ -109,6 +109,20 @@ FactoryBot.define do
end
end
trait :cilium do
monitoring_tool { Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:cilium] }
payload do
{
annotations: {
title: 'This is a cilium alert',
summary: 'Summary of the alert',
description: 'Description of the alert'
},
startsAt: started_at
}.with_indifferent_access
end
end
trait :all_fields do
with_issue
with_assignee

View File

@ -47,23 +47,7 @@ RSpec.describe ::Packages::Maven::PackageFinder do
context 'within a group' do
let(:param_group) { group }
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
expect(finder).to receive(:packages_visible_to_user).with(user, within_group: group).and_call_original
end
it_behaves_like 'handling valid and invalid paths'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
expect(finder).not_to receive(:packages_visible_to_user)
end
it_behaves_like 'handling valid and invalid paths'
end
it_behaves_like 'handling valid and invalid paths'
end
context 'across all projects' do
@ -93,38 +77,14 @@ RSpec.describe ::Packages::Maven::PackageFinder do
create(:package_file, :xml, package: package2)
end
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
expect(finder).not_to receive(:versionless_package?)
end
context 'without order by package file' do
it { is_expected.to eq(package3) }
end
context 'with order by package file' do
let(:param_order_by_package_file) { true }
it { is_expected.to eq(package2) }
end
context 'without order by package file' do
it { is_expected.to eq(package3) }
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
expect(finder).to receive(:versionless_package?).and_call_original
end
context 'with order by package file' do
let(:param_order_by_package_file) { true }
context 'without order by package file' do
it { is_expected.to eq(package2) }
end
context 'with order by package file' do
let(:param_order_by_package_file) { true }
it { is_expected.to eq(package2) }
end
it { is_expected.to eq(package2) }
end
end
end

View File

@ -0,0 +1,19 @@
---
key_path: counts_weekly.test_metric
name: test metric name
description:
product_section:
product_stage:
product_group:
product_category:
value_type: number
status: implemented
milestone: "13.9"
introduced_by_url:
time_frame: 7d
data_source:
distribution:
- ee
tier:
#- premium
- ultimate

View File

@ -15,8 +15,8 @@ time_frame: 7d
data_source:
distribution:
- ce
# Add here corresponding tiers
# tier:
# - free
# - premium
# - ultimate
- ee
tier:
- free
- premium
- ultimate

View File

@ -0,0 +1,45 @@
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import EmptyState, { i18n } from '~/pipelines/components/test_reports/empty_state.vue';
describe('Test report empty state', () => {
let wrapper;
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const createComponent = ({ hasTestReport = true } = {}) => {
wrapper = shallowMount(EmptyState, {
provide: {
emptyStateImagePath: '/image/path',
hasTestReport,
},
stubs: {
GlEmptyState,
},
});
};
describe('when pipeline has a test report', () => {
it('should render empty test report message', () => {
createComponent();
expect(findEmptyState().props()).toMatchObject({
primaryButtonText: i18n.noTestsButton,
description: i18n.noTestsDescription,
title: i18n.noTestsTitle,
});
});
});
describe('when pipeline does not have a test report', () => {
it('should render no test report message', () => {
createComponent({ hasTestReport: false });
expect(findEmptyState().props()).toMatchObject({
primaryButtonText: i18n.noReportsButton,
description: i18n.noReportsDescription,
title: i18n.noReportsTitle,
});
});
});
});

View File

@ -2,6 +2,8 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { getJSONFixture } from 'helpers/fixtures';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EmptyState from '~/pipelines/components/test_reports/empty_state.vue';
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
import TestSummary from '~/pipelines/components/test_reports/test_summary.vue';
import TestSummaryTable from '~/pipelines/components/test_reports/test_summary_table.vue';
@ -16,11 +18,11 @@ describe('Test reports app', () => {
const testReports = getJSONFixture('pipelines/test_report.json');
const loadingSpinner = () => wrapper.find(GlLoadingIcon);
const testsDetail = () => wrapper.find('[data-testid="tests-detail"]');
const noTestsToShow = () => wrapper.find('[data-testid="no-tests-to-show"]');
const testSummary = () => wrapper.find(TestSummary);
const testSummaryTable = () => wrapper.find(TestSummaryTable);
const loadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const testsDetail = () => wrapper.findByTestId('tests-detail');
const emptyState = () => wrapper.findComponent(EmptyState);
const testSummary = () => wrapper.findComponent(TestSummary);
const testSummaryTable = () => wrapper.findComponent(TestSummaryTable);
const actionSpies = {
fetchTestSuite: jest.fn(),
@ -29,7 +31,7 @@ describe('Test reports app', () => {
removeSelectedSuiteIndex: jest.fn(),
};
const createComponent = (state = {}) => {
const createComponent = ({ state = {}, hasTestReport = true } = {}) => {
store = new Vuex.Store({
state: {
isLoading: false,
@ -41,10 +43,15 @@ describe('Test reports app', () => {
getters,
});
wrapper = shallowMount(TestReports, {
store,
localVue,
});
wrapper = extendedWrapper(
shallowMount(TestReports, {
store,
localVue,
provide: {
hasTestReport,
},
}),
);
};
afterEach(() => {
@ -52,33 +59,34 @@ describe('Test reports app', () => {
});
describe('when component is created', () => {
beforeEach(() => {
it('should call fetchSummary when pipeline has test report', () => {
createComponent();
expect(actionSpies.fetchSummary).toHaveBeenCalled();
});
it('should call fetchSummary', () => {
expect(actionSpies.fetchSummary).toHaveBeenCalled();
it('should not call fetchSummary when pipeline does not have test report', () => {
createComponent({ state: {}, hasTestReport: false });
expect(actionSpies.fetchSummary).not.toHaveBeenCalled();
});
});
describe('when loading', () => {
beforeEach(() => createComponent({ isLoading: true }));
beforeEach(() => createComponent({ state: { isLoading: true } }));
it('shows the loading spinner', () => {
expect(noTestsToShow().exists()).toBe(false);
expect(emptyState().exists()).toBe(false);
expect(testsDetail().exists()).toBe(false);
expect(loadingSpinner().exists()).toBe(true);
});
});
describe('when the api returns no data', () => {
beforeEach(() => createComponent({ testReports: {} }));
it('displays empty state component', () => {
createComponent({ state: { testReports: {} } });
it('displays that there are no tests to show', () => {
const noTests = noTestsToShow();
expect(noTests.exists()).toBe(true);
expect(noTests.text()).toBe('There are no tests to show.');
expect(emptyState().exists()).toBe(true);
});
});
@ -97,7 +105,7 @@ describe('Test reports app', () => {
describe('when a suite is clicked', () => {
beforeEach(() => {
createComponent({ hasFullReport: true });
createComponent({ state: { hasFullReport: true } });
testSummaryTable().vm.$emit('row-click', 0);
});
@ -109,7 +117,7 @@ describe('Test reports app', () => {
describe('when clicking back to summary', () => {
beforeEach(() => {
createComponent({ selectedSuiteIndex: 0 });
createComponent({ state: { selectedSuiteIndex: 0 } });
testSummary().vm.$emit('on-back-click');
});

View File

@ -33,19 +33,6 @@ describe('~/whats_new/utils/notification', () => {
expect(notificationEl.classList).toContain('with-notifications');
});
it('removes class and count element when legacy storage key is false', () => {
const notificationEl = findNotificationEl();
notificationEl.classList.add('with-notifications');
localStorage.setItem('display-whats-new-notification-13.10', 'false');
expect(findNotificationCountEl()).toExist();
subject();
expect(findNotificationCountEl()).not.toExist();
expect(notificationEl.classList).not.toContain('with-notifications');
});
it('removes class and count element when storage key has current digest', () => {
const notificationEl = findNotificationEl();
notificationEl.classList.add('with-notifications');

View File

@ -20,20 +20,37 @@ RSpec.describe Gitlab::UsageMetricDefinitionGenerator do
end
describe 'Creating metric definition file' do
let(:sample_metric) { load_sample_metric_definition(filename: sample_filename) }
# Stub version so that `milestone` key remains constant between releases to prevent flakiness.
before do
stub_const('Gitlab::VERSION', '13.9.0')
allow(::Gitlab::Usage::Metrics::NamesSuggestions::Generator).to receive(:generate).and_return('test metric name')
end
let(:sample_metric) { load_sample_metric_definition(filename: 'sample_metric_with_name_suggestions.yml') }
context 'without ee option' do
let(:sample_filename) { 'sample_metric_with_name_suggestions.yml' }
let(:metric_definition_path) { Dir.glob(File.join(temp_dir, 'metrics/counts_7d/*_test_metric.yml')).first }
it 'creates a metric definition file using the template' do
described_class.new([key_path], { 'dir' => dir }).invoke_all
it 'creates a metric definition file using the template' do
described_class.new([key_path], { 'dir' => dir }).invoke_all
expect(YAML.safe_load(File.read(metric_definition_path))).to eq(sample_metric)
end
end
metric_definition_path = Dir.glob(File.join(temp_dir, 'metrics/counts_7d/*_test_metric.yml')).first
context 'with ee option' do
let(:sample_filename) { 'sample_metric_with_ee.yml' }
let(:metric_definition_path) { Dir.glob(File.join(temp_dir, 'ee/config/metrics/counts_7d/*_test_metric.yml')).first }
expect(YAML.safe_load(File.read(metric_definition_path))).to eq(sample_metric)
before do
stub_const("#{described_class}::TOP_LEVEL_DIR", 'config')
stub_const("#{described_class}::TOP_LEVEL_DIR_EE", File.join(temp_dir, 'ee'))
end
it 'creates a metric definition file using the template' do
described_class.new([key_path], { 'dir' => dir, 'ee': true }).invoke_all
expect(YAML.safe_load(File.read(metric_definition_path))).to eq(sample_metric)
end
end
end

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings do
let(:migration) do
ActiveRecord::Migration.new.extend(described_class)
end
describe '#add_cascading_namespace_setting' do
it 'creates the required columns', :aggregate_failures do
expect(migration).to receive(:add_column).with(:namespace_settings, :some_setting, :integer, null: true, default: nil)
expect(migration).to receive(:add_column).with(:namespace_settings, :lock_some_setting, :boolean, null: false, default: false)
expect(migration).to receive(:add_column).with(:application_settings, :some_setting, :integer, null: false, default: 5)
expect(migration).to receive(:add_column).with(:application_settings, :lock_some_setting, :boolean, null: false, default: false)
migration.add_cascading_namespace_setting(:some_setting, :integer, null: false, default: 5)
end
context 'when columns already exist' do
before do
migration.add_column(:namespace_settings, :cascading_setting, :integer)
migration.add_column(:application_settings, :lock_cascading_setting, :boolean)
end
it 'raises an error when some columns already exist' do
expect do
migration.add_cascading_namespace_setting(:cascading_setting, :integer)
end.to raise_error %r/Existing columns: namespace_settings.cascading_setting, application_settings.lock_cascading_setting/
end
end
end
describe '#remove_cascading_namespace_setting' do
before do
allow(migration).to receive(:column_exists?).and_return(true)
end
it 'removes the columns', :aggregate_failures do
expect(migration).to receive(:remove_column).with(:namespace_settings, :some_setting)
expect(migration).to receive(:remove_column).with(:namespace_settings, :lock_some_setting)
expect(migration).to receive(:remove_column).with(:application_settings, :some_setting)
expect(migration).to receive(:remove_column).with(:application_settings, :lock_some_setting)
migration.remove_cascading_namespace_setting(:some_setting)
end
end
end

View File

@ -63,7 +63,7 @@ RSpec.describe Gitlab::UsageDataQueries do
it 'returns the histogram sql' do
expect(described_class.histogram(AlertManagement::HttpIntegration.active,
:project_id, buckets: 1..2, bucket_size: 101))
.to eq('WITH "count_cte" AS (SELECT COUNT(*) AS count_grouped FROM "alert_management_http_integrations" WHERE "alert_management_http_integrations"."active" = TRUE GROUP BY "alert_management_http_integrations"."project_id") SELECT WIDTH_BUCKET("count_cte"."count_grouped", 1, 2, 100) AS buckets, "count_cte"."count" FROM "count_cte" GROUP BY buckets ORDER BY buckets')
.to match(/^WITH "count_cte" AS #{Gitlab::Database::AsWithMaterialized.materialized_if_supported}/)
end
end

View File

@ -114,18 +114,6 @@ RSpec.describe Packages::Package, type: :model do
expect(subject).to match_array([package1, package2])
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it 'returns package1 and package2' do
expect(projects).to receive(:any?).and_call_original
expect(subject).to match_array([package1, package2])
end
end
end
describe 'validations' do

View File

@ -124,6 +124,7 @@ RSpec.describe WikiPage::Meta do
context 'the slug is already in the DB (but not canonical)' do
let_it_be(:slug_record) { create(:wiki_page_slug, wiki_page_meta: meta) }
let(:slug) { slug_record.slug }
let(:query_limit) { 4 }
@ -132,6 +133,7 @@ RSpec.describe WikiPage::Meta do
context 'the slug is already in the DB (and canonical)' do
let_it_be(:slug_record) { create(:wiki_page_slug, :canonical, wiki_page_meta: meta) }
let(:slug) { slug_record.slug }
let(:query_limit) { 4 }

View File

@ -640,6 +640,7 @@ RSpec.describe WikiPage do
let_it_be(:existing_page) { create_wiki_page(title: 'test page') }
let_it_be(:directory_page) { create_wiki_page(title: 'parent directory/child page') }
let_it_be(:page_with_special_characters) { create_wiki_page(title: 'test+page') }
let(:untitled_page) { described_class.new(wiki) }
where(:page, :title, :changed) do

View File

@ -299,22 +299,6 @@ RSpec.describe API::MavenPackages do
end
end
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'handling all conditions'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'handling all conditions'
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
@ -346,22 +330,6 @@ RSpec.describe API::MavenPackages do
it_behaves_like 'processing HEAD requests', instance_level: true
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'processing HEAD requests', instance_level: true
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'processing HEAD requests', instance_level: true
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
@ -468,8 +436,7 @@ RSpec.describe API::MavenPackages do
subject
status = Feature.enabled?(:maven_packages_group_level_improvements, default_enabled: :yaml) ? :not_found : :forbidden
expect(response).to have_gitlab_http_status(status)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'denies download when no private token' do
@ -594,22 +561,6 @@ RSpec.describe API::MavenPackages do
end
end
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'handling all conditions'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'handling all conditions'
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
@ -639,22 +590,6 @@ RSpec.describe API::MavenPackages do
let(:path) { package.maven_metadatum.path }
let(:url) { "/groups/#{group.id}/-/packages/maven/#{path}/#{package_file.file_name}" }
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'processing HEAD requests'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'processing HEAD requests'
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
@ -743,22 +678,6 @@ RSpec.describe API::MavenPackages do
end
end
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'handling all conditions'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'handling all conditions'
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)
@ -789,22 +708,6 @@ RSpec.describe API::MavenPackages do
let(:path) { package.maven_metadatum.path }
let(:url) { "/projects/#{project.id}/packages/maven/#{path}/#{package_file.file_name}" }
context 'with maven_packages_group_level_improvements enabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: true)
end
it_behaves_like 'processing HEAD requests'
end
context 'with maven_packages_group_level_improvements disabled' do
before do
stub_feature_flags(maven_packages_group_level_improvements: false)
end
it_behaves_like 'processing HEAD requests'
end
context 'with check_maven_path_first enabled' do
before do
stub_feature_flags(check_maven_path_first: true)

View File

@ -30,39 +30,6 @@ RSpec.describe MemberSerializer do
.from(nil).to(true)
.and change(group_member, :last_blocked_owner).from(nil).to(false)
end
context "with LastGroupOwnerAssigner query improvements" do
it "avoids N+1 database queries for last group owner assignment in MembersPresenter" do
group_member = create(:group_member, group: group)
control_count = ActiveRecord::QueryRecorder.new { member_last_owner_with_preload([group_member]) }.count
group_members = create_list(:group_member, 3, group: group)
expect { member_last_owner_with_preload(group_members) }.not_to exceed_query_limit(control_count)
end
it "avoids N+1 database queries for last blocked owner assignment in MembersPresenter" do
group_member = create(:group_member, group: group)
control_count = ActiveRecord::QueryRecorder.new { member_last_blocked_owner_with_preload([group_member]) }.count
group_members = create_list(:group_member, 3, group: group)
expect { member_last_blocked_owner_with_preload(group_members) }.not_to exceed_query_limit(control_count)
end
def member_last_owner_with_preload(members)
assigner_with_preload(members)
members.map { |m| group.member_last_owner?(m) }
end
def member_last_blocked_owner_with_preload(members)
assigner_with_preload(members)
members.map { |m| group.member_last_blocked_owner?(m) }
end
def assigner_with_preload(members)
MembersPreloader.new(members).preload_all
Members::LastGroupOwnerAssigner.new(group, members).execute
end
end
end
context 'project member' do