Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-09 12:10:09 +00:00
parent 65688a5092
commit 53af44b90f
30 changed files with 441 additions and 165 deletions

View File

@ -46,6 +46,7 @@ import diffsEventHub from '../event_hub';
import { reviewStatuses } from '../utils/file_reviews';
import { diffsApp } from '../utils/performance';
import { fileByFile } from '../utils/preferences';
import { queueRedisHllEvents } from '../utils/queue_events';
import CollapsedFilesWarning from './collapsed_files_warning.vue';
import CommitWidget from './commit_widget.vue';
import CompareVersions from './compare_versions.vue';
@ -336,29 +337,33 @@ export default {
}
if (window.gon?.features?.diffSettingsUsageData) {
const events = [];
if (this.renderTreeList) {
api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE);
events.push(TRACKING_FILE_BROWSER_TREE);
} else {
api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST);
events.push(TRACKING_FILE_BROWSER_LIST);
}
if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) {
api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE);
events.push(TRACKING_DIFF_VIEW_INLINE);
} else {
api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL);
events.push(TRACKING_DIFF_VIEW_PARALLEL);
}
if (this.showWhitespace) {
api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW);
events.push(TRACKING_WHITESPACE_SHOW);
} else {
api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE);
events.push(TRACKING_WHITESPACE_HIDE);
}
if (this.viewDiffsFileByFile) {
api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE);
events.push(TRACKING_SINGLE_FILE_MODE);
} else {
api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE);
events.push(TRACKING_MULTIPLE_FILES_MODE);
}
queueRedisHllEvents(events);
}
},
beforeCreate() {

View File

@ -111,6 +111,8 @@ export const CONFLICT_MARKER_OUR = 'conflict_marker_our';
export const CONFLICT_MARKER_THEIR = 'conflict_marker_their';
// Tracking events
export const DEFER_DURATION = 750;
export const TRACKING_CLICK_DIFF_VIEW_SETTING = 'i_code_review_click_diff_view_setting';
export const TRACKING_DIFF_VIEW_INLINE = 'i_code_review_diff_view_inline';
export const TRACKING_DIFF_VIEW_PARALLEL = 'i_code_review_diff_view_parallel';

View File

@ -1,6 +1,5 @@
import Cookies from 'js-cookie';
import Vue from 'vue';
import api from '~/api';
import createFlash from '~/flash';
import { diffViewerModes } from '~/ide/constants';
import axios from '~/lib/utils/axios_utils';
@ -50,6 +49,7 @@ import eventHub from '../event_hub';
import { isCollapsed } from '../utils/diff_file';
import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews';
import { getDerivedMergeRequestInformation } from '../utils/merge_request';
import { queueRedisHllEvents } from '../utils/queue_events';
import TreeWorker from '../workers/tree_worker';
import * as types from './mutation_types';
import {
@ -368,8 +368,7 @@ export const setInlineDiffViewType = ({ commit }) => {
historyPushState(url);
if (window.gon?.features?.diffSettingsUsageData) {
api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING);
api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE);
queueRedisHllEvents([TRACKING_CLICK_DIFF_VIEW_SETTING, TRACKING_DIFF_VIEW_INLINE]);
}
};
@ -381,8 +380,7 @@ export const setParallelDiffViewType = ({ commit }) => {
historyPushState(url);
if (window.gon?.features?.diffSettingsUsageData) {
api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING);
api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL);
queueRedisHllEvents([TRACKING_CLICK_DIFF_VIEW_SETTING, TRACKING_DIFF_VIEW_PARALLEL]);
}
};
@ -570,13 +568,15 @@ export const setRenderTreeList = ({ commit }, { renderTreeList, trackClick = tru
localStorage.setItem(TREE_LIST_STORAGE_KEY, renderTreeList);
if (window.gon?.features?.diffSettingsUsageData && trackClick) {
api.trackRedisHllUserEvent(TRACKING_CLICK_FILE_BROWSER_SETTING);
const events = [TRACKING_CLICK_FILE_BROWSER_SETTING];
if (renderTreeList) {
api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE);
events.push(TRACKING_FILE_BROWSER_TREE);
} else {
api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST);
events.push(TRACKING_FILE_BROWSER_LIST);
}
queueRedisHllEvents(events);
}
};
@ -592,13 +592,15 @@ export const setShowWhitespace = async (
notesEventHub.$emit('refetchDiffData');
if (window.gon?.features?.diffSettingsUsageData && trackClick) {
api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING);
const events = [TRACKING_CLICK_WHITESPACE_SETTING];
if (showWhitespace) {
api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW);
events.push(TRACKING_WHITESPACE_SHOW);
} else {
api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE);
events.push(TRACKING_WHITESPACE_HIDE);
}
queueRedisHllEvents(events);
}
};
@ -819,13 +821,15 @@ export const setFileByFile = ({ state, commit }, { fileByFile }) => {
Cookies.set(DIFF_FILE_BY_FILE_COOKIE_NAME, fileViewMode);
if (window.gon?.features?.diffSettingsUsageData) {
api.trackRedisHllUserEvent(TRACKING_CLICK_SINGLE_FILE_SETTING);
const events = [TRACKING_CLICK_SINGLE_FILE_SETTING];
if (fileByFile) {
api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE);
events.push(TRACKING_SINGLE_FILE_MODE);
} else {
api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE);
events.push(TRACKING_MULTIPLE_FILES_MODE);
}
queueRedisHllEvents(events);
}
return axios

