Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_discussion.vue36
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_note.vue8
-rw-r--r--app/assets/javascripts/design_management/constants.js1
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue18
-rw-r--r--app/assets/javascripts/design_management/utils/design_management_utils.js11
-rw-r--r--changelogs/unreleased/232839-fj-track-unique-edit-web-ide-action.yml5
-rw-r--r--changelogs/unreleased/fix-design-managemnt-comment-highlight.yml5
-rw-r--r--changelogs/unreleased/id-bump-doorkeeper-5-1.yml5
-rw-r--r--doc/administration/reference_architectures/2k_users.md4
-rw-r--r--doc/administration/reference_architectures/3k_users.md4
-rw-r--r--doc/administration/reference_architectures/5k_users.md4
-rw-r--r--doc/ci/quick_start/README.md65
-rw-r--r--doc/development/cicd/templates.md81
-rw-r--r--doc/user/packages/maven_repository/index.md30
-rw-r--r--lib/api/commits.rb5
-rw-r--r--spec/controllers/oauth/token_info_controller_spec.rb16
-rw-r--r--spec/frontend/design_management/components/design_notes/design_note_spec.js4
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap4
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js126
-rw-r--r--spec/frontend/design_management/pages/index_spec.js4
-rw-r--r--spec/frontend/design_management/utils/design_management_utils_spec.js17
-rw-r--r--spec/requests/api/commits_spec.rb27
-rw-r--r--spec/requests/api/oauth_tokens_spec.rb6
26 files changed, 337 insertions, 159 deletions
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index 62546e59368..b871ba33974 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -42,7 +42,7 @@ docs lint:
extends:
- .default-retry
- .docs:rules:docs-lint
- image: "registry.gitlab.com/gitlab-org/gitlab-docs:lint"
+ image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:vale-2.3.3-markdownlint-0.23.2"
stage: test
needs: []
script:
diff --git a/Gemfile b/Gemfile
index b45a2c67257..a9f9912969f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -26,7 +26,7 @@ gem 'marginalia', '~> 1.9.0'
# Authentication libraries
gem 'devise', '~> 4.6'
-gem 'doorkeeper', '~> 5.0.3'
+gem 'doorkeeper', '~> 5.1.1'
gem 'doorkeeper-openid_connect', '~> 1.6.3'
gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 2.0.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5d64692e6fb..0f5cfcdc056 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -254,8 +254,8 @@ GEM
docile (1.3.2)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
- doorkeeper (5.0.3)
- railties (>= 4.2)
+ doorkeeper (5.1.1)
+ railties (>= 5)
doorkeeper-openid_connect (1.6.3)
doorkeeper (>= 5.0, < 5.2)
json-jwt (~> 1.6)
@@ -1265,7 +1265,7 @@ DEPENDENCIES
diff_match_patch (~> 0.1.0)
diffy (~> 3.3)
discordrb-webhooks-blackst0ne (~> 3.3)
- doorkeeper (~> 5.0.3)
+ doorkeeper (~> 5.1.1)
doorkeeper-openid_connect (~> 1.6.3)
ed25519 (~> 1.2)
elasticsearch-api (~> 6.8)
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
index 6a20517eed7..cd2545b48de 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue
@@ -62,22 +62,20 @@ export default {
activeDiscussion: {
query: activeDiscussionQuery,
result({ data }) {
- const discussionId = data.activeDiscussion.id;
if (this.discussion.resolved && !this.resolvedDiscussionsExpanded) {
return;
}
- // We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists
- // We don't want scrollIntoView to be triggered from the discussion click itself
- if (
- discussionId &&
- data.activeDiscussion.source === ACTIVE_DISCUSSION_SOURCE_TYPES.pin &&
- discussionId === this.discussion.notes[0].id
- ) {
- this.$el.scrollIntoView({
- behavior: 'smooth',
- inline: 'start',
- });
- }
+
+ this.$nextTick(() => {
+ // We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists.
+ // We don't want scrollIntoView to be triggered from the discussion click itself.
+ if (this.$el && this.shouldScrollToDiscussion(data.activeDiscussion)) {
+ this.$el.scrollIntoView({
+ behavior: 'smooth',
+ inline: 'start',
+ });
+ }
+ });
},
},
},
@@ -136,6 +134,18 @@ export default {
isFormVisible() {
return this.isFormRendered && this.discussionWithOpenForm === this.discussion.id;
},
+ shouldScrollToDiscussion(activeDiscussion) {
+ const ALLOWED_ACTIVE_DISCUSSION_SOURCES = [
+ ACTIVE_DISCUSSION_SOURCE_TYPES.pin,
+ ACTIVE_DISCUSSION_SOURCE_TYPES.url,
+ ];
+ const { id: activeDiscussionId, source: activeDiscussionSource } = activeDiscussion;
+
+ return (
+ ALLOWED_ACTIVE_DISCUSSION_SOURCES.includes(activeDiscussionSource) &&
+ activeDiscussionId === this.discussion.notes[0].id
+ );
+ },
},
methods: {
addDiscussionComment(
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
index 43165261f6b..eab19e38a45 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
@@ -60,9 +60,11 @@ export default {
},
},
mounted() {
- if (this.isNoteLinked) {
- this.$el.scrollIntoView({ behavior: 'smooth', inline: 'start' });
- }
+ this.$nextTick(() => {
+ if (this.isNoteLinked) {
+ this.$el.scrollIntoView({ behavior: 'smooth', inline: 'start' });
+ }
+ });
},
methods: {
hideForm() {
diff --git a/app/assets/javascripts/design_management/constants.js b/app/assets/javascripts/design_management/constants.js
index 21ff361a277..63a92ef5ec0 100644
--- a/app/assets/javascripts/design_management/constants.js
+++ b/app/assets/javascripts/design_management/constants.js
@@ -11,6 +11,7 @@ export const VALID_DATA_TRANSFER_TYPE = 'Files';
export const ACTIVE_DISCUSSION_SOURCE_TYPES = {
pin: 'pin',
discussion: 'discussion',
+ url: 'url',
};
export const DESIGN_DETAIL_LAYOUT_CLASSLIST = ['design-detail-layout', 'overflow-hidden', 'm-0'];
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index 17b72e73127..93fb9f37b72 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -19,6 +19,8 @@ import {
extractDiscussions,
extractDesign,
updateImageDiffNoteOptimisticResponse,
+ toDiffNoteGid,
+ extractDesignNoteId,
} from '../../utils/design_management_utils';
import {
updateStoreAfterAddImageDiffNote,
@@ -145,8 +147,11 @@ export default {
mounted() {
Mousetrap.bind('esc', this.closeDesign);
this.trackEvent();
- // We need to reset the active discussion when opening a new design
- this.updateActiveDiscussion();
+
+ // Set active discussion immediately.
+ // This will ensure that, if a note is specified in the URL hash,
+ // the browser will scroll to, and highlight, the note in the UI
+ this.updateActiveDiscussionFromUrl();
},
beforeDestroy() {
Mousetrap.unbind('esc', this.closeDesign);
@@ -266,15 +271,20 @@ export default {
this.isLatestVersion,
);
},
- updateActiveDiscussion(id) {
+ updateActiveDiscussion(id, source = ACTIVE_DISCUSSION_SOURCE_TYPES.discussion) {
this.$apollo.mutate({
mutation: updateActiveDiscussionMutation,
variables: {
id,
- source: ACTIVE_DISCUSSION_SOURCE_TYPES.discussion,
+ source,
},
});
},
+ updateActiveDiscussionFromUrl() {
+ const noteId = extractDesignNoteId(this.$route.hash);
+ const diffNoteGid = noteId ? toDiffNoteGid(noteId) : undefined;
+ return this.updateActiveDiscussion(diffNoteGid, ACTIVE_DISCUSSION_SOURCE_TYPES.url);
+ },
toggleResolvedComments() {
this.resolvedDiscussionsExpanded = !this.resolvedDiscussionsExpanded;
},
diff --git a/app/assets/javascripts/design_management/utils/design_management_utils.js b/app/assets/javascripts/design_management/utils/design_management_utils.js
index da8f89ff960..a5514597fcf 100644
--- a/app/assets/javascripts/design_management/utils/design_management_utils.js
+++ b/app/assets/javascripts/design_management/utils/design_management_utils.js
@@ -34,6 +34,17 @@ export const extractDesigns = data => data.project.issue.designCollection.design
export const extractDesign = data => (extractDesigns(data) || [])[0];
+export const toDiffNoteGid = noteId => `gid://gitlab/DiffNote/${noteId}`;
+
+/**
+ * Return the note ID from a URL hash parameter
+ * @param {String} urlHash URL hash, including `#` prefix
+ */
+export const extractDesignNoteId = urlHash => {
+ const [, noteId] = urlHash.match('#note_([0-9]+$)') || [];
+ return noteId || null;
+};
+
/**
* Generates optimistic response for a design upload mutation
* @param {Array<File>} files
diff --git a/changelogs/unreleased/232839-fj-track-unique-edit-web-ide-action.yml b/changelogs/unreleased/232839-fj-track-unique-edit-web-ide-action.yml
new file mode 100644
index 00000000000..8ebdb49ffc4
--- /dev/null
+++ b/changelogs/unreleased/232839-fj-track-unique-edit-web-ide-action.yml
@@ -0,0 +1,5 @@
+---
+title: Track unique web ide edit action for usage ping
+merge_request: 40246
+author:
+type: changed
diff --git a/changelogs/unreleased/fix-design-managemnt-comment-highlight.yml b/changelogs/unreleased/fix-design-managemnt-comment-highlight.yml
new file mode 100644
index 00000000000..d808e52e9b7
--- /dev/null
+++ b/changelogs/unreleased/fix-design-managemnt-comment-highlight.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure design comment is highlighted when comment is in URL
+merge_request: 40477
+author:
+type: fixed
diff --git a/changelogs/unreleased/id-bump-doorkeeper-5-1.yml b/changelogs/unreleased/id-bump-doorkeeper-5-1.yml
new file mode 100644
index 00000000000..d8a5d2749b2
--- /dev/null
+++ b/changelogs/unreleased/id-bump-doorkeeper-5-1.yml
@@ -0,0 +1,5 @@
+---
+title: Bump doorkeeper to 5.1.1
+merge_request: 40546
+author:
+type: changed
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 23cc1352d11..44c210efa62 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -405,7 +405,7 @@ To configure the Gitaly server:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
- gitaly['auth_token'] = 'gitlaysecret'
+ gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
@@ -586,7 +586,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
- gitlab_rails['gitaly_token'] = 'gitalyecret'
+ gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index 2a9590d1908..e1e6250632e 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -1131,7 +1131,7 @@ On each node:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
- gitaly['auth_token'] = 'gitlaysecret'
+ gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
@@ -1474,7 +1474,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
- gitlab_rails['gitaly_token'] = 'gitalyecret'
+ gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index f485b7d35e8..5a52b6d9d0f 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -1130,7 +1130,7 @@ On each node:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the GitLab Rails application setup
- gitaly['auth_token'] = 'gitlaysecret'
+ gitaly['auth_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
# Avoid running unnecessary services on the Gitaly server
@@ -1473,7 +1473,7 @@ On each node perform the following:
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
# The following two values must be the same as their respective values
# of the Gitaly setup
- gitlab_rails['gitaly_token'] = 'gitalyecret'
+ gitlab_rails['gitaly_token'] = 'gitalysecret'
gitlab_shell['secret_token'] = 'shellsecret'
git_data_dirs({
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 050df243af4..25421b067dc 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -11,9 +11,9 @@ GitLab offers a [continuous integration](https://about.gitlab.com/stages-devops-
[pipeline](../pipelines/index.md), you must:
- Add a [`.gitlab-ci.yml` file](#creating-a-gitlab-ciyml-file) to your repository's root directory.
-- Ensure your project is configured to use a [Runner](#configuring-a-runner).
+- Ensure your project is configured to use a [runner](#configuring-a-runner).
-The `.gitlab-ci.yml` file tells the GitLab Runner what to do. A simple pipeline commonly has
+The `.gitlab-ci.yml` file tells the runner what to do. A simple pipeline commonly has
three [stages](../yaml/README.md#stages):
- `build`
@@ -57,7 +57,7 @@ The `.gitlab-ci.yml` file is where you configure what CI does with your project.
It lives in the root of your repository.
On any push to your repository, GitLab will look for the `.gitlab-ci.yml`
-file and start jobs on _Runners_ according to the contents of the file,
+file and start jobs on _runners_ according to the contents of the file,
for that commit.
Because `.gitlab-ci.yml` is in the repository and is version controlled, old
@@ -109,7 +109,7 @@ The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when
they should be run. The jobs are defined as top-level elements with a name (in
our case `rspec` and `rubocop`) and always have to contain the `script` keyword.
Jobs are used to create jobs, which are then picked by
-[Runners](../runners/README.md) and executed within the environment of the Runner.
+[runners](../runners/README.md) and executed within the environment of the runner.
What is important is that each job is run independently from each other.
@@ -148,59 +148,54 @@ Clicking on it you will be directed to the jobs page for that specific commit.
![Single commit jobs page](img/single_commit_status_pending.png)
Notice that there is a pending job which is named after what we wrote in
-`.gitlab-ci.yml`. "stuck" indicates that there is no Runner configured
+`.gitlab-ci.yml`. "stuck" indicates that there is no runner configured
yet for this job.
-The next step is to configure a Runner so that it picks the pending jobs.
+The next step is to configure a runner so that it picks the pending jobs.
-## Configuring a Runner
+## Configuring a runner
-In GitLab, Runners run the jobs that you define in `.gitlab-ci.yml`. A Runner
-can be a virtual machine, a VPS, a bare-metal machine, a Docker container or
-even a cluster of containers. GitLab and the Runners communicate through an API,
-so the only requirement is that the Runner's machine has network access to the
+In GitLab, runners run the jobs that you define in `.gitlab-ci.yml`. A runner
+can be a virtual machine, a VPS, a bare-metal machine, a Docker container, or
+even a cluster of containers. GitLab and the runner communicate through an API,
+so the only requirement is that the runner's machine has network access to the
GitLab server.
-A Runner can be specific to a certain project or serve multiple projects in
-GitLab. If it serves all projects it's called a _Shared Runner_.
+A runner can be specific to a certain project or serve multiple projects in
+GitLab. If it serves all projects, it's called a _shared runner_.
-Find more information about different Runners in the
-[Runners](../runners/README.md) documentation.
+Find more information about runners in the
+[runner](../runners/README.md) documentation.
-You can find whether any Runners are assigned to your project by going to
-**Settings ➔ CI/CD**. Setting up a Runner is easy and straightforward. The
-official Runner supported by GitLab is written in Go and its documentation
-can be found at <https://docs.gitlab.com/runner/>.
+The official runner supported by GitLab is written in Go.
+View [the documentation](https://docs.gitlab.com/runner/).
-In order to have a functional Runner you need to follow two steps:
+For a runner to be available in GitLab, you must:
-1. [Install it](https://docs.gitlab.com/runner/install/)
-1. [Configure it](https://docs.gitlab.com/runner/configuration/)
+1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
+1. [Register a runner for your group or project](https://docs.gitlab.com/runner/register/).
-Follow the links above to set up your own Runner or use a Shared Runner as
-described in the next section.
-
-Once the Runner has been set up, you should see it on the Runners page of your
-project, following **Settings ➔ CI/CD**.
+When a runner is available, you can view it by
+clicking **Settings > CI/CD** and expanding **Runners**.
![Activated runners](img/runners_activated.png)
-### Shared Runners
+### Shared runners
-If you use [GitLab.com](https://gitlab.com/) you can use the **Shared Runners**
-provided by GitLab Inc.
+If you use [GitLab.com](https://gitlab.com/), you can use the **shared runners**
+provided by GitLab.
These are special virtual machines that run on GitLab's infrastructure and can
build any project.
-To enable the **Shared Runners** you have to go to your project's
-**Settings ➔ CI/CD** and click **Enable shared runners**.
+To enable shared runners, go to your project's or group's
+**Settings > CI/CD** and click **Enable shared runners**.
-[Read more on Shared Runners](../runners/README.md).
+[Read more about shared runners](../runners/README.md#shared-runners).
-## Seeing the status of your pipeline and jobs
+## Viewing the status of your pipeline and jobs
-After configuring the Runner successfully, you should see the status of your
+After configuring the runner successfully, you should see the status of your
last commit change from _pending_ to either _running_, _success_ or _failed_.
You can view all pipelines by going to the **Pipelines** page in your project.
diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md
index 7d0cd1ec08a..efcc7b363ff 100644
--- a/doc/development/cicd/templates.md
+++ b/doc/development/cicd/templates.md
@@ -64,6 +64,65 @@ users have to fix their `.gitlab-ci.yml` that could annoy their workflow.
Please read [versioning](#versioning) section for introducing breaking change safely.
+## Versioning
+
+Versioning allows you to introduce a new template without modifying the existing
+one. This process is useful when we need to introduce a breaking change,
+but don't want to affect the existing projects that depends on the current template.
+
+### Stable version
+
+A stable CI/CD template is a template that only introduces breaking changes in major
+release milestones. Name the stable version of a template as `<template-name>.gitlab-ci.yml`,
+for example `Jobs/Deploy.gitlab-ci.yml`.
+
+You can make a new stable template by copying [the latest template](#latest-version)
+available in a major milestone release of GitLab like `13.0`. All breaking changes
+must be announced in a blog post before the official release, for example
+[GitLab.com is moving to 13.0, with narrow breaking changes](https://about.gitlab.com/releases/2020/05/06/gitlab-com-13-0-breaking-changes/)
+
+You can change a stable template version in a minor GitLab release like `13.1` if:
+
+- The change is not a [breaking change](#backward-compatibility).
+- The change is ported to [the latest template](#latest-version), if one exists.
+
+### Latest version
+
+Templates marked as `latest` can be updated in any release, even with
+[breaking changes](#backward-compatibility). Add `.latest` to the template name if
+it's considered the latest version, for example `Jobs/Deploy.latest.gitlab-ci.yml`.
+
+When you introduce [a breaking change](#backward-compatibility),
+you **must** test and document [the upgrade path](#verify-breaking-changes).
+In general, we should not promote the latest template as the best option, as it could surprise users with unexpected problems.
+
+If the `latest` template does not exist yet, you can copy [the stable template](#stable-version).
+
+### How to include an older stable template
+
+Users may want to use an older [stable template](#stable-version) that is not bundled
+in the current GitLab package. For example, the stable templates in GitLab v13.0 and
+GitLab v14.0 could be so different that a user will want to continue using the v13.0 template even
+after upgrading to GitLab 14.0.
+
+You can add a note in the template or in documentation explaining how to use `include:remote`
+to include older template versions:
+
+```yaml
+# To use the v13 stable template, which is not included in v14, fetch the specifc
+# template from the remote template repository with the `include:remote:` keyword.
+# If you fetch from the GitLab canonical project, use the following URL format:
+# https://gitlab.com/gitlab-org/gitlab/-/raw/<version>/lib/gitlab/ci/templates/<template-name>
+include:
+ remote: https://gitlab.com/gitlab-org/gitlab/-/raw/v13.0.1-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+```
+
+### Further reading
+
+There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about
+introducing versioning concepts in GitLab CI Templates. You can check that issue to
+follow the progress.
+
## Testing
Each CI/CD template must be tested in order to make sure that it's safe to be published.
@@ -95,18 +154,20 @@ You should write an RSpec test to make sure that pipeline jobs will be generated
1. Add a test file at `spec/lib/gitlab/ci/templates/<template-category>/<template-name>_spec.rb`
1. Test that pipeline jobs are properly created via `Ci::CreatePipelineService`.
+### Verify breaking changes
+
+When you introduce a breaking change to [a `latest` template](#latest-version),
+you must:
+
+1. Test the upgrade path from [the stable template](#stable-version).
+1. Verify what kind of errors users will encounter.
+1. Document it as a troubleshooting guide.
+
+This information will be important for users when [a stable template](#stable-version)
+is updated in a major version GitLab release.
+
## Security
A template could contain malicious code. For example, a template that contains the `export` shell command in a job
might accidentally expose project secret variables in a job log.
If you're unsure if it's secure or not, you need to ask security experts for cross-validation.
-
-## Versioning
-
-Versioning allows you to introduce a new template without modifying the existing
-one. This is useful process especially when we need to introduce a breaking change,
-but don't want to affect the existing projects that depends on the current template.
-
-There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) for
-introducing versioning concept in GitLab Ci Template. Please follow the issue for
-checking the progress.
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index f98a8eb9c6d..7329725a643 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -20,7 +20,7 @@ NOTE: **Note:**
This option is available only if your GitLab administrator has
[enabled support for the Maven repository](../../../administration/packages/index.md).
-After the Packages feature is enabled, the Maven Repository will be available for
+After the Packages feature is enabled, the Maven Repository is available for
all new projects by default. To enable it for existing projects, or if you want
to disable it:
@@ -34,7 +34,7 @@ repository.
## Getting Started with Maven
-This section will cover installing Maven and building a package. This is a
+This section covers installing Maven and building a package. This is a
quickstart to help if you're new to building Maven packages. If you're already
using Maven and understand how to build your own packages, move onto the
[next section](#adding-the-gitlab-package-registry-as-a-maven-remote).
@@ -107,7 +107,7 @@ You should see a new directory where you ran this command matching your
## Getting started with Gradle
-This section will cover installing Gradle and initializing a Java project. This is a
+This section covers installing Gradle and initializing a Java project. This is a
quickstart to help if you're new to Gradle. If you're already
using Gradle and understand how to build your own packages, move onto the
[next section](#adding-the-gitlab-package-registry-as-a-maven-remote).
@@ -128,7 +128,7 @@ If you want to use an existing Gradle project, installation is not necessary.
Simply execute `gradlew` (on Linux) or `gradlew.bat` (on Windows) in the project
directory instead.
-You should see something imilar to the below printed in the output:
+You should see something similar to the below printed in the output:
```plaintext
------------------------------------------------------------
@@ -191,7 +191,7 @@ Select build script DSL:
Enter selection (default: Groovy) [1..2]
```
-Choose `1` to create a new Java Library project which will be described in Groovy DSL. The output should be:
+Choose `1` to create a new Java Library project which is described in Groovy DSL. The output should be:
```plaintext
Select test framework:
@@ -213,7 +213,7 @@ Enter a project name or hit enter to use the directory name as project name.
The next step is to add the GitLab Package Registry as a Maven remote. If a
project is private or you want to upload Maven artifacts to GitLab,
-credentials will need to be provided for authorization too. Support is available
+credentials must be provided for authorization too. Support is available
for [personal access tokens](#authenticating-with-a-personal-access-token),
[CI job tokens](#authenticating-with-a-ci-job-token), and
[deploy tokens](../../project/deploy_tokens/index.md) only. Regular username/password
@@ -388,7 +388,7 @@ repositories {
To download and upload packages from GitLab, you need a `repository` and
`distributionManagement` section in your `pom.xml` file. If you're following the
-steps from above, then you'll need to add the following information to your
+steps from above, then you must add the following information to your
`my-project/pom.xml` file.
Depending on your workflow and the amount of Maven packages you have, there are
@@ -462,13 +462,13 @@ project's ID can be used for uploading.
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the group level endpoint for
all your Maven packages stored within one GitLab group. Only packages you have access to
-will be available for download.
+are available for download.
The group level endpoint works with any package names, which means the you
have the flexibility of naming compared to [instance level endpoint](#instance-level-maven-endpoint).
-However, GitLab will not guarantee the uniqueness of the package names within
+However, GitLab does not guarantee the uniqueness of the package names within
the group. You can have two projects with the same package name and package
-version. As a result, GitLab will serve whichever one is more recent.
+version. As a result, GitLab serves whichever one is more recent.
The example below shows how the relevant `repository` section of your `pom.xml`
would look like. You still need a project specific URL for uploading a package in
@@ -524,7 +524,7 @@ For retrieving artifacts, you can use either the
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the instance level endpoint for
-all maven packages stored in GitLab and the packages you have access to will be available
+all maven packages stored in GitLab and the packages you have access to are available
for download.
Note that **only packages that have the same path as the project** are exposed via
@@ -662,7 +662,7 @@ artifacts or even delete them.
Installing a package from the GitLab Package Registry requires that you set up
the [remote and authentication](#adding-the-gitlab-package-registry-as-a-maven-remote)
-as above. Once this is completed, there are two ways for installaing a package.
+as above. Once this is completed, there are two ways to install a package.
### Install using Maven with `mvn install`
@@ -732,7 +732,7 @@ you can configure GitLab CI/CD to build new packages automatically.
The example below shows how to create a new package each time the `master` branch
is updated:
-1. Create a `ci_settings.xml` file that will serve as Maven's `settings.xml` file.
+1. Create a `ci_settings.xml` file that serves as Maven's `settings.xml` file.
Add the server section with the same ID you defined in your `pom.xml` file.
For example, in our case it's `gitlab-maven`:
@@ -792,9 +792,9 @@ is updated:
1. Push those files to your repository.
-The next time the `deploy` job runs, it will copy `ci_settings.xml` to the
+The next time the `deploy` job runs, it copies `ci_settings.xml` to the
user's home location (in this case the user is `root` since it runs in a
-Docker container), and Maven will utilize the configured CI
+Docker container), and Maven uses the configured CI
[environment variables](../../../ci/variables/README.md#predefined-environment-variables).
### Creating Maven packages with GitLab CI/CD using Gradle
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 3c7ed2a25a0..20877fb5c5f 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -136,7 +136,10 @@ module API
if result[:status] == :success
commit_detail = user_project.repository.commit(result[:result])
- Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count if find_user_from_warden
+ if find_user_from_warden
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_commits_count
+ Gitlab::UsageDataCounters::EditorUniqueCounter.track_web_ide_edit_action(author: current_user)
+ end
present commit_detail, with: Entities::CommitDetail, stats: params[:stats]
else
diff --git a/spec/controllers/oauth/token_info_controller_spec.rb b/spec/controllers/oauth/token_info_controller_spec.rb
index 91a986db251..6d01a534673 100644
--- a/spec/controllers/oauth/token_info_controller_spec.rb
+++ b/spec/controllers/oauth/token_info_controller_spec.rb
@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe Oauth::TokenInfoController do
describe '#show' do
context 'when the user is not authenticated' do
- it 'responds with a 401' do
+ it 'responds with a 400' do
get :show
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
@@ -36,10 +36,10 @@ RSpec.describe Oauth::TokenInfoController do
end
context 'when the doorkeeper_token is not recognised' do
- it 'responds with a 401' do
+ it 'responds with a 400' do
get :show, params: { access_token: 'unknown_token' }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
@@ -49,10 +49,10 @@ RSpec.describe Oauth::TokenInfoController do
create(:oauth_access_token, created_at: 2.days.ago, expires_in: 10.minutes)
end
- it 'responds with a 401' do
+ it 'responds with a 400' do
get :show, params: { access_token: access_token.token }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
@@ -60,10 +60,10 @@ RSpec.describe Oauth::TokenInfoController do
context 'when the token is revoked' do
let(:access_token) { create(:oauth_access_token, revoked_at: 2.days.ago) }
- it 'responds with a 401' do
+ it 'responds with a 400' do
get :show, params: { access_token: access_token.token }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(Gitlab::Json.parse(response.body)).to include('error' => 'invalid_request')
end
end
diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js
index 8eeaa6cd5ab..a59a2223cf3 100644
--- a/spec/frontend/design_management/components/design_notes/design_note_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js
@@ -91,7 +91,9 @@ describe('Design note component', () => {
note,
});
- expect(scrollIntoViewMock).toHaveBeenCalled();
+ return wrapper.vm.$nextTick().then(() => {
+ expect(scrollIntoViewMock).toHaveBeenCalled();
+ });
});
it('should not render edit icon when user does not have a permission', () => {
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index 5fdc41bbfbf..3c5de543eb3 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -57,7 +57,7 @@ exports[`Design management design index page renders design index 1`] = `
<design-discussion-stub
data-testid="unresolved-discussion"
- designid="test"
+ designid="design-id"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
@@ -105,7 +105,7 @@ exports[`Design management design index page renders design index 1`] = `
>
<design-discussion-stub
data-testid="resolved-discussion"
- designid="test"
+ designid="design-id"
discussion="[object Object]"
discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index 369c8667f4d..d189bdb4345 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -7,18 +7,19 @@ import DesignIndex from '~/design_management/pages/design/index.vue';
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
import DesignPresentation from '~/design_management/components/design_presentation.vue';
import createImageDiffNoteMutation from '~/design_management/graphql/mutations/create_image_diff_note.mutation.graphql';
-import design from '../../mock_data/design';
-import mockResponseWithDesigns from '../../mock_data/designs';
-import mockResponseNoDesigns from '../../mock_data/no_designs';
-import mockAllVersions from '../../mock_data/all_versions';
+import updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
import {
DESIGN_NOT_FOUND_ERROR,
DESIGN_VERSION_NOT_EXIST_ERROR,
} from '~/design_management/utils/error_messages';
-import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
+import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants';
import createRouter from '~/design_management/router';
import * as utils from '~/design_management/utils/design_management_utils';
import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants';
+import design from '../../mock_data/design';
+import mockResponseWithDesigns from '../../mock_data/designs';
+import mockResponseNoDesigns from '../../mock_data/no_designs';
+import mockAllVersions from '../../mock_data/all_versions';
jest.mock('~/flash');
jest.mock('mousetrap', () => ({
@@ -34,6 +35,12 @@ const DesignReplyForm = {
focusInput,
},
};
+const mockDesignNoDiscussions = {
+ ...design,
+ discussions: {
+ nodes: [],
+ },
+};
const localVue = createLocalVue();
localVue.use(VueRouter);
@@ -75,7 +82,7 @@ describe('Design management design index page', () => {
const findSidebar = () => wrapper.find(DesignSidebar);
const findDesignPresentation = () => wrapper.find(DesignPresentation);
- function createComponent(loading = false, data = {}) {
+ function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) {
const $apollo = {
queries: {
design: {
@@ -87,6 +94,8 @@ describe('Design management design index page', () => {
router = createRouter();
+ router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions });
+
wrapper = shallowMount(DesignIndex, {
propsData: { id: '1' },
mocks: { $apollo },
@@ -126,29 +135,28 @@ describe('Design management design index page', () => {
},
};
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl);
- createComponent(true);
+ createComponent({ loading: true });
- wrapper.vm.$router.push('/designs/test');
expect(mockEl.classList.add).toHaveBeenCalledTimes(1);
expect(mockEl.classList.add).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
});
});
it('sets loading state', () => {
- createComponent(true);
+ createComponent({ loading: true });
expect(wrapper.element).toMatchSnapshot();
});
it('renders design index', () => {
- createComponent(false, { design });
+ createComponent({ loading: false }, { data: { design } });
expect(wrapper.element).toMatchSnapshot();
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
it('passes correct props to sidebar component', () => {
- createComponent(false, { design });
+ createComponent({ loading: false }, { data: { design } });
expect(findSidebar().props()).toEqual({
design,
@@ -158,14 +166,14 @@ describe('Design management design index page', () => {
});
it('opens a new discussion form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
},
},
- });
+ );
findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 });
@@ -175,15 +183,15 @@ describe('Design management design index page', () => {
});
it('keeps new discussion form focused', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
},
},
- annotationCoordinates,
- });
+ );
findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 });
@@ -191,16 +199,16 @@ describe('Design management design index page', () => {
});
it('sends a mutation on submitting form and closes form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
+ comment: newComment,
},
},
- annotationCoordinates,
- comment: newComment,
- });
+ );
findDiscussionForm().vm.$emit('submitForm');
expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables);
@@ -216,16 +224,16 @@ describe('Design management design index page', () => {
});
it('closes the form and clears the comment on canceling form', () => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ annotationCoordinates,
+ comment: newComment,
},
},
- annotationCoordinates,
- comment: newComment,
- });
+ );
findDiscussionForm().vm.$emit('cancelForm');
@@ -238,15 +246,15 @@ describe('Design management design index page', () => {
describe('with error', () => {
beforeEach(() => {
- createComponent(false, {
- design: {
- ...design,
- discussions: {
- nodes: [],
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design: mockDesignNoDiscussions,
+ errorMessage: 'woops',
},
},
- errorMessage: 'woops',
- });
+ );
});
it('GlAlert is rendered in correct position with correct content', () => {
@@ -257,7 +265,7 @@ describe('Design management design index page', () => {
describe('onDesignQueryResult', () => {
describe('with no designs', () => {
it('redirects to /designs', () => {
- createComponent(true);
+ createComponent({ loading: true });
router.push = jest.fn();
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
@@ -272,7 +280,7 @@ describe('Design management design index page', () => {
describe('when no design exists for given version', () => {
it('redirects to /designs', () => {
- createComponent(true);
+ createComponent({ loading: true });
wrapper.setData({
allVersions: mockAllVersions,
});
@@ -291,4 +299,24 @@ describe('Design management design index page', () => {
});
});
});
+
+ describe('when hash present in current route', () => {
+ it('calls updateActiveDiscussion mutation', () => {
+ createComponent(
+ { loading: false },
+ {
+ data: {
+ design,
+ },
+ intialRouteOptions: { hash: '#note_123' },
+ },
+ );
+
+ expect(mutate).toHaveBeenCalledTimes(1);
+ expect(mutate).toHaveBeenCalledWith({
+ mutation: updateActiveDiscussion,
+ variables: { id: 'gid://gitlab/DiffNote/123', source: 'url' },
+ });
+ });
+ });
});
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 4728648b49c..2da02732b1e 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -579,7 +579,9 @@ describe('Design management index page', () => {
});
createComponent(true);
- expect(scrollIntoViewMock).toHaveBeenCalled();
+ return wrapper.vm.$nextTick().then(() => {
+ expect(scrollIntoViewMock).toHaveBeenCalled();
+ });
});
});
});
diff --git a/spec/frontend/design_management/utils/design_management_utils_spec.js b/spec/frontend/design_management/utils/design_management_utils_spec.js
index e6d836b9157..7e857d08d25 100644
--- a/spec/frontend/design_management/utils/design_management_utils_spec.js
+++ b/spec/frontend/design_management/utils/design_management_utils_spec.js
@@ -6,6 +6,7 @@ import {
updateImageDiffNoteOptimisticResponse,
isValidDesignFile,
extractDesign,
+ extractDesignNoteId,
} from '~/design_management/utils/design_management_utils';
import mockResponseNoDesigns from '../mock_data/no_designs';
import mockResponseWithDesigns from '../mock_data/designs';
@@ -171,3 +172,19 @@ describe('extractDesign', () => {
});
});
});
+
+describe('extractDesignNoteId', () => {
+ it.each`
+ hash | expectedNoteId
+ ${'#note_0'} | ${'0'}
+ ${'#note_1'} | ${'1'}
+ ${'#note_23'} | ${'23'}
+ ${'#note_456'} | ${'456'}
+ ${'note_1'} | ${null}
+ ${'#note_'} | ${null}
+ ${'#note_asd'} | ${null}
+ ${'#note_1asd'} | ${null}
+ `('returns $expectedNoteId when hash is $hash', ({ hash, expectedNoteId }) => {
+ expect(extractDesignNoteId(hash)).toBe(expectedNoteId);
+ });
+});
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 21ff0a94db9..d34244771ad 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -367,10 +367,31 @@ RSpec.describe API::Commits do
end
end
- it 'does not increment the usage counters using access token authentication' do
- expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
+ context 'when using access token authentication' do
+ it 'does not increment the usage counters' do
+ expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
+ expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_web_ide_edit_action)
- post api(url, user), params: valid_c_params
+ post api(url, user), params: valid_c_params
+ end
+ end
+
+ context 'when using warden' do
+ it 'increments usage counters', :clean_gitlab_redis_shared_state do
+ session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
+ session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
+ end
+
+ cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+
+ expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
+ expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
+
+ post api(url), params: valid_c_params
+ end
end
context 'a new file in project repo' do
diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb
index f5971054b3c..23d5df873d4 100644
--- a/spec/requests/api/oauth_tokens_spec.rb
+++ b/spec/requests/api/oauth_tokens_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'OAuth tokens' do
request_oauth_token(user, client_basic_auth_header(client))
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('invalid_grant')
end
end
@@ -62,7 +62,7 @@ RSpec.describe 'OAuth tokens' do
request_oauth_token(user, basic_auth_header(client.uid, 'invalid secret'))
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('invalid_client')
end
end
@@ -72,7 +72,7 @@ RSpec.describe 'OAuth tokens' do
shared_examples 'does not create an access token' do
let(:user) { create(:user) }
- it { expect(response).to have_gitlab_http_status(:unauthorized) }
+ it { expect(response).to have_gitlab_http_status(:bad_request) }
end
context 'when user is blocked' do