Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
47dc051075
commit
095467eafb
|
@ -1,14 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { GlAlert, GlButton, GlIcon, GlFormCheckbox, GlTooltipDirective } from '@gitlab/ui';
|
||||||
GlAlert,
|
|
||||||
GlButton,
|
|
||||||
GlIcon,
|
|
||||||
GlFormCheckbox,
|
|
||||||
GlTooltipDirective,
|
|
||||||
GlDropdown,
|
|
||||||
GlDropdownItem,
|
|
||||||
GlDropdownDivider,
|
|
||||||
} from '@gitlab/ui';
|
|
||||||
import Autosize from 'autosize';
|
import Autosize from 'autosize';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||||
|
@ -34,6 +25,7 @@ import { COMMENT_FORM } from '../i18n';
|
||||||
|
|
||||||
import issuableStateMixin from '../mixins/issuable_state';
|
import issuableStateMixin from '../mixins/issuable_state';
|
||||||
import CommentFieldLayout from './comment_field_layout.vue';
|
import CommentFieldLayout from './comment_field_layout.vue';
|
||||||
|
import CommentTypeDropdown from './comment_type_dropdown.vue';
|
||||||
import discussionLockedWidget from './discussion_locked_widget.vue';
|
import discussionLockedWidget from './discussion_locked_widget.vue';
|
||||||
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||||
|
|
||||||
|
@ -42,8 +34,6 @@ const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
|
||||||
export default {
|
export default {
|
||||||
name: 'CommentForm',
|
name: 'CommentForm',
|
||||||
i18n: COMMENT_FORM,
|
i18n: COMMENT_FORM,
|
||||||
noteTypeComment: constants.COMMENT,
|
|
||||||
noteTypeDiscussion: constants.DISCUSSION,
|
|
||||||
components: {
|
components: {
|
||||||
noteSignedOutWidget,
|
noteSignedOutWidget,
|
||||||
discussionLockedWidget,
|
discussionLockedWidget,
|
||||||
|
@ -53,10 +43,8 @@ export default {
|
||||||
TimelineEntryItem,
|
TimelineEntryItem,
|
||||||
GlIcon,
|
GlIcon,
|
||||||
CommentFieldLayout,
|
CommentFieldLayout,
|
||||||
|
CommentTypeDropdown,
|
||||||
GlFormCheckbox,
|
GlFormCheckbox,
|
||||||
GlDropdown,
|
|
||||||
GlDropdownItem,
|
|
||||||
GlDropdownDivider,
|
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
GlTooltip: GlTooltipDirective,
|
GlTooltip: GlTooltipDirective,
|
||||||
|
@ -88,12 +76,6 @@ export default {
|
||||||
'hasDrafts',
|
'hasDrafts',
|
||||||
]),
|
]),
|
||||||
...mapState(['isToggleStateButtonLoading']),
|
...mapState(['isToggleStateButtonLoading']),
|
||||||
isNoteTypeComment() {
|
|
||||||
return this.noteType === constants.COMMENT;
|
|
||||||
},
|
|
||||||
isNoteTypeDiscussion() {
|
|
||||||
return this.noteType === constants.DISCUSSION;
|
|
||||||
},
|
|
||||||
noteableDisplayName() {
|
noteableDisplayName() {
|
||||||
return splitCamelCase(this.noteableType).toLowerCase();
|
return splitCamelCase(this.noteableType).toLowerCase();
|
||||||
},
|
},
|
||||||
|
@ -105,15 +87,8 @@ export default {
|
||||||
? this.$options.i18n.comment
|
? this.$options.i18n.comment
|
||||||
: this.$options.i18n.startThread;
|
: this.$options.i18n.startThread;
|
||||||
},
|
},
|
||||||
startDiscussionDescription() {
|
discussionsRequireResolution() {
|
||||||
return this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
|
return this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE;
|
||||||
? this.$options.i18n.discussionThatNeedsResolution
|
|
||||||
: this.$options.i18n.discussion;
|
|
||||||
},
|
|
||||||
commentDescription() {
|
|
||||||
return sprintf(this.$options.i18n.submitButton.commentHelp, {
|
|
||||||
noteableDisplayName: this.noteableDisplayName,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
isOpen() {
|
isOpen() {
|
||||||
return this.openState === constants.OPENED || this.openState === constants.REOPENED;
|
return this.openState === constants.OPENED || this.openState === constants.REOPENED;
|
||||||
|
@ -314,15 +289,6 @@ export default {
|
||||||
|
|
||||||
this.autosave.reset();
|
this.autosave.reset();
|
||||||
},
|
},
|
||||||
setNoteType(type) {
|
|
||||||
this.noteType = type;
|
|
||||||
},
|
|
||||||
setNoteTypeToComment() {
|
|
||||||
this.setNoteType(constants.COMMENT);
|
|
||||||
},
|
|
||||||
setNoteTypeToDiscussion() {
|
|
||||||
this.setNoteType(constants.DISCUSSION);
|
|
||||||
},
|
|
||||||
editCurrentUserLastNote() {
|
editCurrentUserLastNote() {
|
||||||
if (this.note === '') {
|
if (this.note === '') {
|
||||||
const lastNote = this.getCurrentUserLastNote;
|
const lastNote = this.getCurrentUserLastNote;
|
||||||
|
@ -448,40 +414,15 @@ export default {
|
||||||
class="gl-text-gray-500"
|
class="gl-text-gray-500"
|
||||||
/>
|
/>
|
||||||
</gl-form-checkbox>
|
</gl-form-checkbox>
|
||||||
<gl-dropdown
|
<comment-type-dropdown
|
||||||
split
|
v-model="noteType"
|
||||||
:text="commentButtonTitle"
|
class="gl-mr-3"
|
||||||
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
|
|
||||||
category="primary"
|
|
||||||
variant="confirm"
|
|
||||||
:disabled="disableSubmitButton"
|
:disabled="disableSubmitButton"
|
||||||
data-testid="comment-button"
|
:tracking-label="trackingLabel"
|
||||||
data-qa-selector="comment_button"
|
:noteable-display-name="noteableDisplayName"
|
||||||
:data-track-label="trackingLabel"
|
:discussions-require-resolution="discussionsRequireResolution"
|
||||||
data-track-event="click_button"
|
@click="handleSave"
|
||||||
@click="handleSave()"
|
/>
|
||||||
>
|
|
||||||
<gl-dropdown-item
|
|
||||||
is-check-item
|
|
||||||
:is-checked="isNoteTypeComment"
|
|
||||||
:selected="isNoteTypeComment"
|
|
||||||
@click="setNoteTypeToComment"
|
|
||||||
>
|
|
||||||
<strong>{{ $options.i18n.submitButton.comment }}</strong>
|
|
||||||
<p class="gl-m-0">{{ commentDescription }}</p>
|
|
||||||
</gl-dropdown-item>
|
|
||||||
<gl-dropdown-divider />
|
|
||||||
<gl-dropdown-item
|
|
||||||
is-check-item
|
|
||||||
:is-checked="isNoteTypeDiscussion"
|
|
||||||
:selected="isNoteTypeDiscussion"
|
|
||||||
data-qa-selector="discussion_menu_item"
|
|
||||||
@click="setNoteTypeToDiscussion"
|
|
||||||
>
|
|
||||||
<strong>{{ $options.i18n.submitButton.startThread }}</strong>
|
|
||||||
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
|
|
||||||
</gl-dropdown-item>
|
|
||||||
</gl-dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
<gl-button
|
<gl-button
|
||||||
v-if="canToggleIssueState"
|
v-if="canToggleIssueState"
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
<script>
|
||||||
|
import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
|
||||||
|
|
||||||
|
import { sprintf } from '~/locale';
|
||||||
|
import { COMMENT_FORM } from '~/notes/i18n';
|
||||||
|
import * as constants from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
i18n: COMMENT_FORM,
|
||||||
|
components: {
|
||||||
|
GlDropdown,
|
||||||
|
GlDropdownItem,
|
||||||
|
GlDropdownDivider,
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'noteType',
|
||||||
|
event: 'change',
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
trackingLabel: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
discussionsRequireResolution: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
noteableDisplayName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
noteType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNoteTypeComment() {
|
||||||
|
return this.noteType === constants.COMMENT;
|
||||||
|
},
|
||||||
|
isNoteTypeDiscussion() {
|
||||||
|
return this.noteType === constants.DISCUSSION;
|
||||||
|
},
|
||||||
|
commentButtonTitle() {
|
||||||
|
return this.noteType === constants.COMMENT
|
||||||
|
? this.$options.i18n.comment
|
||||||
|
: this.$options.i18n.startThread;
|
||||||
|
},
|
||||||
|
startDiscussionDescription() {
|
||||||
|
return this.discussionsRequireResolution
|
||||||
|
? this.$options.i18n.discussionThatNeedsResolution
|
||||||
|
: this.$options.i18n.discussion;
|
||||||
|
},
|
||||||
|
commentDescription() {
|
||||||
|
return sprintf(this.$options.i18n.submitButton.commentHelp, {
|
||||||
|
noteableDisplayName: this.noteableDisplayName,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
this.$emit('click');
|
||||||
|
},
|
||||||
|
setNoteTypeToComment() {
|
||||||
|
if (this.noteType !== constants.COMMENT) {
|
||||||
|
this.$emit('change', constants.COMMENT);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setNoteTypeToDiscussion() {
|
||||||
|
if (this.noteType !== constants.DISCUSSION) {
|
||||||
|
this.$emit('change', constants.DISCUSSION);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<gl-dropdown
|
||||||
|
split
|
||||||
|
:text="commentButtonTitle"
|
||||||
|
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
|
||||||
|
category="primary"
|
||||||
|
variant="confirm"
|
||||||
|
:disabled="disabled"
|
||||||
|
data-testid="comment-button"
|
||||||
|
data-qa-selector="comment_button"
|
||||||
|
:data-track-label="trackingLabel"
|
||||||
|
data-track-event="click_button"
|
||||||
|
@click="$emit('click')"
|
||||||
|
>
|
||||||
|
<gl-dropdown-item is-check-item :is-checked="isNoteTypeComment" @click="setNoteTypeToComment">
|
||||||
|
<strong>{{ $options.i18n.submitButton.comment }}</strong>
|
||||||
|
<p class="gl-m-0">{{ commentDescription }}</p>
|
||||||
|
</gl-dropdown-item>
|
||||||
|
<gl-dropdown-divider />
|
||||||
|
<gl-dropdown-item
|
||||||
|
is-check-item
|
||||||
|
:is-checked="isNoteTypeDiscussion"
|
||||||
|
data-qa-selector="discussion_menu_item"
|
||||||
|
@click="setNoteTypeToDiscussion"
|
||||||
|
>
|
||||||
|
<strong>{{ $options.i18n.submitButton.startThread }}</strong>
|
||||||
|
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
|
||||||
|
</gl-dropdown-item>
|
||||||
|
</gl-dropdown>
|
||||||
|
</template>
|
|
@ -293,6 +293,13 @@ class Packages::Package < ApplicationRecord
|
||||||
::Packages::Maven::Metadata::SyncWorker.perform_async(user.id, project.id, name)
|
::Packages::Maven::Metadata::SyncWorker.perform_async(user.id, project.id, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_build_infos!(build)
|
||||||
|
return unless build&.pipeline
|
||||||
|
|
||||||
|
# TODO: use an upsert call when https://gitlab.com/gitlab-org/gitlab/-/issues/339093 is implemented
|
||||||
|
build_infos.find_or_create_by!(pipeline: build.pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def composer_tag_version?
|
def composer_tag_version?
|
||||||
|
|
|
@ -29,7 +29,8 @@ module Packages
|
||||||
|
|
||||||
package.update_column(:status, params[:status]) if params[:status] && params[:status] != package.status
|
package.update_column(:status, params[:status]) if params[:status] && params[:status] != package.status
|
||||||
|
|
||||||
package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present?
|
package.create_build_infos!(params[:build])
|
||||||
|
|
||||||
package
|
package
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ module Packages
|
||||||
.execute
|
.execute
|
||||||
end
|
end
|
||||||
|
|
||||||
package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present?
|
package.create_build_infos!(params[:build])
|
||||||
|
|
||||||
ServiceResponse.success(payload: { package: package })
|
ServiceResponse.success(payload: { package: package })
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveCloudLicenseEnabledFromApplicationSettings < ActiveRecord::Migration[6.1]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
with_lock_retries do
|
||||||
|
remove_column :application_settings, :cloud_license_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
with_lock_retries do
|
||||||
|
add_column :application_settings, :cloud_license_enabled, :boolean, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
5dd1596d0d6e6f5aa39cbf8a65be294650bead7a099cf50917b438cf75529257
|
|
@ -9598,7 +9598,6 @@ CREATE TABLE application_settings (
|
||||||
encrypted_cloud_license_auth_token text,
|
encrypted_cloud_license_auth_token text,
|
||||||
encrypted_cloud_license_auth_token_iv text,
|
encrypted_cloud_license_auth_token_iv text,
|
||||||
secret_detection_revocation_token_types_url text,
|
secret_detection_revocation_token_types_url text,
|
||||||
cloud_license_enabled boolean DEFAULT false NOT NULL,
|
|
||||||
disable_feed_token boolean DEFAULT false NOT NULL,
|
disable_feed_token boolean DEFAULT false NOT NULL,
|
||||||
personal_access_token_prefix text,
|
personal_access_token_prefix text,
|
||||||
rate_limiting_response_text text,
|
rate_limiting_response_text text,
|
||||||
|
|
|
@ -96,12 +96,14 @@ Example response:
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"active": true,
|
"active": true,
|
||||||
"public_key": "glet_aa77551d849c083f76d0bc545ed053a3"
|
"public_key": "glet_aa77551d849c083f76d0bc545ed053a3",
|
||||||
|
"sentry_dsn": "https://glet_aa77551d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"active": true,
|
"active": true,
|
||||||
"public_key": "glet_0ff98b1d849c083f76d0bc545ed053a3"
|
"public_key": "glet_0ff98b1d849c083f76d0bc545ed053a3",
|
||||||
|
"sentry_dsn": "https://glet_0ff98b1d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -129,7 +131,8 @@ Example response:
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"active": true,
|
"active": true,
|
||||||
"public_key": "glet_0ff98b1d849c083f76d0bc545ed053a3"
|
"public_key": "glet_0ff98b1d849c083f76d0bc545ed053a3",
|
||||||
|
"sentry_dsn": "https://glet_0ff98b1d849c083f76d0bc545ed053a3@gitlab.example.com/api/v4/error_tracking/collector/5"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -405,76 +405,7 @@ on how the left-side navigation menu is built and updated.
|
||||||
|
|
||||||
## Previewing the changes live
|
## Previewing the changes live
|
||||||
|
|
||||||
NOTE:
|
See how you can use review apps to [preview your changes live](review_apps.md).
|
||||||
To preview your changes to documentation locally, follow this
|
|
||||||
[development guide](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/README.md#development-when-contributing-to-gitlab-documentation) or [these instructions for GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/gitlab_docs.md).
|
|
||||||
|
|
||||||
The live preview is currently enabled for the following projects:
|
|
||||||
|
|
||||||
- [`gitlab`](https://gitlab.com/gitlab-org/gitlab)
|
|
||||||
- [`omnibus-gitlab`](https://gitlab.com/gitlab-org/omnibus-gitlab)
|
|
||||||
- [`gitlab-runner`](https://gitlab.com/gitlab-org/gitlab-runner)
|
|
||||||
|
|
||||||
If your merge request has docs changes, you can use the manual `review-docs-deploy` job
|
|
||||||
to deploy the docs review app for your merge request.
|
|
||||||
|
|
||||||
![Manual trigger a docs build](img/manual_build_docs.png)
|
|
||||||
|
|
||||||
You must push a branch to those repositories, as it doesn't work for forks.
|
|
||||||
|
|
||||||
The `review-docs-deploy*` job:
|
|
||||||
|
|
||||||
1. Triggers a cross project pipeline and build the docs site with your changes.
|
|
||||||
|
|
||||||
In case the review app URL returns 404, this means that either the site is not
|
|
||||||
yet deployed, or something went wrong with the remote pipeline. Give it a few
|
|
||||||
minutes and it should appear online, otherwise you can check the status of the
|
|
||||||
remote pipeline from the link in the merge request's job output.
|
|
||||||
If the pipeline failed or got stuck, drop a line in the `#docs` chat channel.
|
|
||||||
|
|
||||||
NOTE:
|
|
||||||
Someone with no merge rights to the GitLab projects (think of forks from
|
|
||||||
contributors) cannot run the manual job. In that case, you can
|
|
||||||
ask someone from the GitLab team who has the permissions to do that for you.
|
|
||||||
|
|
||||||
### Troubleshooting review apps
|
|
||||||
|
|
||||||
In case the review app URL returns 404, follow these steps to debug:
|
|
||||||
|
|
||||||
1. **Did you follow the URL from the merge request widget?** If yes, then check if
|
|
||||||
the link is the same as the one in the job output.
|
|
||||||
1. **Did you follow the URL from the job output?** If yes, then it means that
|
|
||||||
either the site is not yet deployed or something went wrong with the remote
|
|
||||||
pipeline. Give it a few minutes and it should appear online, otherwise you
|
|
||||||
can check the status of the remote pipeline from the link in the job output.
|
|
||||||
If the pipeline failed or got stuck, drop a line in the `#docs` chat channel.
|
|
||||||
|
|
||||||
### Technical aspects
|
|
||||||
|
|
||||||
If you want to know the in-depth details, here's what's really happening:
|
|
||||||
|
|
||||||
1. You manually run the `review-docs-deploy` job in a merge request.
|
|
||||||
1. The job runs the [`scripts/trigger-build`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/trigger-build)
|
|
||||||
script with the `docs deploy` flag, which triggers the "Triggered from `gitlab-org/gitlab` 'review-docs-deploy' job"
|
|
||||||
pipeline trigger in the `gitlab-org/gitlab-docs` project for the `$DOCS_BRANCH` (defaults to `main`).
|
|
||||||
1. The preview URL is shown both at the job output and in the merge request
|
|
||||||
widget. You also get the link to the remote pipeline.
|
|
||||||
1. In the `gitlab-org/gitlab-docs` project, the pipeline is created and it
|
|
||||||
[skips the test jobs](https://gitlab.com/gitlab-org/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55)
|
|
||||||
to lower the build time.
|
|
||||||
1. Once the docs site is built, the HTML files are uploaded as artifacts.
|
|
||||||
1. A specific runner tied only to the docs project, runs the Review App job
|
|
||||||
that downloads the artifacts and uses `rsync` to transfer the files over
|
|
||||||
to a location where NGINX serves them.
|
|
||||||
|
|
||||||
The following GitLab features are used among others:
|
|
||||||
|
|
||||||
- [Manual jobs](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually)
|
|
||||||
- [Multi project pipelines](../../ci/pipelines/multi_project_pipelines.md)
|
|
||||||
- [Review Apps](../../ci/review_apps/index.md)
|
|
||||||
- [Artifacts](../../ci/yaml/index.md#artifacts)
|
|
||||||
- [Specific runner](../../ci/runners/runners_scope.md#prevent-a-specific-runner-from-being-enabled-for-other-projects)
|
|
||||||
- [Pipelines for merge requests](../../ci/pipelines/merge_request_pipelines.md)
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
stage: none
|
||||||
|
group: Documentation Guidelines
|
||||||
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||||
|
description: Learn how documentation review apps work.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Documentation review apps
|
||||||
|
|
||||||
|
If your merge request contains documentation changes, you can use a review app to preview
|
||||||
|
how they would look if they were deployed to the [GitLab Docs site](https://docs.gitlab.com).
|
||||||
|
|
||||||
|
Review apps are enabled for the following projects:
|
||||||
|
|
||||||
|
- [GitLab](https://gitlab.com/gitlab-org/gitlab)
|
||||||
|
- [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab)
|
||||||
|
- [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner)
|
||||||
|
- [GitLab Charts](https://gitlab.com/gitlab-org/charts/gitlab)
|
||||||
|
|
||||||
|
Alternatively, check the [`gitlab-docs` development guide](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/README.md#development-when-contributing-to-gitlab-documentation)
|
||||||
|
or [the GDK documentation](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/gitlab_docs.md)
|
||||||
|
to render and preview the documentation locally.
|
||||||
|
|
||||||
|
## How to trigger a review app
|
||||||
|
|
||||||
|
If a merge request has documentation changes, use the `review-docs-deploy` manual job
|
||||||
|
to deploy the documentation review app for your merge request.
|
||||||
|
|
||||||
|
![Manual trigger a documentation review app](img/manual_build_docs.png)
|
||||||
|
|
||||||
|
The `review-docs-deploy*` job triggers a cross project pipeline and builds the
|
||||||
|
docs site with your changes. When the pipeline finishes, the review app URL
|
||||||
|
appears in the merge request widget. Use it to navigate to your changes.
|
||||||
|
|
||||||
|
You must have the Developer role in the project. Users without the Developer role, such
|
||||||
|
as external contributors, cannot run the manual job. In that case, ask someone from
|
||||||
|
the GitLab team to run the job.
|
||||||
|
|
||||||
|
## Technical aspects
|
||||||
|
|
||||||
|
If you want to know the in-depth details, here's what's really happening:
|
||||||
|
|
||||||
|
1. You manually run the `review-docs-deploy` job in a merge request.
|
||||||
|
1. The job runs the [`scripts/trigger-build`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/trigger-build)
|
||||||
|
script with the `docs deploy` flag, which triggers the "Triggered from `gitlab-org/gitlab` 'review-docs-deploy' job"
|
||||||
|
pipeline trigger in the `gitlab-org/gitlab-docs` project for the `$DOCS_BRANCH` (defaults to `main`).
|
||||||
|
1. The preview URL is shown both at the job output and in the merge request
|
||||||
|
widget. You also get the link to the remote pipeline.
|
||||||
|
1. In the `gitlab-org/gitlab-docs` project, the pipeline is created and it
|
||||||
|
[skips the test jobs](https://gitlab.com/gitlab-org/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55)
|
||||||
|
to lower the build time.
|
||||||
|
1. Once the docs site is built, the HTML files are uploaded as artifacts.
|
||||||
|
1. A specific runner tied only to the docs project, runs the Review App job
|
||||||
|
that downloads the artifacts and uses `rsync` to transfer the files over
|
||||||
|
to a location where NGINX serves them.
|
||||||
|
|
||||||
|
The following GitLab features are used among others:
|
||||||
|
|
||||||
|
- [Manual jobs](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually)
|
||||||
|
- [Multi project pipelines](../../ci/pipelines/multi_project_pipelines.md)
|
||||||
|
- [Review Apps](../../ci/review_apps/index.md)
|
||||||
|
- [Artifacts](../../ci/yaml/index.md#artifacts)
|
||||||
|
- [Specific runner](../../ci/runners/runners_scope.md#prevent-a-specific-runner-from-being-enabled-for-other-projects)
|
||||||
|
- [Pipelines for merge requests](../../ci/pipelines/merge_request_pipelines.md)
|
||||||
|
|
||||||
|
## Troubleshooting review apps
|
||||||
|
|
||||||
|
### Review app returns a 404 error
|
||||||
|
|
||||||
|
If the review app URL returns a 404 error, either the site is not
|
||||||
|
yet deployed, or something went wrong with the remote pipeline. You can:
|
||||||
|
|
||||||
|
- Wait a few minutes and it should appear online.
|
||||||
|
- Check the manual job's log and verify the URL. If the URL is different, try the
|
||||||
|
one from the job log.
|
||||||
|
- Check the status of the remote pipeline from the link in the merge request's job output.
|
||||||
|
If the pipeline failed or got stuck, GitLab team members can ask for help in the `#docs`
|
||||||
|
chat channel. Contributors can ping a technical writer in the merge request.
|
||||||
|
|
||||||
|
### Not enough disk space
|
||||||
|
|
||||||
|
Sometimes the review app server is full and there is no more disk space. Each review
|
||||||
|
app takes about 570MB of disk space.
|
||||||
|
|
||||||
|
A cron job to remove review apps older than 20 days runs hourly,
|
||||||
|
but the disk space still occasionally fills up. To manually free up more space,
|
||||||
|
a GitLab technical writing team member can:
|
||||||
|
|
||||||
|
1. Navigate to the [`gitlab-docs` schedules page](https://gitlab.com/gitlab-org/gitlab-docs/-/pipeline_schedules).
|
||||||
|
1. Select the play button for the `Remove old review apps from review app server`
|
||||||
|
schedule. By default, this cleans up review apps older than 14 days.
|
||||||
|
1. Navigate to the [pipelines page](https://gitlab.com/gitlab-org/gitlab-docs/-/pipelines)
|
||||||
|
and start the manual job called `clean-pages`.
|
||||||
|
|
||||||
|
If the job says no review apps were found in that period, edit the `CLEAN_REVIEW_APPS_DAYS`
|
||||||
|
variable in the schedule, and repeat the process above. Gradually decrease the variable
|
||||||
|
until the free disk space reaches an acceptable amount (for example, 3GB).
|
||||||
|
Remember to set it to 14 again when you're done.
|
||||||
|
|
||||||
|
There's an issue to [migrate from the DigitalOcean server to GCP buckets](https://gitlab.com/gitlab-org/gitlab-docs/-/issues/735)),
|
||||||
|
which should solve the disk space problem.
|
|
@ -46,18 +46,18 @@ The following table lists project permissions available for each role:
|
||||||
| Action | Guest | Reporter | Developer | Maintainer | Owner |
|
| Action | Guest | Reporter | Developer | Maintainer | Owner |
|
||||||
|-------------------------------------------------------------------------------------------------------------------------|----------|----------|-----------|------------|-------|
|
|-------------------------------------------------------------------------------------------------------------------------|----------|----------|-----------|------------|-------|
|
||||||
| [Analytics](analytics/index.md):<br>View issue analytics **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View issue analytics **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View [merge request analytics](analytics/merge_request_analytics.md) **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View [merge request analytics](analytics/merge_request_analytics.md) **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View value stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View value stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View [DORA metrics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View [DORA metrics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View [CI/CD analytics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View [CI/CD analytics](analytics/ci_cd_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View [code review analytics](analytics/code_review_analytics.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View [code review analytics](analytics/code_review_analytics.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Analytics](analytics/index.md):<br>View [repository analytics](analytics/repository_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
| [Analytics](analytics/index.md):<br>View [repository analytics](analytics/repository_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>View licenses in [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>View licenses in [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>Create and run [on-demand DAST scans](application_security/dast/index.md#on-demand-scans) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>Create and run [on-demand DAST scans](application_security/dast/index.md#on-demand-scans) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>Manage [security policy](application_security/policies/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>Manage [security policy](application_security/policies/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>View [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>View [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>View [threats list](application_security/threat_monitoring/index.md#threat-monitoring) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>View [threats list](application_security/threat_monitoring/index.md#threat-monitoring) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>Create a [CVE ID Request](application_security/cve_id_request.md) **(FREE SAAS)** | | | | ✓ | ✓ |
|
| [Application security](application_security/index.md):<br>Create a [CVE ID Request](application_security/cve_id_request.md) **(FREE SAAS)** | | | | ✓ | ✓ |
|
||||||
| [Application security](application_security/index.md):<br>Create or assign [security policy project](application_security/policies/index.md) **(ULTIMATE)** | | | | | ✓ |
|
| [Application security](application_security/index.md):<br>Create or assign [security policy project](application_security/policies/index.md) **(ULTIMATE)** | | | | | ✓ |
|
||||||
| [CI/CD](../ci/README.md):<br>Download and browse job artifacts | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
|
| [CI/CD](../ci/README.md):<br>Download and browse job artifacts | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [CI/CD](../ci/README.md):<br>View a job log | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
|
| [CI/CD](../ci/README.md):<br>View a job log | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
@ -74,13 +74,16 @@ The following table lists project permissions available for each role:
|
||||||
| [CI/CD](../ci/README.md):<br>Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
|
| [CI/CD](../ci/README.md):<br>Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
|
||||||
| [CI/CD](../ci/README.md):<br>Use [environment terminals](../ci/environments/index.md#web-terminals) | | | | ✓ | ✓ |
|
| [CI/CD](../ci/README.md):<br>Use [environment terminals](../ci/environments/index.md#web-terminals) | | | | ✓ | ✓ |
|
||||||
| [CI/CD](../ci/README.md):<br>Delete pipelines | | | | | ✓ |
|
| [CI/CD](../ci/README.md):<br>Delete pipelines | | | | | ✓ |
|
||||||
|
| [Container Registry](packages/container_registry/index.md):<br>Create, edit, delete cleanup policies | | | ✓ | ✓ | ✓ |
|
||||||
|
| [Container Registry](packages/container_registry/index.md):<br>Remove a container registry image | | | ✓ | ✓ | ✓ |
|
||||||
|
| [Container Registry](packages/container_registry/index.md):<br>Update container registry | | | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Add Labels | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Add Labels | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Assign | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Assign | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Create | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Create | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Create [confidential issues](project/issues/confidential_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Create [confidential issues](project/issues/confidential_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>View [Design Management](project/issues/design_management.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>View [Design Management](project/issues/design_management.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>View related issues | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>View related issues | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Set weight | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Set weight | ✓ (*16*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>View [confidential issues](project/issues/confidential_issues.md) | (*2*) | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>View [confidential issues](project/issues/confidential_issues.md) | (*2*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Lock threads | | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Lock threads | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Issues](project/issues/index.md):<br>Manage related issues | | ✓ | ✓ | ✓ | ✓ |
|
| [Issues](project/issues/index.md):<br>Manage related issues | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
@ -97,20 +100,28 @@ The following table lists project permissions available for each role:
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Create | | | ✓ | ✓ | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Create | | | ✓ | ✓ | ✓ |
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Add labels | | | ✓ | ✓ | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Add labels | | | ✓ | ✓ | ✓ |
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Lock threads | | | ✓ | ✓ | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Lock threads | | | ✓ | ✓ | ✓ |
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Manage or accept | | | ✓ | ✓ | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Manage or accept | | | ✓ | ✓ | ✓ |
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Manage merge approval rules (project settings) | | | | ✓ | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Manage merge approval rules (project settings) | | | | ✓ | ✓ |
|
||||||
| [Merge requests](project/merge_requests/index.md):<br>Delete | | | | | ✓ |
|
| [Merge requests](project/merge_requests/index.md):<br>Delete | | | | | ✓ |
|
||||||
|
| [Package registry](packages/index.md):<br>Pull package | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| [Package registry](packages/index.md):<br>Publish package | | | ✓ | ✓ | ✓ |
|
||||||
|
| [Package registry](packages/index.md):<br>Delete package | | | | ✓ | ✓ |
|
||||||
|
| [Project operations](../operations/index.md):<br>View [Error Tracking](../operations/error_tracking.md) list | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| [Project operations](../operations/index.md):<br>Manage [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ |
|
||||||
|
| [Project operations](../operations/index.md):<br>Manage [Error Tracking](../operations/error_tracking.md) | | | | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Reposition comments on images (posted by any user) | ✓ (*10*) | ✓ (*10*) | ✓ (*10*) | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Reposition comments on images (posted by any user) | ✓ (*10*) | ✓ (*10*) | ✓ (*10*) | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View Insights **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View Insights **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| [Projects](project/index.md):<br>View [releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View Requirements **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View Requirements **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View [time tracking](project/time_tracking.md) reports | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View [time tracking](project/time_tracking.md) reports | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View [wiki](project/wiki/index.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View [wiki](project/wiki/index.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Create [snippets](snippets.md) | | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Create [snippets](snippets.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View project statistics | | ✓ | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View project statistics | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Create, edit, delete [milestones](project/milestones/index.md). | | | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Create, edit, delete [milestones](project/milestones/index.md). | | | ✓ | ✓ | ✓ |
|
||||||
|
| [Projects](project/index.md):<br>Create, edit, delete [releases](project/releases/index.md) | | | ✓ (*13*) | ✓ (*13*) | ✓ (*13*) |
|
||||||
| [Projects](project/index.md):<br>Create, edit [wiki](project/wiki/index.md) pages | | | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Create, edit [wiki](project/wiki/index.md) pages | | | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>Enable Review Apps | | | ✓ | ✓ | ✓ |
|
| [Projects](project/index.md):<br>Enable Review Apps | | | ✓ | ✓ | ✓ |
|
||||||
| [Projects](project/index.md):<br>View project [Audit Events](../administration/audit_events.md) | | | ✓ (*11*) | ✓ | ✓ |
|
| [Projects](project/index.md):<br>View project [Audit Events](../administration/audit_events.md) | | | ✓ (*11*) | ✓ | ✓ |
|
||||||
|
@ -147,7 +158,7 @@ The following table lists project permissions available for each role:
|
||||||
| [Repository](project/repository/index.md):<br>Enable or disable tag protection | | | | ✓ | ✓ |
|
| [Repository](project/repository/index.md):<br>Enable or disable tag protection | | | | ✓ | ✓ |
|
||||||
| [Repository](project/repository/index.md):<br>Manage [push rules](../push_rules/push_rules.md) | | | | ✓ | ✓ |
|
| [Repository](project/repository/index.md):<br>Manage [push rules](../push_rules/push_rules.md) | | | | ✓ | ✓ |
|
||||||
| [Repository](project/repository/index.md):<br>Push to protected branches | | | | ✓ | ✓ |
|
| [Repository](project/repository/index.md):<br>Push to protected branches | | | | ✓ | ✓ |
|
||||||
| [Repository](project/repository/index.md):<br>Turn on or off protected branch push for developers | | | | ✓ | ✓ |
|
| [Repository](project/repository/index.md):<br>Turn on or off protected branch push for developers | | | | ✓ | ✓ |
|
||||||
| [Repository](project/repository/index.md):<br>Remove fork relationship | | | | | ✓ |
|
| [Repository](project/repository/index.md):<br>Remove fork relationship | | | | | ✓ |
|
||||||
| [Repository](project/repository/index.md):<br>Force push to protected branches (*4*) | | | | | |
|
| [Repository](project/repository/index.md):<br>Force push to protected branches (*4*) | | | | | |
|
||||||
| [Repository](project/repository/index.md):<br>Remove protected branches (*4*) | | | | | |
|
| [Repository](project/repository/index.md):<br>Remove protected branches (*4*) | | | | | |
|
||||||
|
@ -162,7 +173,6 @@ The following table lists project permissions available for each role:
|
||||||
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability findings in [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability findings in [dependency list](application_security/dependency_list/index.md) **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
|
||||||
| Manage user-starred metrics dashboards (*7*) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| Manage user-starred metrics dashboards (*7*) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| View allowed and denied licenses **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
| View allowed and denied licenses **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View License Compliance reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
| View License Compliance reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
@ -172,24 +182,14 @@ The following table lists project permissions available for each role:
|
||||||
| Create/edit requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
| Create/edit requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Import/export requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
| Import/export requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Move [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
|
| Move [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Pull [packages](packages/index.md) | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Reopen [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
|
| Reopen [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| View License list **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
| View License list **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
|
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Create/edit/delete [releases](project/releases/index.md)| | | ✓ (*13*) | ✓ (*13*) | ✓ (*13*) |
|
|
||||||
| Create/edit/delete a Cleanup policy | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
|
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
|
||||||
| Manage Feature Flags **(PREMIUM)** | | | ✓ | ✓ | ✓ |
|
|
||||||
| Publish [packages](packages/index.md) | | | ✓ | ✓ | ✓ |
|
|
||||||
| Read Terraform state | | | ✓ | ✓ | ✓ |
|
| Read Terraform state | | | ✓ | ✓ | ✓ |
|
||||||
| Remove a container registry image | | | ✓ | ✓ | ✓ |
|
|
||||||
| Update a container registry | | | ✓ | ✓ | ✓ |
|
|
||||||
| View Pods logs | | | ✓ | ✓ | ✓ |
|
| View Pods logs | | | ✓ | ✓ | ✓ |
|
||||||
| Configure project hooks | | | | ✓ | ✓ |
|
| Configure project hooks | | | | ✓ | ✓ |
|
||||||
| Delete [packages](packages/index.md) | | | | ✓ | ✓ |
|
|
||||||
| Manage clusters | | | | ✓ | ✓ |
|
| Manage clusters | | | | ✓ | ✓ |
|
||||||
| Manage Error Tracking | | | | ✓ | ✓ |
|
|
||||||
| Manage GitLab Pages | | | | ✓ | ✓ |
|
| Manage GitLab Pages | | | | ✓ | ✓ |
|
||||||
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
|
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
|
||||||
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
|
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
|
||||||
|
|
|
@ -87,59 +87,64 @@ Example `.compliance-gitlab-ci.yml`
|
||||||
# Allows compliance team to control the ordering and interweaving of stages/jobs.
|
# Allows compliance team to control the ordering and interweaving of stages/jobs.
|
||||||
# Stages without jobs defined will remain hidden.
|
# Stages without jobs defined will remain hidden.
|
||||||
stages:
|
stages:
|
||||||
- pre-compliance
|
- pre-compliance
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
- pre-deploy-compliance
|
- pre-deploy-compliance
|
||||||
- deploy
|
- deploy
|
||||||
- post-compliance
|
- post-compliance
|
||||||
|
|
||||||
variables: # can be overriden by a developer's local .gitlab-ci.yml
|
variables: # Can be overridden by setting a job-specific variable in project's local .gitlab-ci.yml
|
||||||
FOO: sast
|
FOO: sast
|
||||||
|
|
||||||
sast: # none of these attributes can be overriden by a developer's local .gitlab-ci.yml
|
sast: # None of these attributes can be overridden by a project's local .gitlab-ci.yml
|
||||||
variables:
|
variables:
|
||||||
FOO: sast
|
FOO: sast
|
||||||
image: ruby:2.6
|
image: ruby:2.6
|
||||||
stage: pre-compliance
|
stage: pre-compliance
|
||||||
rules:
|
rules:
|
||||||
- when: always
|
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
|
||||||
|
when: never
|
||||||
|
- when: always # or when: on_success
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
before_script:
|
before_script:
|
||||||
- "# No before scripts."
|
- "# No before scripts."
|
||||||
script:
|
script:
|
||||||
- echo "running $FOO"
|
- echo "running $FOO"
|
||||||
after_script:
|
after_script:
|
||||||
- "# No after scripts."
|
- "# No after scripts."
|
||||||
|
|
||||||
sanity check:
|
sanity check:
|
||||||
image: ruby:2.6
|
image: ruby:2.6
|
||||||
stage: pre-deploy-compliance
|
stage: pre-deploy-compliance
|
||||||
rules:
|
rules:
|
||||||
- when: always
|
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
|
||||||
|
when: never
|
||||||
|
- when: always # or when: on_success
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
before_script:
|
before_script:
|
||||||
- "# No before scripts."
|
- "# No before scripts."
|
||||||
script:
|
script:
|
||||||
- echo "running $FOO"
|
- echo "running $FOO"
|
||||||
after_script:
|
after_script:
|
||||||
- "# No after scripts."
|
- "# No after scripts."
|
||||||
|
|
||||||
|
|
||||||
audit trail:
|
audit trail:
|
||||||
image: ruby:2.6
|
image: ruby:2.6
|
||||||
stage: post-compliance
|
stage: post-compliance
|
||||||
rules:
|
rules:
|
||||||
- when: always
|
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
|
||||||
|
when: never
|
||||||
|
- when: always # or when: on_success
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
before_script:
|
before_script:
|
||||||
- "# No before scripts."
|
- "# No before scripts."
|
||||||
script:
|
script:
|
||||||
- echo "running $FOO"
|
- echo "running $FOO"
|
||||||
after_script:
|
after_script:
|
||||||
- "# No after scripts."
|
- "# No after scripts."
|
||||||
|
|
||||||
include: # Execute individual project's configuration
|
include: # Execute individual project's configuration (if project contains .gitlab-ci.yml)
|
||||||
project: '$CI_PROJECT_PATH'
|
project: '$CI_PROJECT_PATH'
|
||||||
file: '$CI_CONFIG_PATH'
|
file: '$CI_CONFIG_PATH'
|
||||||
ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch.
|
ref: '$CI_COMMIT_REF_NAME' # Must be defined or MR pipelines always use the use default branch.
|
||||||
|
@ -187,7 +192,7 @@ section.
|
||||||
You can now change the [Project visibility](../../../public_access/public_access.md).
|
You can now change the [Project visibility](../../../public_access/public_access.md).
|
||||||
If you set **Project Visibility** to public, you can limit access to some features
|
If you set **Project Visibility** to public, you can limit access to some features
|
||||||
to **Only Project Members**. In addition, you can select the option to
|
to **Only Project Members**. In addition, you can select the option to
|
||||||
[Allow users to request access](../members/index.md#prevent-users-from-requesting-access-to-a-project).
|
[Allow users to request access](../members/index.md#request-access-to-a-project).
|
||||||
|
|
||||||
Use the switches to enable or disable the following features:
|
Use the switches to enable or disable the following features:
|
||||||
|
|
||||||
|
@ -350,7 +355,7 @@ to transfer a project.
|
||||||
|
|
||||||
You can transfer an existing project into a [group](../../group/index.md) if:
|
You can transfer an existing project into a [group](../../group/index.md) if:
|
||||||
|
|
||||||
- You have at least the Maintainer** role in that group.
|
- You have at least **Maintainer** [role](../../permissions.md#project-members-permissions) in that group.
|
||||||
- You're at least an **Owner** of the project to be transferred.
|
- You're at least an **Owner** of the project to be transferred.
|
||||||
- The group to which the project is being transferred to must allow creation of new projects.
|
- The group to which the project is being transferred to must allow creation of new projects.
|
||||||
|
|
||||||
|
@ -457,7 +462,7 @@ To do so:
|
||||||
1. Confirm the action by typing the project's path as instructed.
|
1. Confirm the action by typing the project's path as instructed.
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
|
Only project owners have the [permissions](../../permissions.md#project-members-permissions)
|
||||||
to remove a fork relationship.
|
to remove a fork relationship.
|
||||||
|
|
||||||
## Monitor settings
|
## Monitor settings
|
||||||
|
|
|
@ -15,6 +15,7 @@ module API
|
||||||
expose :id
|
expose :id
|
||||||
expose :active
|
expose :active
|
||||||
expose :public_key
|
expose :public_key
|
||||||
|
expose :sentry_dsn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,8 +14,11 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
|
base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
|
||||||
element :comment_button
|
|
||||||
element :comment_field
|
element :comment_field
|
||||||
|
end
|
||||||
|
|
||||||
|
base.view 'app/assets/javascripts/notes/components/comment_type_dropdown.vue' do
|
||||||
|
element :comment_button
|
||||||
element :discussion_menu_item
|
element :discussion_menu_item
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -354,4 +354,12 @@ FactoryBot.define do
|
||||||
package
|
package
|
||||||
sequence(:name) { |n| "tag-#{n}"}
|
sequence(:name) { |n| "tag-#{n}"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :packages_build_info, class: 'Packages::BuildInfo' do
|
||||||
|
package
|
||||||
|
|
||||||
|
trait :with_pipeline do
|
||||||
|
association :pipeline, factory: [:ci_pipeline, :with_job]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import CommentForm from '~/notes/components/comment_form.vue';
|
import CommentForm from '~/notes/components/comment_form.vue';
|
||||||
|
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
|
||||||
import * as constants from '~/notes/constants';
|
import * as constants from '~/notes/constants';
|
||||||
import eventHub from '~/notes/event_hub';
|
import eventHub from '~/notes/event_hub';
|
||||||
import { COMMENT_FORM } from '~/notes/i18n';
|
import { COMMENT_FORM } from '~/notes/i18n';
|
||||||
|
@ -33,8 +34,8 @@ describe('issue_comment_form component', () => {
|
||||||
const findAddToReviewButton = () => wrapper.findByTestId('add-to-review-button');
|
const findAddToReviewButton = () => wrapper.findByTestId('add-to-review-button');
|
||||||
const findAddCommentNowButton = () => wrapper.findByTestId('add-comment-now-button');
|
const findAddCommentNowButton = () => wrapper.findByTestId('add-comment-now-button');
|
||||||
const findConfidentialNoteCheckbox = () => wrapper.findByTestId('confidential-note-checkbox');
|
const findConfidentialNoteCheckbox = () => wrapper.findByTestId('confidential-note-checkbox');
|
||||||
const findCommentGlDropdown = () => wrapper.findByTestId('comment-button');
|
const findCommentTypeDropdown = () => wrapper.findComponent(CommentTypeDropdown);
|
||||||
const findCommentButton = () => findCommentGlDropdown().find('button');
|
const findCommentButton = () => findCommentTypeDropdown().find('button');
|
||||||
const findErrorAlerts = () => wrapper.findAllComponents(GlAlert).wrappers;
|
const findErrorAlerts = () => wrapper.findAllComponents(GlAlert).wrappers;
|
||||||
|
|
||||||
async function clickCommentButton({ waitForComponent = true, waitForNetwork = true } = {}) {
|
async function clickCommentButton({ waitForComponent = true, waitForNetwork = true } = {}) {
|
||||||
|
@ -381,7 +382,7 @@ describe('issue_comment_form component', () => {
|
||||||
it('should render comment button as disabled', () => {
|
it('should render comment button as disabled', () => {
|
||||||
mountComponent();
|
mountComponent();
|
||||||
|
|
||||||
expect(findCommentGlDropdown().props('disabled')).toBe(true);
|
expect(findCommentTypeDropdown().props('disabled')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enable comment button if it has note', async () => {
|
it('should enable comment button if it has note', async () => {
|
||||||
|
@ -389,7 +390,7 @@ describe('issue_comment_form component', () => {
|
||||||
|
|
||||||
await wrapper.setData({ note: 'Foo' });
|
await wrapper.setData({ note: 'Foo' });
|
||||||
|
|
||||||
expect(findCommentGlDropdown().props('disabled')).toBe(false);
|
expect(findCommentTypeDropdown().props('disabled')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update buttons texts when it has note', () => {
|
it('should update buttons texts when it has note', () => {
|
||||||
|
@ -624,7 +625,7 @@ describe('issue_comment_form component', () => {
|
||||||
it('when no drafts exist, should not render', () => {
|
it('when no drafts exist, should not render', () => {
|
||||||
mountComponent();
|
mountComponent();
|
||||||
|
|
||||||
expect(findCommentGlDropdown().exists()).toBe(true);
|
expect(findCommentTypeDropdown().exists()).toBe(true);
|
||||||
expect(findAddToReviewButton().exists()).toBe(false);
|
expect(findAddToReviewButton().exists()).toBe(false);
|
||||||
expect(findAddCommentNowButton().exists()).toBe(false);
|
expect(findAddCommentNowButton().exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
@ -637,7 +638,7 @@ describe('issue_comment_form component', () => {
|
||||||
it('should render', () => {
|
it('should render', () => {
|
||||||
mountComponent();
|
mountComponent();
|
||||||
|
|
||||||
expect(findCommentGlDropdown().exists()).toBe(false);
|
expect(findCommentTypeDropdown().exists()).toBe(false);
|
||||||
expect(findAddToReviewButton().exists()).toBe(true);
|
expect(findAddToReviewButton().exists()).toBe(true);
|
||||||
expect(findAddCommentNowButton().exists()).toBe(true);
|
expect(findAddCommentNowButton().exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||||
|
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
|
||||||
|
import * as constants from '~/notes/constants';
|
||||||
|
import { COMMENT_FORM } from '~/notes/i18n';
|
||||||
|
|
||||||
|
describe('CommentTypeDropdown component', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const findCommentGlDropdown = () => wrapper.findComponent(GlDropdown);
|
||||||
|
const findCommentDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(0);
|
||||||
|
const findDiscussionDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(1);
|
||||||
|
|
||||||
|
const mountComponent = ({ props = {} } = {}) => {
|
||||||
|
wrapper = extendedWrapper(
|
||||||
|
mount(CommentTypeDropdown, {
|
||||||
|
propsData: {
|
||||||
|
noteableDisplayName: 'issue',
|
||||||
|
noteType: constants.COMMENT,
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should label action button "Comment" and correct dropdown item checked when selected', () => {
|
||||||
|
mountComponent({ props: { noteType: constants.COMMENT } });
|
||||||
|
|
||||||
|
expect(findCommentGlDropdown().props()).toMatchObject({ text: COMMENT_FORM.comment });
|
||||||
|
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: true });
|
||||||
|
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should label action button "Start Thread" and correct dropdown item option checked when selected', () => {
|
||||||
|
mountComponent({ props: { noteType: constants.DISCUSSION } });
|
||||||
|
|
||||||
|
expect(findCommentGlDropdown().props()).toMatchObject({ text: COMMENT_FORM.startThread });
|
||||||
|
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: false });
|
||||||
|
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should emit `change` event when clicking on an alternate dropdown option', () => {
|
||||||
|
mountComponent({ props: { noteType: constants.DISCUSSION } });
|
||||||
|
|
||||||
|
findCommentDropdownOption().vm.$emit('click');
|
||||||
|
findDiscussionDropdownOption().vm.$emit('click');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('change')[0]).toEqual([constants.COMMENT]);
|
||||||
|
expect(wrapper.emitted('change').length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should emit `click` event when clicking on the action button', () => {
|
||||||
|
mountComponent({ props: { noteType: constants.DISCUSSION } });
|
||||||
|
|
||||||
|
findCommentGlDropdown().vm.$emit('click');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('click').length > 0).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1165,4 +1165,47 @@ RSpec.describe Packages::Package, type: :model do
|
||||||
it_behaves_like 'not enqueuing a sync worker job'
|
it_behaves_like 'not enqueuing a sync worker job'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#create_build_infos!' do
|
||||||
|
let_it_be(:package) { create(:package) }
|
||||||
|
let_it_be(:pipeline) { create(:ci_pipeline) }
|
||||||
|
|
||||||
|
let(:build) { double(pipeline: pipeline) }
|
||||||
|
|
||||||
|
subject { package.create_build_infos!(build) }
|
||||||
|
|
||||||
|
context 'with a valid build' do
|
||||||
|
it 'creates a build info' do
|
||||||
|
expect { subject }.to change { ::Packages::BuildInfo.count }.by(1)
|
||||||
|
|
||||||
|
last_build = ::Packages::BuildInfo.last
|
||||||
|
expect(last_build.package).to eq(package)
|
||||||
|
expect(last_build.pipeline).to eq(pipeline)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an already existing build info' do
|
||||||
|
let_it_be(:build_info) { create(:packages_build_info, package: package, pipeline: pipeline) }
|
||||||
|
|
||||||
|
it 'does not create a build info' do
|
||||||
|
expect { subject }.not_to change { ::Packages::BuildInfo.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a nil build' do
|
||||||
|
let(:build) { nil }
|
||||||
|
|
||||||
|
it 'does not create a build info' do
|
||||||
|
expect { subject }.not_to change { ::Packages::BuildInfo.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a build without a pipeline' do
|
||||||
|
let(:build) { double(pipeline: nil) }
|
||||||
|
|
||||||
|
it 'does not create a build info' do
|
||||||
|
expect { subject }.not_to change { ::Packages::BuildInfo.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,6 +63,7 @@ RSpec.describe API::ErrorTrackingClientKeys do
|
||||||
|
|
||||||
expect(json_response['id']).to eq(new_key.id)
|
expect(json_response['id']).to eq(new_key.id)
|
||||||
expect(json_response['public_key']).to eq(new_key.public_key)
|
expect(json_response['public_key']).to eq(new_key.public_key)
|
||||||
|
expect(json_response['sentry_dsn']).to eq(new_key.sentry_dsn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -105,6 +105,37 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
|
||||||
it { expect { execute_service }.to change { project.package_files.count }.by(1) }
|
it { expect { execute_service }.to change { project.package_files.count }.by(1) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with multiple files for the same package and the same pipeline' do
|
||||||
|
let(:file_2_params) { params.merge(file_name: 'myfile.tar.gz.2', file: file2) }
|
||||||
|
let(:file_3_params) { params.merge(file_name: 'myfile.tar.gz.3', file: file3) }
|
||||||
|
|
||||||
|
let(:temp_file2) { Tempfile.new("test2") }
|
||||||
|
let(:temp_file3) { Tempfile.new("test3") }
|
||||||
|
|
||||||
|
let(:file2) { UploadedFile.new(temp_file2.path, sha256: sha256) }
|
||||||
|
let(:file3) { UploadedFile.new(temp_file3.path, sha256: sha256) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
FileUtils.touch(temp_file2)
|
||||||
|
FileUtils.touch(temp_file3)
|
||||||
|
expect(::Packages::Generic::FindOrCreatePackageService).to receive(:new).with(project, user, package_params).and_return(package_service).twice
|
||||||
|
expect(package_service).to receive(:execute).and_return(package).twice
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
FileUtils.rm_f(temp_file2)
|
||||||
|
FileUtils.rm_f(temp_file3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the build info only once' do
|
||||||
|
expect do
|
||||||
|
described_class.new(project, user, params).execute
|
||||||
|
described_class.new(project, user, file_2_params).execute
|
||||||
|
described_class.new(project, user, file_3_params).execute
|
||||||
|
end.to change { package.build_infos.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -98,6 +98,19 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
|
||||||
it 'creates a build_info' do
|
it 'creates a build_info' do
|
||||||
expect { subject }.to change { Packages::BuildInfo.count }.by(1)
|
expect { subject }.to change { Packages::BuildInfo.count }.by(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with multiple files for the same package and the same pipeline' do
|
||||||
|
let(:file_2_params) { params.merge(file_name: 'test2.jar') }
|
||||||
|
let(:file_3_params) { params.merge(file_name: 'test3.jar') }
|
||||||
|
|
||||||
|
it 'creates a single build info' do
|
||||||
|
expect do
|
||||||
|
described_class.new(project, user, params).execute
|
||||||
|
described_class.new(project, user, file_2_params).execute
|
||||||
|
described_class.new(project, user, file_3_params).execute
|
||||||
|
end.to change { ::Packages::BuildInfo.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when package duplicates are not allowed' do
|
context 'when package duplicates are not allowed' do
|
||||||
|
|
Loading…
Reference in New Issue