View File

@ -0,0 +1,13 @@
import { delay } from 'lodash';
import api from '~/api';
import { DEFER_DURATION } from '../constants';
function trackRedisHllUserEvent(event, deferDuration = 0) {
delay(() => api.trackRedisHllUserEvent(event), deferDuration);
}
export function queueRedisHllEvents(events) {
events.forEach((event, index) => {
trackRedisHllUserEvent(event, DEFER_DURATION * (index + 1));
});
}

View File

@ -39,7 +39,9 @@ import {
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
SHOW_DELETE_SUCCESS_ALERT,
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import Tracking from '~/tracking';
@ -156,19 +158,42 @@ export default {
// this.fetchPackageVersions();
}
},
deletePackage() {
return this.$apollo
.mutate({
mutation: destroyPackageMutation,
variables: {
id: this.packageEntity.id,
},
})
.then(({ data }) => {
if (data?.destroyPackage?.errors[0]) {
throw data.destroyPackage.errors[0];
}
});
},
async confirmPackageDeletion() {
this.track(DELETE_PACKAGE_TRACKING_ACTION);
await this.deletePackage();
try {
await this.deletePackage();
const returnTo =
!this.groupListUrl || document.referrer.includes(this.projectName)
? this.projectListUrl
: this.groupListUrl; // to avoid security issue url are supplied from backend
const returnTo =
!this.groupListUrl || document.referrer.includes(this.projectName)
? this.projectListUrl
: this.groupListUrl; // to avoid security issue url are supplied from backend
const modalQuery = objectToQuery({ [SHOW_DELETE_SUCCESS_ALERT]: true });
const modalQuery = objectToQuery({ [SHOW_DELETE_SUCCESS_ALERT]: true });
window.location.replace(`${returnTo}?${modalQuery}`);
window.location.replace(`${returnTo}?${modalQuery}`);
} catch (error) {
createFlash({
message: DELETE_PACKAGE_ERROR_MESSAGE,
type: 'warning',
captureError: true,
error,
});
}
},
handleFileDelete(file) {
this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION);
@ -225,10 +250,10 @@ export default {
<gl-button
v-if="canDelete"
v-gl-modal="'delete-modal'"
class="js-delete-button"
variant="danger"
category="primary"
data-qa-selector="delete_button"
data-testid="delete-package"
>
{{ __('Delete') }}
</gl-button>
@ -303,7 +328,6 @@ export default {
<gl-modal
ref="deleteModal"
class="js-delete-modal"
modal-id="delete-modal"
:action-primary="$options.modal.packageDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"

View File

@ -0,0 +1,5 @@
mutation destroyPackage($id: PackagesPackageID!) {
destroyPackage(input: { id: $id }) {
errors
}
}

View File

@ -52,7 +52,7 @@ class GroupPolicy < BasePolicy
condition(:dependency_proxy_access_allowed) do
if Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true)
access_level >= GroupMember::REPORTER || valid_dependency_proxy_deploy_token
access_level >= GroupMember::GUEST || valid_dependency_proxy_deploy_token
else
can?(:read_group)
end

View File

@ -31,7 +31,8 @@ Prerequisites:
To view a list of environments and deployments:
1. Go to the project's **Deployments > Environments** page.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
The environments are displayed.
![Environments list](img/environments_list.png)
@ -57,7 +58,8 @@ You can create an environment and deployment in the UI or in your `.gitlab-ci.ym
In the UI:
1. Go to the project's **Deployments > Environments** page.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select **New environment**.
1. Enter a name and external URL.
1. Select **Save**.
@ -326,7 +328,8 @@ If there is a problem with a deployment, you can retry it or roll it back.
To retry or rollback a deployment:
1. Go to the project's **Deployments > Environments**.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the environment.
1. To the right of the deployment name:
- To retry a deployment, select **Re-deploy to environment**.
@ -465,7 +468,8 @@ GitLab automatically triggers the `stop_review_app` job to stop the environment.
You can view a deployment's expiration date in the GitLab UI.
1. Go to the project's **Deployments > Environments** page.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the name of the deployment.
In the top left, next to the environment name, the expiration date is displayed.
@ -474,7 +478,8 @@ In the top left, next to the environment name, the expiration date is displayed.
You can manually override a deployment's expiration date.
1. Go to the project's **Deployments > Environments** page.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the deployment name.
1. On the top right, select the thumbtack (**{thumbtack}**).
@ -491,7 +496,8 @@ You can delete [stopped environments](#stopping-an-environment) in the GitLab UI
To delete a stopped environment in the GitLab UI:
1. Go to the project's **Deployments > Environments** page.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the **Stopped** tab.
1. Next to the environment you want to delete, select **Delete environment**.
1. On the confirmation dialog box, select **Delete environment**.
@ -596,7 +602,9 @@ Limitations of GitLab Auto Rollback:
GitLab Auto Rollback is turned off by default. To turn it on:
1. Go to **Project > Settings > CI/CD > Automatic deployment rollbacks**.
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Automatic deployment rollbacks**.
1. Select the checkbox for **Enable automatic rollbacks**.
1. Select **Save changes**.

View File

@ -30,18 +30,19 @@ To protect, update, or unprotect an environment, you need to have at least the
To protect an environment:
1. Navigate to your project's **Settings > CI/CD**.
1. Expand the **Protected environments** section.
1. From the **Environment** dropdown menu, select the environment you want to protect.
1. In the **Allowed to Deploy** dropdown menu, select the role, users, or groups you
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Protected environments**.
1. From the **Environment** list, select the environment you want to protect.
1. In the **Allowed to deploy** list, select the role, users, or groups you
want to give deploy access to. Keep in mind that:
- There are two roles to choose from:
- **Maintainers**: Allows access to all maintainers in the project.
- **Developers**: Allows access to all maintainers and all developers in the project.
- You can only select groups that are already associated with the project.
- Only users that have at least the Developer role appear in
the **Allowed to Deploy** dropdown menu.
1. Click the **Protect** button.
- **Maintainers**: Allows access to all of the project's users with the Maintainer role.
- **Developers**: Allows access to all of the project's users with the Maintainer and Developer role.
- You can select groups that are already associated with the project only.
- Users must have at least the Developer role to appear in
the **Allowed to deploy** list.
1. Select **Protect**.
The protected environment now appears in the list of protected environments.
@ -134,10 +135,10 @@ appears in the dropdown menu for deployment-only access.
To add deployment-only access:
1. Add a group with Reporter permissions.
1. Add user(s) to the group.
1. Add a group with the Reporter role.
1. Add users to the group.
1. Invite the group to be a project member.
1. Follow the steps outlined in [Protecting Environments](#protecting-environments).
1. Follow the steps in [Protecting Environments](#protecting-environments).
Note that deployment-only access is the only possible access level for groups with [Reporter permissions](../../user/permissions.md).

View File

@ -850,66 +850,6 @@ restoring the new data, which causes an error.
Read more about [configuring NFS mounts](../administration/nfs.md)
### Restore for installation from source
First, ensure your backup tar file is in the backup directory described in the
`gitlab.yml` configuration:
```yaml
## Backup settings
backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
```
The default is `/home/git/gitlab/tmp/backups`, and it needs to be owned by the `git` user. Now, you can begin the backup procedure:
```shell
# Stop processes that are connected to the database
sudo service gitlab stop
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
Example output:
```plaintext
Unpacking backup... [DONE]
Restoring database tables:
-- create_table("events", {:force=>true})
-> 0.2231s
[...]
- Loading fixture events...[DONE]
- Loading fixture issues...[DONE]
- Loading fixture keys...[SKIPPING]
- Loading fixture merge_requests...[DONE]
- Loading fixture milestones...[DONE]
- Loading fixture namespaces...[DONE]
- Loading fixture notes...[DONE]
- Loading fixture projects...[DONE]
- Loading fixture protected_branches...[SKIPPING]
- Loading fixture schema_migrations...[DONE]
- Loading fixture services...[SKIPPING]
- Loading fixture snippets...[SKIPPING]
- Loading fixture taggings...[SKIPPING]
- Loading fixture tags...[SKIPPING]
- Loading fixture users...[DONE]
- Loading fixture users_projects...[DONE]
- Loading fixture web_hooks...[SKIPPING]
- Loading fixture wikis...[SKIPPING]
Restoring repositories:
- Restoring repository abcd... [DONE]
- Object pool 1 ...
Deleting tmp directories...[DONE]
```
Next, restore `/home/git/gitlab/.secret` if necessary, [as previously mentioned](#restore-prerequisites).
Restart GitLab:
```shell
sudo service gitlab restart
```
### Restore for Omnibus GitLab installations
This procedure assumes that:
@ -1027,6 +967,66 @@ issue.
The GitLab Helm chart uses a different process, documented in
[restoring a GitLab Helm chart installation](https://gitlab.com/gitlab-org/charts/gitlab/blob/master/doc/backup-restore/restore.md).
### Restore for installation from source
First, ensure your backup tar file is in the backup directory described in the
`gitlab.yml` configuration:
```yaml
## Backup settings
backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
```
The default is `/home/git/gitlab/tmp/backups`, and it needs to be owned by the `git` user. Now, you can begin the backup procedure:
```shell
# Stop processes that are connected to the database
sudo service gitlab stop
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
Example output:
```plaintext
Unpacking backup... [DONE]
Restoring database tables:
-- create_table("events", {:force=>true})
-> 0.2231s
[...]
- Loading fixture events...[DONE]
- Loading fixture issues...[DONE]
- Loading fixture keys...[SKIPPING]
- Loading fixture merge_requests...[DONE]
- Loading fixture milestones...[DONE]
- Loading fixture namespaces...[DONE]
- Loading fixture notes...[DONE]
- Loading fixture projects...[DONE]
- Loading fixture protected_branches...[SKIPPING]
- Loading fixture schema_migrations...[DONE]
- Loading fixture services...[SKIPPING]
- Loading fixture snippets...[SKIPPING]
- Loading fixture taggings...[SKIPPING]
- Loading fixture tags...[SKIPPING]
- Loading fixture users...[DONE]
- Loading fixture users_projects...[DONE]
- Loading fixture web_hooks...[SKIPPING]
- Loading fixture wikis...[SKIPPING]
Restoring repositories:
- Restoring repository abcd... [DONE]
- Object pool 1 ...
Deleting tmp directories...[DONE]
```
Next, restore `/home/git/gitlab/.secret` if necessary, [as previously mentioned](#restore-prerequisites).
Restart GitLab:
```shell
sudo service gitlab restart
```
### Restoring only one or a few projects or groups from a backup
Although the Rake task used to restore a GitLab instance doesn't support

View File

@ -203,15 +203,15 @@ accordingly, while also consulting the
The following table, while not exhaustive, shows some examples of the supported
upgrade paths.
| Target version | Your version | Supported upgrade path | Note |
| --------------------- | ------------ | ------------------------ | ---- |
| `14.1.0` | `13.9.2` | `13.9.2` -> `13.12.6` -> `14.0.5` -> `14.1.0` | Two intermediate versions are required: `13.12` and `14.0`, then `14.1`. |
| `13.5.4` | `12.9.2` | `12.9.2` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.5.4` | Three intermediate versions are required: `12.10`, `13.0` and `13.1`, then `13.5.4`. |
| `13.2.10` | `11.5.0` | `11.5.0` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.2.10` | Six intermediate versions are required: `11.11`, `12.0`, `12.1`, `12.10`, `13.0` and `13.1`, then `13.2.10`. |
| `12.10.14` | `11.3.4` | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` | Three intermediate versions are required: `11.11`, `12.0` and `12.1`, then `12.10.14`. |
| `12.9.5` | `10.4.5` | `10.4.5` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.9.5` | Four intermediate versions are required: `10.8`, `11.11`, `12.0` and `12.1`, then `12.9.5`. |
| `12.2.5` | `9.2.6` | `9.2.6` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.2.5` | Five intermediate versions are required: `9.5`, `10.8`, `11.11`, `12.0`, `12.1`, then `12.2.5`. |
| `11.3.4` | `8.13.4` | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version 8, `9.5.10` is the last version in version 9, `10.8.7` is the last version in version 10. |
| Target version | Your version | Supported upgrade path | Note |
| -------------- | ------------ | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `14.1.2` | `13.9.2` | `13.9.2` -> `13.12.9` -> `14.0.7` -> `14.1.2` | Two intermediate versions are required: `13.12` and `14.0`, then `14.1`. |
| `13.5.4` | `12.9.2` | `12.9.2` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.5.4` | Three intermediate versions are required: `12.10`, `13.0` and `13.1`, then `13.5.4`. |
| `13.2.10` | `11.5.0` | `11.5.0` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` -> `13.0.14` -> `13.1.11` -> `13.2.10` | Six intermediate versions are required: `11.11`, `12.0`, `12.1`, `12.10`, `13.0` and `13.1`, then `13.2.10`. |
| `12.10.14` | `11.3.4` | `11.3.4` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.10.14` | Three intermediate versions are required: `11.11`, `12.0` and `12.1`, then `12.10.14`. |
| `12.9.5` | `10.4.5` | `10.4.5` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.9.5` | Four intermediate versions are required: `10.8`, `11.11`, `12.0` and `12.1`, then `12.9.5`. |
| `12.2.5` | `9.2.6` | `9.2.6` -> `9.5.10` -> `10.8.7` -> `11.11.8` -> `12.0.12` -> `12.1.17` -> `12.2.5` | Five intermediate versions are required: `9.5`, `10.8`, `11.11`, `12.0`, `12.1`, then `12.2.5`. |
| `11.3.4` | `8.13.4` | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version 8, `9.5.10` is the last version in version 9, `10.8.7` is the last version in version 10. |
## Upgrading to a new major version
@ -222,7 +222,7 @@ cannot guarantee that upgrading between major versions will be seamless.
It is required to follow the following upgrade steps to ensure a successful *major* version upgrade:
1. Upgrade to the latest minor version of the preceeding major version.
1. Upgrade to the latest minor version of the preceding major version.
1. Upgrade to the first minor version (`X.0.Z`) of the target major version.
1. Proceed with upgrading to a newer release.
@ -379,7 +379,7 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
- Due to an issue where `BatchedBackgroundMigrationWorkers` were
[not working](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/2785#note_614738345)
for self-managed instances, a [fix was created](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65106)
and a [14.0.Z](#1400) version was released. If you haven't udpated to 14.0.Z, you need
and a [14.0.Z](#1400) version was released. If you haven't updated to 14.0.Z, you need
to update to at least 14.1.0 that contains the same fix before you update to
a later version.

View File

@ -301,7 +301,7 @@ The following table lists group permissions available for each role:
| Create/edit/delete iterations | | | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
| Enable/disable a dependency proxy | | | ✓ | ✓ | ✓ |
| Pull a container image using the dependency proxy | | ✓ | ✓ | ✓ | ✓ |
| Pull a container image using the dependency proxy | | ✓ | ✓ | ✓ | ✓ |
| Purge the dependency proxy for a group | | | | | ✓ |
| Publish [packages](packages/index.md) | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |

View File

@ -21,7 +21,7 @@ module Gitlab
end
rescue Errno::EEXIST
puts 'Skipping config.toml generation:'
puts 'A configuration file already exists.'
puts "A configuration file for #{config_path} already exists."
rescue ArgumentError => e
puts 'Skipping config.toml generation:'
puts e.message

View File

@ -6497,6 +6497,9 @@ msgstr ""
msgid "Checkout|%{totalCiMinutes} CI minutes"
msgstr ""
msgid "Checkout|(may be %{linkStart}charged upon purchase%{linkEnd})"
msgstr ""
msgid "Checkout|(x%{numberOfUsers})"
msgstr ""

View File

@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.209.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "32.0.0",
"@gitlab/ui": "32.1.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.3-2",
"@rails/ujs": "6.1.3-2",

View File

@ -8,7 +8,7 @@ gem 'allure-rspec', '~> 2.14.1'
gem 'capybara', '~> 3.35.0'
gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.3'
gem 'rspec', '~> 3.7'
gem 'rspec', '~> 3.10'
gem 'selenium-webdriver', '~> 4.0.0.beta4'
gem 'airborne', '~> 0.3.4'
gem 'rest-client', '~> 2.1.0'
@ -28,6 +28,8 @@ gem 'webdrivers', '~> 4.6'
gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
gem 'deprecation_toolkit', '~> 1.5.1', require: false
group :development do
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem "ruby-debug-ide", "~> 0.7.0"

View File

@ -56,6 +56,8 @@ GEM
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
concurrent-ruby (1.1.9)
deprecation_toolkit (1.5.1)
activesupport (>= 4.2)
diff-lcs (1.3)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
@ -141,18 +143,18 @@ GEM
netrc (~> 0.8)
rexml (3.2.5)
rotp (3.1.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.3)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.1)
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (~> 3.10.0)
rspec-parameterized (0.4.2)
binding_ninja (>= 0.2.3)
parser
@ -161,7 +163,7 @@ GEM
unparser
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-support (3.9.4)
rspec-support (3.10.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
ruby-debug-ide (0.7.2)
@ -215,6 +217,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.23)
chemlab (~> 0.7)
chemlab-library-www-gitlab-com (~> 0.1)
deprecation_toolkit (~> 1.5.1)
faker (~> 1.6, >= 1.6.6)
gitlab-qa
knapsack (~> 1.17)
@ -226,7 +229,7 @@ DEPENDENCIES
rake (~> 12.3.3)
rest-client (~> 2.1.0)
rotp (~> 3.1.0)
rspec (~> 3.7)
rspec (~> 3.10)
rspec-parameterized (~> 0.4.2)
rspec-retry (~> 0.6.1)
rspec_junit_formatter (~> 0.4.1)

View File

@ -108,7 +108,7 @@ module QA
wait_for_requests(skip_finished_loading_check: skip_finished_loading_check)
element_selector = element_selector_css(name, reject_capybara_query_keywords(kwargs))
find(element_selector, only_capybara_query_keywords(kwargs))
find(element_selector, **only_capybara_query_keywords(kwargs))
end
def only_capybara_query_keywords(kwargs)

View File

@ -26,7 +26,7 @@ module QA
end
def restore_application_settings(*application_settings_keys)
set_application_settings(@original_application_settings.slice(*application_settings_keys))
set_application_settings(**@original_application_settings.slice(*application_settings_keys))
end
private

View File

@ -148,7 +148,7 @@ module QA
Capybara::Selenium::Driver.new(
app,
selenium_options
**selenium_options
)
end

View File

@ -32,7 +32,7 @@ module QA
def enabled?(key, **scopes)
feature = JSON.parse(get_features).find { |flag| flag['name'] == key.to_s }
feature && (feature['state'] == 'on' || feature['state'] == 'conditional' && scopes.present? && enabled_scope?(feature['gates'], scopes))
feature && (feature['state'] == 'on' || feature['state'] == 'conditional' && scopes.present? && enabled_scope?(feature['gates'], **scopes))
end
private
@ -43,7 +43,7 @@ module QA
raise AuthorizationError, "Administrator access is required to enable/disable feature flags. #{e.message}"
end
def enabled_scope?(gates, scopes)
def enabled_scope?(gates, **scopes)
scopes.each do |key, value|
case key
when :project, :group, :user
@ -71,16 +71,16 @@ module QA
# scopes: Any scope (user, project, group) to restrict the change to
def set_and_verify(key, enable:, **scopes)
msg = "#{enable ? 'En' : 'Dis'}abling feature: #{key}"
msg += " for scope \"#{scopes_to_s(scopes)}\"" if scopes.present?
msg += " for scope \"#{scopes_to_s(**scopes)}\"" if scopes.present?
QA::Runtime::Logger.info(msg)
Support::Retrier.retry_on_exception(sleep_interval: 2) do
set_feature(key, enable, scopes)
set_feature(key, enable, **scopes)
is_enabled = nil
QA::Support::Waiter.wait_until(sleep_interval: 1) do
is_enabled = enabled?(key, scopes)
is_enabled = enabled?(key, **scopes)
is_enabled == enable || !enable && scopes.present?
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'deprecation_toolkit'
require 'deprecation_toolkit/rspec'
require 'concurrent/utility/monotonic_time'
require 'active_support/gem_version'
module QaDeprecationToolkitEnv
# Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
def self.kwargs_warning
%r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z}
end
def self.configure!
# Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7.2
Warning[:deprecated] = true
DeprecationToolkit::Configuration.test_runner = :rspec
DeprecationToolkit::Configuration.deprecation_path = 'deprecations'
DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning]
end
end

View File

@ -14,7 +14,7 @@ RSpec.describe QA::Runtime::API::Request do
end
context 'when oauth_access_token is passed in the query string' do
let(:request) { described_class.new(client, '/users', { oauth_access_token: 'foo' }) }
let(:request) { described_class.new(client, '/users', oauth_access_token: 'foo') }
it 'does not adds a private_token query string' do
expect(request.url).to eq 'http://example.com/api/v4/users?oauth_access_token=foo'

View File

@ -6,6 +6,9 @@ require 'rspec-parameterized'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank'
require_relative 'qa_deprecation_toolkit_env'
QaDeprecationToolkitEnv.configure!
if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind

View File

@ -185,7 +185,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
before do
group.add_reporter(user)
group.add_guest(user)
end
it 'proxies status from the remote token request', :aggregate_failures do
@ -206,7 +206,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
before do
group.add_reporter(user)
group.add_guest(user)
end
it 'proxies status from the remote manifest request', :aggregate_failures do
@ -219,7 +219,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
context 'a valid user' do
before do
group.add_reporter(user)
group.add_guest(user)
end
it_behaves_like 'a successful manifest pull'
@ -308,7 +308,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
before do
group.add_reporter(user)
group.add_guest(user)
end
it 'proxies status from the remote blob request', :aggregate_failures do
@ -321,7 +321,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
context 'a valid user' do
before do
group.add_reporter(user)
group.add_guest(user)
end
it_behaves_like 'a successful blob pull'

View File

@ -0,0 +1,36 @@
import api from '~/api';
import { DEFER_DURATION } from '~/diffs/constants';
import { queueRedisHllEvents } from '~/diffs/utils/queue_events';
jest.mock('~/api', () => ({
trackRedisHllUserEvent: jest.fn(),
}));
describe('diffs events queue', () => {
describe('queueRedisHllEvents', () => {
it('does not dispatch the event immediately', () => {
queueRedisHllEvents(['know_event']);
expect(api.trackRedisHllUserEvent).not.toHaveBeenCalled();
});
it('does dispatch the event after the defer duration', () => {
queueRedisHllEvents(['know_event']);
jest.advanceTimersByTime(DEFER_DURATION + 1);
expect(api.trackRedisHllUserEvent).toHaveBeenCalled();
});
it('increase defer duration based on the provided events count', () => {
let deferDuration = DEFER_DURATION + 1;
const events = ['know_event_a', 'know_event_b', 'know_event_c'];
queueRedisHllEvents(events);
expect(api.trackRedisHllUserEvent).not.toHaveBeenCalled();
events.forEach((event, index) => {
jest.advanceTimersByTime(deferDuration);
expect(api.trackRedisHllUserEvent).toHaveBeenLastCalledWith(event);
deferDuration *= index + 1;
});
});
});
});

View File

@ -1,7 +1,9 @@
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlModal } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
@ -10,11 +12,22 @@ import PackagesApp from '~/packages_and_registries/package_registry/components/d
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
import PackageHistory from '~/packages_and_registries/package_registry/components/details/package_history.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue';
import { FETCH_PACKAGE_DETAILS_ERROR_MESSAGE } from '~/packages_and_registries/package_registry/constants';
import {
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import { packageDetailsQuery, packageData, emptyPackageDetailsQuery } from '../../mock_data';
import {
packageDetailsQuery,
packageData,
emptyPackageDetailsQuery,
packageDestroyMutation,
packageDestroyMutationError,
} from '../../mock_data';
jest.mock('~/flash');
useMockLocationHelper();
const localVue = createLocalVue();
@ -34,16 +47,23 @@ describe('PackagesApp', () => {
groupListUrl: 'groupListUrl',
};
function createComponent({ resolver = jest.fn().mockResolvedValue(packageDetailsQuery()) } = {}) {
function createComponent({
resolver = jest.fn().mockResolvedValue(packageDetailsQuery()),
mutationResolver = jest.fn().mockResolvedValue(packageDestroyMutation()),
} = {}) {
localVue.use(VueApollo);
const requestHandlers = [[getPackageDetails, resolver]];
const requestHandlers = [
[getPackageDetails, resolver],
[destroyPackageMutation, mutationResolver],
];
apolloProvider = createMockApollo(requestHandlers);
wrapper = shallowMount(PackagesApp, {
wrapper = shallowMountExtended(PackagesApp, {
localVue,
apolloProvider,
provide,
stubs: { PackageTitle },
});
}
@ -52,6 +72,8 @@ describe('PackagesApp', () => {
const findPackageHistory = () => wrapper.findComponent(PackageHistory);
const findAdditionalMetadata = () => wrapper.findComponent(AdditionalMetadata);
const findInstallationCommands = () => wrapper.findComponent(InstallationCommands);
const findDeleteModal = () => wrapper.findComponent(GlModal);
const findDeleteButton = () => wrapper.findByTestId('delete-package');
afterEach(() => {
wrapper.destroy();
@ -121,4 +143,101 @@ describe('PackagesApp', () => {
packageEntity: expect.objectContaining(packageData()),
});
});
describe('delete package', () => {
const originalReferrer = document.referrer;
const setReferrer = (value = provide.projectName) => {
Object.defineProperty(document, 'referrer', {
value,
configurable: true,
});
};
const performDeletePackage = async () => {
await findDeleteButton().trigger('click');
findDeleteModal().vm.$emit('primary');
await waitForPromises();
};
afterEach(() => {
Object.defineProperty(document, 'referrer', {
value: originalReferrer,
configurable: true,
});
});
it('shows the delete confirmation modal when delete is clicked', async () => {
createComponent();
await waitForPromises();
await findDeleteButton().trigger('click');
expect(findDeleteModal().exists()).toBe(true);
});
describe('successful request', () => {
it('when referrer contains project name calls window.replace with project url', async () => {
setReferrer();
createComponent();
await waitForPromises();
await performDeletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'projectListUrl?showSuccessDeleteAlert=true',
);
});
it('when referrer does not contain project name calls window.replace with group url', async () => {
setReferrer('baz');
createComponent();
await waitForPromises();
await performDeletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'groupListUrl?showSuccessDeleteAlert=true',
);
});
});
describe('request failure', () => {
it('on global failure it displays an alert', async () => {
createComponent({ mutationResolver: jest.fn().mockRejectedValue() });
await waitForPromises();
await performDeletePackage();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_ERROR_MESSAGE,
}),
);
});
it('on payload with error it displays an alert', async () => {
createComponent({
mutationResolver: jest.fn().mockResolvedValue(packageDestroyMutationError()),
});
await waitForPromises();
await performDeletePackage();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_ERROR_MESSAGE,
}),
);
});
});
});
});

