Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-11 18:09:10 +00:00
parent 5fc3c77e2b
commit 6d5a18ac65
50 changed files with 797 additions and 371 deletions

View file

@ -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

View file

@ -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}`;
};

View file

@ -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">
<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"
>
{{ __('New release') }}
</gl-button>
<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="js-new-release-btn"
>
{{ __('New release') }}
</gl-button>
</div>
<release-skeleton-loader v-if="isLoading" class="js-loading" />

View 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>

View file

@ -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'),
},
];

View file

@ -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
}

View file

@ -4,6 +4,7 @@ fragment Release on Release {
tagPath
descriptionHtml
releasedAt
createdAt
upcomingRelease
assets {
count

View file

@ -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);

View file

@ -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';

View file

@ -39,4 +39,8 @@ export default {
state.restPageInfo = {};
state.graphQlPageInfo = {};
},
[types.SET_SORTING](state, sorting) {
state.sorting = { ...state.sorting, ...sorting };
},
};

View file

@ -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,
},
});

View file

@ -29,7 +29,8 @@ export default {
},
changing: {
type: Boolean,
required: true,
required: false,
default: false,
},
},
computed: {

View file

@ -24,9 +24,11 @@ module IconsHelper
end
def custom_icon(icon_name, size: DEFAULT_ICON_SIZE)
# 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
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
@ -46,21 +48,23 @@ module IconsHelper
end
def sprite_icon(icon_name, size: DEFAULT_ICON_SIZE, css_class: nil)
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)
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)
end
css_classes = []
css_classes << "s#{size}" if size
css_classes << "#{css_class}" unless css_class.blank?
content_tag(
:svg,
content_tag(:use, '', { 'xlink:href' => "#{sprite_icon_path}##{icon_name}" } ),
class: css_classes.empty? ? nil : css_classes.join(' '),
data: { testid: "#{icon_name}-icon" }
)
end
css_classes = []
css_classes << "s#{size}" if size
css_classes << "#{css_class}" unless css_class.blank?
content_tag(
:svg,
content_tag(:use, '', { 'xlink:href' => "#{sprite_icon_path}##{icon_name}" } ),
class: css_classes.empty? ? nil : css_classes.join(' '),
data: { testid: "#{icon_name}-icon" }
)
end
def loading_icon(container: false, color: 'orange', size: 'sm', css_class: nil)
@ -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

View file

@ -0,0 +1,5 @@
---
title: Add ability to sort releases on Releases page
merge_request: 43963
author:
type: added

View file

@ -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

View file

@ -25,6 +25,7 @@ exceptions:
- CPU
- CSS
- CSV
- DAG
- DAST
- DNS
- EKS

View file

@ -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.

View file

@ -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:

View file

@ -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).

View file

@ -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).

View file

@ -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 |

View file

@ -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

View file

@ -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
View 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"
```

View file

@ -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)).

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -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 projects 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) |

View file

@ -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.

View file

@ -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).

View file

@ -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.

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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 ""

View file

@ -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",

View file

@ -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)
expect(page).not_to have_content('Upcoming Release')
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)
expect(page).to have_content('Upcoming Release')
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

View file

@ -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',
);
});
});
});

View 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();
});
});

View file

@ -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' } }],
[],
);
});
});
});

View file

@ -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' });
});
});
});

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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"