Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3c3b7c12a0
commit
d30a9499fa
|
@ -84,13 +84,13 @@ Are there any other stages or teams involved that need to be kept in the loop?
|
|||
|
||||
### Global rollout on production
|
||||
|
||||
- [ ] [Incrementally roll out](https://docs.gitlab.com/ee/development/feature_flags/controls.html#process) the feature. If there is no risk that the feature affects usability or server loads, skip to the global rollout.
|
||||
- [ ] [Incrementally roll out](https://docs.gitlab.com/ee/development/feature_flags/controls.html#process) the feature.
|
||||
- If the feature flag in code has [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform **actor-based** rollout.
|
||||
- [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage> --actors`
|
||||
- If the feature flag in code does **NOT** have [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform time-based rollout (**random** rollout).
|
||||
- [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage>`
|
||||
- [ ] Enable the feature globally on production environment.
|
||||
- [ ] `/chatops run feature set <feature-flag-name> true`
|
||||
- Enable the feature globally on production environment.
|
||||
- [ ] `/chatops run feature set <feature-flag-name> true`
|
||||
- [ ] Announce on [the feature issue](ISSUE LINK) that the feature has been globally enabled.
|
||||
- [ ] Cross-post chatops slack command to `#support_gitlab-com`.
|
||||
([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#communicate-the-change)) and in your team channel
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<!-- Use this section to explain the feature and how it will work. It can be helpful to add technical details, design proposals, and links to related epics or issues. -->
|
||||
|
||||
<!-- Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
|
||||
<!-- Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
|
||||
|
||||
/label ~"group::" ~"section::" ~"Category::" ~"GitLab Core"/~"GitLab Premium"/~"GitLab Ultimate"
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
- https://gitlab.com/gitlab-org/gitlab/-/labels
|
||||
- https://about.gitlab.com/handbook/product/categories/features/
|
||||
|
||||
Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
|
||||
Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
|
||||
|
||||
Other sections to consider adding:
|
||||
|
|
@ -201,7 +201,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="qa-loading-animation prepend-top-20" />
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="qa-loading-animation gl-mt-6" />
|
||||
|
||||
<template v-else-if="shouldRenderContent">
|
||||
<div class="build-page" data-testid="job-content">
|
||||
|
@ -272,17 +272,17 @@ export default {
|
|||
|
||||
<div
|
||||
v-if="job.archived"
|
||||
class="gl-mt-3 archived-job"
|
||||
:class="{ 'sticky-top border-bottom-0': hasTrace }"
|
||||
class="gl-mt-3 gl-py-2 gl-px-3 gl-align-items-center gl-z-index-1 gl-m-auto archived-job"
|
||||
:class="{ 'sticky-top gl-border-bottom-0': hasTrace }"
|
||||
data-testid="archived-job"
|
||||
>
|
||||
<gl-icon name="lock" class="align-text-bottom" />
|
||||
<gl-icon name="lock" class="gl-vertical-align-bottom" />
|
||||
{{ __('This job is archived. Only the complete pipeline can be retried.') }}
|
||||
</div>
|
||||
<!-- job log -->
|
||||
<div
|
||||
v-if="hasTrace"
|
||||
class="build-trace-container position-relative"
|
||||
class="build-trace-container gl-relative"
|
||||
:class="{ 'gl-mt-3': !job.archived }"
|
||||
>
|
||||
<log-top-bar
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div
|
||||
class="build-job position-relative"
|
||||
class="build-job gl-relative"
|
||||
:class="{
|
||||
retried: job.retried,
|
||||
active: isActive,
|
||||
|
@ -56,7 +56,7 @@ export default {
|
|||
<gl-icon
|
||||
v-if="isActive"
|
||||
name="arrow-right"
|
||||
class="js-arrow-right icon-arrow-right position-absolute d-block"
|
||||
class="js-arrow-right icon-arrow-right gl-absolute gl-display-block"
|
||||
/>
|
||||
|
||||
<ci-icon :status="job.status" />
|
||||
|
|
|
@ -69,7 +69,10 @@ export default {
|
|||
<template>
|
||||
<div class="top-bar">
|
||||
<!-- truncate information -->
|
||||
<div class="truncated-info d-none d-sm-block float-left" data-testid="log-truncated-info">
|
||||
<div
|
||||
class="truncated-info gl-display-none gl-sm-display-block gl-float-left"
|
||||
data-testid="log-truncated-info"
|
||||
>
|
||||
<template v-if="isTraceSizeVisible">
|
||||
{{ jobLogSize }}
|
||||
<gl-link
|
||||
|
@ -83,7 +86,7 @@ export default {
|
|||
</div>
|
||||
<!-- eo truncate information -->
|
||||
|
||||
<div class="controllers float-right">
|
||||
<div class="controllers gl-float-right">
|
||||
<!-- links -->
|
||||
<gl-button
|
||||
v-if="rawPath"
|
||||
|
|
|
@ -11,10 +11,6 @@
|
|||
color: var(--orange-600, $orange-600);
|
||||
background-color: var(--orange-50, $orange-50);
|
||||
border: 1px solid var(--border-color, $border-color);
|
||||
padding: 3px 12px;
|
||||
margin: auto;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
|
||||
.with-performance-bar & {
|
||||
top: $header-height + $performance-bar-height;
|
||||
|
|
|
@ -100,7 +100,7 @@ module NotificationRecipients
|
|||
# Get project/group users with CUSTOM notification level
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def add_custom_notifications
|
||||
return new_add_custom_notifications if Feature.enabled?(:notification_setting_recipient_refactor, project)
|
||||
return new_add_custom_notifications if Feature.enabled?(:notification_setting_recipient_refactor, project, default_enabled: :yaml)
|
||||
|
||||
user_ids = []
|
||||
|
||||
|
@ -172,7 +172,7 @@ module NotificationRecipients
|
|||
# Get project users with WATCH notification level
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def project_watchers
|
||||
return new_project_watchers if Feature.enabled?(:notification_setting_recipient_refactor, project)
|
||||
return new_project_watchers if Feature.enabled?(:notification_setting_recipient_refactor, project, default_enabled: :yaml)
|
||||
|
||||
project_members_ids = user_ids_notifiable_on(project)
|
||||
|
||||
|
@ -200,7 +200,7 @@ module NotificationRecipients
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def group_watchers
|
||||
return new_group_watchers if Feature.enabled?(:notification_setting_recipient_refactor, project)
|
||||
return new_group_watchers if Feature.enabled?(:notification_setting_recipient_refactor, project, default_enabled: :yaml)
|
||||
|
||||
user_ids_with_group_global = user_ids_notifiable_on(group, :global)
|
||||
user_ids = user_ids_with_global_level_watch(user_ids_with_group_global)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable notification settings recipient refactor by default
|
||||
merge_request: 61443
|
||||
author:
|
||||
type: performance
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327303
|
|||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -22,12 +22,14 @@ Geo undergoes significant changes from release to release. Upgrades **are** supp
|
|||
|
||||
Fetching large repositories can take a long time for teams located far from a single GitLab instance.
|
||||
|
||||
Geo provides local, read-only instances of your GitLab instances. This can reduce the time it takes
|
||||
Geo provides local, read-only sites of your GitLab instances. This can reduce the time it takes
|
||||
to clone and fetch large repositories, speeding up development.
|
||||
|
||||
For a video introduction to Geo, see [Introduction to GitLab Geo - GitLab Features](https://www.youtube.com/watch?v=-HDLxSjEh6w).
|
||||
|
||||
To make sure you're using the right version of the documentation, navigate to [the Geo page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown. For example, [`v11.2.3-ee`](https://gitlab.com/gitlab-org/gitlab/blob/v11.2.3-ee/doc/administration/geo/index.md).
|
||||
To make sure you're using the right version of the documentation, navigate to [the Geo page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown. For example, [`v13.7.6-ee`](https://gitlab.com/gitlab-org/gitlab/-/blob/v13.7.6-ee/doc/administration/geo/index.md).
|
||||
|
||||
Geo uses a set of defined terms that is described in the [Geo Glossary](glossary.md), please familiarize yourself with those terms.
|
||||
|
||||
## Use cases
|
||||
|
||||
|
@ -35,21 +37,21 @@ Implementing Geo provides the following benefits:
|
|||
|
||||
- Reduce from minutes to seconds the time taken for your distributed developers to clone and fetch large repositories and projects.
|
||||
- Enable all of your developers to contribute ideas and work in parallel, no matter where they are.
|
||||
- Balance the read-only load between your **primary** and **secondary** nodes.
|
||||
- Balance the read-only load between your **primary** and **secondary** sites.
|
||||
|
||||
In addition, it:
|
||||
|
||||
- Can be used for cloning and fetching projects, in addition to reading any data available in the GitLab web interface (see [limitations](#limitations)).
|
||||
- Overcomes slow connections between distant offices, saving time by improving speed for distributed teams.
|
||||
- Helps reducing the loading time for automated tasks, custom integrations, and internal workflows.
|
||||
- Can quickly fail over to a **secondary** node in a [disaster recovery](disaster_recovery/index.md) scenario.
|
||||
- Allows [planned failover](disaster_recovery/planned_failover.md) to a **secondary** node.
|
||||
- Can quickly fail over to a **secondary** site in a [disaster recovery](disaster_recovery/index.md) scenario.
|
||||
- Allows [planned failover](disaster_recovery/planned_failover.md) to a **secondary** site.
|
||||
|
||||
Geo provides:
|
||||
|
||||
- Read-only **secondary** nodes: Maintain one **primary** GitLab node while still enabling read-only **secondary** nodes for each of your distributed teams.
|
||||
- Authentication system hooks: **Secondary** nodes receives all authentication data (like user accounts and logins) from the **primary** instance.
|
||||
- An intuitive UI: **Secondary** nodes use the same web interface your team has grown accustomed to. In addition, there are visual notifications that block write operations and make it clear that a user is on a **secondary** node.
|
||||
- Read-only **secondary** sites: Maintain one **primary** GitLab site while still enabling read-only **secondary** sites for each of your distributed teams.
|
||||
- Authentication system hooks: **Secondary** sites receives all authentication data (like user accounts and logins) from the **primary** instance.
|
||||
- An intuitive UI: **Secondary** sites use the same web interface your team has grown accustomed to. In addition, there are visual notifications that block write operations and make it clear that a user is on a **secondary** sites.
|
||||
|
||||
### Gitaly Cluster
|
||||
|
||||
|
@ -64,16 +66,16 @@ Your Geo instance can be used for cloning and fetching projects, in addition to
|
|||
|
||||
When Geo is enabled, the:
|
||||
|
||||
- Original instance is known as the **primary** node.
|
||||
- Replicated read-only nodes are known as **secondary** nodes.
|
||||
- Original instance is known as the **primary** site.
|
||||
- Replicated read-only sites are known as **secondary** sites.
|
||||
|
||||
Keep in mind that:
|
||||
|
||||
- **Secondary** nodes talk to the **primary** node to:
|
||||
- **Secondary** sites talk to the **primary** site to:
|
||||
- Get user data for logins (API).
|
||||
- Replicate repositories, LFS Objects, and Attachments (HTTPS + JWT).
|
||||
- In GitLab Premium 10.0 and later, the **primary** node no longer talks to **secondary** nodes to notify for changes (API).
|
||||
- Pushing directly to a **secondary** node (for both HTTP and SSH, including Git LFS) was [introduced](https://about.gitlab.com/releases/2018/09/22/gitlab-11-3-released/) in [GitLab Premium](https://about.gitlab.com/pricing/#self-managed) 11.3.
|
||||
- In GitLab Premium 10.0 and later, the **primary** site no longer talks to **secondary** sites to notify for changes (API).
|
||||
- Pushing directly to a **secondary** site (for both HTTP and SSH, including Git LFS) was [introduced](https://about.gitlab.com/releases/2018/09/22/gitlab-11-3-released/) in [GitLab Premium](https://about.gitlab.com/pricing/#self-managed) 11.3.
|
||||
- There are [limitations](#limitations) when using Geo.
|
||||
|
||||
### Architecture
|
||||
|
@ -84,31 +86,31 @@ The following diagram illustrates the underlying architecture of Geo.
|
|||
|
||||
In this diagram:
|
||||
|
||||
- There is the **primary** node and the details of one **secondary** node.
|
||||
- Writes to the database can only be performed on the **primary** node. A **secondary** node receives database
|
||||
- There is the **primary** site and the details of one **secondary** site.
|
||||
- Writes to the database can only be performed on the **primary** site. A **secondary** site receives database
|
||||
updates via PostgreSQL streaming replication.
|
||||
- If present, the [LDAP server](#ldap) should be configured to replicate for [Disaster Recovery](disaster_recovery/index.md) scenarios.
|
||||
- A **secondary** node performs different type of synchronizations against the **primary** node, using a special
|
||||
- A **secondary** site performs different type of synchronizations against the **primary** site, using a special
|
||||
authorization protected by JWT:
|
||||
- Repositories are cloned/updated via Git over HTTPS.
|
||||
- Attachments, LFS objects, and other files are downloaded via HTTPS using a private API endpoint.
|
||||
|
||||
From the perspective of a user performing Git operations:
|
||||
|
||||
- The **primary** node behaves as a full read-write GitLab instance.
|
||||
- **Secondary** nodes are read-only but proxy Git push operations to the **primary** node. This makes **secondary** nodes appear to support push operations themselves.
|
||||
- The **primary** site behaves as a full read-write GitLab instance.
|
||||
- **Secondary** sites are read-only but proxy Git push operations to the **primary** site. This makes **secondary** sites appear to support push operations themselves.
|
||||
|
||||
To simplify the diagram, some necessary components are omitted. Note that:
|
||||
|
||||
- Git over SSH requires [`gitlab-shell`](https://gitlab.com/gitlab-org/gitlab-shell) and OpenSSH.
|
||||
- Git over HTTPS required [`gitlab-workhorse`](https://gitlab.com/gitlab-org/gitlab-workhorse).
|
||||
|
||||
Note that a **secondary** node needs two different PostgreSQL databases:
|
||||
Note that a **secondary** site needs two different PostgreSQL databases:
|
||||
|
||||
- A read-only database instance that streams data from the main GitLab database.
|
||||
- [Another database instance](#geo-tracking-database) used internally by the **secondary** node to record what data has been replicated.
|
||||
- [Another database instance](#geo-tracking-database) used internally by the **secondary** site to record what data has been replicated.
|
||||
|
||||
In **secondary** nodes, there is an additional daemon: [Geo Log Cursor](#geo-log-cursor).
|
||||
In **secondary** sites, there is an additional daemon: [Geo Log Cursor](#geo-log-cursor).
|
||||
|
||||
## Requirements for running Geo
|
||||
|
||||
|
@ -122,7 +124,7 @@ The following are required to run Geo:
|
|||
- PostgreSQL 11+ with [Streaming Replication](https://wiki.postgresql.org/wiki/Streaming_Replication)
|
||||
- Git 2.9+
|
||||
- Git-lfs 2.4.2+ on the user side when using LFS
|
||||
- All nodes must run the same GitLab version.
|
||||
- All sites must run the same GitLab version.
|
||||
|
||||
Additionally, check the GitLab [minimum requirements](../../install/requirements.md),
|
||||
and we recommend you use:
|
||||
|
@ -132,9 +134,9 @@ and we recommend you use:
|
|||
|
||||
### Firewall rules
|
||||
|
||||
The following table lists basic ports that must be open between the **primary** and **secondary** nodes for Geo.
|
||||
The following table lists basic ports that must be open between the **primary** and **secondary** sites for Geo.
|
||||
|
||||
| **Primary** node | **Secondary** node | Protocol |
|
||||
| **Primary** site | **Secondary** site | Protocol |
|
||||
|:-----------------|:-------------------|:-------------|
|
||||
| 80 | 80 | HTTP |
|
||||
| 443 | 443 | TCP or HTTPS |
|
||||
|
@ -153,10 +155,10 @@ If you wish to terminate SSL at the GitLab application server instead, use TCP p
|
|||
|
||||
### LDAP
|
||||
|
||||
We recommend that if you use LDAP on your **primary** node, you also set up secondary LDAP servers on each **secondary** node. Otherwise, users will not be able to perform Git operations over HTTP(s) on the **secondary** node using HTTP Basic Authentication. However, Git via SSH and personal access tokens will still work.
|
||||
We recommend that if you use LDAP on your **primary** site, you also set up secondary LDAP servers on each **secondary** site. Otherwise, users will not be able to perform Git operations over HTTP(s) on the **secondary** site using HTTP Basic Authentication. However, Git via SSH and personal access tokens will still work.
|
||||
|
||||
NOTE:
|
||||
It is possible for all **secondary** nodes to share an LDAP server, but additional latency can be an issue. Also, consider what LDAP server will be available in a [disaster recovery](disaster_recovery/index.md) scenario if a **secondary** node is promoted to be a **primary** node.
|
||||
It is possible for all **secondary** sites to share an LDAP server, but additional latency can be an issue. Also, consider what LDAP server will be available in a [disaster recovery](disaster_recovery/index.md) scenario if a **secondary** site is promoted to be a **primary** site.
|
||||
|
||||
Check for instructions on how to set up replication in your LDAP service. Instructions will be different depending on the software or service used. For example, OpenLDAP provides [these instructions](https://www.openldap.org/doc/admin24/replication.html).
|
||||
|
||||
|
@ -168,32 +170,32 @@ The tracking database instance is used as metadata to control what needs to be u
|
|||
- Fetch new LFS Objects.
|
||||
- Fetch changes from a repository that has recently been updated.
|
||||
|
||||
Because the replicated database instance is read-only, we need this additional database instance for each **secondary** node.
|
||||
Because the replicated database instance is read-only, we need this additional database instance for each **secondary** site.
|
||||
|
||||
### Geo Log Cursor
|
||||
|
||||
This daemon:
|
||||
|
||||
- Reads a log of events replicated by the **primary** node to the **secondary** database instance.
|
||||
- Reads a log of events replicated by the **primary** site to the **secondary** database instance.
|
||||
- Updates the Geo Tracking Database instance with changes that need to be executed.
|
||||
|
||||
When something is marked to be updated in the tracking database instance, asynchronous jobs running on the **secondary** node will execute the required operations and update the state.
|
||||
When something is marked to be updated in the tracking database instance, asynchronous jobs running on the **secondary** site will execute the required operations and update the state.
|
||||
|
||||
This new architecture allows GitLab to be resilient to connectivity issues between the nodes. It doesn't matter how long the **secondary** node is disconnected from the **primary** node as it will be able to replay all the events in the correct order and become synchronized with the **primary** node again.
|
||||
This new architecture allows GitLab to be resilient to connectivity issues between the sites. It doesn't matter how long the **secondary** site is disconnected from the **primary** site as it will be able to replay all the events in the correct order and become synchronized with the **primary** site again.
|
||||
|
||||
## Limitations
|
||||
|
||||
WARNING:
|
||||
This list of limitations only reflects the latest version of GitLab. If you are using an older version, extra limitations may be in place.
|
||||
|
||||
- Pushing directly to a **secondary** node redirects (for HTTP) or proxies (for SSH) the request to the **primary** node instead of [handling it directly](https://gitlab.com/gitlab-org/gitlab/-/issues/1381), except when using Git over HTTP with credentials embedded within the URI. For example, `https://user:password@secondary.tld`.
|
||||
- The **primary** node has to be online for OAuth login to happen. Existing sessions and Git are not affected. Support for the **secondary** node to use an OAuth provider independent from the primary is [being planned](https://gitlab.com/gitlab-org/gitlab/-/issues/208465).
|
||||
- Pushing directly to a **secondary** site redirects (for HTTP) or proxies (for SSH) the request to the **primary** site instead of [handling it directly](https://gitlab.com/gitlab-org/gitlab/-/issues/1381), except when using Git over HTTP with credentials embedded within the URI. For example, `https://user:password@secondary.tld`.
|
||||
- The **primary** site has to be online for OAuth login to happen. Existing sessions and Git are not affected. Support for the **secondary** site to use an OAuth provider independent from the primary is [being planned](https://gitlab.com/gitlab-org/gitlab/-/issues/208465).
|
||||
- The installation takes multiple manual steps that together can take about an hour depending on circumstances. We are working on improving this experience. See [Omnibus GitLab issue #2978](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/2978) for details.
|
||||
- Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** node.
|
||||
- [Selective synchronization](replication/configuration.md#selective-synchronization) applies only to files and repositories. Other datasets are replicated to the **secondary** node in full, making it inappropriate for use as an access control mechanism.
|
||||
- Object pools for forked project deduplication work only on the **primary** node, and are duplicated on the **secondary** node.
|
||||
- GitLab Runners cannot register with a **secondary** node. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294).
|
||||
- Geo **secondary** nodes can not be configured to [use high-availability configurations of PostgreSQL](https://gitlab.com/groups/gitlab-org/-/epics/2536).
|
||||
- Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** site.
|
||||
- [Selective synchronization](replication/configuration.md#selective-synchronization) applies only to files and repositories. Other datasets are replicated to the **secondary** site in full, making it inappropriate for use as an access control mechanism.
|
||||
- Object pools for forked project deduplication work only on the **primary** site, and are duplicated on the **secondary** site.
|
||||
- GitLab Runners cannot register with a **secondary** site. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294).
|
||||
- Configuring Geo **secondary** sites to [use high-availability configurations of PostgreSQL](https://gitlab.com/groups/gitlab-org/-/epics/2536) is currently in **alpha** support.
|
||||
- [Selective synchronization](replication/configuration.md#selective-synchronization) only limits what repositories are replicated. The entire PostgreSQL data is still replicated. Selective synchronization is not built to accomodate compliance / export control use cases.
|
||||
|
||||
### Limitations on replication/verification
|
||||
|
@ -206,7 +208,7 @@ For setup instructions, see [Setting up Geo](setup/index.md).
|
|||
|
||||
## Post-installation documentation
|
||||
|
||||
After installing GitLab on the **secondary** nodes and performing the initial configuration, see the following documentation for post-installation information.
|
||||
After installing GitLab on the **secondary** site(s) and performing the initial configuration, see the following documentation for post-installation information.
|
||||
|
||||
### Configuring Geo
|
||||
|
||||
|
@ -214,16 +216,16 @@ For information on configuring Geo, see [Geo configuration](replication/configur
|
|||
|
||||
### Updating Geo
|
||||
|
||||
For information on how to update your Geo nodes to the latest GitLab version, see [Updating the Geo nodes](replication/updating_the_geo_nodes.md).
|
||||
For information on how to update your Geo site(s) to the latest GitLab version, see [Updating the Geo sites](replication/updating_the_geo_nodes.md).
|
||||
|
||||
### Pausing and resuming replication
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35913) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
|
||||
|
||||
WARNING:
|
||||
In GitLab 13.2 and 13.3, promoting a secondary node to a primary while the
|
||||
In GitLab 13.2 and 13.3, promoting a secondary site to a primary while the
|
||||
secondary is paused fails. Do not pause replication before promoting a
|
||||
secondary. If the node is paused, be sure to resume before promoting. This
|
||||
secondary. If the site is paused, be sure to resume before promoting. This
|
||||
issue has been fixed in GitLab 13.4 and later.
|
||||
|
||||
WARNING:
|
||||
|
@ -232,7 +234,7 @@ Omnibus GitLab-managed database. External databases are currently not supported.
|
|||
|
||||
In some circumstances, like during [upgrades](replication/updating_the_geo_nodes.md) or a [planned failover](disaster_recovery/planned_failover.md), it is desirable to pause replication between the primary and secondary.
|
||||
|
||||
Pausing and resuming replication is done via a command line tool from the secondary node where the `postgresql` service is enabled.
|
||||
Pausing and resuming replication is done via a command line tool from the a node in the secondary site where the `postgresql` service is enabled.
|
||||
|
||||
If `postgresql` is on a standalone database node, ensure that `gitlab.rb` on that node contains the configuration line `gitlab_rails['geo_node_name'] = 'node_name'`, where `node_name` is the same as the `geo_name_name` on the application node.
|
||||
|
||||
|
@ -262,7 +264,7 @@ For information on using Geo in disaster recovery situations to mitigate data-lo
|
|||
|
||||
### Replicating the Container Registry
|
||||
|
||||
For more information on how to replicate the Container Registry, see [Docker Registry for a **secondary** node](replication/docker_registry.md).
|
||||
For more information on how to replicate the Container Registry, see [Docker Registry for a **secondary** site](replication/docker_registry.md).
|
||||
|
||||
### Security Review
|
||||
|
||||
|
@ -278,9 +280,9 @@ For an example of how to set up a location-aware Git remote URL with AWS Route53
|
|||
|
||||
### Backfill
|
||||
|
||||
Once a **secondary** node is set up, it will start replicating missing data from
|
||||
the **primary** node in a process known as **backfill**. You can monitor the
|
||||
synchronization process on each Geo node from the **primary** node's **Geo Nodes**
|
||||
Once a **secondary** site is set up, it will start replicating missing data from
|
||||
the **primary** site in a process known as **backfill**. You can monitor the
|
||||
synchronization process on each Geo site from the **primary** site's **Geo Nodes**
|
||||
dashboard in your browser.
|
||||
|
||||
Failures that happen during a backfill are scheduled to be retried at the end
|
||||
|
@ -288,7 +290,7 @@ of the backfill.
|
|||
|
||||
## Remove Geo site
|
||||
|
||||
For more information on removing a Geo node, see [Removing **secondary** Geo nodes](replication/remove_geo_site.md).
|
||||
For more information on removing a Geo site, see [Removing **secondary** Geo sites](replication/remove_geo_site.md).
|
||||
|
||||
## Disable Geo
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ code snippet right after your description in a new line: `~feature`.
|
|||
Please keep feature proposals as small and simple as possible, complex ones
|
||||
might be edited to make them small and simple.
|
||||
|
||||
Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/issue_templates/Feature%20proposal.md) provided on the issue tracker.
|
||||
Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) provided on the issue tracker.
|
||||
|
||||
For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
|
||||
be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
|
||||
|
|
|
@ -114,9 +114,9 @@ Prerequisites:
|
|||
To create an issue:
|
||||
|
||||
1. Go to **Issues > List**.
|
||||
1. In the top right, click **New issue**.
|
||||
1. In the top right, select **New issue**.
|
||||
1. Complete the fields. (If you have a reference topic that lists each field, link to it here.)
|
||||
1. Click **Create issue**.
|
||||
1. Select **Create issue**.
|
||||
|
||||
The issue is created. You can view it by going to **Issues > List**.
|
||||
```
|
||||
|
|
|
@ -198,7 +198,7 @@ upgrade paths.
|
|||
| Target version | Your version | Supported upgrade path | Note |
|
||||
| --------------------- | ------------ | ------------------------ | ---- |
|
||||
| `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: the final `12.10` release, plus `13.0` and `13.1`. |
|
||||
| `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.2.10` | Five intermediate versions are required: the final `11.11`, `12.0`, `12.1` and `12.10` releases, plus `13.0`. |
|
||||
| `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: the final `11.11`, `12.0`, `12.1`, `12.10`, `13.0` releases, plus `13.1`. |
|
||||
| `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: the final `11.11` and `12.0` releases, plus `12.1` |
|
||||
| `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`. |
|
||||
|
|
|
@ -7,17 +7,51 @@ type: reference, howto
|
|||
|
||||
# Web API Fuzz Testing **(ULTIMATE)**
|
||||
|
||||
You can add web API fuzzing to your [GitLab CI/CD](../../../ci/README.md)
|
||||
pipelines. This helps you discover bugs and potential security issues that other QA processes may
|
||||
miss. API fuzzing performs fuzz testing of API operation parameters. Fuzz testing sets operation
|
||||
Web API fuzzing performs fuzz testing of API operation parameters. Fuzz testing sets operation
|
||||
parameters to unexpected values in an effort to cause unexpected behavior and errors in the API
|
||||
backend.
|
||||
backend. This helps you discover bugs and potential security issues that other QA processes may
|
||||
miss.
|
||||
|
||||
We recommend that you use fuzz testing in addition to [GitLab Secure](../index.md)'s
|
||||
other security scanners and your own test processes. If you're using [GitLab CI/CD](../../../ci/README.md),
|
||||
you can run fuzz tests as part your CI/CD workflow.
|
||||
|
||||
## Requirements
|
||||
## When Web API fuzzing runs
|
||||
|
||||
Web API fuzzing runs in the `fuzz` stage of the CI/CD pipeline. To ensure API fuzzing scans the
|
||||
latest code, your CI/CD pipeline should deploy changes to a test environment in one of the stages
|
||||
preceding the `fuzz` stage.
|
||||
|
||||
Note the following changes have been made to the API fuzzing template:
|
||||
|
||||
- In GitLab 14.0 and later, you must define a `fuzz` stage in your `.gitlab-ci.yml` file.
|
||||
- In GitLab 13.12 and earlier, the API fuzzing template defines `build`, `test`, `deploy`, and
|
||||
`fuzz` stages. The `fuzz` stage runs last by default. The predefined stages were deprecated, and removed from the `API-Fuzzing.latest.gitlab-ci.yml` template. They will be removed in a future GitLab
|
||||
version.
|
||||
|
||||
If your pipeline is configured to deploy to the same web server on each run, running a
|
||||
pipeline while another is still running could cause a race condition in which one pipeline
|
||||
overwrites the code from another. The API to scan should be excluded from changes for the duration
|
||||
of a fuzzing scan. The only changes to the API should be from the fuzzing scanner. Any changes made
|
||||
to the API (for example, by users, scheduled tasks, database changes, code changes, other pipelines,
|
||||
or other scanners) during a scan could cause inaccurate results.
|
||||
|
||||
You can run a Web API fuzzing scan using the following methods:
|
||||
|
||||
- [OpenAPI Specification](#openapi-specification) - version 2.0 or 3.0
|
||||
- [HTTP Archive](#http-archive-har) (HAR)
|
||||
- [Postman Collection](#postman-collection) - version 2.0 or 2.1
|
||||
|
||||
Example projects using these methods are available:
|
||||
|
||||
- [Example OpenAPI v2 Specification project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing-example/-/tree/openapi)
|
||||
- [Example HTTP Archive (HAR) project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing-example/-/tree/har)
|
||||
- [Example Postman Collection project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing/postman-api-fuzzing-example)
|
||||
- [Example GraphQL project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing/graphql-api-fuzzing-example)
|
||||
|
||||
## Enable Web API fuzzing
|
||||
|
||||
Requirements:
|
||||
|
||||
- One of the following web API types:
|
||||
- REST API
|
||||
|
@ -29,49 +63,26 @@ you can run fuzz tests as part your CI/CD workflow.
|
|||
- HTTP Archive (HAR) of API requests to test
|
||||
- Postman Collection v2.0 or v2.1
|
||||
|
||||
## When fuzzing scans run
|
||||
WARNING:
|
||||
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
|
||||
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
|
||||
data. Only run fuzzing against a test server.
|
||||
|
||||
When using the `API-Fuzzing.gitlab-ci.yml` template, the `fuzz` job runs last, as shown here. To
|
||||
ensure API fuzzing scans the latest code, your CI pipeline should deploy changes to a test
|
||||
environment in one of the jobs preceding the `fuzz` job:
|
||||
To enable Web API fuzzing:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- fuzz
|
||||
```
|
||||
- Include the API fuzzing template in your `.gitlab-ci.yml` file.
|
||||
- From GitLab 13.10 and later, use the Web API fuzzing configuration form.
|
||||
|
||||
Note that if your pipeline is configured to deploy to the same web server on each run, running a
|
||||
pipeline while another is still running could cause a race condition in which one pipeline
|
||||
overwrites the code from another. The API to scan should be excluded from changes for the duration
|
||||
of a fuzzing scan. The only changes to the API should be from the fuzzing scanner. Be aware that
|
||||
any changes made to the API (for example, by users, scheduled tasks, database changes, code
|
||||
changes, other pipelines, or other scanners) during a scan could cause inaccurate results.
|
||||
- For manual configuration instructions, see the respective section, depending on the API type:
|
||||
- [OpenAPI Specification](#openapi-specification)
|
||||
- [HTTP Archive (HAR)](#http-archive-har)
|
||||
- [Postman Collection](#postman-collection)
|
||||
- Otherwise, see [Web API fuzzing configuration form](#web-api-fuzzing-configuration-form).
|
||||
|
||||
## Configuration
|
||||
In GitLab 14.0 and later, API fuzzing configuration files must be in your repository's
|
||||
`.gitlab` directory instead of your repository's root.
|
||||
|
||||
There are three ways to perform scans. See the configuration section for the one you wish to use:
|
||||
|
||||
- [OpenAPI v2 or v3 specification](#openapi-specification)
|
||||
- [HTTP Archive (HAR)](#http-archive-har)
|
||||
- [Postman Collection v2.0 or v2.1](#postman-collection)
|
||||
|
||||
Examples of both configurations can be found here:
|
||||
|
||||
- [Example OpenAPI v2 specification project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing-example/-/tree/openapi)
|
||||
- [Example HTTP Archive (HAR) project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing-example/-/tree/har)
|
||||
- [Example Postman Collection project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing/postman-api-fuzzing-example)
|
||||
- [Example GraphQL project](https://gitlab.com/gitlab-org/security-products/demos/api-fuzzing/graphql-api-fuzzing-example)
|
||||
|
||||
WARNING:
|
||||
GitLab 14.0 will require that you place API fuzzing configuration files (for example,
|
||||
`gitlab-api-fuzzing-config.yml`) in your repository's `.gitlab` directory instead of your
|
||||
repository's root. You can continue using your existing configuration files as they are, but
|
||||
starting in GitLab 14.0, GitLab will not check your repository's root for configuration files.
|
||||
|
||||
### Configuration form
|
||||
### Web API fuzzing configuration form
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299234) in GitLab 13.10.
|
||||
|
||||
|
@ -82,6 +93,8 @@ The API fuzzing configuration form helps you create or modify your project's API
|
|||
configuration. The form lets you choose values for the most common API fuzzing options and builds
|
||||
a YAML snippet that you can paste in your GitLab CI/CD configuration.
|
||||
|
||||
#### Configure Web API fuzzing with the configuration form
|
||||
|
||||
To generate an API Fuzzing configuration snippet:
|
||||
|
||||
1. From your project's home page, go to **Security & Compliance > Configuration** in the left
|
||||
|
@ -89,15 +102,13 @@ To generate an API Fuzzing configuration snippet:
|
|||
1. Select **Configure** in the **API Fuzzing** row.
|
||||
1. Complete the form as needed. Read below for more information on available configuration options.
|
||||
1. Select **Generate code snippet**.
|
||||
|
||||
A modal opens with the YAML snippet corresponding to the options you've selected in the form.
|
||||
|
||||
![API Fuzzing configuration snippet](img/api_fuzzing_configuration_snippet_v13.10.png)
|
||||
|
||||
Select **Copy code and open `.gitlab-ci.yml` file** to copy the snippet to your clipboard and be redirected
|
||||
to your project's `.gitlab-ci.yml` file where you can paste the YAML configuration.
|
||||
|
||||
Select **Copy code only** to copy the snippet to your clipboard and close the modal.
|
||||
A modal opens with the YAML snippet corresponding to the options you've selected in the form.
|
||||
![API Fuzzing configuration snippet](img/api_fuzzing_configuration_snippet_v13.10.png)
|
||||
1. Choose one of the following actions:
|
||||
1. Select **Copy code and open `.gitlab-ci.yml` file** to copy the snippet to your clipboard and
|
||||
be redirected to your project's `.gitlab-ci.yml` file where you can paste the YAML
|
||||
configuration.
|
||||
1. Select **Copy code only** to copy the snippet to your clipboard and close the modal.
|
||||
|
||||
### OpenAPI Specification
|
||||
|
||||
|
@ -106,7 +117,7 @@ Select **Copy code only** to copy the snippet to your clipboard and close the mo
|
|||
|
||||
The [OpenAPI Specification](https://www.openapis.org/) (formerly the Swagger Specification) is an
|
||||
API description format for REST APIs. This section shows you how to configure API fuzzing by using
|
||||
an OpenAPI specification to provide information about the target API to test. OpenAPI specifications
|
||||
an OpenAPI Specification to provide information about the target API to test. OpenAPI Specifications
|
||||
are provided as a file system resource or URL.
|
||||
|
||||
API fuzzing uses an OpenAPI document to generate the request body. When a request body is required,
|
||||
|
@ -116,57 +127,42 @@ the body generation is limited to these body types:
|
|||
- `multipart/form-data`
|
||||
- `application/json`
|
||||
|
||||
Follow these steps to configure API fuzzing in GitLab with an OpenAPI specification:
|
||||
#### Configure Web API fuzzing with an OpenAPI Specification
|
||||
|
||||
1. To use API fuzzing, you must [include](../../../ci/yaml/README.md#includetemplate)
|
||||
To configure API fuzzing in GitLab with an OpenAPI Specification:
|
||||
|
||||
1. Add the `fuzz` stage to your `.gitlab-ci.yml` file.
|
||||
|
||||
1. [Include](../../../ci/yaml/README.md#includetemplate)
|
||||
the [`API-Fuzzing.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml)
|
||||
that's provided as part of your GitLab installation. To do so, add the following to your
|
||||
`.gitlab-ci.yml` file:
|
||||
in your `.gitlab-ci.yml` file.
|
||||
|
||||
1. Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file.
|
||||
The profile specifies how many tests are run. Substitute `Quick-10` for the profile you choose. For more details, see [API fuzzing profiles](#api-fuzzing-profiles).
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
```
|
||||
|
||||
1. The [configuration file](#configuration-files) has several testing profiles defined with varying
|
||||
amounts of fuzzing. We recommend that you start with the `Quick-10` profile. Testing with this
|
||||
profile completes quickly, allowing for easier configuration validation.
|
||||
|
||||
Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file,
|
||||
substituting `Quick-10` for the profile you choose:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
```
|
||||
|
||||
1. Provide the location of the OpenAPI specification. You can provide the specification as a file
|
||||
or URL. Specify the location by adding the `FUZZAPI_OPENAPI` variable:
|
||||
1. Provide the location of the OpenAPI Specification. You can provide the specification as a file
|
||||
or URL. Specify the location by adding the `FUZZAPI_OPENAPI` variable.
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
FUZZAPI_OPENAPI: test-api-specification.json
|
||||
```
|
||||
|
||||
1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
|
||||
variable or an `environment_url.txt` file.
|
||||
1. Provide the target API instance's base URL. Use either the `FUZZAPI_TARGET_URL` variable or an
|
||||
`environment_url.txt` file.
|
||||
|
||||
Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
|
||||
dynamic environments. To run API fuzzing against an app dynamically created during a GitLab CI/CD
|
||||
pipeline, have the app persist its domain in an `environment_url.txt` file. API fuzzing
|
||||
automatically parses that file to find its scan target. You can see an
|
||||
[example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
dynamic environments. To run API fuzzing against an application dynamically created during a
|
||||
GitLab CI/CD pipeline, have the application persist its URL in an `environment_url.txt` file.
|
||||
API fuzzing automatically parses that file to find its scan target. You can see an
|
||||
example of this in the [Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
|
||||
Here's an example of using `FUZZAPI_TARGET_URL`:
|
||||
Example `.gitlab-ci.yml` file using an OpenAPI Specification:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -182,10 +178,7 @@ This is a minimal configuration for API Fuzzing. From here you can:
|
|||
- [Add authentication](#authentication).
|
||||
- Learn how to [handle false positives](#handling-false-positives).
|
||||
|
||||
WARNING:
|
||||
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
|
||||
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
|
||||
data. Only run fuzzing against a test server.
|
||||
For details of API fuzzing configuration options, see [Available CI/CD variables](#available-cicd-variables).
|
||||
|
||||
### HTTP Archive (HAR)
|
||||
|
||||
|
@ -200,45 +193,27 @@ WARNING:
|
|||
HAR files may contain sensitive information such as authentication tokens, API keys, and session
|
||||
cookies. We recommend that you review the HAR file contents before adding them to a repository.
|
||||
|
||||
Follow these steps to configure API fuzzing to use a HAR file that provides information about the
|
||||
target API to test:
|
||||
#### Configure Web API fuzzing with a HAR file
|
||||
|
||||
1. To use API fuzzing, you must [include](../../../ci/yaml/README.md#includetemplate)
|
||||
To configure API fuzzing to use a HAR file:
|
||||
|
||||
1. Add the `fuzz` stage to your `.gitlab-ci.yml` file.
|
||||
|
||||
1. [Include](../../../ci/yaml/README.md#includetemplate)
|
||||
the [`API-Fuzzing.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml)
|
||||
that's provided as part of your GitLab installation. To do so, add the following to your
|
||||
`.gitlab-ci.yml` file:
|
||||
in your `.gitlab-ci.yml` file.
|
||||
|
||||
1. Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file.
|
||||
The profile specifies how many tests are run. Substitute `Quick-10` for the profile you choose. For more details, see [API fuzzing profiles](#api-fuzzing-profiles).
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
```
|
||||
|
||||
1. The [configuration file](#configuration-files) has several testing profiles defined with varying
|
||||
amounts of fuzzing. We recommend that you start with the `Quick-10` profile. Testing with this
|
||||
profile completes quickly, allowing for easier configuration validation.
|
||||
|
||||
Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file,
|
||||
substituting `Quick-10` for the profile you choose:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
```
|
||||
|
||||
1. Provide the location of the HAR specification. You can provide the specification as a file
|
||||
or URL. [URL support was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285020) in GitLab 13.10 and later. Specify the location by adding the `FUZZAPI_HAR` variable:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
FUZZAPI_HAR: test-api-recording.har
|
||||
```
|
||||
or URL. [URL support was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285020)
|
||||
in GitLab 13.10 and later. Specify the location by adding the `FUZZAPI_HAR` variable.
|
||||
|
||||
1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
|
||||
variable or an `environment_url.txt` file.
|
||||
|
@ -249,9 +224,12 @@ target API to test:
|
|||
automatically parses that file to find its scan target. You can see an
|
||||
[example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
|
||||
Here's an example of using `FUZZAPI_TARGET_URL`:
|
||||
Example `.gitlab-ci.yml` file using a HAR file:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -261,16 +239,13 @@ target API to test:
|
|||
FUZZAPI_TARGET_URL: http://test-deployment/
|
||||
```
|
||||
|
||||
This is a minimal configuration for API Fuzzing. From here you can:
|
||||
This is a minimal configuration for API fuzzing. From here you can:
|
||||
|
||||
- [Run your first scan](#running-your-first-scan).
|
||||
- [Add authentication](#authentication).
|
||||
- Learn how to [handle false positives](#handling-false-positives).
|
||||
|
||||
WARNING:
|
||||
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
|
||||
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
|
||||
data. Only run fuzzing against a test server.
|
||||
For details of API fuzzing configuration options, see [Available CI/CD variables](#available-cicd-variables).
|
||||
|
||||
### Postman Collection
|
||||
|
||||
|
@ -289,48 +264,31 @@ Postman Collection files may contain sensitive information such as authenticatio
|
|||
and session cookies. We recommend that you review the Postman Collection file contents before adding
|
||||
them to a repository.
|
||||
|
||||
Follow these steps to configure API fuzzing to use a Postman Collection file that provides
|
||||
information about the target API to test:
|
||||
#### Configure Web API fuzzing with a Postman Collection file
|
||||
|
||||
1. To use API fuzzing, you must [include](../../../ci/yaml/README.md#includetemplate)
|
||||
To configure API fuzzing to use a Postman Collection file:
|
||||
|
||||
1. Add the `fuzz` stage to your `.gitlab-ci.yml` file.
|
||||
|
||||
1. [Include](../../../ci/yaml/README.md#includetemplate)
|
||||
the [`API-Fuzzing.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml)
|
||||
that's provided as part of your GitLab installation. To do so, add the following to your
|
||||
`.gitlab-ci.yml` file:
|
||||
in your `.gitlab-ci.yml` file.
|
||||
|
||||
1. Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file.
|
||||
The profile specifies how many tests are run. Substitute `Quick-10` for the profile you choose. For more details, see [API fuzzing profiles](#api-fuzzing-profiles).
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
```
|
||||
|
||||
1. The [configuration file](#configuration-files) has several testing profiles defined with varying
|
||||
amounts of fuzzing. We recommend that you start with the `Quick-10` profile. Testing with this
|
||||
profile completes quickly, allowing for easier configuration validation.
|
||||
|
||||
Provide the profile by adding the `FUZZAPI_PROFILE` CI/CD variable to your `.gitlab-ci.yml` file,
|
||||
substituting `Quick-10` for the profile you choose:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
```
|
||||
|
||||
1. Provide the location of the Postman Collection specification. You can provide the specification as a file
|
||||
or URL. [URL support was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285020) in GitLab 13.10 and later. Specify the location by adding the `FUZZAPI_POSTMAN_COLLECTION` variable:
|
||||
1. Provide the location of the Postman Collection specification. You can provide the specification
|
||||
as a file or URL. [URL support was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285020)
|
||||
in GitLab 13.10 and later. Specify the location by adding the `FUZZAPI_POSTMAN_COLLECTION`
|
||||
variable.
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
FUZZAPI_PROFILE: Quick-10
|
||||
FUZZAPI_POSTMAN_COLLECTION: postman-collection_serviceA.json
|
||||
```
|
||||
|
||||
1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
|
||||
variable or an `environment_url.txt` file.
|
||||
1. Provide the target API instance's base URL. Use either the `FUZZAPI_TARGET_URL` variable or an
|
||||
`environment_url.txt` file.
|
||||
|
||||
Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
|
||||
dynamic environments. To run API fuzzing against an app dynamically created during a GitLab CI/CD
|
||||
|
@ -338,9 +296,12 @@ information about the target API to test:
|
|||
automatically parses that file to find its scan target. You can see an
|
||||
[example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
|
||||
Here's an example of using `FUZZAPI_TARGET_URL`:
|
||||
Example `.gitlab-ci.yml` file using a Postman Collection file:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -356,15 +317,13 @@ This is a minimal configuration for API Fuzzing. From here you can:
|
|||
- [Add authentication](#authentication).
|
||||
- Learn how to [handle false positives](#handling-false-positives).
|
||||
|
||||
WARNING:
|
||||
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
|
||||
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
|
||||
data. Only run fuzzing against a test server.
|
||||
For details of API fuzzing configuration options, see [Available CI/CD variables](#available-cicd-variables).
|
||||
|
||||
#### Postman variables
|
||||
|
||||
Postman allows the developer to define placeholders that can be used in different parts of the
|
||||
requests. These placeholders are called variables, as explained in [Using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
requests. These placeholders are called variables, as explained in the Postman documentation,
|
||||
[Using variables](https://learning.postman.com/docs/sending-requests/variables/).
|
||||
You can use variables to store and reuse values in your requests and scripts. For example, you can
|
||||
edit the collection to add variables to the document:
|
||||
|
||||
|
@ -385,7 +344,7 @@ Postman file. For example, Postman does not export environment-scoped variables
|
|||
file.
|
||||
|
||||
By default, the API fuzzer uses the Postman file to resolve Postman variable values. If a JSON file
|
||||
is set in a GitLab CI environment variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`, then the JSON
|
||||
is set in a GitLab CI/CD variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`, then the JSON
|
||||
file takes precedence to get Postman variable values.
|
||||
|
||||
Although Postman can export environment variables into a JSON file, the format is not compatible
|
||||
|
@ -394,6 +353,9 @@ with the JSON expected by `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`.
|
|||
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -415,6 +377,13 @@ values. For example:
|
|||
}
|
||||
```
|
||||
|
||||
## API fuzzing configuration
|
||||
|
||||
The API fuzzing behavior can be changed through CI/CD variables.
|
||||
|
||||
From GitLab 13.12 and later, the default API fuzzing configuration file is `.gitlab/gitlab-api-fuzzing-config.yml`. In GitLab 14.0 and later, API fuzzing configuration files must be in your repository's
|
||||
`.gitlab` directory instead of your repository's root.
|
||||
|
||||
### Authentication
|
||||
|
||||
Authentication is handled by providing the authentication token as a header or cookie. You can
|
||||
|
@ -436,6 +405,9 @@ GitLab projects page at **Settings > CI/CD**, in the **Variables** section. Use
|
|||
as the value for `FUZZAPI_HTTP_PASSWORD`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -474,6 +446,9 @@ Follow these steps to provide the bearer token with `FUZZAPI_OVERRIDES_ENV`:
|
|||
1. In your `.gitlab-ci.yml` file, set `FUZZAPI_OVERRIDES_ENV` to the variable you just created:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -509,6 +484,9 @@ This file can be generated by a prior stage and provided to API fuzzing through
|
|||
Set `FUZZAPI_OVERRIDES_FILE` in your `.gitlab-ci.yml` file:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -548,6 +526,9 @@ You must provide three CI/CD variables, each set for correct operation:
|
|||
For example:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -563,19 +544,15 @@ variables:
|
|||
To validate that authentication is working, run an API fuzzing test and review the fuzzing logs and
|
||||
the test API's application logs.
|
||||
|
||||
### Configuration files
|
||||
### API fuzzing profiles
|
||||
|
||||
To get you started quickly, GitLab provides the configuration file
|
||||
GitLab provides the configuration file
|
||||
[`gitlab-api-fuzzing-config.yml`](https://gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing/-/blob/master/gitlab-api-fuzzing-config.yml).
|
||||
This file has several testing profiles that perform various numbers of tests. The run time of each
|
||||
profile increases as the test numbers go up. To use a configuration file, add it to your
|
||||
repository as `.gitlab/gitlab-api-fuzzing-config.yml`.
|
||||
It contains several testing profiles that perform a specific numbers of tests. The runtime of each
|
||||
profile increases as the number of tests increases.
|
||||
|
||||
NOTE:
|
||||
+In GitLab 13.11 and earlier, the configuration file was `.gitlab-api-fuzzing.yml` in the repository's root. In GitLab 13.12 and later, it is `.gitlab/gitlab-api-fuzzing-config.yml` in the repository's root.
|
||||
|
||||
| Profile | Fuzz Tests (per parameter) |
|
||||
|:---------|:-----------|
|
||||
| Profile | Fuzz Tests (per parameter) |
|
||||
|:----------|:---------------------------|
|
||||
| Quick-10 | 10 |
|
||||
| Medium-20 | 20 |
|
||||
| Medium-50 | 50 |
|
||||
|
@ -583,23 +560,23 @@ NOTE:
|
|||
|
||||
### Available CI/CD variables
|
||||
|
||||
| CI/CD variable | Description |
|
||||
|------------------------------------------------------|--------------------|
|
||||
| `SECURE_ANALYZERS_PREFIX` | Specify the Docker registry base address from which to download the analyzer. |
|
||||
| `FUZZAPI_VERSION` | Specify API Fuzzing container version. Defaults to `latest`. |
|
||||
| `FUZZAPI_TARGET_URL` | Base URL of API testing target. |
|
||||
|[`FUZZAPI_CONFIG`](#configuration-files) | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/276395) in GitLab 13.12, replaced with default `.gitlab/gitlab-api-fuzzing-config.yml`. API Fuzzing configuration file. |
|
||||
|[`FUZZAPI_PROFILE`](#configuration-files) | Configuration profile to use during testing. Defaults to `Quick`. |
|
||||
|[`FUZZAPI_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. |
|
||||
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract postman variable values. |
|
||||
|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|
||||
|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|
||||
|[`FUZZAPI_OVERRIDES_CMD`](#overrides) | Overrides command. |
|
||||
|[`FUZZAPI_OVERRIDES_INTERVAL`](#overrides) | How often to run overrides command in seconds. Defaults to `0` (once). |
|
||||
|[`FUZZAPI_HTTP_USERNAME`](#http-basic-authentication) | Username for HTTP authentication. |
|
||||
|[`FUZZAPI_HTTP_PASSWORD`](#http-basic-authentication) | Password for HTTP authentication. |
|
||||
| CI/CD variable | Description |
|
||||
|-------------------------------------------------------------|-------------|
|
||||
| `SECURE_ANALYZERS_PREFIX` | Specify the Docker registry base address from which to download the analyzer. |
|
||||
| `FUZZAPI_VERSION` | Specify API Fuzzing container version. Defaults to `latest`. |
|
||||
| `FUZZAPI_TARGET_URL` | Base URL of API testing target. |
|
||||
| `FUZZAPI_CONFIG` | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/276395) in GitLab 13.12, replaced with default `.gitlab/gitlab-api-fuzzing-config.yml`. API Fuzzing configuration file. |
|
||||
|[`FUZZAPI_PROFILE`](#api-fuzzing-profiles) | Configuration profile to use during testing. Defaults to `Quick-10`. |
|
||||
|[`FUZZAPI_OPENAPI`](#openapi-specification) | OpenAPI Specification file or URL. |
|
||||
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
|
||||
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. |
|
||||
|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|
||||
|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|
||||
|[`FUZZAPI_OVERRIDES_CMD`](#overrides) | Overrides command. |
|
||||
|[`FUZZAPI_OVERRIDES_INTERVAL`](#overrides) | How often to run overrides command in seconds. Defaults to `0` (once). |
|
||||
|[`FUZZAPI_HTTP_USERNAME`](#http-basic-authentication) | Username for HTTP authentication. |
|
||||
|[`FUZZAPI_HTTP_PASSWORD`](#http-basic-authentication) | Password for HTTP authentication. |
|
||||
|
||||
### Overrides
|
||||
|
||||
|
@ -766,6 +743,9 @@ To provide the overrides JSON as a file, the `FUZZAPI_OVERRIDES_FILE` CI/CD vari
|
|||
Here's an example `.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -784,6 +764,9 @@ This allows you to place the JSON as variables that can be masked and protected.
|
|||
In this example `.gitlab-ci.yml`, the `FUZZAPI_OVERRIDES_ENV` variable is set directly to the JSON:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -798,6 +781,9 @@ In this example `.gitlab-ci.yml`, the `SECRET_OVERRIDES` variable provides the J
|
|||
[group or instance level CI/CD variable defined in the UI](../../../ci/variables/README.md#instance-cicd-variables):
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -823,6 +809,9 @@ You must provide three CI/CD variables, each set for correct operation:
|
|||
- `FUZZAPI_OVERRIDES_INTERVAL`: Interval in seconds to run command.
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- fuzz
|
||||
|
||||
include:
|
||||
- template: API-Fuzzing.gitlab-ci.yml
|
||||
|
||||
|
@ -1004,7 +993,7 @@ False positives can be handled in two ways:
|
|||
### Turn off a Check
|
||||
|
||||
Checks perform testing of a specific type and can be turned on and off for specific configuration
|
||||
profiles. The provided [configuration files](#configuration-files) define several profiles that you
|
||||
profiles. The default configuration file defines several profiles that you
|
||||
can use. The profile definition in the configuration file lists all the checks that are active
|
||||
during a scan. To turn off a specific check, remove it from the profile definition in the
|
||||
configuration file. The profiles are defined in the `Profiles` section of the configuration file.
|
||||
|
@ -1131,9 +1120,9 @@ successfully run. For more information, see [Offline environments](../offline_de
|
|||
|
||||
### Error, the OpenAPI document is not valid. Errors were found during validation of the document using the published OpenAPI schema
|
||||
|
||||
At the start of an API Fuzzing job the OpenAPI specification is validated against the [published schema](https://github.com/OAI/OpenAPI-Specification/tree/master/schemas). This error is shown when the provided OpenAPI specification has validation errors. Errors can be introduced when creating an OpenAPI specification manually, and also when the schema is generated.
|
||||
At the start of an API Fuzzing job the OpenAPI Specification is validated against the [published schema](https://github.com/OAI/OpenAPI-Specification/tree/master/schemas). This error is shown when the provided OpenAPI Specification has validation errors. Errors can be introduced when creating an OpenAPI Specification manually, and also when the schema is generated.
|
||||
|
||||
For OpenAPI specifications that are generated automatically validation errors are often the result of missing code annotations.
|
||||
For OpenAPI Specifications that are generated automatically validation errors are often the result of missing code annotations.
|
||||
|
||||
**Error message**
|
||||
|
||||
|
@ -1143,7 +1132,7 @@ For OpenAPI specifications that are generated automatically validation errors ar
|
|||
|
||||
**Solution**
|
||||
|
||||
**For generated OpenAPI specifications**
|
||||
**For generated OpenAPI Specifications**
|
||||
|
||||
1. Identify the validation errors.
|
||||
1. Use the [Swagger Editor](https://editor.swagger.io/) to identify validation problems in your specification. The visual nature of the Swagger Editor makes it easier to understand what needs to change.
|
||||
|
@ -1154,7 +1143,7 @@ For OpenAPI specifications that are generated automatically validation errors ar
|
|||
**For manually created OpenAPI Specifications**
|
||||
|
||||
1. Identify the validation errors.
|
||||
1. The simplest solution is to use a visual tool to edit and validate the OpenAPI document. For example the [Swagger Editor](https://editor.swagger.io/) will point out schema errors and possible solutions.
|
||||
1. The simplest solution is to use a visual tool to edit and validate the OpenAPI document. For example the [Swagger Editor](https://editor.swagger.io/) highlights schema errors and possible solutions.
|
||||
1. Alternatively, you can check the log output and look for schema validation warnings. They are prefixed with messages such as `OpenAPI 2.0 schema validation error` or `OpenAPI 3.0.x schema validation error`. Each failed validation provides extra information about `location` and `description`. Correct each of the validation failures and then resubmit the OpenAPI doc. Note that JSON Schema validation message might not be easy to understand. This is why we recommend the use of editors to validate document.
|
||||
1. Once the validation issues are resolved, re-run your pipeline.
|
||||
|
||||
|
|
|
@ -1,67 +1,8 @@
|
|||
---
|
||||
stage: Plan
|
||||
group: Project Management
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
redirect_to: 'issues/managing_issues.md'
|
||||
---
|
||||
|
||||
# Bulk editing issues and merge requests at the project level
|
||||
This document was moved to [another location](issues/managing_issues.md).
|
||||
|
||||
NOTE:
|
||||
Bulk editing issues, epics, and merge requests is also available at the **group level**.
|
||||
For more details, see
|
||||
[Bulk editing issues, epics, and merge requests at the group level](../group/bulk_editing/index.md).
|
||||
|
||||
If you want to update attributes across multiple issues or merge requests, you can do it
|
||||
by bulk editing them, that is, editing them together.
|
||||
|
||||
Only the items visible on the current page are selected for bulk editing (up to 20).
|
||||
|
||||
![Bulk editing](img/bulk_editing_v13_11.png)
|
||||
|
||||
## Bulk edit issues at the project level
|
||||
|
||||
> - Assigning epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in GitLab 13.2.
|
||||
> - Editing health status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in GitLab 13.2.
|
||||
> - Editing iteration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196806) in GitLab 13.9.
|
||||
|
||||
Users with permission level of [Reporter or higher](../permissions.md) can manage issues.
|
||||
|
||||
When bulk editing issues in a project, you can edit the following attributes:
|
||||
|
||||
- Status (open/closed)
|
||||
- Assignee
|
||||
- [Epic](../group/epics/index.md)
|
||||
- [Milestone](milestones/index.md)
|
||||
- [Labels](labels.md)
|
||||
- [Health status](issues/managing_issues.md#health-status)
|
||||
- Notification subscription
|
||||
- [Iteration](../group/iterations/index.md)
|
||||
|
||||
To update multiple project issues at the same time:
|
||||
|
||||
1. In a project, go to **{issues}** **Issues > List**.
|
||||
1. Click **Edit issues**. A sidebar on the right-hand side of your screen appears with editable fields.
|
||||
1. Select the checkboxes next to each issue you want to edit.
|
||||
1. Select the appropriate fields and their values from the sidebar.
|
||||
1. Click **Update all**.
|
||||
|
||||
## Bulk edit merge requests at the project level
|
||||
|
||||
Users with permission level of [Developer or higher](../permissions.md) can manage merge requests.
|
||||
|
||||
When bulk editing merge requests in a project, you can edit the following attributes:
|
||||
|
||||
- Status (open/closed)
|
||||
- Assignee
|
||||
- Milestone
|
||||
- Labels
|
||||
- Subscriptions
|
||||
|
||||
To update multiple project merge requests at the same time:
|
||||
|
||||
1. In a project, go to **{merge-request}** **Merge Requests**.
|
||||
1. Click **Edit merge requests**. A sidebar on the right-hand side of your screen appears with
|
||||
editable fields.
|
||||
1. Select the checkboxes next to each merge request you want to edit.
|
||||
1. Select the appropriate fields and their values from the sidebar.
|
||||
1. Click **Update all**.
|
||||
<!-- This redirect file can be deleted after <2021-08-12>. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 70 KiB |
|
@ -142,6 +142,33 @@ Follow these examples to form your new issue URL with prefilled fields.
|
|||
- For a new issue in the GitLab Community Edition project with a pre-filled title,
|
||||
a pre-filled description, and the confidential flag set, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true`
|
||||
|
||||
## Bulk edit issues at the project level
|
||||
|
||||
> - Assigning epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in GitLab 13.2.
|
||||
> - Editing health status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in GitLab 13.2.
|
||||
> - Editing iteration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196806) in GitLab 13.9.
|
||||
|
||||
Users with permission level of [Reporter or higher](../../permissions.md) can manage issues.
|
||||
|
||||
When bulk editing issues in a project, you can edit the following attributes:
|
||||
|
||||
- Status (open/closed)
|
||||
- Assignee
|
||||
- [Epic](../../group/epics/index.md)
|
||||
- [Milestone](../milestones/index.md)
|
||||
- [Labels](../labels.md)
|
||||
- [Health status](#health-status)
|
||||
- Notification subscription
|
||||
- [Iteration](../../group/iterations/index.md)
|
||||
|
||||
To update multiple project issues at the same time:
|
||||
|
||||
1. In a project, go to **Issues > List**.
|
||||
1. Click **Edit issues**. A sidebar on the right-hand side of your screen appears with editable fields.
|
||||
1. Select the checkboxes next to each issue you want to edit.
|
||||
1. Select the appropriate fields and their values from the sidebar.
|
||||
1. Click **Update all**.
|
||||
|
||||
## Moving issues
|
||||
|
||||
Moving an issue copies it to the target project, and closes it in the originating project.
|
||||
|
|
|
@ -31,6 +31,27 @@ GitLab displays open merge requests, with tabs to filter the list by open and cl
|
|||
You can [search and filter](../../../search/index.md#filtering-issue-and-merge-request-lists),
|
||||
the results, or select a merge request to begin a review.
|
||||
|
||||
## Bulk edit merge requests at the project level
|
||||
|
||||
Users with permission level of [Developer or higher](../../../permissions.md) can manage merge requests.
|
||||
|
||||
When bulk editing merge requests in a project, you can edit the following attributes:
|
||||
|
||||
- Status (open/closed)
|
||||
- Assignee
|
||||
- Milestone
|
||||
- Labels
|
||||
- Subscriptions
|
||||
|
||||
To update multiple project merge requests at the same time:
|
||||
|
||||
1. In a project, go to **Merge requests**.
|
||||
1. Click **Edit merge requests**. A sidebar on the right-hand side of your screen appears with
|
||||
editable fields.
|
||||
1. Select the checkboxes next to each merge request you want to edit.
|
||||
1. Select the appropriate fields and their values from the sidebar.
|
||||
1. Click **Update all**.
|
||||
|
||||
## Review a merge request
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4213) in GitLab Premium 11.4.
|
||||
|
|
|
@ -5094,6 +5094,9 @@ msgstr ""
|
|||
msgid "Billing|Group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|Group invite"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|No users to display."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -8,37 +8,67 @@ RSpec.describe 'Admin Mode Logout', :js do
|
|||
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
|
||||
gitlab_sign_in(user)
|
||||
gitlab_enable_admin_mode_sign_in(user)
|
||||
visit admin_root_path
|
||||
end
|
||||
|
||||
it 'disable removes admin mode and redirects to root page' do
|
||||
gitlab_disable_admin_mode
|
||||
|
||||
expect(current_path).to eq root_path
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
|
||||
it 'disable shows flash notice' do
|
||||
gitlab_disable_admin_mode
|
||||
|
||||
expect(page).to have_selector('.flash-notice')
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
gitlab_sign_in(user)
|
||||
gitlab_enable_admin_mode_sign_in(user)
|
||||
visit admin_root_path
|
||||
end
|
||||
|
||||
it 'disable removes admin mode and redirects to root page' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
gitlab_disable_admin_mode
|
||||
|
||||
expect(current_path).to eq root_path
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
|
||||
it 'disable shows flash notice' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
gitlab_disable_admin_mode
|
||||
|
||||
expect(page).to have_selector('.flash-notice')
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
end
|
||||
|
||||
it 'disable removes admin mode and redirects to root page' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
gitlab_disable_admin_mode
|
||||
|
||||
expect(current_path).to eq root_path
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,55 +8,43 @@ RSpec.describe 'Admin mode' do
|
|||
|
||||
let(:admin) { create(:admin) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
|
||||
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
|
||||
end
|
||||
|
||||
context 'application setting :admin_mode is enabled', :request_store do
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
|
||||
end
|
||||
|
||||
context 'when not in admin mode' do
|
||||
it 'has no leave admin mode button' do
|
||||
visit new_admin_session_path
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).not_to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
context 'application setting :admin_mode is enabled', :request_store do
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'can open pages not in admin scope' do
|
||||
visit new_admin_session_path
|
||||
context 'when not in admin mode' do
|
||||
it 'has no leave admin mode button' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
find_all('a', text: 'Projects').first.click
|
||||
visit new_admin_session_path
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).not_to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
expect(page).to have_current_path(dashboard_projects_path)
|
||||
end
|
||||
it 'can open pages not in admin scope' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
it 'is necessary to provide credentials again before opening pages in admin scope' do
|
||||
visit general_admin_application_settings_path # admin logged out because not in admin_mode
|
||||
visit new_admin_session_path
|
||||
|
||||
expect(page).to have_current_path(new_admin_session_path)
|
||||
end
|
||||
page.within('.navbar-sub-nav') do
|
||||
find_all('a', text: 'Projects').first.click
|
||||
end
|
||||
|
||||
it 'can enter admin mode' do
|
||||
visit new_admin_session_path
|
||||
expect(page).to have_current_path(dashboard_projects_path)
|
||||
end
|
||||
|
||||
fill_in 'user_password', with: admin.password
|
||||
it 'is necessary to provide credentials again before opening pages in admin scope' do
|
||||
visit general_admin_application_settings_path # admin logged out because not in admin_mode
|
||||
|
||||
click_button 'Enter Admin Mode'
|
||||
|
||||
expect(page).to have_current_path(admin_root_path)
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
expect(page).to have_current_path(new_admin_session_path)
|
||||
end
|
||||
|
||||
it 'can enter admin mode' do
|
||||
|
@ -68,108 +56,163 @@ RSpec.describe 'Admin mode' do
|
|||
|
||||
expect(page).to have_current_path(admin_root_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in admin_mode' do
|
||||
before do
|
||||
gitlab_enable_admin_mode_sign_in(admin)
|
||||
end
|
||||
|
||||
it 'contains link to leave admin mode' do
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can leave admin mode using main dashboard link', :js do
|
||||
page.within('.navbar-sub-nav') do
|
||||
click_on 'Leave Admin Mode'
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can leave admin mode using dropdown menu on smaller screens', :js do
|
||||
resize_screen_xs
|
||||
visit root_dashboard_path
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
page.within '.navbar-sub-nav' do
|
||||
click_on 'Leave Admin Mode'
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can open pages not in admin scope' do
|
||||
page.within('.navbar-sub-nav') do
|
||||
find_all('a', text: 'Projects').first.click
|
||||
|
||||
expect(page).to have_current_path(dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'nav bar' do
|
||||
it 'shows admin dashboard links on bigger screen' do
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'relocates admin dashboard links to dropdown list on smaller screen', :js do
|
||||
resize_screen_xs
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).not_to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
end
|
||||
|
||||
find('.header-more').click
|
||||
it 'can enter admin mode' do
|
||||
visit new_admin_session_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
fill_in 'user_password', with: admin.password
|
||||
|
||||
click_button 'Enter Admin Mode'
|
||||
|
||||
expect(page).to have_current_path(admin_root_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
context 'when in admin_mode' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
gitlab_enable_admin_mode_sign_in(admin)
|
||||
end
|
||||
|
||||
it 'can leave admin mode', :js do
|
||||
it 'contains link to leave admin mode' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can leave admin mode using main dashboard link', :js do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
click_on 'Leave Admin Mode'
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can leave admin mode using dropdown menu on smaller screens', :js do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
resize_screen_xs
|
||||
visit root_dashboard_path
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
page.within '.navbar-sub-nav' do
|
||||
click_on 'Leave Admin Mode'
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can open pages not in admin scope' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
find_all('a', text: 'Projects').first.click
|
||||
|
||||
expect(page).to have_current_path(dashboard_projects_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'nav bar' do
|
||||
it 'shows admin dashboard links on bigger screen' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'relocates admin dashboard links to dropdown list on smaller screen', :js do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
resize_screen_xs
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a read-only instance' do
|
||||
before do
|
||||
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
|
||||
end
|
||||
|
||||
it 'can leave admin mode', :js do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
click_on 'Leave Admin Mode'
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'application setting :admin_mode is disabled' do
|
||||
before do
|
||||
stub_application_setting(admin_mode: false)
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'shows no admin mode buttons in navbar' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
visit admin_root_path
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).not_to have_link(href: new_admin_session_path)
|
||||
expect(page).not_to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'application setting :admin_mode is disabled' do
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
before do
|
||||
stub_application_setting(admin_mode: false)
|
||||
sign_in(admin)
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it 'shows no admin mode buttons in navbar' do
|
||||
visit admin_root_path
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
page.within('.navbar-sub-nav') do
|
||||
expect(page).not_to have_link(href: new_admin_session_path)
|
||||
expect(page).not_to have_link(href: destroy_admin_session_path)
|
||||
end
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,30 +3,56 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Dashboard Active Tab', :js do
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
before do
|
||||
sign_in(create(:user))
|
||||
end
|
||||
|
||||
sign_in(create(:user))
|
||||
end
|
||||
shared_examples 'page has active tab' do |title|
|
||||
it "#{title} tab" do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
shared_examples 'page has active tab' do |title|
|
||||
it "#{title} tab" do
|
||||
subject
|
||||
subject
|
||||
|
||||
expect(page).to have_selector('.navbar-sub-nav li.active', count: 1)
|
||||
expect(find('.navbar-sub-nav li.active')).to have_content(title)
|
||||
expect(page).to have_selector('.navbar-sub-nav li.active', count: 1)
|
||||
expect(find('.navbar-sub-nav li.active')).to have_content(title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on dashboard projects' do
|
||||
it_behaves_like 'page has active tab', 'Projects' do
|
||||
subject { visit dashboard_projects_path }
|
||||
end
|
||||
end
|
||||
|
||||
context 'on dashboard groups' do
|
||||
it_behaves_like 'page has active tab', 'Groups' do
|
||||
subject { visit dashboard_groups_path }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on dashboard projects' do
|
||||
it_behaves_like 'page has active tab', 'Projects' do
|
||||
subject { visit dashboard_projects_path }
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
context 'on dashboard groups' do
|
||||
it_behaves_like 'page has active tab', 'Groups' do
|
||||
subject { visit dashboard_groups_path }
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,36 +7,64 @@ RSpec.describe 'The group dashboard' do
|
|||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
sign_in user
|
||||
describe 'The top navigation' do
|
||||
it 'has all the expected links' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
visit dashboard_groups_path
|
||||
|
||||
within('.navbar') do
|
||||
expect(page).to have_button('Projects')
|
||||
expect(page).to have_button('Groups')
|
||||
expect(page).to have_link('Activity')
|
||||
expect(page).to have_link('Milestones')
|
||||
expect(page).to have_link('Snippets')
|
||||
end
|
||||
end
|
||||
|
||||
it 'hides some links when an external authorization service is enabled' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
enable_external_authorization_service_check
|
||||
visit dashboard_groups_path
|
||||
|
||||
within('.navbar') do
|
||||
expect(page).to have_button('Projects')
|
||||
expect(page).to have_button('Groups')
|
||||
expect(page).not_to have_link('Activity')
|
||||
expect(page).not_to have_link('Milestones')
|
||||
expect(page).to have_link('Snippets')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'The top navigation' do
|
||||
it 'has all the expected links' do
|
||||
visit dashboard_groups_path
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
within('.navbar') do
|
||||
expect(page).to have_button('Projects')
|
||||
expect(page).to have_button('Groups')
|
||||
expect(page).to have_link('Activity')
|
||||
expect(page).to have_link('Milestones')
|
||||
expect(page).to have_link('Snippets')
|
||||
end
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it 'hides some links when an external authorization service is enabled' do
|
||||
enable_external_authorization_service_check
|
||||
visit dashboard_groups_path
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
within('.navbar') do
|
||||
expect(page).to have_button('Projects')
|
||||
expect(page).to have_button('Groups')
|
||||
expect(page).not_to have_link('Activity')
|
||||
expect(page).not_to have_link('Milestones')
|
||||
expect(page).to have_link('Snippets')
|
||||
end
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,71 +3,97 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Dashboard shortcuts', :js do
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
context 'logged in' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
visit root_dashboard_path
|
||||
end
|
||||
|
||||
it 'navigate to tabs' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
find('body').send_keys([:shift, 'I'])
|
||||
|
||||
check_page_title('Issues')
|
||||
|
||||
find('body').send_keys([:shift, 'M'])
|
||||
|
||||
check_page_title('Merge requests')
|
||||
|
||||
find('body').send_keys([:shift, 'T'])
|
||||
|
||||
check_page_title('To-Do List')
|
||||
|
||||
find('body').send_keys([:shift, 'G'])
|
||||
|
||||
check_page_title('Groups')
|
||||
|
||||
find('body').send_keys([:shift, 'P'])
|
||||
|
||||
check_page_title('Projects')
|
||||
|
||||
find('body').send_keys([:shift, 'A'])
|
||||
|
||||
check_page_title('Activity')
|
||||
end
|
||||
end
|
||||
|
||||
context 'logged out' do
|
||||
before do
|
||||
visit explore_root_path
|
||||
end
|
||||
|
||||
it 'navigate to tabs' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
find('body').send_keys([:shift, 'G'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('No public groups')
|
||||
|
||||
find('body').send_keys([:shift, 'S'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('No snippets found')
|
||||
|
||||
find('body').send_keys([:shift, 'P'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('Explore public groups to find projects to contribute to.')
|
||||
end
|
||||
end
|
||||
|
||||
def check_page_title(title)
|
||||
expect(find('.page-title')).to have_content(title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'logged in' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
visit root_dashboard_path
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it 'navigate to tabs' do
|
||||
find('body').send_keys([:shift, 'I'])
|
||||
|
||||
check_page_title('Issues')
|
||||
|
||||
find('body').send_keys([:shift, 'M'])
|
||||
|
||||
check_page_title('Merge requests')
|
||||
|
||||
find('body').send_keys([:shift, 'T'])
|
||||
|
||||
check_page_title('To-Do List')
|
||||
|
||||
find('body').send_keys([:shift, 'G'])
|
||||
|
||||
check_page_title('Groups')
|
||||
|
||||
find('body').send_keys([:shift, 'P'])
|
||||
|
||||
check_page_title('Projects')
|
||||
|
||||
find('body').send_keys([:shift, 'A'])
|
||||
|
||||
check_page_title('Activity')
|
||||
end
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
context 'logged out' do
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
visit explore_root_path
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it 'navigate to tabs' do
|
||||
find('body').send_keys([:shift, 'G'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('No public groups')
|
||||
|
||||
find('body').send_keys([:shift, 'S'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('No snippets found')
|
||||
|
||||
find('body').send_keys([:shift, 'P'])
|
||||
|
||||
find('.nothing-here-block')
|
||||
expect(page).to have_content('Explore public groups to find projects to contribute to.')
|
||||
end
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def check_page_title(title)
|
||||
expect(find('.page-title')).to have_content(title)
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,45 +5,73 @@ require 'spec_helper'
|
|||
RSpec.describe 'Frequently visited items', :js do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
context 'for projects' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
context 'for projects' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
it 'increments localStorage counter when visiting the project' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
it 'increments localStorage counter when visiting the project' do
|
||||
visit project_path(project)
|
||||
visit project_path(project)
|
||||
|
||||
frequent_projects = nil
|
||||
frequent_projects = nil
|
||||
|
||||
wait_for('localStorage frequent-projects') do
|
||||
frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
|
||||
wait_for('localStorage frequent-projects') do
|
||||
frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
|
||||
|
||||
frequent_projects.present?
|
||||
frequent_projects.present?
|
||||
end
|
||||
|
||||
expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
|
||||
end
|
||||
end
|
||||
|
||||
expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
|
||||
context 'for groups' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
|
||||
it 'increments localStorage counter when visiting the group' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
visit group_path(group)
|
||||
|
||||
frequent_groups = nil
|
||||
|
||||
wait_for('localStorage frequent-groups') do
|
||||
frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
|
||||
|
||||
frequent_groups.present?
|
||||
end
|
||||
|
||||
expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for groups' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
it 'increments localStorage counter when visiting the group' do
|
||||
visit group_path(group)
|
||||
|
||||
frequent_groups = nil
|
||||
|
||||
wait_for('localStorage frequent-groups') do
|
||||
frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
|
||||
|
||||
frequent_groups.present?
|
||||
end
|
||||
|
||||
expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,50 +5,48 @@ require 'spec_helper'
|
|||
RSpec.describe 'New project', :js do
|
||||
include Select2Helper
|
||||
|
||||
context 'as a user' do
|
||||
let(:user) { create(:user) }
|
||||
shared_examples 'combined_menu: feature flag examples' do
|
||||
context 'as a user' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'new repo experiment', :experiment do
|
||||
it 'when in control renders "project"' do
|
||||
stub_experiments(new_repo: :control)
|
||||
|
||||
visit new_project_path
|
||||
|
||||
find('li.header-new.dropdown').click
|
||||
|
||||
page.within('li.header-new.dropdown') do
|
||||
expect(page).to have_selector('a', text: 'New project')
|
||||
expect(page).to have_no_selector('a', text: 'New project/repository')
|
||||
end
|
||||
|
||||
expect(page).to have_selector('h3', text: 'Create blank project')
|
||||
expect(page).to have_no_selector('h3', text: 'Create blank project/repository')
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'when in candidate renders "project/repository"' do
|
||||
stub_experiments(new_repo: :candidate)
|
||||
context 'new repo experiment', :experiment do
|
||||
it 'when in control renders "project"' do
|
||||
stub_experiments(new_repo: :control)
|
||||
|
||||
visit new_project_path
|
||||
visit new_project_path
|
||||
|
||||
find('li.header-new.dropdown').click
|
||||
find('li.header-new.dropdown').click
|
||||
|
||||
page.within('li.header-new.dropdown') do
|
||||
expect(page).to have_selector('a', text: 'New project/repository')
|
||||
page.within('li.header-new.dropdown') do
|
||||
expect(page).to have_selector('a', text: 'New project')
|
||||
expect(page).to have_no_selector('a', text: 'New project/repository')
|
||||
end
|
||||
|
||||
expect(page).to have_selector('h3', text: 'Create blank project')
|
||||
expect(page).to have_no_selector('h3', text: 'Create blank project/repository')
|
||||
end
|
||||
|
||||
expect(page).to have_selector('h3', text: 'Create blank project/repository')
|
||||
end
|
||||
it 'when in candidate renders "project/repository"' do
|
||||
stub_experiments(new_repo: :candidate)
|
||||
|
||||
context 'with combined_menu feature disabled' do
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
visit new_project_path
|
||||
|
||||
find('li.header-new.dropdown').click
|
||||
|
||||
page.within('li.header-new.dropdown') do
|
||||
expect(page).to have_selector('a', text: 'New project/repository')
|
||||
end
|
||||
|
||||
expect(page).to have_selector('h3', text: 'Create blank project/repository')
|
||||
end
|
||||
|
||||
it 'when in control it renders "project" in the new projects dropdown' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
stub_experiments(new_repo: :control)
|
||||
|
||||
visit new_project_path
|
||||
|
@ -64,6 +62,8 @@ RSpec.describe 'New project', :js do
|
|||
end
|
||||
|
||||
it 'when in candidate it renders "project/repository" in the new projects dropdown' do
|
||||
pending_on_combined_menu_flag
|
||||
|
||||
stub_experiments(new_repo: :candidate)
|
||||
|
||||
visit new_project_path
|
||||
|
@ -76,347 +76,373 @@ RSpec.describe 'New project', :js do
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows a message if multiple levels are restricted' do
|
||||
Gitlab::CurrentSettings.update!(
|
||||
restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL]
|
||||
)
|
||||
it 'shows a message if multiple levels are restricted' do
|
||||
Gitlab::CurrentSettings.update!(
|
||||
restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL]
|
||||
)
|
||||
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_content 'Other visibility settings have been disabled by the administrator.'
|
||||
end
|
||||
|
||||
it 'shows a message if all levels are restricted' do
|
||||
Gitlab::CurrentSettings.update!(
|
||||
restricted_visibility_levels: Gitlab::VisibilityLevel.values
|
||||
)
|
||||
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_content 'Visibility settings have been disabled by the administrator.'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as an admin' do
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'shows "New project" page', :js do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_content('Project name')
|
||||
expect(page).to have_content('Project URL')
|
||||
expect(page).to have_content('Project slug')
|
||||
|
||||
click_link('New project')
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
|
||||
expect(page).to have_link('GitHub')
|
||||
expect(page).to have_link('Bitbucket')
|
||||
expect(page).to have_link('GitLab.com')
|
||||
expect(page).to have_button('Repo by URL')
|
||||
expect(page).to have_link('GitLab export')
|
||||
end
|
||||
|
||||
describe 'manifest import option' do
|
||||
before do
|
||||
visit new_project_path
|
||||
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
end
|
||||
|
||||
it { expect(page).to have_link('Manifest file') }
|
||||
end
|
||||
|
||||
context 'Visibility level selector', :js do
|
||||
Gitlab::VisibilityLevel.options.each do |key, level|
|
||||
it "sets selector to #{key}" do
|
||||
stub_application_setting(default_project_visibility: level)
|
||||
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{level}")).to be_checked
|
||||
end
|
||||
end
|
||||
|
||||
it "saves visibility level #{level} on validation error" do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
choose(key)
|
||||
click_button('Create project')
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{level}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group visibility is private but default is internal' do
|
||||
let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
|
||||
|
||||
before do
|
||||
stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'has private selected' do
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'is not allowed' do
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
|
||||
expect(page).to have_content('Not Found')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group visibility is public but user requests private' do
|
||||
let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
|
||||
|
||||
before do
|
||||
stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'has private selected' do
|
||||
visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'is not allowed' do
|
||||
visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
|
||||
|
||||
expect(page).to have_content('Not Found')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Readme selector' do
|
||||
it 'shows the initialize with Readme checkbox on "Blank project" tab' do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_css('input#project_initialize_with_readme')
|
||||
expect(page).to have_content('Initialize repository with a README')
|
||||
expect(page).to have_content 'Other visibility settings have been disabled by the administrator.'
|
||||
end
|
||||
|
||||
it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
|
||||
it 'shows a message if all levels are restricted' do
|
||||
Gitlab::CurrentSettings.update!(
|
||||
restricted_visibility_levels: Gitlab::VisibilityLevel.values
|
||||
)
|
||||
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="create_from_template_link"]').click
|
||||
first('.choose-template').click
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
page.within '.project-fields-form' do
|
||||
expect(page).not_to have_css('input#project_initialize_with_readme')
|
||||
expect(page).not_to have_content('Initialize repository with a README')
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not show the initialize with Readme checkbox on "Import project" tab' do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
first('.js-import-git-toggle-button').click
|
||||
|
||||
page.within '#import-project-pane' do
|
||||
expect(page).not_to have_css('input#project_initialize_with_readme')
|
||||
expect(page).not_to have_content('Initialize repository with a README')
|
||||
end
|
||||
expect(page).to have_content 'Visibility settings have been disabled by the administrator.'
|
||||
end
|
||||
end
|
||||
|
||||
context 'Namespace selector' do
|
||||
context 'with user namespace' do
|
||||
context 'as an admin' do
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'shows "New project" page', :js do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_content('Project name')
|
||||
expect(page).to have_content('Project URL')
|
||||
expect(page).to have_content('Project slug')
|
||||
|
||||
click_link('New project')
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
|
||||
expect(page).to have_link('GitHub')
|
||||
expect(page).to have_link('Bitbucket')
|
||||
expect(page).to have_link('GitLab.com')
|
||||
expect(page).to have_button('Repo by URL')
|
||||
expect(page).to have_link('GitLab export')
|
||||
end
|
||||
|
||||
describe 'manifest import option' do
|
||||
before do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the user namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username)
|
||||
it 'has Manifest file' do
|
||||
expect(page).to have_link('Manifest file')
|
||||
end
|
||||
end
|
||||
|
||||
context 'Visibility level selector', :js do
|
||||
Gitlab::VisibilityLevel.options.each do |key, level|
|
||||
it "sets selector to #{key}" do
|
||||
stub_application_setting(default_project_visibility: level)
|
||||
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{level}")).to be_checked
|
||||
end
|
||||
end
|
||||
|
||||
it "saves visibility level #{level} on validation error" do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
choose(key)
|
||||
click_button('Create project')
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{level}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group visibility is private but default is internal' do
|
||||
let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
|
||||
|
||||
before do
|
||||
stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'has private selected' do
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'is not allowed' do
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
|
||||
expect(page).to have_content('Not Found')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group visibility is public but user requests private' do
|
||||
let_it_be(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
|
||||
|
||||
before do
|
||||
stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'has private selected' do
|
||||
visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
page.within('#blank-project-pane') do
|
||||
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'is not allowed' do
|
||||
visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
|
||||
|
||||
expect(page).to have_content('Not Found')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with group namespace' do
|
||||
let(:group) { create(:group, :private) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
context 'Readme selector' do
|
||||
it 'shows the initialize with Readme checkbox on "Blank project" tab' do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
|
||||
expect(page).to have_css('input#project_initialize_with_readme')
|
||||
expect(page).to have_content('Initialize repository with a README')
|
||||
end
|
||||
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name)
|
||||
it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="create_from_template_link"]').click
|
||||
first('.choose-template').click
|
||||
|
||||
page.within '.project-fields-form' do
|
||||
expect(page).not_to have_css('input#project_initialize_with_readme')
|
||||
expect(page).not_to have_content('Initialize repository with a README')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with subgroup namespace' do
|
||||
let(:group) { create(:group) }
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
|
||||
before do
|
||||
group.add_maintainer(user)
|
||||
visit new_project_path(namespace_id: subgroup.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when changing namespaces dynamically', :js do
|
||||
let(:public_group) { create(:group, :public) }
|
||||
let(:internal_group) { create(:group, :internal) }
|
||||
let(:private_group) { create(:group, :private) }
|
||||
|
||||
before do
|
||||
public_group.add_owner(user)
|
||||
internal_group.add_owner(user)
|
||||
private_group.add_owner(user)
|
||||
visit new_project_path(namespace_id: public_group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'enables the correct visibility options' do
|
||||
select2(user.namespace_id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
|
||||
|
||||
select2(public_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
|
||||
|
||||
select2(internal_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
|
||||
|
||||
select2(private_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Import project options', :js do
|
||||
before do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
end
|
||||
|
||||
context 'from git repository url, "Repo by URL"' do
|
||||
before do
|
||||
it 'does not show the initialize with Readme checkbox on "Import project" tab' do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
first('.js-import-git-toggle-button').click
|
||||
end
|
||||
|
||||
it 'does not autocomplete sensitive git repo URL' do
|
||||
autocomplete = find('#project_import_url')['autocomplete']
|
||||
|
||||
expect(autocomplete).to eq('off')
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
git_import_instructions = first('.js-toggle-content')
|
||||
|
||||
expect(git_import_instructions).to be_visible
|
||||
expect(git_import_instructions).to have_content 'Git repository URL'
|
||||
end
|
||||
|
||||
it 'reports error if repo URL does not end with .git' do
|
||||
fill_in 'project_import_url', with: 'http://foo/bar'
|
||||
fill_in 'project_name', with: 'import-project-without-git-suffix'
|
||||
fill_in 'project_path', with: 'import-project-without-git-suffix'
|
||||
|
||||
click_button 'Create project'
|
||||
|
||||
expect(page).to have_text('Please provide a valid URL ending with .git')
|
||||
end
|
||||
|
||||
it 'keeps "Import project" tab open after form validation error' do
|
||||
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
|
||||
|
||||
fill_in 'project_import_url', with: collision_project.http_url_to_repo
|
||||
fill_in 'project_name', with: collision_project.name
|
||||
|
||||
click_on 'Create project'
|
||||
|
||||
expect(page).to have_css('#import-project-pane.active')
|
||||
expect(page).not_to have_css('.toggle-import-form.hide')
|
||||
page.within '#import-project-pane' do
|
||||
expect(page).not_to have_css('input#project_initialize_with_readme')
|
||||
expect(page).not_to have_content('Initialize repository with a README')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'from GitHub' do
|
||||
before do
|
||||
first('.js-import-github').click
|
||||
context 'Namespace selector' do
|
||||
context 'with user namespace' do
|
||||
before do
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the user namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
expect(page).to have_content('Authenticate with GitHub')
|
||||
expect(current_path).to eq new_import_github_path
|
||||
context 'with group namespace' do
|
||||
let(:group) { create(:group, :private) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with subgroup namespace' do
|
||||
let(:group) { create(:group) }
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
|
||||
before do
|
||||
group.add_maintainer(user)
|
||||
visit new_project_path(namespace_id: subgroup.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when changing namespaces dynamically', :js do
|
||||
let(:public_group) { create(:group, :public) }
|
||||
let(:internal_group) { create(:group, :internal) }
|
||||
let(:private_group) { create(:group, :private) }
|
||||
|
||||
before do
|
||||
public_group.add_owner(user)
|
||||
internal_group.add_owner(user)
|
||||
private_group.add_owner(user)
|
||||
visit new_project_path(namespace_id: public_group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'enables the correct visibility options' do
|
||||
select2(user.namespace_id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
|
||||
|
||||
select2(public_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).not_to be_disabled
|
||||
|
||||
select2(internal_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
|
||||
|
||||
select2(private_group.id, from: '#project_namespace_id')
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).not_to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::INTERNAL}")).to be_disabled
|
||||
expect(find("#project_visibility_level_#{Gitlab::VisibilityLevel::PUBLIC}")).to be_disabled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'from manifest file' do
|
||||
context 'Import project options', :js do
|
||||
before do
|
||||
first('.import_manifest').click
|
||||
visit new_project_path
|
||||
find('[data-qa-selector="import_project_link"]').click
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
expect(page).to have_content('Manifest file import')
|
||||
expect(current_path).to eq new_import_manifest_path
|
||||
context 'from git repository url, "Repo by URL"' do
|
||||
before do
|
||||
first('.js-import-git-toggle-button').click
|
||||
end
|
||||
|
||||
it 'does not autocomplete sensitive git repo URL' do
|
||||
autocomplete = find('#project_import_url')['autocomplete']
|
||||
|
||||
expect(autocomplete).to eq('off')
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
git_import_instructions = first('.js-toggle-content')
|
||||
|
||||
expect(git_import_instructions).to be_visible
|
||||
expect(git_import_instructions).to have_content 'Git repository URL'
|
||||
end
|
||||
|
||||
it 'reports error if repo URL does not end with .git' do
|
||||
fill_in 'project_import_url', with: 'http://foo/bar'
|
||||
fill_in 'project_name', with: 'import-project-without-git-suffix'
|
||||
fill_in 'project_path', with: 'import-project-without-git-suffix'
|
||||
|
||||
click_button 'Create project'
|
||||
|
||||
expect(page).to have_text('Please provide a valid URL ending with .git')
|
||||
end
|
||||
|
||||
it 'keeps "Import project" tab open after form validation error' do
|
||||
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
|
||||
|
||||
fill_in 'project_import_url', with: collision_project.http_url_to_repo
|
||||
fill_in 'project_name', with: collision_project.name
|
||||
|
||||
click_on 'Create project'
|
||||
|
||||
expect(page).to have_css('#import-project-pane.active')
|
||||
expect(page).not_to have_css('.toggle-import-form.hide')
|
||||
end
|
||||
end
|
||||
|
||||
context 'from GitHub' do
|
||||
before do
|
||||
first('.js-import-github').click
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
expect(page).to have_content('Authenticate with GitHub')
|
||||
expect(current_path).to eq new_import_github_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'from manifest file' do
|
||||
before do
|
||||
first('.import_manifest').click
|
||||
end
|
||||
|
||||
it 'shows import instructions' do
|
||||
expect(page).to have_content('Manifest file import')
|
||||
expect(current_path).to eq new_import_manifest_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Namespace selector' do
|
||||
context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
|
||||
let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
|
||||
context 'Namespace selector' do
|
||||
context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
|
||||
let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
|
||||
|
||||
before do
|
||||
group.add_developer(user)
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
before do
|
||||
group.add_developer(user)
|
||||
visit new_project_path(namespace_id: group.id)
|
||||
find('[data-qa-selector="blank_project_link"]').click
|
||||
end
|
||||
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path)
|
||||
it 'selects the group namespace' do
|
||||
page.within('#blank-project-pane') do
|
||||
expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with combined_menu: feature flag on' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
context 'with combined_menu feature flag off' do
|
||||
let(:needs_rewrite_for_combined_menu_flag_on) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'combined_menu: feature flag examples'
|
||||
end
|
||||
|
||||
def pending_on_combined_menu_flag
|
||||
pending 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56587' if needs_rewrite_for_combined_menu_flag_on
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue