Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
25861ffdce
commit
7e5f8d0881
2
Gemfile
2
Gemfile
|
|
@ -148,7 +148,7 @@ gem 'creole', '~> 0.5.0'
|
|||
gem 'wikicloth', '0.8.1'
|
||||
gem 'asciidoctor', '~> 2.0.10'
|
||||
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
||||
gem 'asciidoctor-plantuml', '0.0.10'
|
||||
gem 'asciidoctor-plantuml', '~> 0.0.12'
|
||||
gem 'rouge', '~> 3.19.0'
|
||||
gem 'truncato', '~> 0.7.11'
|
||||
gem 'bootstrap_form', '~> 4.2.0'
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ GEM
|
|||
asciidoctor (2.0.10)
|
||||
asciidoctor-include-ext (0.3.1)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-plantuml (0.0.10)
|
||||
asciidoctor-plantuml (0.0.12)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
ast (2.4.0)
|
||||
atlassian-jwt (0.2.0)
|
||||
|
|
@ -1162,7 +1162,7 @@ DEPENDENCIES
|
|||
asana (~> 0.9)
|
||||
asciidoctor (~> 2.0.10)
|
||||
asciidoctor-include-ext (~> 0.3.1)
|
||||
asciidoctor-plantuml (= 0.0.10)
|
||||
asciidoctor-plantuml (~> 0.0.12)
|
||||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.1.0)
|
||||
awesome_print
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import {
|
|||
timeRangeFromUrl,
|
||||
panelToUrl,
|
||||
expandedPanelPayloadFromUrl,
|
||||
convertVariablesForURL,
|
||||
} from '../utils';
|
||||
import { metricStates } from '../constants';
|
||||
import { defaultTimeRange, timeRanges } from '~/vue_shared/constants';
|
||||
|
|
@ -272,7 +273,7 @@ export default {
|
|||
handler({ group, panel }) {
|
||||
const dashboardPath = this.currentDashboard || this.selectedDashboard?.path;
|
||||
updateHistory({
|
||||
url: panelToUrl(dashboardPath, group, panel),
|
||||
url: panelToUrl(dashboardPath, convertVariablesForURL(this.promVariables), group, panel),
|
||||
title: document.title,
|
||||
});
|
||||
},
|
||||
|
|
@ -343,7 +344,7 @@ export default {
|
|||
},
|
||||
generatePanelUrl(groupKey, panel) {
|
||||
const dashboardPath = this.currentDashboard || this.selectedDashboard?.path;
|
||||
return panelToUrl(dashboardPath, groupKey, panel);
|
||||
return panelToUrl(dashboardPath, convertVariablesForURL(this.promVariables), groupKey, panel);
|
||||
},
|
||||
hideAddMetricModal() {
|
||||
this.$refs.addMetricModal.hide();
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { VARIABLE_PREFIX } from './constants';
|
|||
* This will be removed once we add support for free text variables
|
||||
* via the dashboard yaml files in https://gitlab.com/gitlab-org/gitlab/-/issues/215689
|
||||
*/
|
||||
export const dashboardParams = ['dashboard', 'group', 'title', 'y_label'];
|
||||
export const dashboardParams = ['dashboard', 'group', 'title', 'y_label', 'embedded'];
|
||||
|
||||
/**
|
||||
* This method is used to validate if the graph data format for a chart component
|
||||
|
|
@ -262,14 +262,22 @@ export const expandedPanelPayloadFromUrl = (dashboard, search = window.location.
|
|||
* If no group/panel is set, the dashboard URL is returned.
|
||||
*
|
||||
* @param {?String} dashboard - Dashboard path, used as identifier for a dashboard
|
||||
* @param {?Object} promVariables - Custom variables that came from the URL
|
||||
* @param {?String} group - Group Identifier
|
||||
* @param {?Object} panel - Panel object from the dashboard
|
||||
* @param {?String} url - Base URL including current search params
|
||||
* @returns Dashboard URL which expands a panel (chart)
|
||||
*/
|
||||
export const panelToUrl = (dashboard = null, group, panel, url = window.location.href) => {
|
||||
export const panelToUrl = (
|
||||
dashboard = null,
|
||||
promVariables,
|
||||
group,
|
||||
panel,
|
||||
url = window.location.href,
|
||||
) => {
|
||||
const params = {
|
||||
dashboard,
|
||||
...promVariables,
|
||||
};
|
||||
|
||||
if (group && panel) {
|
||||
|
|
|
|||
|
|
@ -49,23 +49,19 @@ class SnippetsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
create_params = snippet_params.merge(spammable_params)
|
||||
create_params = snippet_params.merge(files: params.delete(:files))
|
||||
service_response = Snippets::CreateService.new(nil, current_user, create_params).execute
|
||||
@snippet = service_response.payload[:snippet]
|
||||
|
||||
if service_response.error? && @snippet.errors[:repository].present?
|
||||
handle_repository_error(:new)
|
||||
else
|
||||
move_temporary_files if @snippet.valid? && params[:files]
|
||||
|
||||
recaptcha_check_with_fallback { render :new }
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
update_params = snippet_params.merge(spammable_params)
|
||||
|
||||
service_response = Snippets::UpdateService.new(nil, current_user, update_params).execute(@snippet)
|
||||
service_response = Snippets::UpdateService.new(nil, current_user, snippet_params).execute(@snippet)
|
||||
@snippet = service_response.payload[:snippet]
|
||||
|
||||
handle_repository_error(:edit)
|
||||
|
|
@ -150,12 +146,6 @@ class SnippetsController < ApplicationController
|
|||
end
|
||||
|
||||
def snippet_params
|
||||
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
|
||||
end
|
||||
|
||||
def move_temporary_files
|
||||
params[:files].each do |file|
|
||||
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
|
||||
end
|
||||
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description).merge(spammable_params)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ module Snippets
|
|||
def execute
|
||||
filter_spam_check_params
|
||||
|
||||
@files = Array(params.delete(:files).presence)
|
||||
|
||||
@snippet = if project
|
||||
project.snippets.build(params)
|
||||
else
|
||||
|
|
@ -29,6 +31,8 @@ module Snippets
|
|||
UserAgentDetailService.new(@snippet, @request).create
|
||||
Gitlab::UsageDataCounters::SnippetCounter.count(:create)
|
||||
|
||||
move_temporary_files
|
||||
|
||||
ServiceResponse.success(payload: { snippet: @snippet } )
|
||||
else
|
||||
snippet_error_response(@snippet, 400)
|
||||
|
|
@ -83,5 +87,13 @@ module Snippets
|
|||
def snippet_files
|
||||
[{ file_path: params[:file_name], content: params[:content] }]
|
||||
end
|
||||
|
||||
def move_temporary_files
|
||||
return unless @snippet.is_a?(PersonalSnippet)
|
||||
|
||||
@files.each do |file|
|
||||
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
|
||||
|
||||
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: dropdown_class }
|
||||
= dropdown_title "Select Git revision"
|
||||
= dropdown_filter "Filter by Git revision"
|
||||
= dropdown_title _('Select Git revision')
|
||||
= dropdown_filter _('Filter by Git revision')
|
||||
= dropdown_content
|
||||
= dropdown_loading
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/_ref_dropdown.html.haml
|
||||
merge_request: 32102
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Ensure links generated by the copy link feature contain variables
|
||||
merge_request: 31636
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update asciidoctor-plantuml gem to v0.0.12
|
||||
merge_request: 32376
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -115,6 +115,19 @@ that, login with an Admin account and do following:
|
|||
- Check **Enable PlantUML** checkbox.
|
||||
- Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`.
|
||||
|
||||
NOTE: **Note:** If you are using a PlantUML server running v1.2020.9 and
|
||||
above (for example, [plantuml.com](https://plantuml.com)), set the `PLANTUML_ENCODING`
|
||||
environment variable to enable the `deflate` compression. On Omnibus,
|
||||
this can be done set in `/etc/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['env'] = { 'PLANTUML_ENCODING' => 'deflate' }
|
||||
```
|
||||
|
||||
From GitLab 13.1 and later, PlantUML integration now
|
||||
[requires a header prefix in the URL](https://github.com/plantuml/plantuml/issues/117#issuecomment-6235450160)
|
||||
to distinguish different encoding types.
|
||||
|
||||
## Creating Diagrams
|
||||
|
||||
With PlantUML integration enabled and configured, we can start adding diagrams to
|
||||
|
|
|
|||
|
|
@ -572,29 +572,7 @@ Ci::Pipeline.where(project_id: p.id).where(status: 'pending').count
|
|||
|
||||
### Remove artifacts more than a week old
|
||||
|
||||
The Latest version of these steps can be found in the [job artifacts documentation](../job_artifacts.md)
|
||||
|
||||
```ruby
|
||||
### SELECTING THE BUILDS TO CLEAR
|
||||
# For a single project:
|
||||
project = Project.find_by_full_path('')
|
||||
builds_with_artifacts = project.builds.with_downloadable_artifacts
|
||||
|
||||
# Instance-wide:
|
||||
builds_with_artifacts = Ci::Build.with_downloadable_artifacts
|
||||
|
||||
# Prior to 10.6 the above lines would be:
|
||||
# builds_with_artifacts = project.builds.with_artifacts
|
||||
# builds_with_artifacts = Ci::Build.with_artifacts
|
||||
|
||||
### CLEAR THEM OUT
|
||||
# Note that this will also erase artifacts that developers marked to "Keep"
|
||||
builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
|
||||
builds_to_clear.each do |build|
|
||||
build.artifacts_expire_at = Time.now
|
||||
build.erase_erasable_artifacts!
|
||||
end
|
||||
```
|
||||
This section has been moved to the [job artifacts troubleshooting documentation](../job_artifacts.md#delete-job-artifacts-from-jobs-completed-before-a-specific-date).
|
||||
|
||||
### Find reason failure (for when build trace is empty) (Introduced in 10.3.0)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ First of all, our [Quick Start Guide](../quick_start/README.md) contains a good
|
|||
You may also be interested in [Auto DevOps](../../topics/autodevops/index.md) which can potentially be used to build, test,
|
||||
and deploy your applications with little to no configuration needed at all.
|
||||
|
||||
For an example of how to convert a Jenkins pipeline into a GitLab CI/CD pipeline,
|
||||
or how to use Auto DevOps to test your code automatically, watch the
|
||||
[Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y) video.
|
||||
|
||||
For advanced CI/CD teams, [templates](#templates) can enable the reuse of pipeline configurations.
|
||||
|
||||
Otherwise, read on for important information that will help you get the ball rolling. Welcome
|
||||
|
|
|
|||
|
|
@ -37,79 +37,23 @@ To enable pipelines for merge requests:
|
|||
|
||||
## Configuring pipelines for merge requests
|
||||
|
||||
To configure pipelines for merge requests, configure your [CI/CD configuration file](../yaml/README.md).
|
||||
There are a few different ways to do this.
|
||||
To configure pipelines for merge requests you need to configure your [CI/CD configuration file](../yaml/README.md).
|
||||
There are a few different ways to do this:
|
||||
|
||||
### Enable pipelines for merge requests for all jobs
|
||||
### Use `rules` to run pipelines for merge requests
|
||||
|
||||
The recommended method for enabling pipelines for merge requests for all jobs in
|
||||
a pipeline is to use [`workflow:rules`](../yaml/README.md#workflowrules).
|
||||
|
||||
In this example, the pipeline always runs for all merge requests, as well as for all changes
|
||||
to the master branch:
|
||||
|
||||
```yaml
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID # Execute jobs in merge request context
|
||||
- if: $CI_COMMIT_BRANCH == 'master' # Execute jobs when a new commit is pushed to master branch
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: ./build
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: ./test
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: ./deploy
|
||||
```
|
||||
|
||||
### Enable pipelines for merge requests for specific jobs
|
||||
|
||||
To enable pipelines for merge requests for specific jobs, you can use
|
||||
[`rules`](../yaml/README.md#rules).
|
||||
|
||||
In the following example:
|
||||
|
||||
- The `build` job runs for all changes to the `master` branch, as well as for all merge requests.
|
||||
- The `test` job runs for all merge requests.
|
||||
- The `deploy` job runs for all changes to the `master` branch, but does *not* run
|
||||
for merge requests.
|
||||
|
||||
```yaml
|
||||
build:
|
||||
stage: build
|
||||
script: ./build
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' # Execute jobs when a new commit is pushed to master branch
|
||||
- if: $CI_MERGE_REQUEST_ID # Execute jobs in merge request context
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script: ./test
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID # Execute jobs in merge request context
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: ./deploy
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == 'master' # Execute jobs when a new commit is pushed to master branch
|
||||
```
|
||||
When using `rules`, which is the preferred method, we recommend starting with one
|
||||
of the [`workflow:rules` templates](../yaml/README.md#workflowrules-templates) to ensure
|
||||
your basic configuration is correct. Instructions on how to do this, as well as how
|
||||
to customize, are available at that link.
|
||||
|
||||
### Use `only` or `except` to run pipelines for merge requests
|
||||
|
||||
NOTE: **Note**:
|
||||
The [`only` / `except`](../yaml/README.md#onlyexcept-basic) keywords are going to be deprecated
|
||||
and you should not use them.
|
||||
If you want to continue using `only/except`, this is possible but please review the drawbacks
|
||||
below.
|
||||
|
||||
To enable pipelines for merge requests, you can use `only / except`. When you use this method,
|
||||
you have to specify `only: - merge_requests` for each job.
|
||||
|
||||
In this example, the pipeline contains a `test` job that is configured to run on merge requests.
|
||||
When you use this method, you have to specify `only: - merge_requests` for each job. In this
|
||||
example, the pipeline contains a `test` job that is configured to run on merge requests.
|
||||
|
||||
The `build` and `deploy` jobs don't have the `only: - merge_requests` parameter,
|
||||
so they will not run on merge requests.
|
||||
|
|
@ -259,60 +203,16 @@ The variable names begin with the `CI_MERGE_REQUEST_` prefix.
|
|||
|
||||
### Two pipelines created when pushing to a merge request
|
||||
|
||||
If two pipelines are created when you push a new change to a merge request,
|
||||
check your CI configuration file.
|
||||
If you are experiencing duplicated pipelines when using `rules`, take a look at
|
||||
the [key details when using `rules`](../yaml/README.md#key-details-when-using-rules),
|
||||
which will help you get your starting configuration correct.
|
||||
|
||||
For example, with this `.gitlab-ci.yml` configuration:
|
||||
|
||||
```yaml
|
||||
test:
|
||||
script: ./test
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID # Include this job in pipelines for merge request
|
||||
- if: $CI_COMMIT_BRANCH # Include this job in all branch pipelines
|
||||
# Or, if you are using the `only:` keyword:
|
||||
# only:
|
||||
# - merge_requests
|
||||
# - branches
|
||||
```
|
||||
|
||||
Two pipelines are created when you push a commit to a branch that also has a pending
|
||||
merge request:
|
||||
|
||||
- A merge request pipeline that runs for the changes in the merge request. In
|
||||
**CI/CD > Pipelines**, the merge request icon (**{merge-request}**)
|
||||
and the merge request ID are displayed. If you hover over the ID, the merge request name is displayed.
|
||||
|
||||

|
||||
|
||||
- A "branch" pipeline that runs for the commit pushed to the branch. In **CI/CD > Pipelines**,
|
||||
the branch icon (**{branch}**) and branch name are displayed. This pipeline is
|
||||
created even if no merge request exists.
|
||||
|
||||

|
||||
|
||||
With the example configuration above, there is overlap between these two events.
|
||||
When you push a commit to a branch that also has an open merge request pending,
|
||||
both types of pipelines are created.
|
||||
|
||||
To fix this overlap, you must explicitly define which job should run for which
|
||||
purpose, for example:
|
||||
|
||||
```yaml
|
||||
test:
|
||||
script: ./test
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID # Include this job in pipelines for merge request
|
||||
- if: $CI_COMMIT_BRANCH == 'master' # Include this job in master branch pipelines
|
||||
```
|
||||
|
||||
Similar `rules:` should be added to all jobs to avoid any overlapping pipelines. Alternatively,
|
||||
you can use the [`workflow:`](../yaml/README.md#exclude-jobs-with-rules-from-certain-pipelines)
|
||||
parameter to add the same rules to all jobs globally.
|
||||
If you are seeing two pipelines when using `only/except`, please see the caveats
|
||||
related to using `only/except` above (or, consider moving to `rules`).
|
||||
|
||||
### Two pipelines created when pushing an invalid CI configuration file
|
||||
|
||||
Similar to above, pushing to a branch with an invalid CI configuration file can trigger
|
||||
Pushing to a branch with an invalid CI configuration file can trigger
|
||||
the creation of two types of failed pipelines. One pipeline is a failed merge request
|
||||
pipeline, and the other is a failed branch pipeline, but both are caused by the same
|
||||
invalid configuration.
|
||||
|
|
|
|||
|
|
@ -286,7 +286,45 @@ determine whether or not a pipeline is created. It currently accepts a single
|
|||
`rules:` key that operates similarly to [`rules:` defined within jobs](#rules),
|
||||
enabling dynamic configuration of the pipeline.
|
||||
|
||||
The configuration options currently available for `workflow:rules` are:
|
||||
#### `workflow:rules` templates
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217732) in GitLab 13.0.
|
||||
|
||||
We provide pre-made templates for use with your pipelines that set up `workflow: rules`
|
||||
for common scenarios. Usage of these will make things easier and prevent duplicate pipelines from running.
|
||||
|
||||
The [`Branch-Pipelines` template](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/Branch-Pipelines.gitlab-ci.yml)
|
||||
makes your pipelines run for branches and tags.
|
||||
|
||||
Branch pipeline status will be displayed within merge requests that use that branch
|
||||
as a source, but this pipeline type does not support any features offered by
|
||||
[Merge Request Pipelines](../merge_request_pipelines/) like
|
||||
[Pipelines for Merge Results](../merge_request_pipelines/#pipelines-for-merged-results-premium)
|
||||
or [Merge Trains](../merge_request_pipelines/pipelines_for_merged_results/merge_trains/).
|
||||
Use this template if you are intentionally avoiding those features.
|
||||
|
||||
It is [included](#include) as follows:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: 'Workflows/Branch-Pipelines.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
The [`MergeRequest-Pipelines` include](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml) sets your pipelines to run for the default branch (usually `master`), tags, and
|
||||
The [`MergeRequest-Pipelines` template](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml)
|
||||
makes your pipelines run for the default branch (usually `master`), tags, and
|
||||
all types of merge request pipelines. Use this template if you use any of the
|
||||
the [Pipelines for Merge Requests features](../merge_request_pipelines/), as mentioned
|
||||
above.
|
||||
|
||||
It is [included](#include) as follows:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
If you prefer to define your own rules, the configuration options currently available are:
|
||||
|
||||
- [`if`](#rulesif): Define a rule.
|
||||
- [`when`](#when): May be set to `always` or `never` only. If not provided, the default value is `always`.
|
||||
|
|
@ -858,10 +896,26 @@ the `.template` job, and uses the `alpine` Docker image as defined in the local
|
|||
|
||||
`rules` allows for a list of individual rule objects to be evaluated
|
||||
*in order*, until one matches and dynamically provides attributes to the job.
|
||||
Note that `rules` can't be used in combination with `only/except` since it's intended
|
||||
to replace that functionality. If you attempt to do this the linter will return a
|
||||
|
||||
CAUTION: **Caution:**
|
||||
`rules` can't be used in combination with `only/except` as it is a replacement for that functionality. If you attempt to do this, the linter will return a
|
||||
`key may not be used with rules` error.
|
||||
|
||||
#### Key details when using `rules`
|
||||
|
||||
A very important difference between `rules` and `only/except`, is that jobs defined
|
||||
with `rules` trigger merge request pipelines by default, but `only/except` jobs do not.
|
||||
This may be surprising if migrating from `only` and `except`, so new users of `rules`
|
||||
can use one of the [`workflow: rules` templates](#workflowrules-templates) to get started.
|
||||
This will ensure that the behavior is more stable as you start adding additional `rules`
|
||||
blocks, and will avoid issues like creating a duplicate, merge request (detached) pipeline.
|
||||
|
||||
We don't recomment mixing `only/except` jobs with `rules` jobs in the same pipeline.
|
||||
It may not cause YAML errors, but debugging the exact execution behavior can be complex
|
||||
due to the different default behaviors of `only/except` and `rules`.
|
||||
|
||||
### Rules clauses
|
||||
|
||||
Available rule clauses include:
|
||||
|
||||
- [`if`](#rulesif) (similar to [`only:variables`](#onlyvariablesexceptvariables))
|
||||
|
|
@ -995,47 +1049,6 @@ job:
|
|||
|
||||
In this example, if the first rule matches, then the job will have `when: manual` and `allow_failure: true`.
|
||||
|
||||
#### Exclude jobs with `rules:` from certain pipelines
|
||||
|
||||
Jobs with `rules:` can cause two pipelines to be created unexpectedly:
|
||||
|
||||
- One pipeline from pushing a commit to a branch.
|
||||
- A second ["detached" pipeline for a merge request](../merge_request_pipelines/index.md).
|
||||
|
||||
`only` and `except` jobs don't trigger merge request pipelines by default, but this
|
||||
is not the case for jobs with `rules:`, which may be surprising if migrating from `only`
|
||||
and `except` to `rules:`.
|
||||
|
||||
If you're using `rules:` and you see two pipelines for commits to branches that have
|
||||
a merge request, you have two options:
|
||||
|
||||
- Individually exclude each job that uses `rules:` from merge request pipelines. The
|
||||
example below will cause the job to **not** run in *pipelines for merge requests*,
|
||||
but it **will** run in pipelines for *new tags and pipelines running on branch refs*:
|
||||
|
||||
```yaml
|
||||
job:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
when: never
|
||||
- when: manual
|
||||
script:
|
||||
- echo hello
|
||||
```
|
||||
|
||||
- Add a global [`workflow: rules`](#workflowrules) to allow pipelines in only certain
|
||||
situations. The example below will only run pipelines for merge requests, new tags and
|
||||
changes to master. It will **not** run any pipelines *on any branch except master*, but
|
||||
it will run **detached merge request pipelines** for any merge request, targeting any branch:
|
||||
|
||||
```yaml
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
- if: $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
```
|
||||
|
||||
#### Complex rule clauses
|
||||
|
||||
To conjoin `if`, `changes`, and `exists` clauses with an AND, use them in the
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ From GitLab, you can trigger a Jenkins build when you push code to a repository,
|
|||
request is created. In return, Jenkins shows the pipeline status on merge requests widgets and
|
||||
on the GitLab project's home page.
|
||||
|
||||
To better understand GitLab's Jenkins integration, watch the following videos:
|
||||
To better understand GitLab's Jenkins integration, watch the following video:
|
||||
|
||||
- [GitLab workflow with Jira issues and Jenkins pipelines](https://youtu.be/Jn-_fyra7xQ)
|
||||
- [Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y)
|
||||
|
||||
Use the Jenkins integration with GitLab when:
|
||||
|
||||
- You plan to migrate your CI from Jenkins to [GitLab CI/CD](../ci/README.md) in the future, but
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ The following table shows which languages, package managers and frameworks are s
|
|||
|
||||
| Language (package managers) / framework | Scan tool | Introduced in GitLab Version |
|
||||
|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------------------|
|
||||
| .NET | [Security Code Scan](https://security-code-scan.github.io) | 11.0 |
|
||||
| .NET Core | [Security Code Scan](https://security-code-scan.github.io) | 11.0 |
|
||||
| .NET Framework | [Security Code Scan](https://security-code-scan.github.io) | 13.0 |
|
||||
| Any | [Gitleaks](https://github.com/zricethezav/gitleaks) and [TruffleHog](https://github.com/dxa4481/truffleHog) | 11.9 |
|
||||
| Apex (Salesforce) | [PMD](https://pmd.github.io/pmd/index.html) | 12.1 |
|
||||
| C/C++ | [Flawfinder](https://dwheeler.com/flawfinder/) | 10.7 |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# Read more on when to use this template at
|
||||
# https://docs.gitlab.com/ee/ci/yaml/#workflowrules
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Read more on when to use this template at
|
||||
# https://docs.gitlab.com/ee/ci/yaml/#workflowrules
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_IID
|
||||
- if: $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
|
@ -9473,6 +9473,9 @@ msgstr ""
|
|||
msgid "Filter by %{page_context_word} that are currently opened."
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter by Git revision"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter by commit message"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18975,6 +18978,9 @@ msgstr ""
|
|||
msgid "Select Archive Format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select Git revision"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select GitLab project to link with your Slack team"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,23 @@ describe Projects::LogsController do
|
|||
let(:container) { 'container-1' }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:empty_project) { create(:project) }
|
||||
|
||||
it 'returns 404 with developer access' do
|
||||
project.add_developer(user)
|
||||
|
||||
get :index, params: environment_params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'renders empty logs page if no environment exists' do
|
||||
empty_project.add_maintainer(user)
|
||||
|
||||
get :index, params: { namespace_id: empty_project.namespace, project_id: empty_project }
|
||||
|
||||
expect(response).to be_ok
|
||||
|
|
@ -33,6 +40,8 @@ describe Projects::LogsController do
|
|||
end
|
||||
|
||||
it 'renders index template' do
|
||||
project.add_maintainer(user)
|
||||
|
||||
get :index, params: environment_params
|
||||
|
||||
expect(response).to be_ok
|
||||
|
|
@ -60,70 +69,84 @@ describe Projects::LogsController do
|
|||
end
|
||||
end
|
||||
|
||||
it 'returns the service result' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(json_response).to eq(service_result_json)
|
||||
end
|
||||
|
||||
it 'registers a usage of the endpoint' do
|
||||
expect(::Gitlab::UsageCounters::PodLogs).to receive(:increment).with(project.id)
|
||||
it 'returns 404 with developer access' do
|
||||
project.add_developer(user)
|
||||
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'sets the polling header' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(response.headers['Poll-Interval']).to eq('3000')
|
||||
end
|
||||
|
||||
context 'when service is processing' do
|
||||
let(:service_result) { nil }
|
||||
|
||||
it 'returns a 202' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:accepted)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unsuccessful execution response' do |message|
|
||||
let(:service_result) do
|
||||
{
|
||||
status: :error,
|
||||
message: message
|
||||
}
|
||||
context 'with maintainer access' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'returns the error' do
|
||||
it 'returns the service result' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(json_response).to eq(service_result_json)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when service is failing' do
|
||||
it_behaves_like 'unsuccessful execution response', 'some error'
|
||||
end
|
||||
it 'registers a usage of the endpoint' do
|
||||
expect(::Gitlab::UsageCounters::PodLogs).to receive(:increment).with(project.id)
|
||||
|
||||
context 'when cluster is nil' do
|
||||
let!(:cluster) { nil }
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments'
|
||||
end
|
||||
|
||||
context 'when namespace is empty' do
|
||||
before do
|
||||
allow(environment).to receive(:deployment_namespace).and_return('')
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
end
|
||||
|
||||
it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments'
|
||||
it 'sets the polling header' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(response.headers['Poll-Interval']).to eq('3000')
|
||||
end
|
||||
|
||||
context 'when service is processing' do
|
||||
let(:service_result) { nil }
|
||||
|
||||
it 'returns a 202' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:accepted)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'unsuccessful execution response' do |message|
|
||||
let(:service_result) do
|
||||
{
|
||||
status: :error,
|
||||
message: message
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns the error' do
|
||||
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response).to eq(service_result_json)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when service is failing' do
|
||||
it_behaves_like 'unsuccessful execution response', 'some error'
|
||||
end
|
||||
|
||||
context 'when cluster is nil' do
|
||||
let!(:cluster) { nil }
|
||||
|
||||
it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments'
|
||||
end
|
||||
|
||||
context 'when namespace is empty' do
|
||||
before do
|
||||
allow(environment).to receive(:deployment_namespace).and_return('')
|
||||
end
|
||||
|
||||
it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -243,39 +243,13 @@ describe SnippetsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the snippet description contains a file' do
|
||||
include FileMoverHelpers
|
||||
context 'when the controller receives the files param' do
|
||||
let(:files) { %w(foo bar) }
|
||||
|
||||
let(:picture_secret) { SecureRandom.hex }
|
||||
let(:text_secret) { SecureRandom.hex }
|
||||
let(:picture_file) { "/-/system/user/#{user.id}/#{picture_secret}/picture.jpg" }
|
||||
let(:text_file) { "/-/system/user/#{user.id}/#{text_secret}/text.txt" }
|
||||
let(:description) do
|
||||
"Description with picture:  and "\
|
||||
"text: [text.txt](/uploads#{text_file})"
|
||||
end
|
||||
it 'passes the files param to the snippet create service' do
|
||||
expect(Snippets::CreateService).to receive(:new).with(nil, user, hash_including(files: files)).and_call_original
|
||||
|
||||
before do
|
||||
allow(FileUtils).to receive(:mkdir_p)
|
||||
allow(FileUtils).to receive(:move)
|
||||
stub_file_mover(text_file)
|
||||
stub_file_mover(picture_file)
|
||||
end
|
||||
|
||||
subject { create_snippet({ description: description }, { files: [picture_file, text_file] }) }
|
||||
|
||||
it 'creates the snippet' do
|
||||
expect { subject }.to change { Snippet.count }.by(1)
|
||||
end
|
||||
|
||||
it 'stores the snippet description correctly' do
|
||||
snippet = subject
|
||||
|
||||
expected_description = "Description with picture: "\
|
||||
" and "\
|
||||
"text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/#{text_secret}/text.txt)"
|
||||
|
||||
expect(snippet.description).to eq(expected_description)
|
||||
create_snippet({ title: nil }, { files: files })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -315,24 +315,31 @@ describe('monitoring/utils', () => {
|
|||
const getUrlParams = url => urlUtils.queryToObject(url.split('?')[1]);
|
||||
|
||||
it('returns URL for a panel when query parameters are given', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, panelGroup.group, panel));
|
||||
const params = getUrlParams(panelToUrl(dashboard, {}, panelGroup.group, panel));
|
||||
|
||||
expect(params).toEqual({
|
||||
dashboard,
|
||||
group: panelGroup.group,
|
||||
title: panel.title,
|
||||
y_label: panel.y_label,
|
||||
});
|
||||
expect(params).toEqual(
|
||||
expect.objectContaining({
|
||||
dashboard,
|
||||
group: panelGroup.group,
|
||||
title: panel.title,
|
||||
y_label: panel.y_label,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns a dashboard only URL if group is missing', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, null, panel));
|
||||
expect(params).toEqual({ dashboard: 'metrics.yml' });
|
||||
const params = getUrlParams(panelToUrl(dashboard, {}, null, panel));
|
||||
expect(params).toEqual(expect.objectContaining({ dashboard: 'metrics.yml' }));
|
||||
});
|
||||
|
||||
it('returns a dashboard only URL if panel is missing', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, panelGroup.group, null));
|
||||
expect(params).toEqual({ dashboard: 'metrics.yml' });
|
||||
const params = getUrlParams(panelToUrl(dashboard, {}, panelGroup.group, null));
|
||||
expect(params).toEqual(expect.objectContaining({ dashboard: 'metrics.yml' }));
|
||||
});
|
||||
|
||||
it('returns URL for a panel when query paramters are given including custom variables', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, { pod: 'pod' }, panelGroup.group, null));
|
||||
expect(params).toEqual(expect.objectContaining({ dashboard: 'metrics.yml', pod: 'pod' }));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ describe EnvironmentEntity do
|
|||
described_class.new(environment, request: spy('request'))
|
||||
end
|
||||
|
||||
let(:environment) { create(:environment) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:environment) { create(:environment, project: project) }
|
||||
|
||||
before do
|
||||
allow(entity).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
subject { entity.as_json }
|
||||
|
||||
|
|
@ -67,28 +73,48 @@ describe EnvironmentEntity do
|
|||
end
|
||||
|
||||
context 'with auto_stop_in' do
|
||||
let(:environment) { create(:environment, :will_auto_stop) }
|
||||
let(:environment) { create(:environment, :will_auto_stop, project: project) }
|
||||
|
||||
it 'exposes auto stop related information' do
|
||||
project.add_maintainer(user)
|
||||
|
||||
expect(subject).to include(:cancel_auto_stop_path, :auto_stop_at)
|
||||
end
|
||||
end
|
||||
|
||||
context 'pod_logs' do
|
||||
it 'exposes logs keys' do
|
||||
expect(subject).to include(:logs_path)
|
||||
expect(subject).to include(:logs_api_path)
|
||||
expect(subject).to include(:enable_advanced_logs_querying)
|
||||
context 'with developer access' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'does not expose logs keys' do
|
||||
expect(subject).not_to include(:logs_path)
|
||||
expect(subject).not_to include(:logs_api_path)
|
||||
expect(subject).not_to include(:enable_advanced_logs_querying)
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses k8s api when ES is not available' do
|
||||
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(environment.project, environment_name: environment.name, format: :json))
|
||||
end
|
||||
context 'with maintainer access' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'uses ES api when ES is available' do
|
||||
allow(environment).to receive(:elastic_stack_available?).and_return(true)
|
||||
it 'exposes logs keys' do
|
||||
expect(subject).to include(:logs_path)
|
||||
expect(subject).to include(:logs_api_path)
|
||||
expect(subject).to include(:enable_advanced_logs_querying)
|
||||
end
|
||||
|
||||
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(environment.project, environment_name: environment.name, format: :json))
|
||||
it 'uses k8s api when ES is not available' do
|
||||
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(project, environment_name: environment.name, format: :json))
|
||||
end
|
||||
|
||||
it 'uses ES api when ES is available' do
|
||||
allow(environment).to receive(:elastic_stack_available?).and_return(true)
|
||||
|
||||
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(project, environment_name: environment.name, format: :json))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -285,6 +285,20 @@ describe Snippets::CreateService do
|
|||
it_behaves_like 'an error service response when save fails'
|
||||
it_behaves_like 'creates repository and files'
|
||||
it_behaves_like 'after_save callback to store_mentions', ProjectSnippet
|
||||
|
||||
context 'when uploaded files are passed to the service' do
|
||||
let(:extra_opts) { { files: ['foo'] } }
|
||||
|
||||
it 'does not move uploaded files to the snippet' do
|
||||
expect_next_instance_of(described_class) do |instance|
|
||||
expect(instance).to receive(:move_temporary_files).and_call_original
|
||||
end
|
||||
|
||||
expect_any_instance_of(FileMover).not_to receive(:execute)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when PersonalSnippet' do
|
||||
|
|
@ -297,6 +311,51 @@ describe Snippets::CreateService do
|
|||
it_behaves_like 'an error service response when save fails'
|
||||
it_behaves_like 'creates repository and files'
|
||||
it_behaves_like 'after_save callback to store_mentions', PersonalSnippet
|
||||
|
||||
context 'when the snippet description contains files' do
|
||||
include FileMoverHelpers
|
||||
|
||||
let(:title) { 'Title' }
|
||||
let(:picture_secret) { SecureRandom.hex }
|
||||
let(:text_secret) { SecureRandom.hex }
|
||||
let(:picture_file) { "/-/system/user/#{creator.id}/#{picture_secret}/picture.jpg" }
|
||||
let(:text_file) { "/-/system/user/#{creator.id}/#{text_secret}/text.txt" }
|
||||
let(:files) { [picture_file, text_file] }
|
||||
let(:description) do
|
||||
"Description with picture:  and "\
|
||||
"text: [text.txt](/uploads#{text_file})"
|
||||
end
|
||||
|
||||
before do
|
||||
allow(FileUtils).to receive(:mkdir_p)
|
||||
allow(FileUtils).to receive(:move)
|
||||
end
|
||||
|
||||
let(:extra_opts) { { description: description, title: title, files: files } }
|
||||
|
||||
it 'stores the snippet description correctly' do
|
||||
stub_file_mover(text_file)
|
||||
stub_file_mover(picture_file)
|
||||
|
||||
snippet = subject.payload[:snippet]
|
||||
|
||||
expected_description = "Description with picture: "\
|
||||
" and "\
|
||||
"text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/#{text_secret}/text.txt)"
|
||||
|
||||
expect(snippet.description).to eq(expected_description)
|
||||
end
|
||||
|
||||
context 'when there is a validation error' do
|
||||
let(:title) { nil }
|
||||
|
||||
it 'does not move uploaded files to the snippet' do
|
||||
expect_any_instance_of(described_class).not_to receive(:move_temporary_files)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue