Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9248363e3e
commit
b28aa8bd7d
32 changed files with 225 additions and 125 deletions
|
@ -11,7 +11,17 @@ function broadcastCount(newCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUserMergeRequestCounts(newCount) {
|
function updateUserMergeRequestCounts(newCount) {
|
||||||
const mergeRequestsCountEl = document.querySelector('.merge-requests-count');
|
const mergeRequestsCountEl = document.querySelector('.js-assigned-mr-count');
|
||||||
|
mergeRequestsCountEl.textContent = newCount.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateReviewerMergeRequestCounts(newCount) {
|
||||||
|
const mergeRequestsCountEl = document.querySelector('.js-reviewer-mr-count');
|
||||||
|
mergeRequestsCountEl.textContent = newCount.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMergeRequestCounts(newCount) {
|
||||||
|
const mergeRequestsCountEl = document.querySelector('.js-merge-requests-count');
|
||||||
mergeRequestsCountEl.textContent = newCount.toLocaleString();
|
mergeRequestsCountEl.textContent = newCount.toLocaleString();
|
||||||
mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0);
|
mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0);
|
||||||
}
|
}
|
||||||
|
@ -22,10 +32,14 @@ function updateUserMergeRequestCounts(newCount) {
|
||||||
export function refreshUserMergeRequestCounts() {
|
export function refreshUserMergeRequestCounts() {
|
||||||
return Api.userCounts()
|
return Api.userCounts()
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const count = data.merge_requests;
|
const assignedMergeRequests = data.assigned_merge_requests;
|
||||||
|
const reviewerMergeRequests = data.review_requested_merge_requests;
|
||||||
|
const fullCount = assignedMergeRequests + reviewerMergeRequests;
|
||||||
|
|
||||||
updateUserMergeRequestCounts(count);
|
updateUserMergeRequestCounts(assignedMergeRequests);
|
||||||
broadcastCount(count);
|
updateReviewerMergeRequestCounts(reviewerMergeRequests);
|
||||||
|
updateMergeRequestCounts(fullCount);
|
||||||
|
broadcastCount(fullCount);
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
console.error(ex); // eslint-disable-line no-console
|
console.error(ex); // eslint-disable-line no-console
|
||||||
|
@ -60,7 +74,7 @@ export function openUserCountsBroadcast() {
|
||||||
if (currentUserId) {
|
if (currentUserId) {
|
||||||
channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`);
|
channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`);
|
||||||
channel.onmessage = (ev) => {
|
channel.onmessage = (ev) => {
|
||||||
updateUserMergeRequestCounts(ev.data);
|
updateMergeRequestCounts(ev.data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// NOTE! For the first iteration, we are simply copying the implementation of Assignees
|
// NOTE! For the first iteration, we are simply copying the implementation of Assignees
|
||||||
// It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736
|
// It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736
|
||||||
import { deprecatedCreateFlash as Flash } from '~/flash';
|
import { deprecatedCreateFlash as Flash } from '~/flash';
|
||||||
|
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
|
||||||
import eventHub from '~/sidebar/event_hub';
|
import eventHub from '~/sidebar/event_hub';
|
||||||
import Store from '~/sidebar/stores/sidebar_store';
|
import Store from '~/sidebar/stores/sidebar_store';
|
||||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
|
@ -80,8 +81,7 @@ export default {
|
||||||
.saveReviewers(this.field)
|
.saveReviewers(this.field)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
// Uncomment once this issue has been addressed > https://gitlab.com/gitlab-org/gitlab/-/issues/237922
|
refreshUserMergeRequestCounts();
|
||||||
// refreshUserMergeRequestCounts();
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
|
@ -796,6 +796,14 @@
|
||||||
.navbar-gitlab {
|
.navbar-gitlab {
|
||||||
li.dropdown {
|
li.dropdown {
|
||||||
position: static;
|
position: static;
|
||||||
|
|
||||||
|
&.user-counter {
|
||||||
|
margin-left: 8px !important;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
padding: 0 4px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
.badge.badge-pill {
|
.badge.badge-pill:not(.merge-request-badge) {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
font-weight: $gl-font-weight-bold;
|
font-weight: $gl-font-weight-bold;
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@
|
||||||
|
|
||||||
.title-container,
|
.title-container,
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
.badge.badge-pill {
|
.badge.badge-pill:not(.merge-request-badge) {
|
||||||
position: inherit;
|
position: inherit;
|
||||||
font-weight: $gl-font-weight-normal;
|
font-weight: $gl-font-weight-normal;
|
||||||
margin-left: -6px;
|
margin-left: -6px;
|
||||||
|
|
|
@ -11,6 +11,10 @@ module DashboardHelper
|
||||||
merge_requests_dashboard_path(assignee_username: current_user.username)
|
merge_requests_dashboard_path(assignee_username: current_user.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reviewer_mrs_dashboard_path
|
||||||
|
merge_requests_dashboard_path(reviewer_username: current_user.username)
|
||||||
|
end
|
||||||
|
|
||||||
def dashboard_nav_links
|
def dashboard_nav_links
|
||||||
@dashboard_nav_links ||= get_dashboard_nav_links
|
@dashboard_nav_links ||= get_dashboard_nav_links
|
||||||
end
|
end
|
||||||
|
|
|
@ -159,6 +159,32 @@ module MergeRequestsHelper
|
||||||
|
|
||||||
issuable_path(issuable, { merge_request: { wip_event: wip_event } })
|
issuable_path(issuable, { merge_request: { wip_event: wip_event } })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_merge_requests_counts
|
||||||
|
@user_merge_requests_counts ||= begin
|
||||||
|
assigned_count = assigned_issuables_count(:merge_requests)
|
||||||
|
review_requested_count = review_requested_merge_requests_count
|
||||||
|
total_count = assigned_count + review_requested_count
|
||||||
|
|
||||||
|
{
|
||||||
|
assigned: assigned_count,
|
||||||
|
review_requested: review_requested_count,
|
||||||
|
total: total_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_request_reviewers_enabled?
|
||||||
|
Feature.enabled?(:merge_request_reviewers, default_enabled: :yaml)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def review_requested_merge_requests_count
|
||||||
|
return 0 unless merge_request_reviewers_enabled?
|
||||||
|
|
||||||
|
current_user.review_requested_open_merge_requests_count
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper')
|
MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper')
|
||||||
|
|
|
@ -1564,6 +1564,12 @@ class User < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def review_requested_open_merge_requests_count(force: false)
|
||||||
|
Rails.cache.fetch(['users', id, 'review_requested_open_merge_requests_count'], force: force, expires_in: 20.minutes) do
|
||||||
|
MergeRequestsFinder.new(self, reviewer_id: id, state: 'opened', non_archived: true).execute.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def assigned_open_issues_count(force: false)
|
def assigned_open_issues_count(force: false)
|
||||||
Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do
|
Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: 20.minutes) do
|
||||||
IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
|
IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
|
||||||
|
@ -1607,6 +1613,7 @@ class User < ApplicationRecord
|
||||||
|
|
||||||
def invalidate_merge_request_cache_counts
|
def invalidate_merge_request_cache_counts
|
||||||
Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count'])
|
Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count'])
|
||||||
|
Rails.cache.delete(['users', id, 'review_requested_open_merge_requests_count'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalidate_todos_done_count
|
def invalidate_todos_done_count
|
||||||
|
|
|
@ -135,10 +135,6 @@ class ProjectPolicy < BasePolicy
|
||||||
::Feature.enabled?(:build_service_proxy, @subject)
|
::Feature.enabled?(:build_service_proxy, @subject)
|
||||||
end
|
end
|
||||||
|
|
||||||
condition(:project_bot_is_member) do
|
|
||||||
user.project_bot? & team_member?
|
|
||||||
end
|
|
||||||
|
|
||||||
with_scope :subject
|
with_scope :subject
|
||||||
condition(:packages_disabled) { !@subject.packages_enabled }
|
condition(:packages_disabled) { !@subject.packages_enabled }
|
||||||
|
|
||||||
|
@ -619,8 +615,6 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :admin_resource_access_tokens
|
enable :admin_resource_access_tokens
|
||||||
end
|
end
|
||||||
|
|
||||||
rule { project_bot_is_member & ~blocked }.enable :bot_log_in
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def user_is_user?
|
def user_is_user?
|
||||||
|
|
|
@ -112,9 +112,11 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_reviewers_change(merge_request, old_reviewers)
|
def handle_reviewers_change(merge_request, old_reviewers)
|
||||||
|
affected_reviewers = (old_reviewers + merge_request.reviewers) - (old_reviewers & merge_request.reviewers)
|
||||||
create_reviewer_note(merge_request, old_reviewers)
|
create_reviewer_note(merge_request, old_reviewers)
|
||||||
notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers)
|
notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers)
|
||||||
todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers)
|
todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers)
|
||||||
|
invalidate_cache_counts(merge_request, users: affected_reviewers.compact)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
|
def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
|
||||||
|
|
|
@ -47,17 +47,36 @@
|
||||||
%span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) }
|
%span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) }
|
||||||
= number_with_delimiter(issues_count)
|
= number_with_delimiter(issues_count)
|
||||||
- if header_link?(:merge_requests)
|
- if header_link?(:merge_requests)
|
||||||
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
|
- reviewers_enabled = merge_request_reviewers_enabled?
|
||||||
= link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _('Merge requests') },
|
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter #{reviewers_enabled ? 'dropdown' : ''}" }) do
|
||||||
data: { qa_selector: 'merge_requests_shortcut_button', toggle: 'tooltip', placement: 'bottom',
|
= link_to assigned_mrs_dashboard_path, class: 'dashboard-shortcuts-merge_requests', title: _('Merge requests'), aria: { label: _('Merge requests') },
|
||||||
|
data: { qa_selector: 'merge_requests_shortcut_button',
|
||||||
|
toggle: reviewers_enabled ? "dropdown" : "tooltip",
|
||||||
|
placement: 'bottom',
|
||||||
track_label: 'main_navigation',
|
track_label: 'main_navigation',
|
||||||
track_event: 'click_merge_link',
|
track_event: 'click_merge_link',
|
||||||
track_property: 'navigation',
|
track_property: 'navigation',
|
||||||
container: 'body' } do
|
container: 'body' } do
|
||||||
= sprite_icon('git-merge')
|
= sprite_icon('git-merge')
|
||||||
- merge_requests_count = assigned_issuables_count(:merge_requests)
|
%span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) }
|
||||||
%span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count == 0) }
|
= number_with_delimiter(user_merge_requests_counts[:total])
|
||||||
= number_with_delimiter(merge_requests_count)
|
- if reviewers_enabled
|
||||||
|
= sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!')
|
||||||
|
- if reviewers_enabled
|
||||||
|
.dropdown-menu.dropdown-menu-right
|
||||||
|
%ul
|
||||||
|
%li.dropdown-header
|
||||||
|
= _('Merge requests')
|
||||||
|
%li
|
||||||
|
= link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do
|
||||||
|
= _('Assigned to you')
|
||||||
|
%span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" }
|
||||||
|
= user_merge_requests_counts[:assigned]
|
||||||
|
%li
|
||||||
|
= link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center' do
|
||||||
|
= _('Review requests for you')
|
||||||
|
%span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" }
|
||||||
|
= user_merge_requests_counts[:review_requested]
|
||||||
- if header_link?(:todos)
|
- if header_link?(:todos)
|
||||||
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
|
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
|
||||||
= link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos',
|
= link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos',
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix project access token regression
|
||||||
|
merge_request: 50800
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
title: Fix empty pipeline analytics charts when time_zone is non-UTC
|
|
||||||
merge_request: 50760
|
|
||||||
author:
|
|
||||||
type: fixed
|
|
|
@ -459,11 +459,11 @@ Cluster.
|
||||||
| `alert` | Hash | yes | Alerts detail. Currently same format as [3rd party alert](../operations/incident_management/alert_integrations.md#customize-the-alert-payload-outside-of-gitlab). |
|
| `alert` | Hash | yes | Alerts detail. Currently same format as [3rd party alert](../operations/incident_management/alert_integrations.md#customize-the-alert-payload-outside-of-gitlab). |
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
POST internal/kubernetes/modules/cilium/network_alert
|
POST internal/kubernetes/modules/cilium_alert
|
||||||
```
|
```
|
||||||
|
|
||||||
Example Request:
|
Example Request:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium/network_alert"
|
curl --request POST --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" --header "Content-Type: application/json" --data '"{\"alert\":{\"title\":\"minimal\",\"message\":\"network problem\",\"evalMatches\":[{\"value\":1,\"metric\":\"Count\",\"tags\":{}}]}}"' "http://localhost:3000/api/v4/internal/kubernetes/modules/cilium_alert"
|
||||||
```
|
```
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 27 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.8 KiB |
BIN
doc/user/admin_area/img/license_upload_v13_8.png
Normal file
BIN
doc/user/admin_area/img/license_upload_v13_8.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -42,18 +42,21 @@ Otherwise, you can:
|
||||||
|
|
||||||
1. Navigate to the **License** tab, and click **Upload New License**.
|
1. Navigate to the **License** tab, and click **Upload New License**.
|
||||||
|
|
||||||
![License Admin Area](img/license_admin_area.png)
|
- *If you've received a `.gitlab-license` file:*
|
||||||
|
1. Download the license file to your local machine.
|
||||||
|
1. Select **Upload `.gitlab-license` file**.
|
||||||
|
1. Select **Choose File** and select the license file.
|
||||||
|
In this example the license file is named `GitLab.gitlab-license`.
|
||||||
|
1. Check the **Subscription Agreement** checkbox.
|
||||||
|
1. Select **Upload License**.
|
||||||
|
|
||||||
- *If you've received a `.gitlab-license` file,* you should have already downloaded
|
![Upload license](img/license_upload_v13_8.png)
|
||||||
it in your local machine. You can then upload it directly by choosing the
|
|
||||||
license file and clicking the **Upload license** button. In the image below,
|
|
||||||
the selected license file is named `GitLab.gitlab-license`.
|
|
||||||
|
|
||||||
![Upload license](img/license_upload.png)
|
- *If you've received your license as plain text:*
|
||||||
|
1. Select **Enter license key**.
|
||||||
- *If you've received your license as plain text,* select the
|
1. Copy the license and paste it into the **License key** field.
|
||||||
**Enter license key** option, copy the license, paste it into the **License key**
|
1. Check the **Subscription Agreement** checkbox.
|
||||||
field, and click **Upload license**.
|
1. Select **Upload License**.
|
||||||
|
|
||||||
## Add your license at install time
|
## Add your license at install time
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,9 @@ To enforce confirmation of the email address used for new sign ups:
|
||||||
1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**.
|
1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**.
|
||||||
1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**.
|
1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**.
|
||||||
|
|
||||||
|
In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/273258), if an administrator disables this setting, the users in pending approval state are
|
||||||
|
automatically approved in a background job.
|
||||||
|
|
||||||
## User cap **(CORE ONLY)**
|
## User cap **(CORE ONLY)**
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6.
|
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6.
|
||||||
|
|
|
@ -102,7 +102,7 @@ To authenticate to the Package Registry, you must use one of the following:
|
||||||
- It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
|
- It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
|
||||||
Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers.
|
Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers.
|
||||||
- A [CI job token](#authenticate-with-a-ci-job-token).
|
- A [CI job token](#authenticate-with-a-ci-job-token).
|
||||||
- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case.
|
- Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case.
|
||||||
|
|
||||||
### Authenticate with a personal access token or deploy token
|
### Authenticate with a personal access token or deploy token
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ Then, you can run `npm publish` either locally or by using GitLab CI/CD.
|
||||||
|
|
||||||
## Package naming convention
|
## Package naming convention
|
||||||
|
|
||||||
Your NPM package name must be in the format of `@scope:package-name`.
|
Your NPM package name must be in the format of `@scope/package-name`.
|
||||||
|
|
||||||
- The `@scope` is the root namespace of the GitLab project. It must match exactly, including the case.
|
- The `@scope` is the root namespace of the GitLab project. It must match exactly, including the case.
|
||||||
- The `package-name` can be whatever you want.
|
- The `package-name` can be whatever you want.
|
||||||
|
@ -241,7 +241,7 @@ Prerequisites:
|
||||||
|
|
||||||
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
|
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
|
||||||
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
|
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
|
||||||
- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case.
|
- Your NPM package name must be in the format of [@scope/package-name](#package-naming-convention). It must match exactly, including the case.
|
||||||
|
|
||||||
To upload an NPM package to your project, run this command:
|
To upload an NPM package to your project, run this command:
|
||||||
|
|
||||||
|
@ -461,7 +461,7 @@ If you get this error, ensure that:
|
||||||
### `npm publish` returns `npm ERR! 400 Bad Request`
|
### `npm publish` returns `npm ERR! 400 Bad Request`
|
||||||
|
|
||||||
If you get this error, your package name may not meet the
|
If you get this error, your package name may not meet the
|
||||||
[@scope:package-name package naming convention](#package-naming-convention).
|
[@scope/package-name package naming convention](#package-naming-convention).
|
||||||
|
|
||||||
Ensure the name meets the convention exactly, including the case.
|
Ensure the name meets the convention exactly, including the case.
|
||||||
Then try to publish again.
|
Then try to publish again.
|
||||||
|
|
|
@ -235,9 +235,12 @@ password = ${env.CI_JOB_TOKEN}
|
||||||
|
|
||||||
When publishing packages, note that:
|
When publishing packages, note that:
|
||||||
|
|
||||||
- The maximum allowed size is 50 MB.
|
- You must [authenticate with the Package Registry](#authenticate-with-the-package-registry).
|
||||||
|
- Your [version string must be valid](#ensure-your-version-string-is-valid).
|
||||||
|
- The maximum allowed package size is 5 GB.
|
||||||
- You can't upload the same version of a package multiple times. If you try,
|
- You can't upload the same version of a package multiple times. If you try,
|
||||||
you receive the error `Validation failed: File name has already been taken`.
|
you receive the error `Validation failed: File name has already been taken`.
|
||||||
|
- You cannot publish PyPI packages to a group, only to a project.
|
||||||
|
|
||||||
### Ensure your version string is valid
|
### Ensure your version string is valid
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,9 @@ module API
|
||||||
unauthorized! unless current_user
|
unauthorized! unless current_user
|
||||||
|
|
||||||
{
|
{
|
||||||
merge_requests: current_user.assigned_open_merge_requests_count
|
merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
|
||||||
|
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
|
||||||
|
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -198,7 +198,9 @@ module Gitlab
|
||||||
|
|
||||||
return unless valid_scoped_token?(token, all_available_scopes)
|
return unless valid_scoped_token?(token, all_available_scopes)
|
||||||
|
|
||||||
if token.user.can?(:log_in) || token.user.can?(:bot_log_in, project)
|
return if project && token.user.project_bot? && !project.bots.include?(token.user)
|
||||||
|
|
||||||
|
if token.user.can?(:log_in) || token.user.project_bot?
|
||||||
Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
|
Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -283,7 +285,7 @@ module Gitlab
|
||||||
return unless build.project.builds_enabled?
|
return unless build.project.builds_enabled?
|
||||||
|
|
||||||
if build.user
|
if build.user
|
||||||
return unless build.user.can?(:log_in) || build.user.can?(:bot_log_in, build.project)
|
return unless build.user.can?(:log_in) || (build.user.project_bot? && build.project.bots&.include?(build.user))
|
||||||
|
|
||||||
# If user is assigned to build, use restricted credentials of user
|
# If user is assigned to build, use restricted credentials of user
|
||||||
Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
|
Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
|
||||||
|
|
|
@ -31,10 +31,9 @@ module Gitlab
|
||||||
|
|
||||||
current = @from
|
current = @from
|
||||||
while current <= @to
|
while current <= @to
|
||||||
label = current.strftime(@format)
|
@labels << current.strftime(@format)
|
||||||
@labels << label
|
@total << (totals_count[current] || 0)
|
||||||
@total << (totals_count[label] || 0)
|
@success << (success_count[current] || 0)
|
||||||
@success << (success_count[label] || 0)
|
|
||||||
|
|
||||||
current += interval_step
|
current += interval_step
|
||||||
end
|
end
|
||||||
|
@ -46,7 +45,6 @@ module Gitlab
|
||||||
query
|
query
|
||||||
.group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)")
|
.group("date_trunc('#{interval}', #{::Ci::Pipeline.table_name}.created_at)")
|
||||||
.count(:created_at)
|
.count(:created_at)
|
||||||
.transform_keys { |date| date.strftime(@format) }
|
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
|
|
@ -3943,6 +3943,9 @@ msgstr ""
|
||||||
msgid "Assigned to me"
|
msgid "Assigned to me"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Assigned to you"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Assignee"
|
msgid "Assignee"
|
||||||
msgid_plural "%d Assignees"
|
msgid_plural "%d Assignees"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
@ -24032,6 +24035,9 @@ msgstr ""
|
||||||
msgid "Review requested from %{name}"
|
msgid "Review requested from %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Review requests for you"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Review the changes locally"
|
msgid "Review the changes locally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,12 @@ RSpec.describe 'Dashboard Merge Requests' do
|
||||||
visit merge_requests_dashboard_path(assignee_username: current_user.username)
|
visit merge_requests_dashboard_path(assignee_username: current_user.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'includes assigned and reviewers in badge' do
|
||||||
|
expect(find('.merge-requests-count')).to have_content('3')
|
||||||
|
expect(find('.js-assigned-mr-count')).to have_content('2')
|
||||||
|
expect(find('.js-reviewer-mr-count')).to have_content('1')
|
||||||
|
end
|
||||||
|
|
||||||
it 'shows assigned merge requests' do
|
it 'shows assigned merge requests' do
|
||||||
expect(page).to have_content(assigned_merge_request.title)
|
expect(page).to have_content(assigned_merge_request.title)
|
||||||
expect(page).to have_content(assigned_merge_request_from_fork.title)
|
expect(page).to have_content(assigned_merge_request_from_fork.title)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Api from '~/api';
|
||||||
jest.mock('~/api');
|
jest.mock('~/api');
|
||||||
|
|
||||||
const TEST_COUNT = 1000;
|
const TEST_COUNT = 1000;
|
||||||
const MR_COUNT_CLASS = 'merge-requests-count';
|
const MR_COUNT_CLASS = 'js-merge-requests-count';
|
||||||
|
|
||||||
describe('User Merge Requests', () => {
|
describe('User Merge Requests', () => {
|
||||||
let channelMock;
|
let channelMock;
|
||||||
|
@ -24,7 +24,9 @@ describe('User Merge Requests', () => {
|
||||||
newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock);
|
newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock);
|
||||||
|
|
||||||
global.BroadcastChannel = newBroadcastChannelMock;
|
global.BroadcastChannel = newBroadcastChannelMock;
|
||||||
setFixtures(`<div class="${MR_COUNT_CLASS}">0</div>`);
|
setFixtures(
|
||||||
|
`<div><div class="${MR_COUNT_CLASS}">0</div><div class="js-assigned-mr-count"></div><div class="js-reviewer-mr-count"></div></div>`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent;
|
const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent;
|
||||||
|
@ -33,7 +35,10 @@ describe('User Merge Requests', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
Api.userCounts.mockReturnValue(
|
Api.userCounts.mockReturnValue(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
data: { merge_requests: TEST_COUNT },
|
data: {
|
||||||
|
assigned_merge_requests: TEST_COUNT,
|
||||||
|
review_requested_merge_requests: TEST_COUNT,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -46,7 +51,7 @@ describe('User Merge Requests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates the top count of merge requests', () => {
|
it('updates the top count of merge requests', () => {
|
||||||
expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString());
|
expect(findMRCountText()).toEqual(Number(TEST_COUNT + TEST_COUNT).toLocaleString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the API', () => {
|
it('calls the API', () => {
|
||||||
|
@ -54,7 +59,7 @@ describe('User Merge Requests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('posts count to BroadcastChannel', () => {
|
it('posts count to BroadcastChannel', () => {
|
||||||
expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT);
|
expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT + TEST_COUNT);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -89,4 +89,10 @@ RSpec.describe DashboardHelper do
|
||||||
|
|
||||||
it { is_expected.to eq(false) }
|
it { is_expected.to eq(false) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#reviewer_mrs_dashboard_path' do
|
||||||
|
subject { helper.reviewer_mrs_dashboard_path }
|
||||||
|
|
||||||
|
it { is_expected.to eq(merge_requests_dashboard_path(reviewer_username: user.username)) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,4 +67,37 @@ RSpec.describe MergeRequestsHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#user_merge_requests_counts' do
|
||||||
|
let(:user) do
|
||||||
|
double(
|
||||||
|
assigned_open_merge_requests_count: 1,
|
||||||
|
review_requested_open_merge_requests_count: 2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { helper.user_merge_requests_counts }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(helper).to receive(:current_user).and_return(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns assigned, review requested and total merge request counts" do
|
||||||
|
expect(subject).to eq(
|
||||||
|
assigned: user.assigned_open_merge_requests_count,
|
||||||
|
review_requested: user.review_requested_open_merge_requests_count,
|
||||||
|
total: user.assigned_open_merge_requests_count + user.review_requested_open_merge_requests_count
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge_request_reviewers is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(merge_request_reviewers: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns review_requested as 0' do
|
||||||
|
expect(subject[:review_requested]).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,10 +47,6 @@ RSpec.describe Gitlab::Ci::Charts do
|
||||||
|
|
||||||
subject { chart.to }
|
subject { chart.to }
|
||||||
|
|
||||||
before do
|
|
||||||
create(:ci_empty_pipeline, project: project, duration: 120)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'includes the whole current day' do
|
it 'includes the whole current day' do
|
||||||
is_expected.to eq(Date.today.end_of_day)
|
is_expected.to eq(Date.today.end_of_day)
|
||||||
end
|
end
|
||||||
|
@ -62,37 +58,6 @@ RSpec.describe Gitlab::Ci::Charts do
|
||||||
it 'uses %d %B as labels format' do
|
it 'uses %d %B as labels format' do
|
||||||
expect(chart.labels).to include(chart.from.strftime('%d %B'))
|
expect(chart.labels).to include(chart.from.strftime('%d %B'))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns count of pipelines run each day in the current week' do
|
|
||||||
expect(chart.total).to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'weekchart_non_utc' do
|
|
||||||
today = Date.today
|
|
||||||
end_of_today = Time.use_zone(Time.find_zone('Asia/Dubai')) { today.end_of_day }
|
|
||||||
|
|
||||||
let(:project) { create(:project) }
|
|
||||||
let(:chart) do
|
|
||||||
allow(Date).to receive(:today).and_return(today)
|
|
||||||
allow(today).to receive(:end_of_day).and_return(end_of_today)
|
|
||||||
Gitlab::Ci::Charts::WeekChart.new(project)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { chart.total }
|
|
||||||
|
|
||||||
before do
|
|
||||||
create(:ci_empty_pipeline, project: project, duration: 120)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses a non-utc time zone for range times' do
|
|
||||||
expect(chart.to.zone).to eq(end_of_today.zone)
|
|
||||||
expect(chart.from.zone).to eq(end_of_today.zone)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns count of pipelines run each day in the current week' do
|
|
||||||
is_expected.to contain_exactly(0, 0, 0, 0, 0, 0, 0, 1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'pipeline_times' do
|
context 'pipeline_times' do
|
||||||
|
|
|
@ -4084,6 +4084,7 @@ RSpec.describe User do
|
||||||
cache_mock = double
|
cache_mock = double
|
||||||
|
|
||||||
expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count'])
|
expect(cache_mock).to receive(:delete).with(['users', user.id, 'assigned_open_merge_requests_count'])
|
||||||
|
expect(cache_mock).to receive(:delete).with(['users', user.id, 'review_requested_open_merge_requests_count'])
|
||||||
|
|
||||||
allow(Rails).to receive(:cache).and_return(cache_mock)
|
allow(Rails).to receive(:cache).and_return(cache_mock)
|
||||||
|
|
||||||
|
@ -4163,6 +4164,20 @@ RSpec.describe User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#review_requested_open_merge_requests_count' do
|
||||||
|
it 'returns number of open merge requests from non-archived projects' do
|
||||||
|
user = create(:user)
|
||||||
|
project = create(:project, :public)
|
||||||
|
archived_project = create(:project, :public, :archived)
|
||||||
|
|
||||||
|
create(:merge_request, source_project: project, author: user, reviewers: [user])
|
||||||
|
create(:merge_request, :closed, source_project: project, author: user, reviewers: [user])
|
||||||
|
create(:merge_request, source_project: archived_project, author: user, reviewers: [user])
|
||||||
|
|
||||||
|
expect(user.review_requested_open_merge_requests_count(force: true)).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#assigned_open_issues_count' do
|
describe '#assigned_open_issues_count' do
|
||||||
it 'returns number of open issues from non-archived projects' do
|
it 'returns number of open issues from non-archived projects' do
|
||||||
user = create(:user)
|
user = create(:user)
|
||||||
|
|
|
@ -401,40 +401,6 @@ RSpec.describe ProjectPolicy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'bot_log_in' do
|
|
||||||
let(:bot_user) { create(:user, :project_bot) }
|
|
||||||
let(:project) { private_project }
|
|
||||||
|
|
||||||
context 'when bot is in project and is not blocked' do
|
|
||||||
before do
|
|
||||||
project.add_maintainer(bot_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'is a valid project bot' do
|
|
||||||
expect(bot_user.can?(:bot_log_in, project)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when project bot is invalid' do
|
|
||||||
context 'when bot is not in project' do
|
|
||||||
it 'is not a valid project bot' do
|
|
||||||
expect(bot_user.can?(:bot_log_in, project)).to be_falsy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when bot user is blocked' do
|
|
||||||
before do
|
|
||||||
project.add_maintainer(bot_user)
|
|
||||||
bot_user.block!
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'is not a valid project bot' do
|
|
||||||
expect(bot_user.can?(:bot_log_in, project)).to be_falsy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'support bot' do
|
context 'support bot' do
|
||||||
let(:current_user) { User.support_bot }
|
let(:current_user) { User.support_bot }
|
||||||
|
|
||||||
|
|
|
@ -521,6 +521,19 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
|
||||||
should_email(user2)
|
should_email(user2)
|
||||||
should_email(user3)
|
should_email(user3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates open merge request counter for reviewers', :use_clean_rails_memory_store_caching do
|
||||||
|
merge_request.reviewers = [user3]
|
||||||
|
|
||||||
|
# Cache them to ensure the cache gets invalidated on update
|
||||||
|
expect(user2.review_requested_open_merge_requests_count).to eq(0)
|
||||||
|
expect(user3.review_requested_open_merge_requests_count).to eq(1)
|
||||||
|
|
||||||
|
update_merge_request(reviewer_ids: [user2.id])
|
||||||
|
|
||||||
|
expect(user2.review_requested_open_merge_requests_count).to eq(1)
|
||||||
|
expect(user3.review_requested_open_merge_requests_count).to eq(0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the milestone is removed' do
|
context 'when the milestone is removed' do
|
||||||
|
|
Loading…
Reference in a new issue