View File

@ -125,3 +125,29 @@ export const emptyPackageDetailsQuery = () => ({
},
},
});
export const packageDestroyMutation = () => ({
data: {
destroyPackage: {
errors: [],
},
},
});
export const packageDestroyMutationError = () => ({
data: {
destroyPackage: null,
},
errors: [
{
message:
"The resource that you are attempting to access does not exist or you don't have permission to perform this action",
locations: [
{
line: 2,
column: 3,
},
],
path: ['destroyPackage'],
},
],
});

View File

@ -265,7 +265,7 @@ module TestEnv
def setup_workhorse
# Always rebuild the config file
if skip_compile_workhorse?
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil, force: true)
return
end

View File

@ -908,10 +908,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@32.0.0":
version "32.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.0.0.tgz#2bf8995e405f35c5fa9dcc2ad0de24d79dc9c006"
integrity sha512-GVQVx8Db9fb/BfYf7Vzdf6MxqLsF5RZta0VanwTm/asGdQxWG7I9fJIPUHqhCK3S9efZ83zXfZBF0M3uHuxveg==
"@gitlab/ui@32.1.0":
version "32.1.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.1.0.tgz#3b7a4b4d68a726f2e6a2d87db03cb604e4841398"
integrity sha512-FtNWIOE00lCLXAPrRpFTOoFDD6mvDiCB8qr03NQsvFSbEuwfkvxhElaKNeKy+w0HeM8S8Tt2JqSyz9UjprgFUQ==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.18.1"