Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5fc3c77e2b
commit
6d5a18ac65
50 changed files with 797 additions and 371 deletions
|
@ -23,14 +23,35 @@ cache-repo:
|
|||
stage: sync
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
TAR_FILENAME: /tmp/gitlab-master.tar
|
||||
SHALLOW_CLONE_TAR_FILENAME: gitlab-master-shallow.tar
|
||||
FULL_CLONE_TAR_FILENAME: gitlab-master.tar
|
||||
before_script:
|
||||
- '[ -z "$CI_REPO_CACHE_CREDENTIALS" ] || gcloud auth activate-service-account --key-file=$CI_REPO_CACHE_CREDENTIALS'
|
||||
script:
|
||||
- cd ..
|
||||
- rm -rf $CI_PROJECT_NAME
|
||||
- git clone --progress $CI_REPOSITORY_URL $CI_PROJECT_NAME
|
||||
- cd $CI_PROJECT_NAME
|
||||
- gcloud auth activate-service-account --key-file=$CI_REPO_CACHE_CREDENTIALS
|
||||
- git remote rm origin
|
||||
- tar cf $TAR_FILENAME .
|
||||
- gzip $TAR_FILENAME
|
||||
- gsutil cp $TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/gitlab-master.tar.gz
|
||||
# Enable shallow repo caching only if the $ENABLE_SHALLOW_REPO_CACHING variable exists
|
||||
- if [ -n "$ENABLE_SHALLOW_REPO_CACHING" ]; then
|
||||
cd .. && rm -rf $CI_PROJECT_NAME;
|
||||
today=$(date +%Y-%m-%d);
|
||||
year=$(date +%Y);
|
||||
last_year=`expr $year - 1`;
|
||||
one_year_ago=$(echo $today | sed "s/$year/$last_year/");
|
||||
echo "Cloning $CI_REPOSITORY_URL into $CI_PROJECT_NAME with commits from $one_year_ago.";
|
||||
time git clone --progress --no-checkout --shallow-since=$one_year_ago $CI_REPOSITORY_URL $CI_PROJECT_NAME;
|
||||
cd $CI_PROJECT_NAME;
|
||||
echo "Archiving $CI_PROJECT_NAME into /tmp/$SHALLOW_CLONE_TAR_FILENAME.";
|
||||
time tar cf /tmp/$SHALLOW_CLONE_TAR_FILENAME .;
|
||||
echo "GZipping /tmp/$SHALLOW_CLONE_TAR_FILENAME.";
|
||||
time gzip /tmp/$SHALLOW_CLONE_TAR_FILENAME;
|
||||
[ -z "$CI_REPO_CACHE_CREDENTIALS" ] || (echo "Uploading /tmp/$SHALLOW_CLONE_TAR_FILENAME.gz to GCloud." && time gsutil cp /tmp/$SHALLOW_CLONE_TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/$SHALLOW_CLONE_TAR_FILENAME.gz);
|
||||
fi
|
||||
# By default, we want to cache the full repo, unless the $DISABLE_FULL_REPO_CACHING variable exists (in the case the shallow clone caching is working well)
|
||||
- if [ -z "$DISABLE_FULL_REPO_CACHING" ]; then
|
||||
cd .. && rm -rf $CI_PROJECT_NAME;
|
||||
echo "Cloning $CI_REPOSITORY_URL into $CI_PROJECT_NAME.";
|
||||
time git clone --progress $CI_REPOSITORY_URL $CI_PROJECT_NAME;
|
||||
echo "Archiving $CI_PROJECT_NAME into /tmp/$FULL_CLONE_TAR_FILENAME.";
|
||||
time tar cf /tmp/$FULL_CLONE_TAR_FILENAME .;
|
||||
echo "GZipping /tmp/$FULL_CLONE_TAR_FILENAME.";
|
||||
time gzip /tmp/$FULL_CLONE_TAR_FILENAME;
|
||||
[ -z "$CI_REPO_CACHE_CREDENTIALS" ] || (echo "Uploading /tmp/$FULL_CLONE_TAR_FILENAME.gz to GCloud." && time gsutil cp /tmp/$FULL_CLONE_TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/$FULL_CLONE_TAR_FILENAME.gz);
|
||||
fi
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
export const pathGenerator = (imageDetails, ending = '?format=json') => {
|
||||
// this method is a temporary workaround, to be removed with graphql implementation
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/276432
|
||||
const basePath = imageDetails.path.replace(`/${imageDetails.name}`, '');
|
||||
const basePath = imageDetails.name
|
||||
? imageDetails.path.replace(`/${imageDetails.name}`, '')
|
||||
: imageDetails.path;
|
||||
return `/${basePath}/registry/repository/${imageDetails.id}/tags${ending}`;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ import { __ } from '~/locale';
|
|||
import ReleaseBlock from './release_block.vue';
|
||||
import ReleasesPagination from './releases_pagination.vue';
|
||||
import ReleaseSkeletonLoader from './release_skeleton_loader.vue';
|
||||
import ReleasesSort from './releases_sort.vue';
|
||||
|
||||
export default {
|
||||
name: 'ReleasesApp',
|
||||
|
@ -16,6 +17,7 @@ export default {
|
|||
ReleaseBlock,
|
||||
ReleasesPagination,
|
||||
ReleaseSkeletonLoader,
|
||||
ReleasesSort,
|
||||
},
|
||||
computed: {
|
||||
...mapState('list', [
|
||||
|
@ -62,16 +64,20 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div class="flex flex-column mt-2">
|
||||
<div class="gl-align-self-end gl-mb-3">
|
||||
<releases-sort class="gl-mr-2" @sort:changed="fetchReleases" />
|
||||
|
||||
<gl-button
|
||||
v-if="newReleasePath"
|
||||
:href="newReleasePath"
|
||||
:aria-describedby="shouldRenderEmptyState && 'releases-description'"
|
||||
category="primary"
|
||||
variant="success"
|
||||
class="align-self-end mb-2 js-new-release-btn"
|
||||
class="js-new-release-btn"
|
||||
>
|
||||
{{ __('New release') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
|
||||
<release-skeleton-loader v-if="isLoading" class="js-loading" />
|
||||
|
||||
|
|
62
app/assets/javascripts/releases/components/releases_sort.vue
Normal file
62
app/assets/javascripts/releases/components/releases_sort.vue
Normal file
|
@ -0,0 +1,62 @@
|
|||
<script>
|
||||
import { GlSorting, GlSortingItem } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { ASCENDING_ODER, DESCENDING_ORDER, SORT_OPTIONS } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'ReleasesSort',
|
||||
components: {
|
||||
GlSorting,
|
||||
GlSortingItem,
|
||||
},
|
||||
computed: {
|
||||
...mapState('list', {
|
||||
orderBy: state => state.sorting.orderBy,
|
||||
sort: state => state.sorting.sort,
|
||||
}),
|
||||
sortOptions() {
|
||||
return SORT_OPTIONS;
|
||||
},
|
||||
sortText() {
|
||||
const option = this.sortOptions.find(s => s.orderBy === this.orderBy);
|
||||
return option.label;
|
||||
},
|
||||
isSortAscending() {
|
||||
return this.sort === ASCENDING_ODER;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('list', ['setSorting']),
|
||||
onDirectionChange() {
|
||||
const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ODER;
|
||||
this.setSorting({ sort });
|
||||
this.$emit('sort:changed');
|
||||
},
|
||||
onSortItemClick(item) {
|
||||
this.setSorting({ orderBy: item });
|
||||
this.$emit('sort:changed');
|
||||
},
|
||||
isActiveSortItem(item) {
|
||||
return this.orderBy === item;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-sorting
|
||||
:text="sortText"
|
||||
:is-ascending="isSortAscending"
|
||||
data-testid="releases-sort"
|
||||
@sortDirectionChange="onDirectionChange"
|
||||
>
|
||||
<gl-sorting-item
|
||||
v-for="item in sortOptions"
|
||||
:key="item.orderBy"
|
||||
:active="isActiveSortItem(item.orderBy)"
|
||||
@click="onSortItemClick(item.orderBy)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</gl-sorting-item>
|
||||
</gl-sorting>
|
||||
</template>
|
|
@ -1,3 +1,5 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
export const MAX_MILESTONES_TO_DISPLAY = 5;
|
||||
|
||||
export const BACK_URL_PARAM = 'back_url';
|
||||
|
@ -12,3 +14,19 @@ export const ASSET_LINK_TYPE = Object.freeze({
|
|||
export const DEFAULT_ASSET_LINK_TYPE = ASSET_LINK_TYPE.OTHER;
|
||||
|
||||
export const PAGE_SIZE = 20;
|
||||
|
||||
export const ASCENDING_ODER = 'asc';
|
||||
export const DESCENDING_ORDER = 'desc';
|
||||
export const RELEASED_AT = 'released_at';
|
||||
export const CREATED_AT = 'created_at';
|
||||
|
||||
export const SORT_OPTIONS = [
|
||||
{
|
||||
orderBy: RELEASED_AT,
|
||||
label: __('Released date'),
|
||||
},
|
||||
{
|
||||
orderBy: CREATED_AT,
|
||||
label: __('Created date'),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
#import "./release.fragment.graphql"
|
||||
|
||||
query allReleases($fullPath: ID!, $first: Int, $last: Int, $before: String, $after: String) {
|
||||
query allReleases(
|
||||
$fullPath: ID!
|
||||
$first: Int
|
||||
$last: Int
|
||||
$before: String
|
||||
$after: String
|
||||
$sort: ReleaseSort
|
||||
) {
|
||||
project(fullPath: $fullPath) {
|
||||
releases(first: $first, last: $last, before: $before, after: $after) {
|
||||
releases(first: $first, last: $last, before: $before, after: $after, sort: $sort) {
|
||||
nodes {
|
||||
...Release
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ fragment Release on Release {
|
|||
tagPath
|
||||
descriptionHtml
|
||||
releasedAt
|
||||
createdAt
|
||||
upcomingRelease
|
||||
assets {
|
||||
count
|
||||
|
|
|
@ -42,6 +42,10 @@ export const fetchReleasesGraphQl = (
|
|||
) => {
|
||||
commit(types.REQUEST_RELEASES);
|
||||
|
||||
const { sort, orderBy } = state.sorting;
|
||||
const orderByParam = orderBy === 'created_at' ? 'created' : orderBy;
|
||||
const sortParams = `${orderByParam}_${sort}`.toUpperCase();
|
||||
|
||||
let paginationParams;
|
||||
if (!before && !after) {
|
||||
paginationParams = { first: PAGE_SIZE };
|
||||
|
@ -60,6 +64,7 @@ export const fetchReleasesGraphQl = (
|
|||
query: allReleasesQuery,
|
||||
variables: {
|
||||
fullPath: state.projectPath,
|
||||
sort: sortParams,
|
||||
...paginationParams,
|
||||
},
|
||||
})
|
||||
|
@ -80,8 +85,10 @@ export const fetchReleasesGraphQl = (
|
|||
export const fetchReleasesRest = ({ dispatch, commit, state }, { page }) => {
|
||||
commit(types.REQUEST_RELEASES);
|
||||
|
||||
const { sort, orderBy } = state.sorting;
|
||||
|
||||
api
|
||||
.releases(state.projectId, { page })
|
||||
.releases(state.projectId, { page, sort, order_by: orderBy })
|
||||
.then(({ data, headers }) => {
|
||||
const restPageInfo = parseIntPagination(normalizeHeaders(headers));
|
||||
const camelCasedReleases = convertObjectPropsToCamelCase(data, { deep: true });
|
||||
|
@ -98,3 +105,5 @@ export const receiveReleasesError = ({ commit }) => {
|
|||
commit(types.RECEIVE_RELEASES_ERROR);
|
||||
createFlash(__('An error occurred while fetching the releases. Please try again.'));
|
||||
};
|
||||
|
||||
export const setSorting = ({ commit }, data) => commit(types.SET_SORTING, data);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const REQUEST_RELEASES = 'REQUEST_RELEASES';
|
||||
export const RECEIVE_RELEASES_SUCCESS = 'RECEIVE_RELEASES_SUCCESS';
|
||||
export const RECEIVE_RELEASES_ERROR = 'RECEIVE_RELEASES_ERROR';
|
||||
export const SET_SORTING = 'SET_SORTING';
|
||||
|
|
|
@ -39,4 +39,8 @@ export default {
|
|||
state.restPageInfo = {};
|
||||
state.graphQlPageInfo = {};
|
||||
},
|
||||
|
||||
[types.SET_SORTING](state, sorting) {
|
||||
state.sorting = { ...state.sorting, ...sorting };
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { DESCENDING_ORDER, RELEASED_AT } from '../../../constants';
|
||||
|
||||
export default ({
|
||||
projectId,
|
||||
projectPath,
|
||||
|
@ -16,4 +18,8 @@ export default ({
|
|||
releases: [],
|
||||
restPageInfo: {},
|
||||
graphQlPageInfo: {},
|
||||
sorting: {
|
||||
sort: DESCENDING_ORDER,
|
||||
orderBy: RELEASED_AT,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -29,7 +29,8 @@ export default {
|
|||
},
|
||||
changing: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -24,10 +24,12 @@ module IconsHelper
|
|||
end
|
||||
|
||||
def custom_icon(icon_name, size: DEFAULT_ICON_SIZE)
|
||||
memoized_icon("#{icon_name}_#{size}") do
|
||||
# We can't simply do the below, because there are some .erb SVGs.
|
||||
# File.read(Rails.root.join("app/views/shared/icons/_#{icon_name}.svg")).html_safe
|
||||
render "shared/icons/#{icon_name}.svg", size: size
|
||||
end
|
||||
end
|
||||
|
||||
def sprite_icon_path
|
||||
@sprite_icon_path ||= begin
|
||||
|
@ -46,6 +48,7 @@ module IconsHelper
|
|||
end
|
||||
|
||||
def sprite_icon(icon_name, size: DEFAULT_ICON_SIZE, css_class: nil)
|
||||
memoized_icon("#{icon_name}_#{size}_#{css_class}") do
|
||||
if known_sprites&.exclude?(icon_name)
|
||||
exception = ArgumentError.new("#{icon_name} is not a known icon in @gitlab-org/gitlab-svg")
|
||||
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception)
|
||||
|
@ -62,6 +65,7 @@ module IconsHelper
|
|||
data: { testid: "#{icon_name}-icon" }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def loading_icon(container: false, color: 'orange', size: 'sm', css_class: nil)
|
||||
css_classes = ['gl-spinner', "gl-spinner-#{color}", "gl-spinner-#{size}"]
|
||||
|
@ -169,4 +173,12 @@ module IconsHelper
|
|||
|
||||
@known_sprites ||= Gitlab::Json.parse(File.read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json')))['icons']
|
||||
end
|
||||
|
||||
def memoized_icon(key)
|
||||
@rendered_icons ||= {}
|
||||
|
||||
@rendered_icons[key] || (
|
||||
@rendered_icons[key] = yield
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add ability to sort releases on Releases page
|
||||
merge_request: 43963
|
||||
author:
|
||||
type: added
|
|
@ -14,7 +14,6 @@ if Gitlab.ee? && Gitlab.dev_or_test_env?
|
|||
# being unique to licensed names. These feature flags should be reworked to
|
||||
# be "development" with explicit check
|
||||
IGNORED_FEATURE_FLAGS = %i[
|
||||
group_wikis
|
||||
swimlanes
|
||||
].to_set
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ exceptions:
|
|||
- CPU
|
||||
- CSS
|
||||
- CSV
|
||||
- DAG
|
||||
- DAST
|
||||
- DNS
|
||||
- EKS
|
||||
|
|
|
@ -452,9 +452,9 @@ CI jobs:
|
|||
from `Dockerfile` that may be overridden in `.gitlab-ci.yml`)
|
||||
1. The runner attaches itself to a running container.
|
||||
1. The runner prepares a script (the combination of
|
||||
[`before_script`](../yaml/README.md#before_script-and-after_script),
|
||||
[`before_script`](../yaml/README.md#before_script),
|
||||
[`script`](../yaml/README.md#script),
|
||||
and [`after_script`](../yaml/README.md#before_script-and-after_script)).
|
||||
and [`after_script`](../yaml/README.md#after_script)).
|
||||
1. The runner sends the script to the container's shell STDIN and receives the
|
||||
output.
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ stages:
|
|||
```
|
||||
|
||||
Setting a step to be performed before and after any job can be done via the
|
||||
[`before_script` and `after_script` keywords](../yaml/README.md#before_script-and-after_script):
|
||||
[`before_script`](../yaml/README.md#before_script) and [`after_script`](../yaml/README.md#after_script) keywords:
|
||||
|
||||
```yaml
|
||||
default:
|
||||
|
|
|
@ -6,8 +6,6 @@ type: reference
|
|||
---
|
||||
|
||||
# Configuring runners in GitLab
|
||||
<!-- This topic contains several commented-out sections that were accidentally added in 13.2.-->
|
||||
<!-- The commented-out sections will be added back in a future release.-->
|
||||
|
||||
In GitLab CI/CD, runners run the code defined in [`.gitlab-ci.yml`](../yaml/README.md).
|
||||
A runner is a lightweight, highly-scalable agent that picks up a CI job through
|
||||
|
@ -39,10 +37,10 @@ multiple projects.
|
|||
|
||||
If you are using a self-managed instance of GitLab:
|
||||
|
||||
- Your administrator can install and register shared runners by [following the documentation](https://docs.gitlab.com/runner/install/index.html).
|
||||
<!-- going to your project's
|
||||
<!-- **Settings > CI / CD**, expanding the **Runners** section, and clicking **Show runner installation instructions**.-->
|
||||
<!-- These instructions are also available [in the documentation](https://docs.gitlab.com/runner/install/index.html).-->
|
||||
- Your administrator can install and register shared runners by
|
||||
going to your project's **Settings > CI / CD**, expanding the
|
||||
**Runners** section, and clicking **Show runner installation instructions**.
|
||||
These instructions are also available [in the documentation](https://docs.gitlab.com/runner/install/index.html).
|
||||
- The administrator can also configure a maximum number of shared runner [pipeline minutes for
|
||||
each group](../../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota).
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ to access it. This is where an SSH key pair comes in handy.
|
|||
# - git config --global user.name "User name"
|
||||
```
|
||||
|
||||
The [`before_script`](../yaml/README.md#before_script-and-after_script) can be set globally
|
||||
The [`before_script`](../yaml/README.md#before_script) can be set globally
|
||||
or per-job.
|
||||
|
||||
1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
|
||||
|
|
|
@ -69,7 +69,7 @@ Kubernetes-specific environment variables are detailed in the
|
|||
| `CI_JOB_MANUAL` | 8.12 | all | The flag to indicate that job was manually started |
|
||||
| `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
|
||||
| `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
|
||||
| `CI_JOB_STATUS` | all | 13.5 | The state of the job as each runner stage is executed. Use with [`after_script`](../yaml/README.md#before_script-and-after_script) where `CI_JOB_STATUS` can be either: `success`, `failed` or `canceled`. |
|
||||
| `CI_JOB_STATUS` | all | 13.5 | The state of the job as each runner stage is executed. Use with [`after_script`](../yaml/README.md#after_script) where `CI_JOB_STATUS` can be either: `success`, `failed` or `canceled`. |
|
||||
| `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with [a few API endpoints](../../api/README.md#gitlab-ci-job-token) and downloading [dependent repositories](../../user/project/new_ci_build_permissions_model.md#dependent-repositories). The token is valid as long as the job is running. |
|
||||
| `CI_JOB_JWT` | 12.10 | all | RS256 JSON web token that can be used for authenticating with third party systems that support JWT authentication, for example [HashiCorp's Vault](../secrets/index.md). |
|
||||
| `CI_JOB_URL` | 11.1 | 0.5 | Job details URL |
|
||||
|
|
|
@ -99,7 +99,7 @@ In the case of `after_script` scripts, they can:
|
|||
- Not use variables defined in `before_script` and `script`.
|
||||
|
||||
These restrictions are because `after_script` scripts are executed in a
|
||||
[separated shell context](../yaml/README.md#before_script-and-after_script).
|
||||
[separated shell context](../yaml/README.md#after_script).
|
||||
|
||||
## Persisted variables
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ The following table lists available keywords for jobs:
|
|||
| Keyword | Description |
|
||||
|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`script`](#script) | Shell script that is executed by a runner. |
|
||||
| [`after_script`](#before_script-and-after_script) | Override a set of commands that are executed after job. |
|
||||
| [`after_script`](#after_script) | Override a set of commands that are executed after job. |
|
||||
| [`allow_failure`](#allow_failure) | Allow job to fail. Failed job does not contribute to commit status. |
|
||||
| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:exclude`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, and `artifacts:reports`. |
|
||||
| [`before_script`](#before_script-and-after_script) | Override a set of commands that are executed before job. |
|
||||
| [`before_script`](#before_script) | Override a set of commands that are executed before job. |
|
||||
| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, `cache:when`, and `cache:policy`. |
|
||||
| [`coverage`](#coverage) | Code coverage settings for a given job. |
|
||||
| [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
|
||||
|
@ -90,8 +90,8 @@ The following job keywords can be defined inside a `default:` block:
|
|||
|
||||
- [`image`](#image)
|
||||
- [`services`](#services)
|
||||
- [`before_script`](#before_script-and-after_script)
|
||||
- [`after_script`](#before_script-and-after_script)
|
||||
- [`before_script`](#before_script)
|
||||
- [`after_script`](#after_script)
|
||||
- [`tags`](#tags)
|
||||
- [`cache`](#cache)
|
||||
- [`artifacts`](#artifacts)
|
||||
|
@ -421,7 +421,7 @@ include:
|
|||
file: '/templates/.gitlab-ci-template.yml'
|
||||
```
|
||||
|
||||
You can also specify `ref`, with the default being the `HEAD` of the project:
|
||||
You can also specify a `ref`. If not specified, it defaults to the `HEAD` of the project:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
|
@ -481,8 +481,8 @@ Feature.disable(:ci_include_multiple_files_from_project)
|
|||
#### `include:remote`
|
||||
|
||||
`include:remote` can be used to include a file from a different location,
|
||||
using HTTP/HTTPS, referenced by using the full URL. The remote file must be
|
||||
publicly accessible through a simple GET request as authentication schemas
|
||||
using HTTP/HTTPS, referenced by the full URL. The remote file must be
|
||||
publicly accessible by a GET request, because authentication schemas
|
||||
in the remote URL are not supported. For example:
|
||||
|
||||
```yaml
|
||||
|
@ -528,7 +528,7 @@ Nested includes allow you to compose a set of includes.
|
|||
A total of 100 includes is allowed, but duplicate includes are considered a configuration error.
|
||||
|
||||
In [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/28212) and later, the time limit
|
||||
for resolving all files is 30 seconds.
|
||||
to resolve all files is 30 seconds.
|
||||
|
||||
#### Additional `includes` examples
|
||||
|
||||
|
@ -544,7 +544,7 @@ Used to specify [a Docker image](../docker/using_docker_images.md#what-is-an-ima
|
|||
|
||||
For:
|
||||
|
||||
- Simple definition examples, see [Define `image` and `services` from `.gitlab-ci.yml`](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
|
||||
- Usage examples, see [Define `image` and `services` from `.gitlab-ci.yml`](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
|
||||
- Detailed usage information, refer to [Docker integration](../docker/README.md) documentation.
|
||||
|
||||
#### `image:name`
|
||||
|
@ -565,7 +565,7 @@ Used to specify a [service Docker image](../docker/using_docker_images.md#what-i
|
|||
|
||||
For:
|
||||
|
||||
- Simple definition examples, see [Define `image` and `services` from `.gitlab-ci.yml`](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
|
||||
- Usage examples, see [Define `image` and `services` from `.gitlab-ci.yml`](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
|
||||
- Detailed usage information, refer to [Docker integration](../docker/README.md) documentation.
|
||||
- For example services, see [GitLab CI/CD Services](../services/README.md).
|
||||
|
||||
|
@ -603,9 +603,9 @@ job:
|
|||
script: "bundle exec rspec"
|
||||
```
|
||||
|
||||
[YAML anchors for scripts](#yaml-anchors-for-script) are available.
|
||||
You can use [YAML anchors with `script`](#yaml-anchors-for-scripts).
|
||||
|
||||
This keyword can also contain several commands using an array:
|
||||
This keyword can also contain several commands in an array:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
|
@ -621,8 +621,8 @@ a "key: value" pair. Be careful when using special characters:
|
|||
`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``.
|
||||
|
||||
If any of the script commands return an exit code other than zero, the job
|
||||
fails and further commands are not executed. You can avoid this behavior by
|
||||
storing the exit code in a variable:
|
||||
fails and further commands are not executed. Store the exit code in a variable to
|
||||
avoid this behavior:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
|
@ -631,21 +631,45 @@ job:
|
|||
- if [ $exit_code -ne 0 ]; then echo "Previous command failed"; fi;
|
||||
```
|
||||
|
||||
#### `before_script` and `after_script`
|
||||
#### `before_script`
|
||||
|
||||
> Introduced in GitLab 8.7 and requires GitLab Runner v1.2.
|
||||
|
||||
`before_script` is used to define commands that should be run before each
|
||||
job, including deploy jobs, but after the restoration of any [artifacts](#artifacts).
|
||||
This must be an array.
|
||||
`before_script` is used to define commands that should run before each job, including
|
||||
deploy jobs, but after the restoration of any [artifacts](#artifacts). This must be an array.
|
||||
|
||||
Scripts specified in `before_script` are concatenated with any scripts specified
|
||||
in the main [`script`](#script), and executed together in a single shell.
|
||||
|
||||
`after_script` is used to define commands that run after each
|
||||
job, including failed jobs. This must be an array. If a job times out or is cancelled,
|
||||
the `after_script` commands are not executed. Support for executing `after_script`
|
||||
commands for timed-out or cancelled jobs
|
||||
It's possible to overwrite a globally defined `before_script` if you define it in a job:
|
||||
|
||||
```yaml
|
||||
default:
|
||||
before_script:
|
||||
- echo "Execute this in all jobs that don't already have a before_script section."
|
||||
|
||||
job1:
|
||||
script:
|
||||
- echo "This executes after the global before_script."
|
||||
|
||||
job:
|
||||
before_script:
|
||||
- echo "Execute this instead of the global before_script."
|
||||
script:
|
||||
- echo "This executes after the job's `before_script`"
|
||||
```
|
||||
|
||||
You can use [YAML anchors with `before_script`](#yaml-anchors-for-scripts).
|
||||
|
||||
#### `after_script`
|
||||
|
||||
Introduced in GitLab 8.7 and requires GitLab Runner v1.2.
|
||||
|
||||
`after_script` is used to define commands that run after each job, including failed
|
||||
jobs. This must be an array.
|
||||
|
||||
If a job times out or is cancelled, the `after_script` commands are not executed.
|
||||
Support for executing `after_script` commands for timed-out or cancelled jobs
|
||||
[is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/15603).
|
||||
|
||||
Scripts specified in `after_script` are executed in a new shell, separate from any
|
||||
|
@ -656,166 +680,37 @@ Scripts specified in `after_script` are executed in a new shell, separate from a
|
|||
- Command aliases and variables exported in `script` scripts.
|
||||
- Changes outside of the working tree (depending on the runner executor), like
|
||||
software installed by a `before_script` or `script` script.
|
||||
- Have a separate timeout, which is hard coded to 5 minutes. See
|
||||
- Have a separate timeout, which is hard coded to 5 minutes. See the
|
||||
[related issue](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2716) for details.
|
||||
- Don't affect the job's exit code. If the `script` section succeeds and the
|
||||
`after_script` times out or fails, the job exits with code `0` (`Job Succeeded`).
|
||||
|
||||
It's possible to overwrite a globally defined `before_script` or `after_script`
|
||||
if you set it per-job:
|
||||
|
||||
```yaml
|
||||
default:
|
||||
before_script:
|
||||
- global before script
|
||||
|
||||
job:
|
||||
before_script:
|
||||
- execute this instead of global before script
|
||||
script:
|
||||
- my command
|
||||
after_script:
|
||||
- execute this after my script
|
||||
```
|
||||
- echo "Execute this in all jobs that don't already have an after_script section."
|
||||
|
||||
[YAML anchors for `before_script` and `after_script`](#yaml-anchors-for-before_script-and-after_script) are available.
|
||||
job1:
|
||||
script:
|
||||
- echo "This executes first. When it completes, the global after_script executes."
|
||||
|
||||
#### Coloring script output
|
||||
|
||||
Script output can be colored using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors),
|
||||
or by running commands or programs that output ANSI escape codes.
|
||||
|
||||
For example, using [Bash with color codes](https://misc.flogisoft.com/bash/tip_colors_and_formatting):
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- echo -e "\e[31mThis text is red,\e[0m but this text isn't\e[31m however this text is red again."
|
||||
- echo "This executes first. When it completes, the job's `after_script` executes."
|
||||
after_script:
|
||||
- echo "Execute this instead of the global after_script."
|
||||
```
|
||||
|
||||
You can define the color codes in Shell variables, or even [custom environment variables](../variables/README.md#custom-environment-variables),
|
||||
which makes the commands easier to read and reusable.
|
||||
You can use [YAML anchors with `after_script`](#yaml-anchors-for-scripts).
|
||||
|
||||
For example, using the same example as above and variables defined in a `before_script`:
|
||||
#### Script syntax
|
||||
|
||||
```yaml
|
||||
job:
|
||||
before_script:
|
||||
- TXT_RED="\e[31m" && TXT_CLEAR="\e[0m"
|
||||
script:
|
||||
- echo -e "${TXT_RED}This text is red,${TXT_CLEAR} but this part isn't${TXT_RED} however this part is again."
|
||||
- echo "This text is not colored"
|
||||
```
|
||||
You can use special syntax in [`script`](README.md#script) sections to:
|
||||
|
||||
Or with [PowerShell color codes](https://superuser.com/a/1259916):
|
||||
|
||||
```yaml
|
||||
job:
|
||||
before_script:
|
||||
- $esc="$([char]27)"; $TXT_RED="$esc[31m"; $TXT_CLEAR="$esc[0m"
|
||||
script:
|
||||
- Write-Host $TXT_RED"This text is red,"$TXT_CLEAR" but this text isn't"$TXT_RED" however this text is red again."
|
||||
- Write-Host "This text is not colored"
|
||||
```
|
||||
|
||||
#### Multi-line commands
|
||||
|
||||
You can split long commands into multi-line commands to improve readability
|
||||
using [`|` (literal) and `>` (folded) YAML multi-line block scalar indicators](https://yaml-multiline.info/).
|
||||
|
||||
CAUTION: **Warning:**
|
||||
If multiple commands are combined into one command string, only the last command's
|
||||
failure or success is reported.
|
||||
[Failures from earlier commands are ignored due to a bug](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25394).
|
||||
To work around this,
|
||||
run each command as a separate `script:` item, or add an `exit 1` command
|
||||
to each command string.
|
||||
|
||||
You can use the `|` (literal) YAML multiline block scalar indicator to write
|
||||
commands over multiple lines in the `script` section of a job description.
|
||||
Each line is treated as a separate command.
|
||||
Only the first command is repeated in the job log, but additional
|
||||
commands are still executed:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- |
|
||||
echo "First command line."
|
||||
echo "Second command line."
|
||||
echo "Third command line."
|
||||
```
|
||||
|
||||
The example above renders in the job log as:
|
||||
|
||||
```shell
|
||||
$ echo First command line # collapsed multi-line command
|
||||
First command line
|
||||
Second command line.
|
||||
Third command line.
|
||||
```
|
||||
|
||||
The `>` (folded) YAML multiline block scalar indicator treats empty lines between
|
||||
sections as the start of a new command:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- >
|
||||
echo "First command line
|
||||
is split over two lines."
|
||||
|
||||
echo "Second command line."
|
||||
```
|
||||
|
||||
This behaves similarly to writing multiline commands without the `>` or `|` block
|
||||
scalar indicators:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- echo "First command line
|
||||
is split over two lines."
|
||||
|
||||
echo "Second command line."
|
||||
```
|
||||
|
||||
Both examples above render in the job log as:
|
||||
|
||||
```shell
|
||||
$ echo First command line is split over two lines. # collapsed multi-line command
|
||||
First command line is split over two lines.
|
||||
Second command line.
|
||||
```
|
||||
|
||||
When you omit the `>` or `|` block scalar indicators, GitLab forms the command
|
||||
by concatenating non-empty lines. Make sure the lines can run when concatenated.
|
||||
|
||||
Shell [here documents](https://en.wikipedia.org/wiki/Here_document) work with the
|
||||
`|` and `>` operators as well. The example below transliterates the lower case letters
|
||||
to upper case:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- |
|
||||
tr a-z A-Z << END_TEXT
|
||||
one two three
|
||||
four five six
|
||||
END_TEXT
|
||||
```
|
||||
|
||||
Results in:
|
||||
|
||||
```shell
|
||||
$ tr a-z A-Z << END_TEXT # collapsed multi-line command
|
||||
ONE TWO THREE
|
||||
FOUR FIVE SIX
|
||||
```
|
||||
|
||||
#### Custom collapsible sections
|
||||
|
||||
See [custom collapsible sections](../pipelines/index.md#custom-collapsible-sections).
|
||||
- [Split long commands](script.md#split-long-commands) into multiline commands.
|
||||
- [Use color codes](script.md#add-color-codes-to-script-output) to make job logs easier to review.
|
||||
- [Create custom collapsible sections](../pipelines/index.md#custom-collapsible-sections)
|
||||
to simplify job log output.
|
||||
|
||||
### `stage`
|
||||
|
||||
|
@ -954,7 +849,7 @@ rspec:
|
|||
- $RSPEC
|
||||
```
|
||||
|
||||
If you do want to include the `rake test`, see [`before_script` and `after_script`](#before_script-and-after_script).
|
||||
If you do want to include the `rake test`, see [`before_script`](#before_script) or [`after_script`](#after_script).
|
||||
|
||||
`.tests` in this example is a [hidden job](#hide-jobs), but it's
|
||||
possible to inherit from regular jobs as well.
|
||||
|
@ -3536,7 +3431,7 @@ exceed the runner-specific timeout.
|
|||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/21480) in GitLab 11.5.
|
||||
|
||||
Use `parallel` to configure how many instances of a job to run in
|
||||
parallel. This value has to be greater than or equal to two (2) and less than or equal to 50.
|
||||
parallel. This value can be from 2 to 50.
|
||||
|
||||
This creates N instances of the same job that run in parallel. They are named
|
||||
sequentially from `job_name 1/N` to `job_name N/N`.
|
||||
|
@ -3635,8 +3530,8 @@ Use `trigger` to define a downstream pipeline trigger. When GitLab starts a job
|
|||
with a `trigger` definition, a downstream pipeline is created.
|
||||
|
||||
Jobs with `trigger` can only use a [limited set of keywords](../multi_project_pipelines.md#limitations).
|
||||
For example, you can't run commands with [`script`](#script), [`before_script`](#before_script-and-after_script),
|
||||
or [`after_script`](#before_script-and-after_script).
|
||||
For example, you can't run commands with [`script`](#script), [`before_script`](#before_script),
|
||||
or [`after_script`](#after_script).
|
||||
|
||||
You can use this keyword to create two different types of downstream pipelines:
|
||||
|
||||
|
@ -4384,50 +4279,30 @@ test:mysql:
|
|||
You can see that the hidden jobs are conveniently used as templates, and
|
||||
`tags: [dev]` has been overwritten by `tags: [postgres]`.
|
||||
|
||||
#### YAML anchors for `before_script` and `after_script`
|
||||
#### YAML anchors for scripts
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23005) in GitLab 12.5.
|
||||
|
||||
You can use [YAML anchors](#anchors) with `before_script` and `after_script`,
|
||||
which makes it possible to include a predefined list of commands in multiple
|
||||
jobs.
|
||||
|
||||
Example:
|
||||
You can use [YAML anchors](#anchors) with [script](#script), [`before_script`](#before_script),
|
||||
and [`after_script`](#after_script) to use predefined commands in multiple jobs:
|
||||
|
||||
```yaml
|
||||
.something_before: &something_before
|
||||
- echo 'something before'
|
||||
.some-script: &some-script
|
||||
- echo "Execute this in `before_script` sections"
|
||||
|
||||
.something_after: &something_after
|
||||
- echo 'something after'
|
||||
- echo 'another thing after'
|
||||
.some-script-before: &some-script-before
|
||||
- echo "Execute this in `script` sections"
|
||||
|
||||
.some-script-after: &some-script-after
|
||||
- echo "Execute this in `after_script` sections"
|
||||
|
||||
job_name:
|
||||
before_script:
|
||||
- *something_before
|
||||
- *some-script-before
|
||||
script:
|
||||
- echo 'this is the script'
|
||||
after_script:
|
||||
- *something_after
|
||||
```
|
||||
|
||||
#### YAML anchors for `script`
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23005) in GitLab 12.5.
|
||||
|
||||
You can use [YAML anchors](#anchors) with scripts, which makes it possible to
|
||||
include a predefined list of commands in multiple jobs.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
.something: &something
|
||||
- echo 'something'
|
||||
|
||||
job_name:
|
||||
script:
|
||||
- *something
|
||||
- echo 'this is the script'
|
||||
- *some-script
|
||||
before_script:
|
||||
- *some-script-after
|
||||
```
|
||||
|
||||
#### YAML anchors for variables
|
||||
|
@ -4503,13 +4378,13 @@ The following keywords are deprecated.
|
|||
|
||||
### Globally-defined `types`
|
||||
|
||||
CAUTION: **Deprecated:**
|
||||
DANGER: **Deprecated:**
|
||||
`types` is deprecated, and could be removed in a future release.
|
||||
Use [`stages`](#stages) instead.
|
||||
|
||||
### Job-defined `type`
|
||||
|
||||
CAUTION: **Deprecated:**
|
||||
DANGER: **Deprecated:**
|
||||
`type` is deprecated, and could be removed in one of the future releases.
|
||||
Use [`stage`](#stage) instead.
|
||||
|
||||
|
|
147
doc/ci/yaml/script.md
Normal file
147
doc/ci/yaml/script.md
Normal file
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
stage: Verify
|
||||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
type: reference
|
||||
---
|
||||
|
||||
# GitLab CI/CD script syntax
|
||||
|
||||
You can use special syntax in [`script`](README.md#script) sections to:
|
||||
|
||||
- [Split long commands](#split-long-commands) into multiline commands.
|
||||
- [Use color codes](#add-color-codes-to-script-output) to make job logs easier to review.
|
||||
- [Create custom collapsible sections](../pipelines/index.md#custom-collapsible-sections)
|
||||
to simplify job log output.
|
||||
|
||||
## Split long commands
|
||||
|
||||
You can split long commands into multiline commands to improve readability with
|
||||
`|` (literal) and `>` (folded) [YAML multiline block scalar indicators](https://yaml-multiline.info/).
|
||||
|
||||
CAUTION: **Warning:**
|
||||
If multiple commands are combined into one command string, only the last command's
|
||||
failure or success is reported.
|
||||
[Failures from earlier commands are ignored due to a bug](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25394).
|
||||
To work around this, run each command as a separate `script:` item, or add an `exit 1`
|
||||
command to each command string.
|
||||
|
||||
You can use the `|` (literal) YAML multiline block scalar indicator to write
|
||||
commands over multiple lines in the `script` section of a job description.
|
||||
Each line is treated as a separate command.
|
||||
Only the first command is repeated in the job log, but additional
|
||||
commands are still executed:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- |
|
||||
echo "First command line."
|
||||
echo "Second command line."
|
||||
echo "Third command line."
|
||||
```
|
||||
|
||||
The example above renders in the job log as:
|
||||
|
||||
```shell
|
||||
$ echo First command line # collapsed multiline command
|
||||
First command line
|
||||
Second command line.
|
||||
Third command line.
|
||||
```
|
||||
|
||||
The `>` (folded) YAML multiline block scalar indicator treats empty lines between
|
||||
sections as the start of a new command:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- >
|
||||
echo "First command line
|
||||
is split over two lines."
|
||||
|
||||
echo "Second command line."
|
||||
```
|
||||
|
||||
This behaves similarly to multiline commands without the `>` or `|` block
|
||||
scalar indicators:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- echo "First command line
|
||||
is split over two lines."
|
||||
|
||||
echo "Second command line."
|
||||
```
|
||||
|
||||
Both examples above render in the job log as:
|
||||
|
||||
```shell
|
||||
$ echo First command line is split over two lines. # collapsed multiline command
|
||||
First command line is split over two lines.
|
||||
Second command line.
|
||||
```
|
||||
|
||||
When you omit the `>` or `|` block scalar indicators, GitLab concatenates non-empty
|
||||
lines to form the command. Make sure the lines can run when concatenated.
|
||||
|
||||
[Shell here documents](https://en.wikipedia.org/wiki/Here_document) work with the
|
||||
`|` and `>` operators as well. The example below transliterates lower case letters
|
||||
to upper case:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- |
|
||||
tr a-z A-Z << END_TEXT
|
||||
one two three
|
||||
four five six
|
||||
END_TEXT
|
||||
```
|
||||
|
||||
Results in:
|
||||
|
||||
```shell
|
||||
$ tr a-z A-Z << END_TEXT # collapsed multiline command
|
||||
ONE TWO THREE
|
||||
FOUR FIVE SIX
|
||||
```
|
||||
|
||||
## Add color codes to script output
|
||||
|
||||
Script output can be colored using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors),
|
||||
or by running commands or programs that output ANSI escape codes.
|
||||
|
||||
For example, using [Bash with color codes](https://misc.flogisoft.com/bash/tip_colors_and_formatting):
|
||||
|
||||
```yaml
|
||||
job:
|
||||
script:
|
||||
- echo -e "\e[31mThis text is red,\e[0m but this text isn't\e[31m however this text is red again."
|
||||
```
|
||||
|
||||
You can define the color codes in Shell variables, or even [custom environment variables](../variables/README.md#custom-environment-variables),
|
||||
which makes the commands easier to read and reusable.
|
||||
|
||||
For example, using the same example as above and variables defined in a `before_script`:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
before_script:
|
||||
- TXT_RED="\e[31m" && TXT_CLEAR="\e[0m"
|
||||
script:
|
||||
- echo -e "${TXT_RED}This text is red,${TXT_CLEAR} but this part isn't${TXT_RED} however this part is again."
|
||||
- echo "This text is not colored"
|
||||
```
|
||||
|
||||
Or with [PowerShell color codes](https://superuser.com/a/1259916):
|
||||
|
||||
```yaml
|
||||
job:
|
||||
before_script:
|
||||
- $esc="$([char]27)"; $TXT_RED="$esc[31m"; $TXT_CLEAR="$esc[0m"
|
||||
script:
|
||||
- Write-Host $TXT_RED"This text is red,"$TXT_CLEAR" but this text isn't"$TXT_RED" however this text is red again."
|
||||
- Write-Host "This text is not colored"
|
||||
```
|
|
@ -57,6 +57,66 @@ Please check this [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules)
|
|||
|
||||
1. Use `.vue` for Vue templates. Do not use `%template` in HAML.
|
||||
|
||||
1. Explicitly define data being passed into the Vue app
|
||||
|
||||
```javascript
|
||||
// bad
|
||||
return new Vue({
|
||||
el: '#element',
|
||||
components: {
|
||||
componentName
|
||||
},
|
||||
provide: {
|
||||
...someDataset
|
||||
},
|
||||
props: {
|
||||
...anotherDataset
|
||||
},
|
||||
render: createElement => createElement('component-name'),
|
||||
}));
|
||||
|
||||
// good
|
||||
const { foobar, barfoo } = someDataset;
|
||||
const { foo, bar } = anotherDataset;
|
||||
|
||||
return new Vue({
|
||||
el: '#element',
|
||||
components: {
|
||||
componentName
|
||||
},
|
||||
provide: {
|
||||
foobar,
|
||||
barfoo
|
||||
},
|
||||
props: {
|
||||
foo,
|
||||
bar
|
||||
},
|
||||
render: createElement => createElement('component-name'),
|
||||
}));
|
||||
```
|
||||
|
||||
We discourage the use of the spread operator in this specific case in
|
||||
order to keep our codebase explicit, discoverable, and searchable.
|
||||
This applies in any place where we'll benefit from the above, such as
|
||||
when [initializing Vuex state](../vuex.md#why-not-just-spread-the-initial-state).
|
||||
The pattern above also enables us to easily parse non scalar values during
|
||||
instantiation.
|
||||
|
||||
```javascript
|
||||
return new Vue({
|
||||
el: '#element',
|
||||
components: {
|
||||
componentName
|
||||
},
|
||||
props: {
|
||||
foo,
|
||||
bar: parseBoolean(bar)
|
||||
},
|
||||
render: createElement => createElement('component-name'),
|
||||
}));
|
||||
```
|
||||
|
||||
## Naming
|
||||
|
||||
1. **Extensions**: Use `.vue` extension for Vue components. Do not use `.js` as file extension ([#34371](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/34371)).
|
||||
|
|
|
@ -66,29 +66,32 @@ You should only do this while initializing the application, because the mounted
|
|||
|
||||
The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function
|
||||
instead of querying the DOM inside the main Vue component is avoiding the need to create a fixture or an HTML element in the unit test,
|
||||
which will make the tests easier. See the following example:
|
||||
which will make the tests easier.
|
||||
|
||||
See the following example, also, please refer to our [Vue style guide](style/vue.md#basic-rules) for additional
|
||||
information on why we explicitly declare the data being passed into the Vue app;
|
||||
|
||||
```javascript
|
||||
// haml
|
||||
#js-vue-app{ data: { endpoint: 'foo' }}
|
||||
|
||||
// index.js
|
||||
document.addEventListener('DOMContentLoaded', () => new Vue({
|
||||
el: '#js-vue-app',
|
||||
data() {
|
||||
const dataset = this.$options.el.dataset;
|
||||
return {
|
||||
endpoint: dataset.endpoint,
|
||||
};
|
||||
},
|
||||
const el = document.getElementById('js-vue-app');
|
||||
|
||||
if (!el) return false;
|
||||
|
||||
const { endpoint } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement('my-component', {
|
||||
props: {
|
||||
endpoint: this.endpoint,
|
||||
endpoint
|
||||
},
|
||||
});
|
||||
},
|
||||
}));
|
||||
}
|
||||
```
|
||||
|
||||
> When adding an `id` attribute to mount a Vue application, please make sure this `id` is unique across the codebase
|
||||
|
@ -100,7 +103,7 @@ By following this practice, we can avoid the need to mock the `gl` object, which
|
|||
It should be done while initializing our Vue instance, and the data should be provided as `props` to the main component:
|
||||
|
||||
```javascript
|
||||
document.addEventListener('DOMContentLoaded', () => new Vue({
|
||||
return new Vue({
|
||||
el: '.js-vue-app',
|
||||
render(createElement) {
|
||||
return createElement('my-component', {
|
||||
|
@ -109,7 +112,7 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
|
|||
},
|
||||
});
|
||||
},
|
||||
}));
|
||||
});
|
||||
```
|
||||
|
||||
#### Accessing feature flags
|
||||
|
|
|
@ -360,8 +360,8 @@ export default initialState => ({
|
|||
```
|
||||
|
||||
We've made the conscious decision to avoid this pattern to aid in the
|
||||
discoverability and searchability of our frontend codebase. The reasoning for
|
||||
this is described in [this
|
||||
discoverability and searchability of our frontend codebase. The same applies
|
||||
when [providing data to a Vue app](vue.md#providing-data-from-haml-to-javascript). The reasoning for this is described in [this
|
||||
discussion](https://gitlab.com/gitlab-org/frontend/rfcs/-/issues/56#note_302514865):
|
||||
|
||||
> Consider a `someStateKey` is being used in the store state. You _may_ not be
|
||||
|
|
|
@ -46,12 +46,12 @@ Because the `script` entry can't be left empty, it must be set to the command th
|
|||
It is not possible to rely on the predefined `ENTRYPOINT` and `CMD` of the Docker image
|
||||
to perform the scan automatically, without passing any command.
|
||||
|
||||
The [`before_script`](../../ci/yaml/README.md#before_script-and-after_script)
|
||||
The [`before_script`](../../ci/yaml/README.md#before_script)
|
||||
should not be used in the job definition because users may rely on this to prepare their projects before performing the scan.
|
||||
For instance, it is common practice to use `before_script` to install system libraries
|
||||
a particular project needs before performing SAST or Dependency Scanning.
|
||||
|
||||
Similarly, [`after_script`](../../ci/yaml/README.md#before_script-and-after_script)
|
||||
Similarly, [`after_script`](../../ci/yaml/README.md#after_script)
|
||||
should not be used in the job definition, because it may be overridden by users.
|
||||
|
||||
### Stage
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
doc/user/admin_area/img/scope_mr_approval_settings_v13_5.png
Normal file
BIN
doc/user/admin_area/img/scope_mr_approval_settings_v13_5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -5,29 +5,50 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: reference, concepts
|
||||
---
|
||||
|
||||
# Merge request approval rules **(PREMIUM ONLY)**
|
||||
# Instance-level merge request approval rules **(PREMIUM ONLY)**
|
||||
|
||||
> Introduced in [GitLab Premium](https://gitlab.com/gitlab-org/gitlab/-/issues/39060) 12.8.
|
||||
|
||||
Merge request approval rules prevent users from overriding certain settings on the project
|
||||
level. When enabled at the instance level, these settings are no longer editable on the
|
||||
project level.
|
||||
Merge request approvals rules prevent users overriding certain settings on a project
|
||||
level. When configured, only administrators can change these settings on a project level
|
||||
if they are enabled at an instance level.
|
||||
|
||||
To enable merge request approval rules for an instance:
|
||||
|
||||
1. Navigate to **Admin Area >** **{push-rules}** **Push Rules** and expand **Merge
|
||||
requests approvals**.
|
||||
requests approvals**.
|
||||
1. Set the required rule.
|
||||
1. Click **Save changes**.
|
||||
|
||||
GitLab administrators can later override these settings in a project’s settings.
|
||||
|
||||
## Available rules
|
||||
|
||||
Merge request approval rules that can be set at an instance level are:
|
||||
|
||||
- **Prevent approval of merge requests by merge request author**. Prevents project
|
||||
maintainers from allowing request authors to merge their own merge requests.
|
||||
maintainers from allowing request authors to merge their own merge requests.
|
||||
- **Prevent approval of merge requests by merge request committers**. Prevents project
|
||||
maintainers from allowing users to approve merge requests if they have submitted
|
||||
any commits to the source branch.
|
||||
- **Prevent users from modifying merge request approvers list**. Prevents users from
|
||||
modifying the approvers list in project settings or in individual merge requests.
|
||||
maintainers from allowing users to approve merge requests if they have submitted
|
||||
any commits to the source branch.
|
||||
- **Can override approvers and approvals required per merge request**. Allows project
|
||||
maintainers to modify the approvers list in individual merge requests.
|
||||
|
||||
## Scope rules to compliance-labeled projects
|
||||
|
||||
> Introduced in [GitLab Premium](https://gitlab.com/groups/gitlab-org/-/epics/3432) 13.2.
|
||||
|
||||
Merge request approval rules can be further scoped to specific compliance frameworks.
|
||||
|
||||
When the compliance framework label is selected and the project is assigned the compliance
|
||||
label, the instance-level MR approval settings will take effect and the
|
||||
[project-level settings](../project/merge_requests/merge_request_approvals.md#adding--editing-a-default-approval-rule)
|
||||
is locked for modification.
|
||||
|
||||
When the compliance framework label is not selected or the project is not assigned the
|
||||
compliance label, the project-level MR approval settings will take effect and the users with
|
||||
Maintainer role and above can modify these.
|
||||
|
||||
| Instance-level | Project-level |
|
||||
| -------------- | ------------- |
|
||||
| ![Scope MR approval settings to compliance frameworks](img/scope_mr_approval_settings_v13_5.png) | ![MR approval settings on compliance projects](img/mr_approval_settings_compliance_project_v13_5.png) |
|
||||
|
|
|
@ -148,8 +148,8 @@ variables:
|
|||
Version `3` of the `container_scanning` Docker image uses [`centos:centos8`](https://hub.docker.com/_/centos)
|
||||
as the new base. It also removes the use of the [start.sh](https://gitlab.com/gitlab-org/security-products/analyzers/klar/-/merge_requests/77)
|
||||
script and instead executes the analyzer by default. Any customizations made to the
|
||||
`container_scanning` job's [`before_script`](../../../ci/yaml/README.md#before_script-and-after_script)
|
||||
and [`after_script`](../../../ci/yaml/README.md#before_script-and-after_script)
|
||||
`container_scanning` job's [`before_script`](../../../ci/yaml/README.md#before_script)
|
||||
and [`after_script`](../../../ci/yaml/README.md#after_script)
|
||||
blocks may not work with the new version. To roll back to the previous [`alpine:3.11.3`](https://hub.docker.com/_/alpine)-based
|
||||
Docker image, you can specify the major version through the [`CS_MAJOR_VERSION`](#available-variables)
|
||||
variable.
|
||||
|
|
|
@ -444,7 +444,7 @@ documentation for a list of settings that you can apply.
|
|||
The `license_scanning` job runs in a [Debian 10](https://www.debian.org/releases/buster/) Docker
|
||||
image. The supplied image ships with some build tools such as [CMake](https://cmake.org/) and [GCC](https://gcc.gnu.org/).
|
||||
However, not all project types are supported by default. To install additional tools needed to
|
||||
compile dependencies, use a [`before_script`](../../../ci/yaml/README.md#before_script-and-after_script)
|
||||
compile dependencies, use a [`before_script`](../../../ci/yaml/README.md#before_script)
|
||||
to install the necessary build tools using the [`apt`](https://wiki.debian.org/PackageManagementTools)
|
||||
package manager. For a comprehensive list, consult [the Conan documentation](https://docs.conan.io/en/latest/introduction.html#all-platforms-all-build-systems-and-compilers).
|
||||
|
||||
|
|
|
@ -409,7 +409,7 @@ test:
|
|||
- For the beta release, we have included a set of software packages in
|
||||
the base VM image. If your CI job requires additional software that's
|
||||
not included in this list, then you will need to add installation
|
||||
commands to [`before_script`](../../ci/yaml/README.md#before_script-and-after_script) or [`script`](../../ci/yaml/README.md#script) to install the required
|
||||
commands to [`before_script`](../../ci/yaml/README.md#before_script) or [`script`](../../ci/yaml/README.md#script) to install the required
|
||||
software. Note that each job runs on a new VM instance, so the
|
||||
installation of additional software packages needs to be repeated for
|
||||
each job in your pipeline.
|
||||
|
|
|
@ -394,13 +394,6 @@ milestones.
|
|||
## Group wikis **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13195) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5.
|
||||
> - It's [deployed behind a feature flag](../feature_flags.md), enabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-group-wikis).
|
||||
|
||||
CAUTION: **Warning:**
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
Group wikis work the same way as [project wikis](../project/wiki/index.md), please refer to those docs for details on usage.
|
||||
|
||||
|
@ -422,25 +415,6 @@ For updates, you can follow:
|
|||
- [The epic tracking feature parity with project wikis](https://gitlab.com/groups/gitlab-org/-/epics/2782).
|
||||
- [The issue for adding the ability to move group wikis using the API](https://gitlab.com/gitlab-org/gitlab/-/issues/219003).
|
||||
|
||||
### Enable or disable group wikis **(PREMIUM ONLY)**
|
||||
|
||||
Group wikis are under development but ready for production use.
|
||||
It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can opt to disable it for your instance.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:group_wikis)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:group_wikis)
|
||||
```
|
||||
|
||||
## Group Security Dashboard **(ULTIMATE)**
|
||||
|
||||
Get an overview of the vulnerabilities of all the projects in a group and its subgroups.
|
||||
|
|
BIN
doc/user/project/releases/img/releases_sort_v13_6.png
Normal file
BIN
doc/user/project/releases/img/releases_sort_v13_6.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
|
@ -43,6 +43,13 @@ To view a list of releases:
|
|||
- On private projects, this number is visible to users with Reporter
|
||||
[permissions](../../permissions.md#project-members-permissions) or higher.
|
||||
|
||||
### Sort Releases
|
||||
|
||||
On the top right of the **Releases** page, you can use the sorting button to order releases by
|
||||
**Released date** or **Created date**. You can sort releases in ascending or descending order.
|
||||
|
||||
![Sort Releases dropdown button](img/releases_sort_v13_6.png)
|
||||
|
||||
## Create a release
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32812) in GitLab 12.9. Releases can be created directly in the GitLab UI.
|
||||
|
|
|
@ -178,7 +178,10 @@ module Banzai
|
|||
collection.where(id: to_query).each { |row| cache[row.id] = row }
|
||||
end
|
||||
|
||||
ids.uniq.map { |id| cache[id] }.compact
|
||||
ids.each_with_object([]) do |id, array|
|
||||
row = cache[id]
|
||||
array << row if row
|
||||
end
|
||||
else
|
||||
collection.where(id: ids)
|
||||
end
|
||||
|
|
|
@ -9,10 +9,12 @@ module Gitlab
|
|||
self.table_name = 'project_settings'
|
||||
|
||||
UPSERT_SQL = <<~SQL
|
||||
WITH upsert_data (project_id, has_vulnerabilities, created_at, updated_at) AS (
|
||||
SELECT projects.id, true, current_timestamp, current_timestamp FROM projects WHERE projects.id IN (%{project_ids})
|
||||
)
|
||||
INSERT INTO project_settings
|
||||
(project_id, has_vulnerabilities, created_at, updated_at)
|
||||
VALUES
|
||||
%{values}
|
||||
(SELECT * FROM upsert_data)
|
||||
ON CONFLICT (project_id)
|
||||
DO UPDATE SET
|
||||
has_vulnerabilities = true,
|
||||
|
@ -20,10 +22,7 @@ module Gitlab
|
|||
SQL
|
||||
|
||||
def self.upsert_for(project_ids)
|
||||
timestamp = connection.quote(Time.now)
|
||||
values = project_ids.map { |project_id| "(#{project_id}, true, #{timestamp}, #{timestamp})" }.join(', ')
|
||||
|
||||
connection.execute(UPSERT_SQL % { values: values })
|
||||
connection.execute(UPSERT_SQL % { project_ids: project_ids.join(', ') })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6951,6 +6951,9 @@ msgstr ""
|
|||
msgid "Compliance framework (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compliance frameworks"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceDashboard|created by:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -20335,9 +20338,6 @@ msgstr ""
|
|||
msgid "Prevent users from changing their profile name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent users from modifying merge request approvers list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
|
||||
msgstr ""
|
||||
|
||||
|
@ -22251,6 +22251,9 @@ msgstr ""
|
|||
msgid "Registry setup"
|
||||
msgstr ""
|
||||
|
||||
msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
|
||||
msgstr ""
|
||||
|
||||
msgid "Reindexing status"
|
||||
msgstr ""
|
||||
|
||||
|
@ -22325,6 +22328,9 @@ msgstr ""
|
|||
msgid "ReleaseAssetLinkType|Runbooks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Released date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Releases"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26667,6 +26673,9 @@ msgstr ""
|
|||
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
|
||||
msgstr ""
|
||||
|
||||
msgid "The above settings apply to all projects with the selected compliance framework(s)."
|
||||
msgstr ""
|
||||
|
||||
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.5",
|
||||
"@gitlab/svgs": "1.175.0",
|
||||
"@gitlab/ui": "23.4.0",
|
||||
"@gitlab/ui": "23.6.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-3",
|
||||
"@rails/ujs": "^6.0.3-2",
|
||||
|
|
|
@ -3,8 +3,14 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User views releases', :js do
|
||||
let_it_be(:today) { Time.zone.now }
|
||||
let_it_be(:yesterday) { today - 1.day }
|
||||
let_it_be(:tomorrow) { today + 1.day }
|
||||
|
||||
let_it_be(:project) { create(:project, :repository, :private) }
|
||||
let_it_be(:release) { create(:release, project: project, name: 'The first release' ) }
|
||||
let_it_be(:release_v1) { create(:release, project: project, tag: 'v1', name: 'The first release', released_at: yesterday, created_at: today) }
|
||||
let_it_be(:release_v2) { create(:release, project: project, tag: 'v2-with-a/slash', name: 'The second release', released_at: today, created_at: yesterday) }
|
||||
let_it_be(:release_v3) { create(:release, project: project, tag: 'v3', name: 'The third release', released_at: tomorrow, created_at: tomorrow) }
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
|
||||
|
@ -17,38 +23,36 @@ RSpec.describe 'User views releases', :js do
|
|||
context('when the user is a maintainer') do
|
||||
before do
|
||||
sign_in(maintainer)
|
||||
|
||||
visit project_releases_path(project)
|
||||
end
|
||||
|
||||
it 'sees the release' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
expect(page).to have_content(release.name)
|
||||
expect(page).to have_content(release.tag)
|
||||
page.within("##{release_v1.tag}") do
|
||||
expect(page).to have_content(release_v1.name)
|
||||
expect(page).to have_content(release_v1.tag)
|
||||
expect(page).not_to have_content('Upcoming Release')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a link as an asset' do
|
||||
let!(:release_link) { create(:release_link, release: release, url: url ) }
|
||||
let!(:release_link) { create(:release_link, release: release_v1, url: url ) }
|
||||
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
|
||||
let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release) << release_link.filepath }
|
||||
let(:direct_asset_link) { Gitlab::Routing.url_helpers.project_release_url(project, release_v1) << release_link.filepath }
|
||||
|
||||
it 'sees the link' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
page.within('.js-assets-list') do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_link release_link.name, href: direct_asset_link
|
||||
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a link redirect' do
|
||||
let!(:release_link) { create(:release_link, release: release, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
|
||||
let!(:release_link) { create(:release_link, release: release_v1, name: 'linux-amd64 binaries', filepath: '/binaries/linux-amd64', url: url) }
|
||||
let(:url) { "#{project.web_url}/-/jobs/1/artifacts/download" }
|
||||
|
||||
it 'sees the link' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
page.within('.js-assets-list') do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_link release_link.name, href: direct_asset_link
|
||||
expect(page).not_to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
|
@ -59,9 +63,7 @@ RSpec.describe 'User views releases', :js do
|
|||
let(:url) { 'http://google.com/download' }
|
||||
|
||||
it 'sees that the link is external resource' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
page.within('.js-assets-list') do
|
||||
page.within("##{release_v1.tag} .js-assets-list") do
|
||||
expect(page).to have_css('[data-testid="external-link-indicator"]')
|
||||
end
|
||||
end
|
||||
|
@ -69,23 +71,57 @@ RSpec.describe 'User views releases', :js do
|
|||
end
|
||||
|
||||
context 'with an upcoming release' do
|
||||
let(:tomorrow) { Time.zone.now + 1.day }
|
||||
let!(:release) { create(:release, project: project, released_at: tomorrow ) }
|
||||
|
||||
it 'sees the upcoming tag' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
page.within("##{release_v3.tag}") do
|
||||
expect(page).to have_content('Upcoming Release')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a tag containing a slash' do
|
||||
it 'sees the release' do
|
||||
release = create :release, project: project, tag: 'debian/2.4.0-1'
|
||||
visit project_releases_path(project)
|
||||
page.within("##{release_v2.tag.parameterize}") do
|
||||
expect(page).to have_content(release_v2.name)
|
||||
expect(page).to have_content(release_v2.tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
expect(page).to have_content(release.name)
|
||||
expect(page).to have_content(release.tag)
|
||||
context 'sorting' do
|
||||
def sort_page(by:, direction:)
|
||||
within '[data-testid="releases-sort"]' do
|
||||
find('.dropdown-toggle').click
|
||||
|
||||
click_button(by, class: 'dropdown-item')
|
||||
|
||||
find('.sorting-direction-button').click if direction == :ascending
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'releases sort order' do
|
||||
it "sorts the releases #{description}" do
|
||||
card_titles = page.all('.release-block .card-title', minimum: expected_releases.count)
|
||||
|
||||
card_titles.each_with_index do |title, index|
|
||||
expect(title).to have_content(expected_releases[index].name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the page is sorted by the default sort order" do
|
||||
let(:expected_releases) { [release_v3, release_v2, release_v1] }
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
end
|
||||
|
||||
context "when the page is sorted by created_at ascending " do
|
||||
let(:expected_releases) { [release_v2, release_v1, release_v3] }
|
||||
|
||||
before do
|
||||
sort_page by: 'Created date', direction: :ascending
|
||||
end
|
||||
|
||||
it_behaves_like 'releases sort order'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -98,14 +134,14 @@ RSpec.describe 'User views releases', :js do
|
|||
it 'renders release info except for Git-related data' do
|
||||
visit project_releases_path(project)
|
||||
|
||||
within('.release-block') do
|
||||
expect(page).to have_content(release.description)
|
||||
within('.release-block', match: :first) do
|
||||
expect(page).to have_content(release_v3.description)
|
||||
|
||||
# The following properties (sometimes) include Git info,
|
||||
# so they are not rendered for Guest users
|
||||
expect(page).not_to have_content(release.name)
|
||||
expect(page).not_to have_content(release.tag)
|
||||
expect(page).not_to have_content(release.commit.short_id)
|
||||
expect(page).not_to have_content(release_v3.name)
|
||||
expect(page).not_to have_content(release_v3.tag)
|
||||
expect(page).not_to have_content(release_v3.commit.short_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,5 +15,17 @@ describe('Utils', () => {
|
|||
it('returns the url with an ending when is passed', () => {
|
||||
expect(pathGenerator(imageDetails, '/foo')).toBe('/foo/bar/registry/repository/1/tags/foo');
|
||||
});
|
||||
|
||||
it('returns the url unchanged when imageDetails have no name', () => {
|
||||
const imageDetailsWithoutName = {
|
||||
path: 'foo/bar/baz',
|
||||
name: '',
|
||||
id: 1,
|
||||
};
|
||||
|
||||
expect(pathGenerator(imageDetailsWithoutName)).toBe(
|
||||
'/foo/bar/baz/registry/repository/1/tags?format=json',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
66
spec/frontend/releases/components/releases_sort_spec.js
Normal file
66
spec/frontend/releases/components/releases_sort_spec.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
import Vuex from 'vuex';
|
||||
import { GlSorting, GlSortingItem } from '@gitlab/ui';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import ReleasesSort from '~/releases/components/releases_sort.vue';
|
||||
import createStore from '~/releases/stores';
|
||||
import createListModule from '~/releases/stores/modules/list';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
describe('~/releases/components/releases_sort.vue', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let listModule;
|
||||
const projectId = 8;
|
||||
|
||||
const createComponent = () => {
|
||||
listModule = createListModule({ projectId });
|
||||
|
||||
store = createStore({
|
||||
modules: {
|
||||
list: listModule,
|
||||
},
|
||||
});
|
||||
|
||||
store.dispatch = jest.fn();
|
||||
|
||||
wrapper = shallowMount(ReleasesSort, {
|
||||
store,
|
||||
stubs: {
|
||||
GlSortingItem,
|
||||
},
|
||||
localVue,
|
||||
});
|
||||
};
|
||||
|
||||
const findReleasesSorting = () => wrapper.find(GlSorting);
|
||||
const findSortingItems = () => wrapper.findAll(GlSortingItem);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('has all the sortable items', () => {
|
||||
expect(findSortingItems()).toHaveLength(wrapper.vm.sortOptions.length);
|
||||
});
|
||||
|
||||
it('on sort change set sorting in vuex and emit event', () => {
|
||||
findReleasesSorting().vm.$emit('sortDirectionChange');
|
||||
expect(store.dispatch).toHaveBeenCalledWith('list/setSorting', { sort: 'asc' });
|
||||
expect(wrapper.emitted('sort:changed')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('on sort item click set sorting and emit event', () => {
|
||||
const item = findSortingItems().at(0);
|
||||
const { orderBy } = wrapper.vm.sortOptions[0];
|
||||
item.vm.$emit('click');
|
||||
expect(store.dispatch).toHaveBeenCalledWith('list/setSorting', { orderBy });
|
||||
expect(wrapper.emitted('sort:changed')).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -6,6 +6,7 @@ import {
|
|||
fetchReleasesGraphQl,
|
||||
fetchReleasesRest,
|
||||
receiveReleasesError,
|
||||
setSorting,
|
||||
} from '~/releases/stores/modules/list/actions';
|
||||
import createState from '~/releases/stores/modules/list/state';
|
||||
import * as types from '~/releases/stores/modules/list/mutation_types';
|
||||
|
@ -114,7 +115,7 @@ describe('Releases State actions', () => {
|
|||
it('makes a GraphQl query with a first variable', () => {
|
||||
expect(gqClient.query).toHaveBeenCalledWith({
|
||||
query: allReleasesQuery,
|
||||
variables: { fullPath: projectPath, first: PAGE_SIZE },
|
||||
variables: { fullPath: projectPath, first: PAGE_SIZE, sort: 'RELEASED_AT_DESC' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -127,7 +128,7 @@ describe('Releases State actions', () => {
|
|||
it('makes a GraphQl query with last and before variables', () => {
|
||||
expect(gqClient.query).toHaveBeenCalledWith({
|
||||
query: allReleasesQuery,
|
||||
variables: { fullPath: projectPath, last: PAGE_SIZE, before },
|
||||
variables: { fullPath: projectPath, last: PAGE_SIZE, before, sort: 'RELEASED_AT_DESC' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -140,7 +141,7 @@ describe('Releases State actions', () => {
|
|||
it('makes a GraphQl query with first and after variables', () => {
|
||||
expect(gqClient.query).toHaveBeenCalledWith({
|
||||
query: allReleasesQuery,
|
||||
variables: { fullPath: projectPath, first: PAGE_SIZE, after },
|
||||
variables: { fullPath: projectPath, first: PAGE_SIZE, after, sort: 'RELEASED_AT_DESC' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -156,6 +157,29 @@ describe('Releases State actions', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the sort parameters are provided', () => {
|
||||
it.each`
|
||||
sort | orderBy | ReleaseSort
|
||||
${'asc'} | ${'released_at'} | ${'RELEASED_AT_ASC'}
|
||||
${'desc'} | ${'released_at'} | ${'RELEASED_AT_DESC'}
|
||||
${'asc'} | ${'created_at'} | ${'CREATED_ASC'}
|
||||
${'desc'} | ${'created_at'} | ${'CREATED_DESC'}
|
||||
`(
|
||||
'correctly sets $ReleaseSort based on $sort and $orderBy',
|
||||
({ sort, orderBy, ReleaseSort }) => {
|
||||
mockedState.sorting.sort = sort;
|
||||
mockedState.sorting.orderBy = orderBy;
|
||||
|
||||
fetchReleasesGraphQl(vuexParams, { before: undefined, after: undefined });
|
||||
|
||||
expect(gqClient.query).toHaveBeenCalledWith({
|
||||
query: allReleasesQuery,
|
||||
variables: { fullPath: projectPath, first: PAGE_SIZE, sort: ReleaseSort },
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the request is successful', () => {
|
||||
|
@ -230,7 +254,11 @@ describe('Releases State actions', () => {
|
|||
});
|
||||
|
||||
it('makes a REST query with a page query parameter', () => {
|
||||
expect(api.releases).toHaveBeenCalledWith(projectId, { page });
|
||||
expect(api.releases).toHaveBeenCalledWith(projectId, {
|
||||
page,
|
||||
order_by: 'released_at',
|
||||
sort: 'desc',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -302,4 +330,16 @@ describe('Releases State actions', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSorting', () => {
|
||||
it('should commit SET_SORTING', () => {
|
||||
return testAction(
|
||||
setSorting,
|
||||
{ orderBy: 'released_at', sort: 'asc' },
|
||||
null,
|
||||
[{ type: types.SET_SORTING, payload: { orderBy: 'released_at', sort: 'asc' } }],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -80,4 +80,16 @@ describe('Releases Store Mutations', () => {
|
|||
expect(stateCopy.graphQlPageInfo).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_SORTING', () => {
|
||||
it('should merge the sorting object with sort value', () => {
|
||||
mutations[types.SET_SORTING](stateCopy, { sort: 'asc' });
|
||||
expect(stateCopy.sorting).toEqual({ ...stateCopy.sorting, sort: 'asc' });
|
||||
});
|
||||
|
||||
it('should merge the sorting object with order_by value', () => {
|
||||
mutations[types.SET_SORTING](stateCopy, { orderBy: 'created_at' });
|
||||
expect(stateCopy.sorting).toEqual({ ...stateCopy.sorting, orderBy: 'created_at' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -323,6 +323,9 @@ RSpec.describe Banzai::ReferenceParser::BaseParser do
|
|||
it 'will not overflow the stack' do
|
||||
ids = 1.upto(1_000_000).to_a
|
||||
|
||||
# Avoid executing a large, unnecessary SQL query
|
||||
expect(User).to receive(:where).with(id: ids).and_return(User.none)
|
||||
|
||||
expect { subject.collection_objects_for_ids(User, ids) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,13 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateHasVulnerabilities, schema:
|
|||
count: 2)
|
||||
end
|
||||
|
||||
context 'when non-existing project_id is given' do
|
||||
it 'populates only for the existing projects' do
|
||||
expect { subject.perform(project_1.id, 0, project_3.id) }.to change { project_settings.count }.from(1).to(2)
|
||||
.and change { project_settings.where(has_vulnerabilities: true).count }.from(0).to(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an error happens' do
|
||||
before do
|
||||
allow(described_class::ProjectSetting).to receive(:upsert_for).and_raise('foo')
|
||||
|
|
|
@ -4,7 +4,6 @@ module WikiHelpers
|
|||
extend self
|
||||
|
||||
def stub_group_wikis(enabled)
|
||||
stub_feature_flags(group_wikis: enabled)
|
||||
stub_licensed_features(group_wikis: enabled)
|
||||
end
|
||||
|
||||
|
|
|
@ -866,10 +866,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.175.0.tgz#734f341784af1cd1d62d160a17bcdfb61ff7b04d"
|
||||
integrity sha512-gXpc87TGSXIzfAr4QER1Qw1v3P47pBO6BXkma52blgwXVmcFNe3nhQzqsqt66wKNzrIrk3lAcB4GUyPHbPVXpg==
|
||||
|
||||
"@gitlab/ui@23.4.0":
|
||||
version "23.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-23.4.0.tgz#09fabb7174a99ba2993f7fe293143470c16c5ef6"
|
||||
integrity sha512-B69i5Tl78aehxPA4iRsGk1d5Za5f5KuJw4UaWeZcGQV9JkKFw+44oPvkwvIslzuq3poReO7toXaMFjXRXLIKaQ==
|
||||
"@gitlab/ui@23.6.1":
|
||||
version "23.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-23.6.1.tgz#3b94945166c756a26c9598501ebef0997a3961e7"
|
||||
integrity sha512-jOEhJUTQmdsV4XEHqY+ray+GHAMa35CsddPFtKjiD8y1YiJFzXES7tVqQr/hXzSAWbBWfxSO1Rvp55+UnCGz5g==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in a new issue