Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-17 00:15:24 +00:00
parent 040df42a88
commit b49ce524ed
36 changed files with 606 additions and 161 deletions

View File

@ -1,8 +1,10 @@
<script>
import { GlBadge, GlTab, GlTabs } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { GlBadge, GlPagination, GlTab, GlTabs } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import { updateHistory, setUrlParams, queryToObject } from '~/lib/utils/url_utility';
import environmentAppQuery from '../graphql/queries/environment_app.query.graphql';
import pollIntervalQuery from '../graphql/queries/poll_interval.query.graphql';
import pageInfoQuery from '../graphql/queries/page_info.query.graphql';
import EnvironmentFolder from './new_environment_folder.vue';
import EnableReviewAppModal from './enable_review_app_modal.vue';
@ -11,6 +13,7 @@ export default {
EnvironmentFolder,
EnableReviewAppModal,
GlBadge,
GlPagination,
GlTab,
GlTabs,
},
@ -20,6 +23,7 @@ export default {
variables() {
return {
scope: this.scope,
page: this.page ?? 1,
};
},
pollInterval() {
@ -29,6 +33,9 @@ export default {
interval: {
query: pollIntervalQuery,
},
pageInfo: {
query: pageInfoQuery,
},
},
inject: ['newEnvironmentPath', 'canCreateEnvironment'],
i18n: {
@ -36,11 +43,21 @@ export default {
reviewAppButtonLabel: s__('Environments|Enable review app'),
available: __('Available'),
stopped: __('Stopped'),
prevPage: __('Go to previous page'),
nextPage: __('Go to next page'),
next: __('Next'),
prev: __('Prev'),
goto: (page) => sprintf(__('Go to page %{page}'), { page }),
},
modalId: 'enable-review-app-info',
data() {
const scope = new URLSearchParams(window.location.search).get('scope') || 'available';
return { interval: undefined, scope, isReviewAppModalVisible: false };
const { page = '1', scope = 'available' } = queryToObject(window.location.search);
return {
interval: undefined,
isReviewAppModalVisible: false,
page: parseInt(page, 10),
scope,
};
},
computed: {
canSetupReviewApp() {
@ -82,6 +99,19 @@ export default {
stoppedCount() {
return this.environmentApp?.stoppedCount;
},
totalItems() {
return this.pageInfo?.total;
},
itemsPerPage() {
return this.pageInfo?.perPage;
},
},
mounted() {
window.addEventListener('popstate', this.syncPageFromQueryParams);
},
destroyed() {
window.removeEventListener('popstate', this.syncPageFromQueryParams);
this.$apollo.queries.environmentApp.stopPolling();
},
methods: {
showReviewAppModal() {
@ -89,12 +119,30 @@ export default {
},
setScope(scope) {
this.scope = scope;
this.resetPolling();
},
movePage(direction) {
this.moveToPage(this.pageInfo[`${direction}Page`]);
},
moveToPage(page) {
this.page = page;
updateHistory({
url: setUrlParams({ page: this.page }),
title: document.title,
});
this.resetPolling();
},
syncPageFromQueryParams() {
const { page = '1' } = queryToObject(window.location.search);
this.page = parseInt(page, 10);
},
resetPolling() {
this.$apollo.queries.environmentApp.stopPolling();
this.$nextTick(() => {
if (this.interval) {
this.$apollo.queries.environmentApp.startPolling(this.interval);
} else {
this.$apollo.queries.environmentApp.refetch({ scope });
this.$apollo.queries.environmentApp.refetch({ scope: this.scope, page: this.page });
}
});
},
@ -139,5 +187,19 @@ export default {
class="gl-mb-3"
:nested-environment="folder"
/>
<gl-pagination
align="center"
:total-items="totalItems"
:per-page="itemsPerPage"
:value="page"
:next="$options.i18n.next"
:prev="$options.i18n.prev"
:label-previous-page="$options.prevPage"
:label-next-page="$options.nextPage"
:label-page="$options.goto"
@next="movePage('next')"
@previous="movePage('previous')"
@input="moveToPage"
/>
</div>
</template>

View File

@ -1,6 +1,7 @@
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import environmentApp from './queries/environment_app.query.graphql';
import pageInfoQuery from './queries/page_info.query.graphql';
import { resolvers } from './resolvers';
import typeDefs from './typedefs.graphql';
@ -19,6 +20,19 @@ export const apolloProvider = (endpoint) => {
stoppedCount: 0,
},
});
cache.writeQuery({
query: pageInfoQuery,
data: {
pageInfo: {
total: 0,
perPage: 20,
nextPage: 0,
previousPage: 0,
__typename: 'LocalPageInfo',
},
},
});
return new VueApollo({
defaultClient,
});

View File

@ -1,5 +1,5 @@
query getEnvironmentApp($scope: String) {
environmentApp(scope: $scope) @client {
query getEnvironmentApp($page: Int, $scope: String) {
environmentApp(page: $page, scope: $scope) @client {
availableCount
stoppedCount
environments

View File

@ -0,0 +1,8 @@
query getPageInfo {
pageInfo @client {
total
perPage
nextPage
previousPage
}
}

View File

@ -1,9 +1,15 @@
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
convertObjectPropsToCamelCase,
parseIntPagination,
normalizeHeaders,
} from '~/lib/utils/common_utils';
import pollIntervalQuery from './queries/poll_interval.query.graphql';
import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql';
import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql';
import pageInfoQuery from './queries/page_info.query.graphql';
const buildErrors = (errors = []) => ({
errors,
@ -21,9 +27,11 @@ const mapEnvironment = (env) => ({
export const resolvers = (endpoint) => ({
Query: {
environmentApp(_context, { scope }, { cache }) {
return axios.get(endpoint, { params: { nested: true, scope } }).then((res) => {
const interval = res.headers['poll-interval'];
environmentApp(_context, { page, scope }, { cache }) {
return axios.get(endpoint, { params: { nested: true, page, scope } }).then((res) => {
const headers = normalizeHeaders(res.headers);
const interval = headers['POLL-INTERVAL'];
const pageInfo = { ...parseIntPagination(headers), __typename: 'LocalPageInfo' };
if (interval) {
cache.writeQuery({ query: pollIntervalQuery, data: { interval: parseFloat(interval) } });
@ -31,6 +39,11 @@ export const resolvers = (endpoint) => ({
cache.writeQuery({ query: pollIntervalQuery, data: { interval: undefined } });
}
cache.writeQuery({
query: pageInfoQuery,
data: { pageInfo },
});
return {
availableCount: res.data.available_count,
environments: res.data.environments.map(mapNestedEnvironment),

View File

@ -55,10 +55,18 @@ type LocalErrors {
errors: [String!]!
}
type LocalPageInfo {
total: Int!
perPage: Int!
nextPage: Int!
previousPage: Int!
}
extend type Query {
environmentApp: LocalEnvironmentApp
environmentApp(page: Int, scope: String): LocalEnvironmentApp
folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder
environmentToDelete: LocalEnvironment
pageInfo: LocalPageInfo
environmentToRollback: LocalEnvironment
isLastDeployment: Boolean
}

View File

@ -17,6 +17,7 @@ export const BV_HIDE_MODAL = 'bv::hide::modal';
export const BV_HIDE_TOOLTIP = 'bv::hide::tooltip';
export const BV_DROPDOWN_SHOW = 'bv::dropdown::show';
export const BV_DROPDOWN_HIDE = 'bv::dropdown::hide';
export const BV_COLLAPSE_STATE = 'bv::collapse::state';
export const DEFAULT_TH_CLASSES =
'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';

View File

@ -45,7 +45,7 @@
= render 'shared/user_dropdown_instance_review'
- if Gitlab.com_but_not_canary?
%li.d-md-none
= link_to _("Switch to GitLab Next"), "https://next.gitlab.com/"
= link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url
- if current_user_menu?(:sign_out)
%li.divider

View File

@ -15,7 +15,7 @@
%span.logo-text.d-none.d-lg-block.gl-ml-3
= logo_text
- if Gitlab.com_and_canary?
= link_to 'https://next.gitlab.com', class: 'canary-badge bg-transparent', data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: :_noopener do
= link_to Gitlab::Saas.canary_toggle_com_url, class: 'canary-badge bg-transparent', data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: 'noopener noreferrer' do
%span.gl-badge.gl-bg-green-500.gl-text-white.gl-rounded-pill.gl-font-weight-bold.gl-py-1
= _('Next')

View File

@ -20,4 +20,4 @@
= render 'shared/user_dropdown_instance_review'
- if Gitlab.com_but_not_canary?
%li
= link_to _("Switch to GitLab Next"), "https://next.gitlab.com/"
= link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url

View File

@ -10,9 +10,7 @@
.card-header
%strong
= s_('PrometheusService|Custom metrics')
-# haml-lint:disable NoPlainNodes
%span.badge.badge-pill.js-custom-monitored-count 0
-# haml-lint:enable NoPlainNodes
= gl_badge_tag 0, nil, class: 'js-custom-monitored-count'
= link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn gl-button btn-confirm gl-ml-auto js-new-metric-button hidden', data: { qa_selector: 'new_metric_button' }
.card-body
.flash-container.hidden

View File

@ -12,7 +12,7 @@
.card-header
%strong
= s_('PrometheusService|Common metrics')
%span.badge.badge-pill.js-monitored-count 0
= gl_badge_tag 0, nil, class: 'js-monitored-count'
.card-body
.loading-metrics.js-loading-metrics
%p.m-3
@ -28,7 +28,7 @@
= sprite_icon('chevron-lg-right', css_class: 'panel-toggle js-panel-toggle-right' )
= sprite_icon('chevron-lg-down', css_class: 'panel-toggle js-panel-toggle-down hidden' )
= s_('PrometheusService|Missing environment variable')
%span.badge.badge-pill.js-env-var-count 0
= gl_badge_tag 0, nil, class: 'js-env-var-count'
.card-body.hidden
.flash-container
.flash-notice

View File

@ -1,15 +1,12 @@
- count_badge_classes = 'badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm gl-display-none gl-sm-display-inline-flex'
- count_badge_classes = 'gl-display-none gl-sm-display-inline-flex'
= gl_tabs_nav( {class: 'gl-border-b-0 gl-flex-grow-1', data: { testid: 'milestones-filter' } } ) do
= gl_tab_link_to milestones_filter_path(state: 'opened'), { item_active: params[:state].blank? || params[:state] == 'opened' } do
= _('Open')
%span{ class: count_badge_classes }
= counts[:opened]
= gl_tab_counter_badge counts[:opened], { class: count_badge_classes }
= gl_tab_link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc'), { item_active: params[:state] == 'closed' } do
= _('Closed')
%span{ class: count_badge_classes }
= counts[:closed]
= gl_tab_counter_badge counts[:closed], { class: count_badge_classes }
= gl_tab_link_to milestones_filter_path(state: 'all', sort: 'due_date_desc'), { item_active: params[:state] == 'all' } do
= _('All')
%span{ class: count_badge_classes }
= counts[:all]
= gl_tab_counter_badge counts[:all], { class: count_badge_classes }

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class RemoveVulnerabilityFindingLinksAgain < Gitlab::Database::Migration[1.0]
BATCH_SIZE = 50_000
MIGRATION = 'RemoveVulnerabilityFindingLinks'
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('vulnerability_finding_links'),
MIGRATION,
2.minutes,
batch_size: BATCH_SIZE
)
end
def down
# no ops
end
end

View File

@ -0,0 +1 @@
9bbd4c3e396e0de130418e705a370ce629ca507c82fa2ff5bbf085cdf01c2ff3

View File

@ -35,7 +35,7 @@ verification methods:
| Git | Project Snippets | Geo with Gitaly | Gitaly Checksum |
| Git | Personal Snippets | Geo with Gitaly | Gitaly Checksum |
| Git | Group wiki repository | Geo with Gitaly | _Not implemented_ |
| Blobs | User uploads _(file system)_ | Geo with API | _Not implemented_ |
| Blobs | User uploads _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | User uploads _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | LFS objects _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | LFS objects _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
@ -188,8 +188,8 @@ successfully, you must replicate their data using some other means.
|[Project repository](../../../user/project/repository/) | **Yes** (10.2) | **Yes** (10.7) | No | |
|[Project wiki repository](../../../user/project/wiki/) | **Yes** (10.2) | **Yes** (10.7) | No | |
|[Group wiki repository](../../../user/project/wiki/group.md) | [**Yes** (13.10)](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | No | Behind feature flag `geo_group_wiki_repository_replication`, enabled by default. |
|[Uploads](../../uploads.md) | **Yes** (10.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | No | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. |
|[LFS objects](../../lfs/index.md) | **Yes** (10.2) | **Yes**(14.6) | Via Object Storage provider if supported. Native Geo support (Beta). | GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).<br /><br />Replication is behind the feature flag `geo_lfs_object_replication`, enabled by default. Verification is behind the feature flag `geo_lfs_object_verification` enabled by default in 14.6. |
|[Uploads](../../uploads.md) | **Yes** (10.2) | **Yes** (14.6) | Via Object Storage provider if supported. Native Geo support (Beta). | Replication is behind the feature flag `geo_upload_replication`, enabled by default. Verification is behind the feature flag `geo_upload_verification` introduced and enabled by default in 14.6. |
|[LFS objects](../../lfs/index.md) | **Yes** (10.2) | **Yes** (14.6) | Via Object Storage provider if supported. Native Geo support (Beta). | GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).<br /><br />Replication is behind the feature flag `geo_lfs_object_replication`, enabled by default. Verification is behind the feature flag `geo_lfs_object_verification` introduced and enabled by default in 14.6. |
|[Personal snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
|[Project snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
|[CI job artifacts](../../../ci/pipelines/job_artifacts.md) | **Yes** (10.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8923) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only manually using [Integrity Check Rake Task](../../raketasks/check.md) on both sites and comparing the output between them. Job logs also verified on transfer. |

View File

@ -16,6 +16,8 @@ This document lists the configuration options for your GitLab `.gitlab-ci.yml` f
When you are editing your `.gitlab-ci.yml` file, you can validate it with the
[CI Lint](../lint.md) tool.
If you are editing this page, make sure you follow the [CI/CD YAML reference style guide](../../development/cicd/cicd_reference_documentation_guide.md).
## Keywords
A GitLab CI/CD pipeline configuration includes:

View File

@ -33,9 +33,14 @@ Try to avoid using **above** when referring to an example or table in a document
Do not use **above** when referring to versions of the product. Use [**later**](#later) instead.
- Do: In GitLab 14.4 and later...
- Do not: In GitLab 14.4 and above...
- Do not: In GitLab 14.4 and higher...
Use:
- In GitLab 14.4 and later...
Instead of:
- In GitLab 14.4 and above...
- In GitLab 14.4 and higher...
## access level
@ -56,9 +61,14 @@ To view the administrator access level, in the GitLab UI, go to the Admin Area a
An **administrator** is not a [role](#roles) or [permission](#permissions).
- Do: To do this thing, you must be an administrator.
- Do: To do this thing, you must have the administrator access level.
- Do not: To do this thing, you must have the Admin role.
Use:
- To do this thing, you must be an administrator.
- To do this thing, you must have the administrator access level.
Instead of:
- To do this thing, you must have the Admin role.
## Admin Area
@ -67,10 +77,16 @@ This area of the UI says **Admin Area** at the top of the page and on the menu.
## allow, enable
Try to avoid **allow** and **enable**, unless you are talking about security-related features. For example:
Try to avoid **allow** and **enable**, unless you are talking about security-related features.
- Do: Use this feature to create a pipeline.
- Do not: This feature allows you to create a pipeline.
Use:
- You can add a file to your repository.
Instead of:
- This feature allows you to add a file to your repository.
- This feature enables users to add files to their repository.
This phrasing is more active and is from the user perspective, rather than the person who implemented the feature.
[View details in the Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/allow-allows).
@ -126,8 +142,13 @@ Use **text box** to refer to the UI field. Do not use **field** or **box**. For
Don't use a descriptor with **button**.
- Do: Select **Run pipelines**.
- Do not: Select the **Run pipelines** button.
Use:
- Select **Run pipelines**.
Instead of:
- Select the **Run pipelines** button.
## cannot, can not
@ -194,8 +215,8 @@ When writing about the Developer role:
- Do not use the phrase, **if you are a developer** to mean someone who is assigned the Developer
role. Instead, write it out. For example, **if you are assigned the Developer role**.
- To describe a situation where the Developer role is the minimum required:
- Do: at least the Developer role
- Do not: the Developer role or higher
- Use: at least the Developer role
- Instead of: the Developer role or higher
Do not use **Developer permissions**. A user who is assigned the Developer role has a set of associated permissions.
@ -217,8 +238,13 @@ For example:
Use **earlier** when talking about version numbers.
- Do: In GitLab 14.1 and earlier.
- Do not: In GitLab 14.1 and lower.
Use:
- In GitLab 14.1 and earlier.
Instead of:
- In GitLab 14.1 and lower.
## easily
@ -254,8 +280,13 @@ Use lowercase for **epic board**.
Try to avoid **etc.**. Be as specific as you can. Do not use
[**and so on**](#and-so-on) as a replacement.
- Do: You can update objects, like merge requests and issues.
- Do not: You can update objects, like merge requests, issues, etc.
Use:
- You can update objects, like merge requests and issues.
Instead of:
- You can update objects, like merge requests, issues, etc.
## expand
@ -265,8 +296,13 @@ Use **expand** instead of **open** when you are talking about expanding or colla
Use **box** instead of **field** or **text box**.
- Do: In the **Variable name** box, enter `my text`.
- Do not: In the **Variable name** field, enter `my text`.
Use:
- In the **Variable name** box, enter `my text`.
Instead of:
- In the **Variable name** field, enter `my text`.
However, you can make an exception when you are writing a task and you need to refer to all
of the fields at once. For example:
@ -320,8 +356,8 @@ When writing about the Guest role:
- Do not use the phrase, **if you are a guest** to mean someone who is assigned the Guest
role. Instead, write it out. For example, **if you are assigned the Guest role**.
- To describe a situation where the Guest role is the minimum required:
- Do: at least the Guest role
- Do not: the Guest role or higher
- Use: at least the Guest role
- Instead of: the Guest role or higher
Do not use **Guest permissions**. A user who is assigned the Guest role has a set of associated permissions.
@ -337,16 +373,26 @@ Do not use **high availability** or **HA**. Instead, direct readers to the GitLa
Do not use **higher** when talking about version numbers.
- Do: In GitLab 14.4 and later...
- Do not: In GitLab 14.4 and higher...
- Do not: In GitLab 14.4 and above...
Use:
- In GitLab 14.4 and later...
Instead of:
- In GitLab 14.4 and higher...
- In GitLab 14.4 and above...
## hit
Don't use **hit** to mean **press**.
- Do: Press **ENTER**.
- Do not: Hit the **ENTER** button.
Use:
- Press **ENTER**.
Instead of:
- Hit the **ENTER** button.
## I
@ -395,9 +441,14 @@ Do not use:
Use **later** when talking about version numbers.
- Do: In GitLab 14.1 and later...
- Do not: In GitLab 14.1 and higher...
- Do not: In GitLab 14.1 and above...
Use:
- In GitLab 14.1 and later...
Instead of:
- In GitLab 14.1 and higher...
- In GitLab 14.1 and above...
## list
@ -412,8 +463,13 @@ Do not use **log in** or **log on**. Use [sign in](#sign-in) instead. If the use
Do not use **lower** when talking about version numbers.
- Do: In GitLab 14.1 and earlier.
- Do not: In GitLab 14.1 and lower.
Use:
- In GitLab 14.1 and earlier.
Instead of:
- In GitLab 14.1 and lower.
## Maintainer
@ -424,8 +480,8 @@ When writing about the Maintainer role:
- Do not use the phrase, **if you are a maintainer** to mean someone who is assigned the Maintainer
role. Instead, write it out. For example, **if you are assigned the Maintainer role**.
- To describe a situation where the Maintainer role is the minimum required:
- Do: at least the Maintainer role
- Do not: the Maintainer role or higher
- Use: at least the Maintainer role
- Instead of: the Maintainer role or higher
Do not use **Maintainer permissions**. A user who is assigned the Maintainer role has a set of associated permissions.
@ -468,8 +524,14 @@ Do not use **navigate**. Use **go** instead. For example:
Try to avoid **needs to**, because it's wordy. Avoid **should** when you can be more specific. If something is required, use **must**.
- Do: You must set the variable. Or: Set the variable.
- Do not: You need to set the variable.
Use:
- You must set the variable.
- Set the variable.
Instead of:
- You need to set the variable.
**Should** is acceptable for recommended actions or items, or in cases where an event may not
happen. For example:
@ -483,22 +545,37 @@ happen. For example:
Do not use **note that** because it's wordy.
- Do: You can change the settings.
- Do not: Note that you can change the settings.
Use:
- You can change the settings.
Instead of:
- Note that you can change the settings.
## on
When documenting how to select high-level UI elements, use the word **on**.
- Do: `On the left sidebar...`
Use:
- `On the left sidebar...`
Instead of:
- Do not: `From the left sidebar...` or `In the left sidebar...`
## once
The word **once** means **one time**. Don't use it to mean **after** or **when**.
- Do: When the process is complete...
- Do not: Once the process is complete...
Use:
- When the process is complete...
Instead of:
- Once the process is complete...
## only
@ -566,8 +643,8 @@ When writing about the Reporter role:
- Do not use the phrase, **if you are a reporter** to mean someone who is assigned the Reporter
role. Instead, write it out. For example, **if you are assigned the Reporter role**.
- To describe a situation where the Reporter role is the minimum required:
- Do: at least the Reporter role
- Do not: the Reporter role or higher
- Use: at least the Reporter role
- Instead of: the Reporter role or higher
Do not use **Reporter permissions**. A user who is assigned the Reporter role has a set of associated permissions.
@ -589,8 +666,13 @@ Use lowercase for **runners**. These are the agents that run CI/CD jobs. See als
Do not use **(s)** to make a word optionally plural. It can slow down comprehension. For example:
- Do: Select the jobs you want.
- Do not: Select the job(s) you want.
Use:
- Select the jobs you want.
Instead of:
- Select the job(s) you want.
If you can select multiples of something, then write the word as plural.
@ -612,7 +694,12 @@ into separate areas, refer to these areas as sections.
We often think of expandable/collapsible areas as **sections**. When you refer to expanding
or collapsing a section, don't include the word **section**.
- Do: Expand **Auto DevOps**.
Use:
- Expand **Auto DevOps**.
Instead of:
- Do not: Expand the **Auto DevOps** section.
## select
@ -645,8 +732,13 @@ Do not use **simply** or **simple**. If the user doesn't find the process to be
The word **since** indicates a timeframe. For example, **Since 1984, Bon Jovi has existed**. Don't use **since** to mean **because**.
- Do: Because you have the Developer role, you can delete the widget.
- Do not: Since you have the Developer role, you can delete the widget.
Use:
- Because you have the Developer role, you can delete the widget.
Instead of:
- Since you have the Developer role, you can delete the widget.
## slashes
@ -664,8 +756,13 @@ Use **subgroup** (no hyphen) instead of **sub-group**. ([Vale](../testing.md#val
Do not use **that** when describing a noun. For example:
- Do: The file you save...
- Do not: The file **that** you save...
Use:
- The file you save...
Instead of:
- The file **that** you save...
See also [this, these, that, those](#this-these-that-those).
@ -684,8 +781,13 @@ Use **text box** instead of **field** or **box** when referring to the UI elemen
Try to avoid **there is** and **there are**. These phrases hide the subject.
- Do: The bucket has holes.
- Do not: There are holes in the bucket.
Use:
- The bucket has holes.
Instead of:
- There are holes in the bucket.
## they
@ -697,17 +799,17 @@ a gender-neutral pronoun.
Always follow these words with a noun. For example:
- Do: **This setting** improves performance.
- Do not: **This** improves performance.
- Use: **This setting** improves performance.
- Instead of: **This** improves performance.
- Do: **These pants** are the best.
- Do not: **These** are the best.
- Use: **These pants** are the best.
- Instead of: **These** are the best.
- Do: **That droid** is the one you are looking for.
- Do not: **That** is the one you are looking for.
- Use: **That droid** is the one you are looking for.
- Instead of: **That** is the one you are looking for.
- Do: **Those settings** need to be configured. (Or even better, **Configure those settings.**)
- Do not: **Those** need to be configured.
- Use: **Those settings** need to be configured. (Or even better, **Configure those settings.**)
- Instead of: **Those** need to be configured.
## to-do item
@ -736,8 +838,13 @@ Do not use **useful**. If the user doesn't find the process to be useful, we los
When possible, address the reader directly, instead of calling them **users**.
Use the [second person](#you-your-yours), **you**, instead.
- Do: You can configure a pipeline.
- Do not: Users can configure a pipeline.
Use:
- You can configure a pipeline.
Instead of:
- Users can configure a pipeline.
## utilize
@ -756,8 +863,13 @@ Do not use Latin abbreviations. Use **with**, **through**, or **by using** inste
Try to avoid **we** and focus instead on how the user can accomplish something in GitLab.
- Do: Use widgets when you have work you want to organize.
- Do not: We created a feature for you to add widgets.
Use:
- Use widgets when you have work you want to organize.
Instead of:
- We created a feature for you to add widgets.
One exception: You can use **we recommend** instead of **it is recommended** or **GitLab recommends**. ([Vale](../testing.md#vale) rule: [`SubstitutionSuggestions.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/SubstitutionSuggestions.yml))
@ -770,8 +882,13 @@ Do not use **whitelist**. Another option is **allowlist**. ([Vale](../testing.md
Use **you**, **your**, and **yours** instead of [**the user** and **the user's**](#user-users).
Documentation should be from the [point of view](https://design.gitlab.com/content/voice-tone#point-of-view) of the reader.
- Do: You can configure a pipeline.
- Do not: Users can configure a pipeline.
Use:
- You can configure a pipeline.
Instead of:
- Users can configure a pipeline.
<!-- vale on -->
<!-- markdownlint-enable -->

View File

@ -492,10 +492,13 @@ image::screenshot.png[block image,800,450]
Press image:reload.svg[reload,16,opts=interactive] to reload the page.
video::movie.mp4[width=640,start=60,end=140,options=autoplay]
```
video::aHjpOzsQ9YI[youtube]
GitLab does not support embedding YouTube and Vimeo videos in AsciiDoc content.
Use a standard AsciiDoc link:
video::300817511[vimeo]
```plaintext
https://www.youtube.com/watch?v=BlaZ65-b7y0[Link text for the video]
```
### Breaks

View File

@ -344,6 +344,11 @@ Here is an example of milestones with no releases, one release, and two releases
![Milestones with and without Release associations](img/milestone_list_with_releases_v12_5.png)
NOTE:
A subgroup's project releases cannot be associated with a supergroup's milestone. To learn
more, read issue #328054,
[Releases cannot be associated with a supergroup milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/328054).
## Get notified when a release is created
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26001) in GitLab 12.4.

View File

@ -143,7 +143,9 @@ you can choose from:
![Filter MRs by their environment](img/filtering_merge_requests_by_environment_v14_6.png)
When filtering by a deploy date, you must enter the date manually. Deploy dates
When filtering by `Deployed-before` or `Deployed-after`, the date refers to when
the deployment to an environment (triggered by the merge commit) completed successfully.
You must enter the deploy date manually. Deploy dates
use the format `YYYY-MM-DD`, and must be quoted if you wish to specify
both a date and time (`"YYYY-MM-DD HH:MM"`):

View File

@ -14,11 +14,11 @@ module Gitlab
@result_dir = result_dir
end
def observe(version:, name:, &block)
def observe(version:, name:, connection:, &block)
observation = Observation.new(version, name)
observation.success = true
observers = observer_classes.map { |c| c.new(observation, @result_dir) }
observers = observer_classes.map { |c| c.new(observation, @result_dir, connection) }
exception = nil

View File

@ -7,8 +7,8 @@ module Gitlab
class MigrationObserver
attr_reader :connection, :observation, :output_dir
def initialize(observation, output_dir)
@connection = ActiveRecord::Base.connection
def initialize(observation, output_dir, connection)
@connection = connection
@observation = observation
@output_dir = output_dir
end

View File

@ -69,7 +69,7 @@ module Gitlab
instrumentation = Instrumentation.new(result_dir: result_dir)
sorted_migrations.each do |migration|
instrumentation.observe(version: migration.version, name: migration.name) do
instrumentation.observe(version: migration.version, name: migration.name, connection: ActiveRecord::Migration.connection) do
ActiveRecord::Migrator.new(direction, migration_context.migrations, migration_context.schema_migration, migration.version).run
end
end

View File

@ -13,6 +13,10 @@ module Gitlab
'https://staging.gitlab.com'
end
def self.canary_toggle_com_url
'https://next.gitlab.com'
end
def self.subdomain_regex
%r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}.freeze
end

View File

@ -16406,6 +16406,9 @@ msgstr ""
msgid "Go to next page"
msgstr ""
msgid "Go to page %{page}"
msgstr ""
msgid "Go to parent"
msgstr ""

View File

@ -5,6 +5,7 @@ import environmentToRollback from '~/environments/graphql/queries/environment_to
import environmentToDelete from '~/environments/graphql/queries/environment_to_delete.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import pollIntervalQuery from '~/environments/graphql/queries/poll_interval.query.graphql';
import pageInfoQuery from '~/environments/graphql/queries/page_info.query.graphql';
import { TEST_HOST } from 'helpers/test_constants';
import {
environmentsApp,
@ -37,9 +38,11 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should fetch environments and map them to frontend data', async () => {
const cache = { writeQuery: jest.fn() };
const scope = 'available';
mock.onGet(ENDPOINT, { params: { nested: true, scope } }).reply(200, environmentsApp, {});
mock
.onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
.reply(200, environmentsApp, {});
const app = await mockResolvers.Query.environmentApp(null, { scope }, { cache });
const app = await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
expect(app).toEqual(resolvedEnvironmentsApp);
expect(cache.writeQuery).toHaveBeenCalledWith({
query: pollIntervalQuery,
@ -49,14 +52,70 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should set the poll interval when there is one', async () => {
const cache = { writeQuery: jest.fn() };
const scope = 'stopped';
const interval = 3000;
mock
.onGet(ENDPOINT, { params: { nested: true, scope } })
.reply(200, environmentsApp, { 'poll-interval': 3000 });
.onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
.reply(200, environmentsApp, {
'poll-interval': interval,
});
await mockResolvers.Query.environmentApp(null, { scope }, { cache });
await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
expect(cache.writeQuery).toHaveBeenCalledWith({
query: pollIntervalQuery,
data: { interval: 3000 },
data: { interval },
});
});
it('should set page info if there is any', async () => {
const cache = { writeQuery: jest.fn() };
const scope = 'stopped';
mock
.onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
.reply(200, environmentsApp, {
'x-next-page': '2',
'x-page': '1',
'X-Per-Page': '2',
'X-Prev-Page': '',
'X-TOTAL': '37',
'X-Total-Pages': '5',
});
await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
expect(cache.writeQuery).toHaveBeenCalledWith({
query: pageInfoQuery,
data: {
pageInfo: {
total: 37,
perPage: 2,
previousPage: NaN,
totalPages: 5,
nextPage: 2,
page: 1,
__typename: 'LocalPageInfo',
},
},
});
});
it('should not set page info if there is none', async () => {
const cache = { writeQuery: jest.fn() };
const scope = 'stopped';
mock
.onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
.reply(200, environmentsApp, {});
await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
expect(cache.writeQuery).toHaveBeenCalledWith({
query: pageInfoQuery,
data: {
pageInfo: {
__typename: 'LocalPageInfo',
nextPage: NaN,
page: NaN,
perPage: NaN,
previousPage: NaN,
total: NaN,
totalPages: NaN,
},
},
});
});
});

View File

@ -1,9 +1,11 @@
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { GlPagination } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { __, s__ } from '~/locale';
import setWindowLocation from 'helpers/set_window_location_helper';
import { sprintf, __, s__ } from '~/locale';
import EnvironmentsApp from '~/environments/components/new_environments_app.vue';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
@ -14,12 +16,14 @@ describe('~/environments/components/new_environments_app.vue', () => {
let wrapper;
let environmentAppMock;
let environmentFolderMock;
let paginationMock;
const createApolloProvider = () => {
const mockResolvers = {
Query: {
environmentApp: environmentAppMock,
folder: environmentFolderMock,
pageInfo: paginationMock,
},
};
@ -37,9 +41,23 @@ describe('~/environments/components/new_environments_app.vue', () => {
apolloProvider,
});
const createWrapperWithMocked = async ({ provide = {}, environmentsApp, folder }) => {
const createWrapperWithMocked = async ({
provide = {},
environmentsApp,
folder,
pageInfo = {
total: 20,
perPage: 5,
nextPage: 3,
page: 2,
previousPage: 1,
__typename: 'LocalPageInfo',
},
}) => {
setWindowLocation('?scope=available&page=2');
environmentAppMock.mockReturnValue(environmentsApp);
environmentFolderMock.mockReturnValue(folder);
paginationMock.mockReturnValue(pageInfo);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider, provide });
@ -50,6 +68,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
beforeEach(() => {
environmentAppMock = jest.fn();
environmentFolderMock = jest.fn();
paginationMock = jest.fn();
});
afterEach(() => {
@ -118,6 +137,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(button.exists()).toBe(false);
});
describe('tabs', () => {
it('should show tabs for available and stopped environmets', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
@ -133,13 +153,10 @@ describe('~/environments/components/new_environments_app.vue', () => {
});
it('should change the requested scope on tab change', async () => {
environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
environmentFolderMock.mockReturnValue(resolvedFolder);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider });
await waitForPromises();
await nextTick();
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const stopped = wrapper.findByRole('tab', {
name: `${__('Stopped')} ${resolvedEnvironmentsApp.stoppedCount}`,
});
@ -151,9 +168,104 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(environmentAppMock).toHaveBeenCalledWith(
expect.anything(),
{ scope: 'stopped' },
expect.objectContaining({ scope: 'stopped' }),
expect.anything(),
expect.anything(),
);
});
});
describe('pagination', () => {
it('should sync page from query params on load', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
expect(wrapper.findComponent(GlPagination).props('value')).toBe(2);
});
it('should change the requested page on next page click', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const next = wrapper.findByRole('link', {
name: __('Go to next page'),
});
next.trigger('click');
await nextTick();
await waitForPromises();
expect(environmentAppMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ page: 3 }),
expect.anything(),
expect.anything(),
);
});
it('should change the requested page on previous page click', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const prev = wrapper.findByRole('link', {
name: __('Go to previous page'),
});
prev.trigger('click');
await nextTick();
await waitForPromises();
expect(environmentAppMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ page: 1 }),
expect.anything(),
expect.anything(),
);
});
it('should change the requested page on specific page click', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const page = 1;
const pageButton = wrapper.findByRole('link', {
name: sprintf(__('Go to page %{page}'), { page }),
});
pageButton.trigger('click');
await nextTick();
await waitForPromises();
expect(environmentAppMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ page }),
expect.anything(),
expect.anything(),
);
});
it('should sync the query params to the new page', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
});
const next = wrapper.findByRole('link', {
name: __('Go to next page'),
});
next.trigger('click');
await nextTick();
expect(window.location.search).toBe('?scope=available&page=3');
});
});
});

View File

@ -3,6 +3,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Instrumentation do
let(:result_dir) { Dir.mktmpdir }
let(:connection) { ActiveRecord::Migration.connection }
after do
FileUtils.rm_rf(result_dir)
@ -14,11 +15,11 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
let(:migration_version) { '12345' }
it 'executes the given block' do
expect { |b| subject.observe(version: migration_version, name: migration_name, &b) }.to yield_control
expect { |b| subject.observe(version: migration_version, name: migration_name, connection: connection, &b) }.to yield_control
end
context 'behavior with observers' do
subject { described_class.new(observer_classes: [Gitlab::Database::Migrations::Observers::MigrationObserver], result_dir: result_dir).observe(version: migration_version, name: migration_name) {} }
subject { described_class.new(observer_classes: [Gitlab::Database::Migrations::Observers::MigrationObserver], result_dir: result_dir).observe(version: migration_version, name: migration_name, connection: connection) {} }
let(:observer) { instance_double('Gitlab::Database::Migrations::Observers::MigrationObserver', before: nil, after: nil, record: nil) }
@ -29,7 +30,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
it 'instantiates observer with observation' do
expect(Gitlab::Database::Migrations::Observers::MigrationObserver)
.to receive(:new)
.with(instance_of(Gitlab::Database::Migrations::Observation), anything) { |observation| expect(observation.version).to eq(migration_version) }
.with(instance_of(Gitlab::Database::Migrations::Observation), anything, connection) { |observation| expect(observation.version).to eq(migration_version) }
.and_return(observer)
subject
@ -63,7 +64,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'on successful execution' do
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name) {} }
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name, connection: connection) {} }
it 'records walltime' do
expect(subject.walltime).not_to be_nil
@ -83,7 +84,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
end
context 'upon failure' do
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name) { raise 'something went wrong' } }
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name, connection: connection) { raise 'something went wrong' } }
it 'raises the exception' do
expect { subject }.to raise_error(/something went wrong/)
@ -93,7 +94,7 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
subject { instance.observations.first }
before do
instance.observe(version: migration_version, name: migration_name) { raise 'something went wrong' }
instance.observe(version: migration_version, name: migration_name, connection: connection) { raise 'something went wrong' }
rescue StandardError
# ignore
end
@ -125,8 +126,8 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
let(:migration2) { double('migration2', call: nil) }
it 'records observations for all migrations' do
subject.observe(version: migration_version, name: migration_name) {}
subject.observe(version: migration_version, name: migration_name) { raise 'something went wrong' } rescue nil
subject.observe(version: migration_version, name: migration_name, connection: connection) {}
subject.observe(version: migration_version, name: migration_name, connection: connection) { raise 'something went wrong' } rescue nil
expect(subject.observations.size).to eq(2)
end

View File

@ -2,10 +2,10 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryDetails do
subject { described_class.new(observation, directory_path) }
subject { described_class.new(observation, directory_path, connection) }
let(:connection) { ActiveRecord::Migration.connection }
let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
let(:connection) { ActiveRecord::Base.connection }
let(:query) { "select date_trunc('day', $1::timestamptz) + $2 * (interval '1 hour')" }
let(:query_binds) { [Time.current, 3] }
let(:directory_path) { Dir.mktmpdir }

View File

@ -2,10 +2,10 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryLog do
subject { described_class.new(observation, directory_path) }
subject { described_class.new(observation, directory_path, connection) }
let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
let(:connection) { ActiveRecord::Base.connection }
let(:connection) { ActiveRecord::Migration.connection }
let(:query) { 'select 1' }
let(:directory_path) { Dir.mktmpdir }
let(:migration_version) { 20210422152437 }

View File

@ -2,10 +2,10 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryStatistics do
subject { described_class.new(observation, double("unused path")) }
subject { described_class.new(observation, double("unused path"), connection) }
let(:observation) { Gitlab::Database::Migrations::Observation.new }
let(:connection) { ActiveRecord::Base.connection }
let(:connection) { ActiveRecord::Migration.connection }
def mock_pgss(enabled: true)
if enabled

View File

@ -2,10 +2,10 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::TotalDatabaseSizeChange do
subject { described_class.new(observation, double('unused path')) }
subject { described_class.new(observation, double('unused path'), connection) }
let(:observation) { Gitlab::Database::Migrations::Observation.new }
let(:connection) { ActiveRecord::Base.connection }
let(:connection) { ActiveRecord::Migration.connection }
let(:query) { 'select pg_database_size(current_database())' }
it 'records the size change' do

View File

@ -2,8 +2,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::TransactionDuration do
subject(:transaction_duration_observer) { described_class.new(observation, directory_path) }
subject(:transaction_duration_observer) { described_class.new(observation, directory_path, connection) }
let(:connection) { ActiveRecord::Migration.connection }
let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
let(:directory_path) { Dir.mktmpdir }
let(:log_file) { "#{directory_path}/#{migration_version}_#{migration_name}-transaction-duration.json" }
@ -78,17 +79,17 @@ RSpec.describe Gitlab::Database::Migrations::Observers::TransactionDuration do
end
def run_real_transactions
ActiveRecord::Base.transaction do
ApplicationRecord.transaction do
end
end
def run_sub_transactions
ActiveRecord::Base.transaction(requires_new: true) do
ApplicationRecord.transaction(requires_new: true) do
end
end
def run_transaction
ActiveRecord::Base.connection_pool.with_connection do |connection|
ApplicationRecord.connection_pool.with_connection do |connection|
Gitlab::Database::SharedModel.using_connection(connection) do
Gitlab::Database::SharedModel.transaction do
Gitlab::Database::SharedModel.transaction(requires_new: true) do

View File

@ -76,7 +76,7 @@ RSpec.describe Gitlab::Database::Migrations::Runner do
it 'runs the unapplied migrations in version order', :aggregate_failures do
up.run
expect(migration_runs.map(&:dir)).to eq([:up, :up])
expect(migration_runs.map(&:dir)).to match_array([:up, :up])
expect(migration_runs.map(&:version_to_migrate)).to eq(pending_migrations.map(&:version))
end
end
@ -101,7 +101,7 @@ RSpec.describe Gitlab::Database::Migrations::Runner do
it 'runs the applied migrations for the current branch in reverse order', :aggregate_failures do
down.run
expect(migration_runs.map(&:dir)).to eq([:down, :down])
expect(migration_runs.map(&:dir)).to match_array([:down, :down])
expect(migration_runs.map(&:version_to_migrate)).to eq(applied_migrations_this_branch.reverse.map(&:version))
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Saas do
describe '.canary_toggle_com_url' do
subject { described_class.canary_toggle_com_url }
let(:next_url) { 'https://next.gitlab.com' }
it { is_expected.to eq(next_url) }
end
end