Commit 425cd326 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 10607-editor-css

* master: (56 commits)
  Fixed commit logic to pick a branch
  Prevent fade out transition on loading-button
  Resolve "Code in other column of side-by-side diff is highlighted when selecting code on one side"
  Add error tracking usage counts
  Fix Emoji URLs
  Docs: Fix missed or newly added broken anchors
  Docs: (EE Port) Fix missed or newly added broken anchors
  Docs: Fixing anchors and links for all docs related to issues.
  Docs: (EE Port) Fixing anchors and links for all docs related to issues.
  Add instance level templates to the examples page.
  Move some tests from Karma to Jest
  Copy vue_mount_component_helper.js to Jest
  Move some tests from Karma to Jest
  Copy vue_mount_component_helper.js to Jest
  Use help path as provided by HAML
  Makes emoji picker full width on mobile
  Set proper default-branch on GitHub Import
  Reset maximum height for create label dropdown
  Move details of e2e tests to the bottom
  Removes EE differences for markdown_area.scss
  ...
parents 678064de 6238ad70
...@@ -9,6 +9,9 @@ plugins: ...@@ -9,6 +9,9 @@ plugins:
- import - import
- html - html
settings: settings:
html/html-extensions:
- '.html'
- '.html.raw'
import/resolver: import/resolver:
webpack: webpack:
config: './config/webpack.config.js' config: './config/webpack.config.js'
......
...@@ -613,7 +613,7 @@ GEM ...@@ -613,7 +613,7 @@ GEM
atomic (>= 1.0.0) atomic (>= 1.0.0)
peek peek
redis redis
pg (1.1.3) pg (1.1.4)
po_to_json (1.0.1) po_to_json (1.0.1)
json (>= 1.6.0) json (>= 1.6.0)
powerpack (0.1.1) powerpack (0.1.1)
......
...@@ -8,6 +8,7 @@ import { updateTooltipTitle } from './lib/utils/common_utils'; ...@@ -8,6 +8,7 @@ import { updateTooltipTitle } from './lib/utils/common_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils'; import { isInVueNoteablePage } from './lib/utils/dom_utils';
import flash from './flash'; import flash from './flash';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import bp from './breakpoints';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
...@@ -264,7 +265,10 @@ export class AwardsHandler { ...@@ -264,7 +265,10 @@ export class AwardsHandler {
const css = { const css = {
top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`, top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
}; };
if (position === 'right') { // for xs screen we position the element on center
if (bp.getBreakpointSize() === 'xs') {
css.left = '5%';
} else if (position === 'right') {
css.left = `${$addBtn.offset().left - $menu.outerWidth() + 20}px`; css.left = `${$addBtn.offset().left - $menu.outerWidth() + 20}px`;
$menu.addClass('is-aligned-right'); $menu.addClass('is-aligned-right');
} else { } else {
......
...@@ -140,7 +140,7 @@ export default { ...@@ -140,7 +140,7 @@ export default {
:id="line.left.line_code" :id="line.left.line_code"
:class="parallelViewLeftLineType" :class="parallelViewLeftLineType"
class="line_content parallel left-side" class="line_content parallel left-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown="handleParallelLineMouseDown"
v-html="line.left.rich_text" v-html="line.left.rich_text"
></td> ></td>
</template> </template>
...@@ -171,7 +171,7 @@ export default { ...@@ -171,7 +171,7 @@ export default {
}, },
]" ]"
class="line_content parallel right-side" class="line_content parallel right-side"
@mousedown.native="handleParallelLineMouseDown" @mousedown="handleParallelLineMouseDown"
v-html="line.right.rich_text" v-html="line.right.rich_text"
></td> ></td>
</template> </template>
......
...@@ -38,8 +38,8 @@ export default { ...@@ -38,8 +38,8 @@ export default {
}, },
}, },
computed: { computed: {
...mapState('commit', ['commitAction']), ...mapState('commit', ['commitAction', 'newBranchName']),
...mapGetters('commit', ['newBranchName']), ...mapGetters('commit', ['placeholderBranchName']),
tooltipTitle() { tooltipTitle() {
return this.disabled ? this.title : ''; return this.disabled ? this.title : '';
}, },
...@@ -73,7 +73,8 @@ export default { ...@@ -73,7 +73,8 @@ export default {
</label> </label>
<div v-if="commitAction === value && showInput" class="ide-commit-new-branch"> <div v-if="commitAction === value && showInput" class="ide-commit-new-branch">
<input <input
:placeholder="newBranchName" :placeholder="placeholderBranchName"
:value="newBranchName"
type="text" type="text"
class="form-control monospace" class="form-control monospace"
@input="updateBranchName($event.target.value)" @input="updateBranchName($event.target.value)"
......
...@@ -14,7 +14,7 @@ const createTranslatedTextForFiles = (files, text) => { ...@@ -14,7 +14,7 @@ const createTranslatedTextForFiles = (files, text) => {
export const discardDraftButtonDisabled = state => export const discardDraftButtonDisabled = state =>
state.commitMessage === '' || state.submitCommitLoading; state.commitMessage === '' || state.submitCommitLoading;
export const newBranchName = (state, _, rootState) => export const placeholderBranchName = (state, _, rootState) =>
`${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr( `${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr(
-BRANCH_SUFFIX_COUNT, -BRANCH_SUFFIX_COUNT,
)}`; )}`;
...@@ -25,7 +25,7 @@ export const branchName = (state, getters, rootState) => { ...@@ -25,7 +25,7 @@ export const branchName = (state, getters, rootState) => {
state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR
) { ) {
if (state.newBranchName === '') { if (state.newBranchName === '') {
return getters.newBranchName; return getters.placeholderBranchName;
} }
return state.newBranchName; return state.newBranchName;
......
...@@ -53,7 +53,7 @@ export default { ...@@ -53,7 +53,7 @@ export default {
<template> <template>
<button :class="containerClass" :disabled="loading || disabled" type="button" @click="onClick"> <button :class="containerClass" :disabled="loading || disabled" type="button" @click="onClick">
<transition name="fade"> <transition name="fade-in">
<gl-loading-icon <gl-loading-icon
v-if="loading" v-if="loading"
:inline="true" :inline="true"
...@@ -63,7 +63,7 @@ export default { ...@@ -63,7 +63,7 @@ export default {
class="js-loading-button-icon" class="js-loading-button-icon"
/> />
</transition> </transition>
<transition name="fade"> <transition name="fade-in">
<slot> <slot>
<span v-if="label" class="js-loading-button-label"> {{ label }} </span> <span v-if="label" class="js-loading-button-label"> {{ label }} </span>
</slot> </slot>
......
...@@ -443,6 +443,7 @@ ...@@ -443,6 +443,7 @@
border-color: transparent; border-color: transparent;
} }
&.btn-secondary-hover-link,
&.btn-default-hover-link { &.btn-default-hover-link {
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
......
...@@ -289,11 +289,9 @@ $gl-line-height: 16px; ...@@ -289,11 +289,9 @@ $gl-line-height: 16px;
$gl-line-height-24: 24px; $gl-line-height-24: 24px;
$gl-line-height-14: 14px; $gl-line-height-14: 14px;
// EE-only CSS variables START
$system-header-height: 35px; $system-header-height: 35px;
$issue-box-upcoming-bg: #8f8f8f; $issue-box-upcoming-bg: #8f8f8f;
$pages-group-name-color: #4c4e54; $pages-group-name-color: #4c4e54;
// EE-only CSS variables END
/* /*
* Common component specific colors * Common component specific colors
...@@ -417,7 +415,7 @@ $award-emoji-menu-shadow: rgba(0, 0, 0, 0.175); ...@@ -417,7 +415,7 @@ $award-emoji-menu-shadow: rgba(0, 0, 0, 0.175);
$award-emoji-positive-add-bg: #fed159; $award-emoji-positive-add-bg: #fed159;
$award-emoji-positive-add-lines: #bb9c13; $award-emoji-positive-add-lines: #bb9c13;
$award-emoji-width: 376px; $award-emoji-width: 376px;
$award-emoji-width-xs: 300px; $award-emoji-width-xs: 90%;
/* /*
* Search Box * Search Box
......
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active,
.fade-in-enter-active,
.fade-out-leave-active {
transition: opacity $sidebar-transition-duration $general-hover-transition-curve; transition: opacity $sidebar-transition-duration $general-hover-transition-curve;
} }
.fade-enter, .fade-enter,
.fade-in-enter,
.fade-out-leave-to,
.fade-leave-to { .fade-leave-to {
opacity: 0; opacity: 0;
} }
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
.dropdown-new-label { .dropdown-new-label {
.dropdown-content { .dropdown-content {
max-height: 136px; max-height: initial;
} }
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Clusters module Clusters
module Applications module Applications
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
VERSION = '0.2.0'.freeze VERSION = '0.3.0'.freeze
self.table_name = 'clusters_applications_runners' self.table_name = 'clusters_applications_runners'
......
...@@ -1384,6 +1384,7 @@ class Project < ActiveRecord::Base ...@@ -1384,6 +1384,7 @@ class Project < ActiveRecord::Base
repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}") repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}")
repository.copy_gitattributes(branch) repository.copy_gitattributes(branch)
repository.after_change_head repository.after_change_head
ProjectCacheWorker.perform_async(self.id, [], [:commit_count])
reload_default_branch reload_default_branch
else else
errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist") errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist")
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
class EnvironmentEntity < Grape::Entity class EnvironmentEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
prepend ::EE::EnvironmentEntity # rubocop: disable Cop/InjectEnterpriseEditionModule
expose :id expose :id
expose :name expose :name
...@@ -12,7 +11,6 @@ class EnvironmentEntity < Grape::Entity ...@@ -12,7 +11,6 @@ class EnvironmentEntity < Grape::Entity
expose :name_without_type expose :name_without_type
expose :last_deployment, using: DeploymentEntity expose :last_deployment, using: DeploymentEntity
expose :stop_action_available?, as: :has_stop_action expose :stop_action_available?, as: :has_stop_action
expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: RolloutStatusEntity
expose :metrics_path, if: -> (*) { environment.has_metrics? } do |environment| expose :metrics_path, if: -> (*) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment) metrics_project_environment_path(environment.project, environment)
...@@ -52,10 +50,6 @@ class EnvironmentEntity < Grape::Entity ...@@ -52,10 +50,6 @@ class EnvironmentEntity < Grape::Entity
request.current_user request.current_user
end end
def can_read_deploy_board?
can?(current_user, :read_deploy_board, environment.project)
end
def can_access_terminal? def can_access_terminal?
can?(request.current_user, :create_environment_terminal, environment) can?(request.current_user, :create_environment_terminal, environment)
end end
...@@ -72,3 +66,5 @@ class EnvironmentEntity < Grape::Entity ...@@ -72,3 +66,5 @@ class EnvironmentEntity < Grape::Entity
deployment_platform.cluster deployment_platform.cluster
end end
end end
EnvironmentEntity.prepend(::EE::EnvironmentEntity)
...@@ -26,6 +26,7 @@ class ProjectCacheWorker ...@@ -26,6 +26,7 @@ class ProjectCacheWorker
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def update_statistics(project, statistics = []) def update_statistics(project, statistics = [])
return if Gitlab::Database.read_only?
return unless try_obtain_lease_for(project.id, :update_statistics) return unless try_obtain_lease_for(project.id, :update_statistics)
Rails.logger.info("Updating statistics for project #{project.id}") Rails.logger.info("Updating statistics for project #{project.id}")
......
---
title: Fixed - Create project label window is cut off at the bottom
merge_request: 26049
author:
type: fixed
---
title: Resolve Code in other column of side-by-side diff is highlighted when selecting
code on one side
merge_request: 26423
author:
type: fixed
---
title: Prevent fade out transition on loading-button component.
merge_request: 26428
author:
type: fixed
---
title: Makes emoji picker full width on mobile.
merge_request: 25883
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Resolves Branch name is lost if I change commit mode in Web IDE
merge_request: 26180
author:
type: fixed
---
title: Add usage counts for error tracking feature
merge_request: 25472
author:
type: added
---
title: Refresh commit count after repository head changes
merge_request: 26473
author:
type: fixed
---
title: Set proper default-branch for repository on GitHub Import
merge_request: 26476
author:
type: fixed
---
title: Update GitLab Runner Helm Chart to 0.3.0/11.9.0
merge_request: 26467
author:
type: other
...@@ -110,7 +110,7 @@ module.exports = function(config) { ...@@ -110,7 +110,7 @@ module.exports = function(config) {
frameworks: ['jasmine'], frameworks: ['jasmine'],
files: [ files: [
{ pattern: 'spec/javascripts/test_bundle.js', watched: false }, { pattern: 'spec/javascripts/test_bundle.js', watched: false },
{ pattern: 'spec/javascripts/fixtures/**/*@(.json|.html|.png)', included: false }, { pattern: 'spec/javascripts/fixtures/**/*@(.json|.html|.html.raw|.png)', included: false },
], ],
preprocessors: { preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'], 'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
......
...@@ -307,7 +307,7 @@ The following documentation relates to the DevOps **Configure** stage: ...@@ -307,7 +307,7 @@ The following documentation relates to the DevOps **Configure** stage:
| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. | | [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
| [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. | | [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. |
| [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. | | [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. |
| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters) **[PREMIUM]** | Associate more than one Kubernetes clusters to your project. | | [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **[PREMIUM]** | Associate more than one Kubernetes clusters to your project. |
| [Protected variables](ci/variables/README.md#protected-variables) | Restrict variables to protected branches and tags. | | [Protected variables](ci/variables/README.md#protected-variables) | Restrict variables to protected branches and tags. |
| [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. | | [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. |
| [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. | | [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. |
......
...@@ -271,7 +271,6 @@ questions from [owasp.org](https://www.owasp.org). ...@@ -271,7 +271,6 @@ questions from [owasp.org](https://www.owasp.org).
- LFS and File ID. - LFS and File ID.
- Upload and File ID. - Upload and File ID.
- Job Artifact and File ID. - Job Artifact and File ID.
- Geo JWTs scopes are not enforced for Git Access yet, but will be in a future version (currently scheduled for GitLab 11.10).
### What access requirements have been defined for URI and Service calls? ### What access requirements have been defined for URI and Service calls?
......
...@@ -23,12 +23,69 @@ to help identify if something is wrong: ...@@ -23,12 +23,69 @@ to help identify if something is wrong:
![Geo health check](img/geo_node_healthcheck.png) ![Geo health check](img/geo_node_healthcheck.png)
There is also an option to check the status of the **secondary** node by running a special rake task: If the UI is not working, or you are unable to log in, you can run the Geo
health check manually to get this information as well as a few more details.
This rake task can be run on an app node in the **primary** or **secondary**
Geo nodes:
```sh
sudo gitlab-rake gitlab:geo:check
```
Example output:
```
Checking Geo ...
GitLab Geo is available ... yes
GitLab Geo is enabled ... yes
GitLab Geo secondary database is correctly configured ... yes
Using database streaming replication? ... yes
GitLab Geo tracking database is configured to use Foreign Data Wrapper? ... yes
GitLab Geo tracking database Foreign Data Wrapper schema is up-to-date? ... yes
GitLab Geo HTTP(S) connectivity ...
* Can connect to the primary node ... yes
HTTP/HTTPS repository cloning is enabled ... yes
Machine clock is synchronized ... yes
Git user has default SSH configuration? ... yes
OpenSSH configured to use AuthorizedKeysCommand ... yes
GitLab configured to disable writing to authorized_keys file ... yes
GitLab configured to store new projects in hashed storage? ... yes
All projects are in hashed storage? ... yes
Checking Geo ... Finished
```
Current sync information can be found manually by running this rake task on any
**secondary** app node:
```sh ```sh
sudo gitlab-rake geo:status sudo gitlab-rake geo:status
``` ```
Example output:
```
http://secondary.example.com/
-----------------------------------------------------
GitLab Version: 11.8.1-ee
Geo Role: Secondary
Health Status: Healthy
Repositories: 190/190 (100%)
Verified Repositories: 190/190 (100%)
Wikis: 190/190 (100%)
Verified Wikis: 190/190 (100%)
LFS Objects: 35/35 (100%)
Attachments: 528/528 (100%)
CI job artifacts: 477/477 (100%)
Repositories Checked: 0/190 (0%)
Sync Settings: Full
Database replication lag: 0 seconds
Last event ID seen from primary: 2158 (about 2 minute ago)
Last event ID processed by cursor: 2158 (about 2 minute ago)
Last status report was: 4 minutes ago
```
## Is Postgres replication working? ## Is Postgres replication working?
### Are my nodes pointing to the correct database instance? ### Are my nodes pointing to the correct database instance?
......
...@@ -1154,7 +1154,7 @@ If you're running into an issue with a component not outlined here, be sure to c ...@@ -1154,7 +1154,7 @@ If you're running into an issue with a component not outlined here, be sure to c
## Configure using Omnibus ## Configure using Omnibus
**Note**: We recommend that you follow the instructions here for a full [PostgreSQL cluster](#configure-using-omnibus-for-high-availability). **Note**: We recommend that you follow the instructions here for a full [PostgreSQL cluster](#high-availability-with-gitlab-omnibus-premium-only).
If you are reading this section due to an old bookmark, you can find that old documentation [in the repository](https://gitlab.com/gitlab-org/gitlab-ce/blob/v10.1.4/doc/administration/high_availability/database.md#configure-using-omnibus). If you are reading this section due to an old bookmark, you can find that old documentation [in the repository](https://gitlab.com/gitlab-org/gitlab-ce/blob/v10.1.4/doc/administration/high_availability/database.md#configure-using-omnibus).
--- ---
......
...@@ -56,7 +56,6 @@ Omnibus: ...@@ -56,7 +56,6 @@ Omnibus:
pgbouncer_exporter['enable'] = false pgbouncer_exporter['enable'] = false
redis_exporter['enable'] = false redis_exporter['enable'] = false
gitlab_monitor['enable'] = false gitlab_monitor['enable'] = false
gitaly['enable'] = false
# Prevent database connections during 'gitlab-ctl reconfigure' # Prevent database connections during 'gitlab-ctl reconfigure'
gitlab_rails['rake_cache_clear'] = false gitlab_rails['rake_cache_clear'] = false
......
...@@ -475,7 +475,7 @@ GET /projects/:id/repository/commits/:sha/statuses ...@@ -475,7 +475,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| `sha` | string | yes | The commit SHA | `sha` | string | yes | The commit SHA
| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch | `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch
| `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test` | `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test`
| `name` | string | no | Filter by [job name](../ci/yaml/README.md#jobs), e.g., `bundler:audit` | `name` | string | no | Filter by [job name](../ci/yaml/README.md#introduction), e.g., `bundler:audit`
| `all` | boolean | no | Return all statuses, not only the latest ones | `all` | boolean | no | Return all statuses, not only the latest ones
```bash ```bash
......
...@@ -15,7 +15,7 @@ GET /projects/:id/releases/:tag_name/assets/links ...@@ -15,7 +15,7 @@ GET /projects/:id/releases/:tag_name/assets/links
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- | | ------------- | -------------- | -------- | --------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. | | `tag_name` | string | yes | The tag associated with the Release. |
Example request: Example request:
...@@ -53,7 +53,7 @@ GET /projects/:id/releases/:tag_name/assets/links/:link_id ...@@ -53,7 +53,7 @@ GET /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- | | ------------- | -------------- | -------- | --------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. | | `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. | | `link_id` | integer | yes | The id of the link. |
...@@ -84,7 +84,7 @@ POST /projects/:id/releases/:tag_name/assets/links ...@@ -84,7 +84,7 @@ POST /projects/:id/releases/:tag_name/assets/links
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- | | ------------- | -------------- | -------- | --------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. | | `tag_name` | string | yes | The tag associated with the Release. |
| `name` | string | yes | The name of the link. | | `name` | string | yes | The name of the link. |
| `url` | string | yes | The URL of the link. | | `url` | string | yes | The URL of the link. |
...@@ -120,7 +120,7 @@ PUT /projects/:id/releases/:tag_name/assets/links/:link_id ...@@ -120,7 +120,7 @@ PUT /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- | | ------------- | -------------- | -------- | --------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. | | `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. | | `link_id` | integer | yes | The id of the link. |
| `name` | string | no | The name of the link. | | `name` | string | no | The name of the link. |
...@@ -156,7 +156,7 @@ DELETE /projects/:id/releases/:tag_name/assets/links/:link_id ...@@ -156,7 +156,7 @@ DELETE /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- | | ------------- | -------------- | -------- | --------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. | | `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. | | `link_id` | integer | yes | The id of the link. |
......
# GitLab CI/CD for external repositories **[PREMIUM]** # GitLab CI/CD for external repositories **[PREMIUM]**
NOTE: **Note:** NOTE: **Note:**
To [promote its release](https://about.gitlab.com/2018/03/22/gitlab-10-6-released/#gitlab-cicd-for-external-repos), GitLab.com users are receiving this feature for free through March 22, 2019. This feature [is available for free](https://about.gitlab.com/2019/03/21/six-more-months-ci-cd-github/) to
GitLab.com users until September 22nd, 2019.
>[Introduced][ee-4642] in [GitLab Premium][eep] 10.6. >[Introduced][ee-4642] in [GitLab Premium][eep] 10.6.
GitLab CI/CD can be used with GitHub or any other Git server. GitLab CI/CD can be used with GitHub or any other Git server.
Instead of moving your entire project to GitLab, you can connect your Instead of moving your entire project to GitLab, you can connect your
external repository to get the benefits of GitLab CI/CD. external repository to get the benefits of GitLab CI/CD.
- [GitHub](github_integration.md) - [GitHub](github_integration.md)
- [Bitbucket Cloud](bitbucket_integration.md) - [Bitbucket Cloud](bitbucket_integration.md)
......
...@@ -21,7 +21,7 @@ all within GitLab. All you need to do is define them in your project's ...@@ -21,7 +21,7 @@ all within GitLab. All you need to do is define them in your project's
history of your deployments per every environment. history of your deployments per every environment.
Environments are like tags for your CI jobs, describing where code gets deployed. Environments are like tags for your CI jobs, describing where code gets deployed.
Deployments are created when [jobs] deploy versions of code to environments, Deployments are created when [jobs](yaml/README.md#introduction) deploy versions of code to environments,
so every environment can have one or more deployments. GitLab keeps track of so every environment can have one or more deployments. GitLab keeps track of
your deployments, so you always know what is currently being deployed on your your deployments, so you always know what is currently being deployed on your
servers. If you have a deployment service such as [Kubernetes][kube] servers. If you have a deployment service such as [Kubernetes][kube]
...@@ -103,7 +103,7 @@ the Git SHA and environment name. ...@@ -103,7 +103,7 @@ the Git SHA and environment name.
To sum up, with the above `.gitlab-ci.yml` we have achieved that: To sum up, with the above `.gitlab-ci.yml` we have achieved that:
- All branches will run the `test` and `build` jobs. - All branches will run the `test` and `build` jobs.
- The `deploy_staging` job will run [only](yaml/README.md#only-and-except-simplified) on the `master` - The `deploy_staging` job will run [only](yaml/README.md#onlyexcept-basic) on the `master`
branch which means all merge requests that are created from branches don't branch which means all merge requests that are created from branches don't
get to deploy to the staging server get to deploy to the staging server
- When a merge request is merged, all jobs will run and the `deploy_staging` - When a merge request is merged, all jobs will run and the `deploy_staging`
...@@ -298,8 +298,8 @@ here because it is guaranteed to be unique, but if you're using a workflow like ...@@ -298,8 +298,8 @@ here because it is guaranteed to be unique, but if you're using a workflow like
environment names to be more closely based on the branch name - the example environment names to be more closely based on the branch name - the example
above would give you an URL like `https://100-do-the-thing.example.com` above would give you an URL like `https://100-do-the-thing.example.com`
Last but not least, we tell the job to run [`only`][only] on branches Last but not least, we tell the job to run [`only`](yaml/README.md#onlyexcept-basic) on branches
[`except`][only] master. [`except`](yaml/README.md#onlyexcept-basic) master.
>**Note:** >**Note:**
You are not bound to use the same prefix or only slashes in the dynamic You are not bound to use the same prefix or only slashes in the dynamic
...@@ -646,14 +646,12 @@ Below are some links you may find interesting: ...@@ -646,14 +646,12 @@ Below are some links you may find interesting:
- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) - [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md)
[Pipelines]: pipelines.md [Pipelines]: pipelines.md
[jobs]: yaml/README.md#jobs
[yaml]: yaml/README.md [yaml]: yaml/README.md
[environments]: #environments [environments]: #environments
[deployments]: #deployments [deployments]: #deployments
[permissions]: ../user/permissions.md [permissions]: ../user/permissions.md
[variables]: variables/README.md [variables]: variables/README.md
[env-name]: yaml/README.md#environmentname [env-name]: yaml/README.md#environmentname
[only]: yaml/README.md#only-and-except-simplified
[onstop]: yaml/README.md#environmenton_stop [onstop]: yaml/README.md#environmenton_stop
[ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015 [ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015
[gitlab-flow]: ../workflow/gitlab_flow.md [gitlab-flow]: ../workflow/gitlab_flow.md
......
...@@ -50,6 +50,10 @@ language users and GitLab by sending a merge request with a guide for that langu ...@@ -50,6 +50,10 @@ language users and GitLab by sending a merge request with a guide for that langu
You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/) You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
to get paid for writing complete articles for GitLab. to get paid for writing complete articles for GitLab.
### Adding templates to your GitLab installation **[PREMIUM ONLY]**
If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](https://docs.gitlab.com/ee/user/admin_area/settings/instance_template_repository.html) that contains examples and templates specific to your enterprise.
## Other resources ## Other resources
This section provides further resources to help you get familiar with different aspects of GitLab CI/CD. This section provides further resources to help you get familiar with different aspects of GitLab CI/CD.
......
...@@ -99,7 +99,7 @@ We've used the `java:8` [docker ...@@ -99,7 +99,7 @@ We've used the `java:8` [docker
image](../../docker/using_docker_images.md) to build image](../../docker/using_docker_images.md) to build
our application as it provides the up-to-date Java 8 JDK on [Docker our application as it provides the up-to-date Java 8 JDK on [Docker
Hub](https://hub.docker.com/). We've also added the [`only` Hub](https://hub.docker.com/). We've also added the [`only`
clause](../../yaml/README.md#only-and-except-simplified) clause](../../yaml/README.md#onlyexcept-basic)
to ensure our deployments only happen when we push to the master branch. to ensure our deployments only happen when we push to the master branch.
Now, since the steps defined in `.gitlab-ci.yml` require credentials to login Now, since the steps defined in `.gitlab-ci.yml` require credentials to login
......
...@@ -139,7 +139,7 @@ new browser window interacting with your app as you specified. ...@@ -139,7 +139,7 @@ new browser window interacting with your app as you specified.
Which brings us to the exciting part: how do we run this in GitLab CI/CD? There are two things we Which brings us to the exciting part: how do we run this in GitLab CI/CD? There are two things we
need to do for this: need to do for this:
1. Set up [CI/CD jobs](../../yaml/README.md#jobs) that actually have a browser available. 1. Set up [CI/CD jobs](../../yaml/README.md#introduction) that actually have a browser available.
2. Update our WebdriverIO configuration to use those browsers to visit the review apps. 2. Update our WebdriverIO configuration to use those browsers to visit the review apps.
For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/README.md#stages) For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/README.md#stages)
...@@ -184,7 +184,7 @@ option as an argument to `npm run confidence-check` on the command line. ...@@ -184,7 +184,7 @@ option as an argument to `npm run confidence-check` on the command line.
However, we still need to tell WebdriverIO which browser is available for it to use. However, we still need to tell WebdriverIO which browser is available for it to use.
[GitLab CI/CD makes [GitLab CI/CD makes
a number of variables available](../../variables/README.html#predefined-variables-environment-variables) a number of variables available](../../variables/README.html#predefined-environment-variables)
with information about the current CI job. We can use this information to dynamically set with information about the current CI job. We can use this information to dynamically set
up our WebdriverIO configuration according to the job that is running. More specifically, we can up our WebdriverIO configuration according to the job that is running. More specifically, we can
tell WebdriverIO what browser to execute the test on depending on the name of the currently running tell WebdriverIO what browser to execute the test on depending on the name of the currently running
......
...@@ -113,8 +113,8 @@ There are a few tools that can produce JUnit reports in Java. ...@@ -113,8 +113,8 @@ There are a few tools that can produce JUnit reports in Java.
In the following example, `gradle` is used to generate the test reports. In the following example, `gradle` is used to generate the test reports.
If there are multiple test tasks defined, `gradle` will generate multiple If there are multiple test tasks defined, `gradle` will generate multiple
directories under `build/test-results/`. In that case, you can leverage regex directories under `build/test-results/`. In that case, you can leverage glob
matching by defining the following path: `build/test-results/test/TEST-*.xml`: matching by defining the following path: `build/test-results/test/**/TEST-*.xml`:
```yaml ```yaml
java: java:
...@@ -123,7 +123,7 @@ java: ...@@ -123,7 +123,7 @@ java:
- gradle test - gradle test
artifacts: artifacts:
reports: reports:
junit: build/test-results/test/TEST-*.xml junit: build/test-results/test/**/TEST-*.xml
``` ```
#### Maven #### Maven
......
...@@ -48,7 +48,7 @@ outbound connections for upstream and downstream pipeline dependencies. ...@@ -48,7 +48,7 @@ outbound connections for upstream and downstream pipeline dependencies.
### Triggering a downstream pipeline using a bridge job ### Triggering a downstream pipeline using a bridge job
Before GitLab 11.8, it was necessary to implement a pipeline job that was Before GitLab 11.8, it was necessary to implement a pipeline job that was
responsible for making the API request [to trigger a pipeline](triggers/README.md#creating-cross-project-pipeline-through-API) responsible for making the API request [to trigger a pipeline](#triggering-multi-project-pipelines-through-api)
in a different project. in a different project.
In GitLab 11.8, GitLab provides a new CI/CD configuration syntax to make this In GitLab 11.8, GitLab provides a new CI/CD configuration syntax to make this
......
...@@ -593,7 +593,7 @@ If any of the conditions in `variables` evaluates to truth when using `only`, ...@@ -593,7 +593,7 @@ If any of the conditions in `variables` evaluates to truth when using `only`,
a new job is going to be created. If any of the expressions evaluates to truth a new job is going to be created. If any of the expressions evaluates to truth
when `except` is being used, a job is not going to be created. when `except` is being used, a job is not going to be created.
This follows usual rules for [`only` / `except` policies][builds-policies]. This follows usual rules for [`only` / `except` policies](../yaml/README.md#onlyexcept-advanced).
### Supported syntax ### Supported syntax
...@@ -659,7 +659,6 @@ Below you can find supported syntax reference: ...@@ -659,7 +659,6 @@ Below you can find supported syntax reference:
[protected tags]: ../../user/project/protected_tags.md [protected tags]: ../../user/project/protected_tags.md
[shellexecutors]: https://docs.gitlab.com/runner/executors/ [shellexecutors]: https://docs.gitlab.com/runner/executors/
[triggered]: ../triggers/README.md [triggered]: ../triggers/README.md
[builds-policies]: ../yaml/README.md#only-and-except-complex
[trigger-job-token]: ../triggers/README.md#ci-job-token [trigger-job-token]: ../triggers/README.md#ci-job-token
[gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
[registry]: ../../user/project/container_registry.md [registry]: ../../user/project/container_registry.md
......
...@@ -423,7 +423,7 @@ If you use multiple keys under `only` or `except`, they act as an AND. The logic ...@@ -423,7 +423,7 @@ If you use multiple keys under `only` or `except`, they act as an AND. The logic
#### `only:refs`/`except:refs` #### `only:refs`/`except:refs`
The `refs` strategy can take the same values as the The `refs` strategy can take the same values as the
[simplified only/except configuration](#only-and-except-simplified). [simplified only/except configuration](#onlyexcept-basic).
In the example below, the `deploy` job is going to be created only when the In the example below, the `deploy` job is going to be created only when the
pipeline has been [scheduled][schedules] or runs for the `master` branch: pipeline has been [scheduled][schedules] or runs for the `master` branch:
......
--- ---
redirect_to: '../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests' redirect_to: '../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests--starter'
--- ---
This document was moved to [description_templates](../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests). This document was moved to [description_templates](../user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests--starter).
...@@ -78,7 +78,7 @@ For issues requiring any new or updated documentation, the Product Manager (PM) ...@@ -78,7 +78,7 @@ For issues requiring any new or updated documentation, the Product Manager (PM)
must: must:
- Add the `Documentation` label. - Add the `Documentation` label.
- Confirm or add the [documentation requirements](#documentation-requirements). - Confirm or add the [documentation requirements](#documentation-requirements-in-feature-issues).
- Ensure the issue contains any new or updated feature name, overview/description, - Ensure the issue contains any new or updated feature name, overview/description,
and use cases, as required per the [documentation structure and template](structure.md), when applicable. and use cases, as required per the [documentation structure and template](structure.md), when applicable.
......
--- ---
redirect_to: 'https://design.gitlab.com/getting-started/personas/' redirect_to: 'https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/'
--- ---
The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/). This document was moved to [another location](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/).
...@@ -20,7 +20,7 @@ From now on, every existing project and newly created ones that don't have a ...@@ -20,7 +20,7 @@ From now on, every existing project and newly created ones that don't have a
`.gitlab-ci.yml`, will use the Auto DevOps pipelines. `.gitlab-ci.yml`, will use the Auto DevOps pipelines.
If you want to disable it for a specific project, you can do so in If you want to disable it for a specific project, you can do so in
[its settings](../../../topics/autodevops/index.md##enablingdisabling-auto-devops). [its settings](../../../topics/autodevops/index.md#enablingdisabling-auto-devops).
## Maximum artifacts size **[CORE ONLY]** ## Maximum artifacts size **[CORE ONLY]**
......
...@@ -12,7 +12,7 @@ You can leave a comment in the following places: ...@@ -12,7 +12,7 @@ You can leave a comment in the following places:
- commit diffs - commit diffs
There are standard comments, and you also have the option to create a comment There are standard comments, and you also have the option to create a comment
in the form of a threaded discussion. A comment can also be [turned into a discussion](#start-a-discussion-by-replying-to-a-non-discussion-comment) in the form of a threaded discussion. A comment can also be [turned into a discussion](#start-a-discussion-by-replying-to-a-standard-comment)
when it receives a reply. when it receives a reply.
The comment area supports [Markdown] and [quick actions]. You can edit your own The comment area supports [Markdown] and [quick actions]. You can edit your own
...@@ -317,7 +317,7 @@ This will add the comment to the review. ...@@ -317,7 +317,7 @@ This will add the comment to the review.
### Resolving/Unresolving discussions ### Resolving/Unresolving discussions
Review comments can also resolve/unresolve [resolvable discussions](#resolvable-discussions). Review comments can also resolve/unresolve [resolvable discussions](#resolvable-comments-and-discussions).
When replying to a comment, you will see a checkbox that you can click in order to resolve or unresolve When replying to a comment, you will see a checkbox that you can click in order to resolve or unresolve
the discussion once published. the discussion once published.
......
...@@ -36,34 +36,34 @@ To get familiar with the concepts needed to develop code on GitLab, read the fol ...@@ -36,34 +36,34 @@ To get familiar with the concepts needed to develop code on GitLab, read the fol
GitLab is a Git-based platform that integrates a great number of essential tools for software development and deployment, and project management: GitLab is a Git-based platform that integrates a great number of essential tools for software development and deployment, and project management:
- Hosting code in repositories with version control - Hosting code in repositories with version control.
- Tracking proposals for new implementations, bug reports, and feedback with a - Tracking proposals for new implementations, bug reports, and feedback with a
fully featured [Issue Tracker](project/issues/index.md#issue-tracker) fully featured [Issue Tracker](project/issues/index.md#issue-tracker).
- Organizing and prioritizing with [Issue Boards](project/issues/index.md#issue-boards) - Organizing and prioritizing with [Issue Boards](project/issues/index.md#issue-board).
- Reviewing code in [Merge Requests](project/merge_requests/index.md) with live-preview changes per - Reviewing code in [Merge Requests](project/merge_requests/index.md) with live-preview changes per
branch with [Review Apps](../ci/review_apps/index.md) branch with [Review Apps](../ci/review_apps/index.md).
- Building, testing and deploying with built-in [Continuous Integration](../ci/README.md) - Building, testing, and deploying with built-in [Continuous Integration](../ci/README.md).
- Deploying personal and professional static websites with [GitLab Pages](project/pages/index.md) - Deploying personal and professional static websites with [GitLab Pages](project/pages/index.md).
- Integrating with Docker by using [GitLab Container Registry](project/container_registry.md) - Integrating with Docker by using [GitLab Container Registry](project/container_registry.md).
- Tracking the development lifecycle by usingn [GitLab Cycle Analytics](project/cycle_analytics.md) - Tracking the development lifecycle by using [GitLab Cycle Analytics](project/cycle_analytics.md).
With GitLab Enterprise Edition, you can also: With GitLab Enterprise Edition, you can also:
- Provide support with [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) - Provide support with [Service Desk](project/service_desk.md).
- Improve collaboration with - Improve collaboration with
[Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#merge-request-approvals), [Merge Request Approvals](project/merge_requests/index.md#merge-request-approvals),
[Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html), [Multiple Assignees for Issues](project/issues/multiple_assignees_for_issues.md),
and [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) and [Multiple Issue Boards](project/issue_board.md#multiple-issue-boards-starter).
- Create formal relationships between issues with [Related Issues](https://docs.gitlab.com/ee/user/project/issues/related_issues.html) - Create formal relationships between issues with [Related Issues](project/issues/related_issues.md).
- Use [Burndown Charts](https://docs.gitlab.com/ee/user/project/milestones/burndown_charts.html) to track progress during a sprint or while working on a new version of their software. - Use [Burndown Charts](project/milestones/burndown_charts.md) to track progress during a sprint or while working on a new version of their software.
- Leverage [Elasticsearch](https://docs.gitlab.com/ee/integration/elasticsearch.html) with [Advanced Global Search](https://docs.gitlab.com/ee/user/search/advanced_global_search.html) and [Advanced Syntax Search](https://docs.gitlab.com/ee/user/search/advanced_search_syntax.html) for faster, more advanced code search across your entire GitLab instance - Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Global Search](search/advanced_global_search.md) and [Advanced Syntax Search](search/advanced_search_syntax.md) for faster, more advanced code search across your entire GitLab instance.
- [Authenticate users with Kerberos](https://docs.gitlab.com/ee/integration/kerberos.html) - [Authenticate users with Kerberos](../integration/kerberos.md).
- [Mirror a repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html) from elsewhere on your local server. - [Mirror a repository](../workflow/repository_mirroring.md) from elsewhere on your local server.
- [Export issues as CSV](https://docs.gitlab.com/ee/user/project/issues/csv_export.html) - [Export issues as CSV](project/issues/csv_export.md).
- View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html) - View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipelines](../ci/multi_project_pipelines.md).
- [Lock files](https://docs.gitlab.com/ee/user/project/file_lock.html) to prevent conflicts - [Lock files](project/file_lock.md) to prevent conflicts.
- View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html) - View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](project/deploy_boards.md).
- Leverage continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html) - Leverage continuous delivery method with [Canary Deployments](project/canary_deployments.md).
- Scan your code for vulnerabilities and [display them in merge requests](project/merge_requests/sast.md). - Scan your code for vulnerabilities and [display them in merge requests](project/merge_requests/sast.md).
You can also [integrate](project/integrations/project_services.md) GitLab with You can also [integrate](project/integrations/project_services.md) GitLab with
...@@ -109,8 +109,8 @@ to enjoy the best of GitLab. ...@@ -109,8 +109,8 @@ to enjoy the best of GitLab.
- [Permissions](permissions.md): Learn the different set of permissions levels for each - [Permissions](permissions.md): Learn the different set of permissions levels for each
user type (guest, reporter, developer, maintainer, owner). user type (guest, reporter, developer, maintainer, owner).
- [Feature highlight](feature_highlight.md): Learn more about the little blue dots - [Feature highlight](feature_highlight.md): Learn more about the little blue dots
around the app that explain certain features around the app that explain certain features.
- [Abuse reports](abuse_reports.md): Report abuse from users to GitLab administrators - [Abuse reports](abuse_reports.md): Report abuse from users to GitLab administrators.
## Groups ## Groups
...@@ -126,7 +126,7 @@ merge requests, code snippets, and commits. ...@@ -126,7 +126,7 @@ merge requests, code snippets, and commits.
When performing inline reviews to implementations When performing inline reviews to implementations
to your codebase through merge requests you can to your codebase through merge requests you can
gather feedback through [resolvable discussions](discussions/index.md#resolvable-discussions). gather feedback through [resolvable discussions](discussions/index.md#resolvable-comments-and-discussions).
### GitLab Flavored Markdown (GFM) ### GitLab Flavored Markdown (GFM)
......
...@@ -289,15 +289,15 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he ...@@ -289,15 +289,15 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default. Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
``` ```
Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you: Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you:
<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px"> <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/zap.png" width="20px" height="20px">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/v.png" width="20px" height="20px">
You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px"> you for that. You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/bug.png" width="20px" height="20px"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/speak_no_evil.png" width="20px" height="20px"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/snail.png" width="20px" height="20px"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/birthday.png" width="20px" height="20px">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/heart.png" width="20px" height="20px"> you for that.
If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px">. All you need to do is to look up one of the supported codes. If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/fearful.png" width="20px" height="20px">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/family.png" width="20px" height="20px">. All you need to do is to look up one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px"> Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/thumbsup.png" width="20px" height="20px">
Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
......
...@@ -69,7 +69,7 @@ new Kubernetes cluster to your project: ...@@ -69,7 +69,7 @@ new Kubernetes cluster to your project:
- **Number of nodes** - Enter the number of nodes you wish the cluster to have. - **Number of nodes** - Enter the number of nodes you wish the cluster to have.
- **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types) - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
of the Virtual Machine instance that the cluster will be based on. of the Virtual Machine instance that the cluster will be based on.
- **RBAC-enabled cluster** - Leave this checked if using default GKE creation options, see the [RBAC section](#role-based-access-control-rbac-core-only) for more information. - **RBAC-enabled cluster** - Leave this checked if using default GKE creation options, see the [RBAC section](#role-based-access-control-rbac) for more information.
1. Finally, click the **Create Kubernetes cluster** button. 1. Finally, click the **Create Kubernetes cluster** button.
After a couple of minutes, your cluster will be ready to go. You can now proceed After a couple of minutes, your cluster will be ready to go. You can now proceed
......
...@@ -41,7 +41,7 @@ specific environment, there are lot of uses cases. To name a few: ...@@ -41,7 +41,7 @@ specific environment, there are lot of uses cases. To name a few:
- You want to promote what's running in staging, to production. You go to the - You want to promote what's running in staging, to production. You go to the
environments list, verify that what's running in staging is what you think is environments list, verify that what's running in staging is what you think is
running, then click on the [manual action] to deploy to production. running, then click on the [manual action](../../ci/yaml/README.md#whenmanual) to deploy to production.
- You trigger a deploy, and you've got lots of containers to upgrade so you know - You trigger a deploy, and you've got lots of containers to upgrade so you know
it'll take a while (you've also throttled your deploy to only take down X it'll take a while (you've also throttled your deploy to only take down X
containers at a time). But you need to tell someone when it's deployed, so you containers at a time). But you need to tell someone when it's deployed, so you
...@@ -129,5 +129,4 @@ version of your application. ...@@ -129,5 +129,4 @@ version of your application.
[variables]: ../../ci/variables/README.md "GitLab CI variables" [variables]: ../../ci/variables/README.md "GitLab CI variables"
[autodeploy]: ../../ci/autodeploy/index.md "GitLab Autodeploy" [autodeploy]: ../../ci/autodeploy/index.md "GitLab Autodeploy"
[kube-image]: https://gitlab.com/gitlab-examples/kubernetes-deploy/container_registry "Kubernetes deploy Container Registry" [kube-image]: https://gitlab.com/gitlab-examples/kubernetes-deploy/container_registry "Kubernetes deploy Container Registry"
[manual action]: ../../ci/yaml/README.md#manual-actions
[runners]: ../../ci/runners/README.md [runners]: ../../ci/runners/README.md
...@@ -17,7 +17,7 @@ When you create a project in GitLab, you'll have access to a large number of ...@@ -17,7 +17,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Issue tracker](issues/index.md): Discuss implementations with your team within issues - [Issue tracker](issues/index.md): Discuss implementations with your team within issues
- [Issue Boards](issue_board.md): Organize and prioritize your workflow - [Issue Boards](issue_board.md): Organize and prioritize your workflow
- [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]** - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]**
- [Repositories](repository/index.md): Host your code in a fully - [Repositories](repository/index.md): Host your code in a fully
integrated platform integrated platform
- [Branches](repository/branches/index.md): use Git branching strategies to - [Branches](repository/branches/index.md): use Git branching strategies to
......
...@@ -28,11 +28,11 @@ Issue Boards** (version introduced in GitLab 8.11 - August 2016). ...@@ -28,11 +28,11 @@ Issue Boards** (version introduced in GitLab 8.11 - August 2016).
### Advanced features of Issue Boards ### Advanced features of Issue Boards
With [GitLab Starter](https://about.gitlab.com/pricing/), you can create With [GitLab Starter](https://about.gitlab.com/pricing/), you can create
[multiple issue boards](#multiple-issue-boards) for a given project. **[STARTER]** [multiple issue boards](#multiple-issue-boards-starter) for a given project. **[STARTER]**
With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple
issue boards for your groups, and add lists for [assignees](#assignee-lists) and issue boards for your groups, and add lists for [assignees](#assignee-lists-premium) and
[milestones](#milestone-lists). **[PREMIUM]** [milestones](#milestone-lists-premium). **[PREMIUM]**
Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards) Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards)
below. below.
...@@ -97,7 +97,7 @@ If we have the labels "**backend**", "**frontend**", "**staging**", and ...@@ -97,7 +97,7 @@ If we have the labels "**backend**", "**frontend**", "**staging**", and
### Use cases for Multiple Issue Boards ### Use cases for Multiple Issue Boards
With [Multiple Issue Boards](#multiple-issue-boards), available only in With [Multiple Issue Boards](#multiple-issue-boards-starter), available only in
[GitLab Enterprise Edition](https://about.gitlab.com/pricing/), [GitLab Enterprise Edition](https://about.gitlab.com/pricing/),
each team can have their own board to organize their workflow individually. each team can have their own board to organize their workflow individually.
...@@ -220,7 +220,7 @@ Click the button at the top right to toggle focus mode on and off. In focus mode ...@@ -220,7 +220,7 @@ Click the button at the top right to toggle focus mode on and off. In focus mode
The top of each list indicates the sum of issue weights for the issues that The top of each list indicates the sum of issue weights for the issues that
belong to that list. This is useful when using boards for capacity allocation, belong to that list. This is useful when using boards for capacity allocation,
especially in combination with [assignee lists](#assignee-lists). especially in combination with [assignee lists](#assignee-lists-premium).
![Issue Board summed weights](img/issue_board_summed_weights.png) ![Issue Board summed weights](img/issue_board_summed_weights.png)
......
# Confidential issues # Confidential issues
> [Introduced][ce-3282] in GitLab 8.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3282) in GitLab 8.6.
Confidential issues are issues visible only to members of a project with Confidential issues are issues visible only to members of a project with
[sufficient permissions](#permissions-and-access-to-confidential-issues). [sufficient permissions](#permissions-and-access-to-confidential-issues).
...@@ -67,7 +67,7 @@ There is also an indicator on the sidebar denoting confidentiality. ...@@ -67,7 +67,7 @@ There is also an indicator on the sidebar denoting confidentiality.
There are two kinds of level access for confidential issues. The general rule There are two kinds of level access for confidential issues. The general rule
is that confidential issues are visible only to members of a project with at is that confidential issues are visible only to members of a project with at
least [Reporter access][permissions]. However, a guest user can also create least [Reporter access](../../permissions.md#project-members-permissions). However, a guest user can also create
confidential issues, but can only view the ones that they created themselves. confidential issues, but can only view the ones that they created themselves.
Confidential issues are also hidden in search results for unprivileged users. Confidential issues are also hidden in search results for unprivileged users.
...@@ -77,6 +77,3 @@ project's search results respectively. ...@@ -77,6 +77,3 @@ project's search results respectively.
| Maintainer access | Guest access | | Maintainer access | Guest access |
| :-----------: | :----------: | | :-----------: | :----------: |
| ![Confidential issues search master](img/confidential_issues_search_master.png) | ![Confidential issues search guest](img/confidential_issues_search_guest.png) | | ![Confidential issues search master](img/confidential_issues_search_master.png) | ![Confidential issues search guest](img/confidential_issues_search_guest.png) |
[permissions]: ../../permissions.md#project
[ce-3282]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3282
...@@ -48,13 +48,12 @@ issues in merge requests. ...@@ -48,13 +48,12 @@ issues in merge requests.
## From Merge Requests ## From Merge Requests
Mentioning issues in merge request comments work exactly the same way Mentioning issues in merge request comments works exactly the same way as
they do for [related issues](#from-related-issues). they do for [related issues](#from-related-issues).
When you mention an issue in a merge request description, you can either When you mention an issue in a merge request description, it will simply
[close the issue as soon as the merge request is merged](closing_issues.md#via-merge-request), [link the issue and merge request together](#from-related-issues). Additionally,
or simply link both issue and merge request as described in the you can also [set an issue to close as soon as the merge request is merged](closing_issues.md#via-merge-request).
[closing issues documentation](closing_issues.md#from-related-issues).
![issue mentioned in MR](img/mention_in_merge_request.png) ![issue mentioned in MR](img/mention_in_merge_request.png)
......
# Due dates # Due dates
> [Introduced][ce-3614] in GitLab 8.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3614) in GitLab 8.7.
Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues. Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
Due dates can be used in issues to keep track of deadlines and make sure Due dates can be used in issues to keep track of deadlines and make sure
features are shipped on time. Due dates require at least [Reporter permissions][permissions] features are shipped on time. Due dates require at least [Reporter permissions](../../permissions.md#project-members-permissions)
to be able to edit them. On the contrary, they can be seen by everybody. to be able to edit them. On the contrary, they can be seen by everybody.
## Setting a due date ## Setting a due date
...@@ -47,6 +47,3 @@ on the _Subscribe to calendar_ button on the following pages: ...@@ -47,6 +47,3 @@ on the _Subscribe to calendar_ button on the following pages:
GitLab header GitLab header
- on the **Project Issues** page - on the **Project Issues** page
- on the **Group Issues** page - on the **Group Issues** page
[ce-3614]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3614
[permissions]: ../../permissions.md#project
...@@ -109,7 +109,7 @@ issue within your team only, you can make that ...@@ -109,7 +109,7 @@ issue within your team only, you can make that
[issue confidential](confidential_issues.md). Even if your project [issue confidential](confidential_issues.md). Even if your project
is public, that issue will be preserved. The browser will is public, that issue will be preserved. The browser will
respond with a 404 error whenever someone who is not a project respond with a 404 error whenever someone who is not a project
member with at least [Reporter level](../../permissions.md#project) tries to member with at least [Reporter level](../../permissions.md#project-members-permissions) tries to
access that issue's URL. access that issue's URL.
Learn more about them on the [confidential issues documentation](confidential_issues.md). Learn more about them on the [confidential issues documentation](confidential_issues.md).
...@@ -139,6 +139,9 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue ...@@ -139,6 +139,9 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md) Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature. to find out more about this feature.
With [GitLab Starter](https://about.gitlab.com/pricing/), you can also
create various boards per project with [Multiple Issue Boards](../issue_board.html#multiple-issue-boards-starter).
### Export Issues to CSV **[STARTER]** ### Export Issues to CSV **[STARTER]**
Issues can be [exported as CSV](csv_export.md) from GitLab and are sent to your email as an attachment. Issues can be [exported as CSV](csv_export.md) from GitLab and are sent to your email as an attachment.
......
...@@ -99,9 +99,9 @@ From the group epic list page, you can [filter](../search/index.md#issues-and-me ...@@ -99,9 +99,9 @@ From the group epic list page, you can [filter](../search/index.md#issues-and-me
### Filtering in issue boards ### Filtering in issue boards
- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [search and filter bar](../search/index.md#issue-boards). - From [project boards](issue_board.md), you can filter by both group labels and project labels in the [search and filter bar](../search/index.md#issue-boards).
- From [group issue boards](issue_board.md#group-issue-boards), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **[PREMIUM]** - From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **[PREMIUM]**
- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#board-with-configuration). **[PREMIUM]** - From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[STARTER]**
- From [group issue boards](issue_board.md#group-issue-boards), you can filter by only group labels in the [issue board configuration](issue_board.md#board-with-configuration). **[PREMIUM]** - From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[PREMIUM]**
## Subscribing to labels ## Subscribing to labels
......
...@@ -13,7 +13,7 @@ your existing `.gitlab-ci.yml` file or by implicitly using ...@@ -13,7 +13,7 @@ your existing `.gitlab-ci.yml` file or by implicitly using
[Auto License Management](../../../topics/autodevops/index.md#auto-license-management-ultimate) [Auto License Management](../../../topics/autodevops/index.md#auto-license-management-ultimate)
that is provided by [Auto DevOps](../../../topics/autodevops/index.md). that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
In addition, you can [manually approve or blacklist](#manual-license-management) licenses in the project's settings. In addition, you can [manually approve or blacklist](#project-policies-for-license-management) licenses in the project's settings.
Going a step further, GitLab can show the licenses list right in the merge Going a step further, GitLab can show the licenses list right in the merge
request widget area, highlighting the presence of licenses you don't want to use, or new request widget area, highlighting the presence of licenses you don't want to use, or new
......
...@@ -84,9 +84,9 @@ From the project issue/merge request list pages and the group issue/merge reques ...@@ -84,9 +84,9 @@ From the project issue/merge request list pages and the group issue/merge reques
### Filtering in issue boards ### Filtering in issue boards
- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [search and filter bar](../../search/index.md#issue-boards). - From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [search and filter bar](../../search/index.md#issue-boards).
- From [group issue boards](../issue_board.md#group-issue-boards), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **[PREMIUM]** - From [group issue boards](../issue_board.md#group-issue-boards-premium), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **[PREMIUM]**
- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#board-with-configuration). **[STARTER]** - From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[STARTER]**
- From [group issue boards](../issue_board.md#group-issue-boards) you can filter by only group milestones in the [issue board configuration](../issue_board.md#board-with-configuration). **[PREMIUM]** - From [group issue boards](../issue_board.md#group-issue-boards-premium) you can filter by only group milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[PREMIUM]**
......
...@@ -24,7 +24,7 @@ one for the first time.** ...@@ -24,7 +24,7 @@ one for the first time.**
[GitLab CI/CD](../../../ci/README.md) serves [GitLab CI/CD](../../../ci/README.md) serves
numerous purposes, to build, test, and deploy your app numerous purposes, to build, test, and deploy your app
from GitLab through from GitLab through
[Continuous Integration, Continuous Delivery, and Continuous Deployment](../../../ci/introduction/index.md#introduction-to-continuous-methods) [Continuous Integration, Continuous Delivery, and Continuous Deployment](../../../ci/introduction/index.md#introduction-to-cicd-methodologies)
methods. You will need it to build your website with GitLab Pages, methods. You will need it to build your website with GitLab Pages,
and deploy it to the Pages server. and deploy it to the Pages server.
......
...@@ -152,7 +152,7 @@ Depending on how you plan to publish your website, the steps defined in the ...@@ -152,7 +152,7 @@ Depending on how you plan to publish your website, the steps defined in the
Be aware that Pages are by default branch/tag agnostic and their deployment Be aware that Pages are by default branch/tag agnostic and their deployment
relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except-simplified), `pages` job with the [`only` parameter](../../../ci/yaml/README.md#onlyexcept-basic),
whenever a new commit is pushed to whatever branch or tag, the Pages will be whenever a new commit is pushed to whatever branch or tag, the Pages will be
overwritten. In the example below, we limit the Pages to be deployed whenever overwritten. In the example below, we limit the Pages to be deployed whenever
a commit is pushed only on the `master` branch: a commit is pushed only on the `master` branch:
...@@ -253,7 +253,7 @@ get you started. ...@@ -253,7 +253,7 @@ get you started.
Remember that GitLab Pages are by default branch/tag agnostic and their Remember that GitLab Pages are by default branch/tag agnostic and their
deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except-simplified), the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#onlyexcept-basic),
whenever a new commit is pushed to a branch that will be used specifically for whenever a new commit is pushed to a branch that will be used specifically for
your pages. your pages.
......
...@@ -59,7 +59,7 @@ GitLab CI so that they can be used in your `.gitlab-ci.yml` file. ...@@ -59,7 +59,7 @@ GitLab CI so that they can be used in your `.gitlab-ci.yml` file.
To configure that a job can be executed only when the pipeline has been To configure that a job can be executed only when the pipeline has been
scheduled (or the opposite), you can use scheduled (or the opposite), you can use
[only and except](../../../ci/yaml/README.md#only-and-except-simplified) configuration keywords. [only and except](../../../ci/yaml/README.md#onlyexcept-basic) configuration keywords.
``` ```
job:on-schedule: job:on-schedule:
......
...@@ -20,7 +20,7 @@ and private. See [Public access](../public_access/public_access.md) for more inf ...@@ -20,7 +20,7 @@ and private. See [Public access](../public_access/public_access.md) for more inf
## Project snippets ## Project snippets
Project snippets are always related to a specific project. Project snippets are always related to a specific project.
See [Project's features](project/index.md#projects-features) for more information. See [Project features](project/index.md#project-features) for more information.
## Discover snippets ## Discover snippets
......
...@@ -34,6 +34,10 @@ export default { ...@@ -34,6 +34,10 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
geoTroubleshootingHelpPath: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -213,6 +217,7 @@ export default { ...@@ -213,6 +217,7 @@ export default {
:primary-node="node.primary" :primary-node="node.primary"
:node-actions-allowed="nodeActionsAllowed" :node-actions-allowed="nodeActionsAllowed"
:node-edit-allowed="nodeEditAllowed" :node-edit-allowed="nodeEditAllowed"
:geo-troubleshooting-help-path="geoTroubleshootingHelpPath"
/> />
<deprecated-modal <deprecated-modal
v-show="showModal" v-show="showModal"
......
<script> <script>
/* eslint-disable vue/no-side-effects-in-computed-properties */ /* eslint-disable vue/no-side-effects-in-computed-properties */
import { GlLink } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import NodeDetailsSectionMain from './node_detail_sections/node_details_section_main.vue'; import NodeDetailsSectionMain from './node_detail_sections/node_details_section_main.vue';
...@@ -9,6 +10,7 @@ import NodeDetailsSectionOther from './node_detail_sections/node_details_section ...@@ -9,6 +10,7 @@ import NodeDetailsSectionOther from './node_detail_sections/node_details_section
export default { export default {
components: { components: {
GlLink,
NodeDetailsSectionMain, NodeDetailsSectionMain,
NodeDetailsSectionSync, NodeDetailsSectionSync,
NodeDetailsSectionVerification, NodeDetailsSectionVerification,
...@@ -31,6 +33,10 @@ export default { ...@@ -31,6 +33,10 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
geoTroubleshootingHelpPath: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -80,7 +86,12 @@ export default { ...@@ -80,7 +86,12 @@ export default {
:node-type-primary="node.primary" :node-type-primary="node.primary"
/> />
<div v-if="hasError || hasVersionMismatch" class="node-health-message-container"> <div v-if="hasError || hasVersionMismatch" class="node-health-message-container">
<p class="node-health-message">{{ errorMessage }}</p> <p class="node-health-message">
{{ errorMessage }}
<gl-link :href="geoTroubleshootingHelpPath">{{
s__('Geo|Please refer to Geo Troubleshooting.')
}}</gl-link>
</p>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { GlLink } from '@gitlab/ui';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import GeoNodeHeader from './geo_node_header.vue'; import GeoNodeHeader from './geo_node_header.vue';
...@@ -6,6 +8,7 @@ import GeoNodeDetails from './geo_node_details.vue'; ...@@ -6,6 +8,7 @@ import GeoNodeDetails from './geo_node_details.vue';
export default { export default {
components: { components: {
GlLink,
GeoNodeHeader, GeoNodeHeader,
GeoNodeDetails, GeoNodeDetails,
}, },
...@@ -26,6 +29,10 @@ export default { ...@@ -26,6 +29,10 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
geoTroubleshootingHelpPath: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -84,9 +91,15 @@ export default { ...@@ -84,9 +91,15 @@ export default {
:node-details="nodeDetails" :node-details="nodeDetails"
:node-edit-allowed="nodeEditAllowed" :node-edit-allowed="nodeEditAllowed"
:node-actions-allowed="nodeActionsAllowed" :node-actions-allowed="nodeActionsAllowed"
:geo-troubleshooting-help-path="geoTroubleshootingHelpPath"
/> />
<div v-if="isNodeDetailsFailed" class="node-health-message-container"> <div v-if="isNodeDetailsFailed" class="node-health-message-container">
<p class="health-message node-health-message">{{ errorMessage }}</p> <p class="node-health-message">
{{ errorMessage
}}<gl-link :href="geoTroubleshootingHelpPath">{{
s__('Geo|Please refer to Geo Troubleshooting.')
}}</gl-link>
</p>
</div> </div>
</div> </div>
</template> </template>
...@@ -18,6 +18,10 @@ export default { ...@@ -18,6 +18,10 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
geoTroubleshootingHelpPath: {
type: String,
required: true,
},
}, },
}; };
</script> </script>
...@@ -31,6 +35,7 @@ export default { ...@@ -31,6 +35,7 @@ export default {
:primary-node="node.primary" :primary-node="node.primary"
:node-actions-allowed="nodeActionsAllowed" :node-actions-allowed="nodeActionsAllowed"
:node-edit-allowed="nodeEditAllowed" :node-edit-allowed="nodeEditAllowed"
:geo-troubleshooting-help-path="geoTroubleshootingHelpPath"
/> />
</div> </div>
</template> </template>
...@@ -24,9 +24,10 @@ export default () => { ...@@ -24,9 +24,10 @@ export default () => {
}, },
data() { data() {
const { dataset } = this.$options.el; const { dataset } = this.$options.el;
const { primaryVersion, primaryRevision, geoTroubleshootingHelpPath } = dataset;
const nodeActionsAllowed = parseBoolean(dataset.nodeActionsAllowed); const nodeActionsAllowed = parseBoolean(dataset.nodeActionsAllowed);
const nodeEditAllowed = parseBoolean(dataset.nodeEditAllowed); const nodeEditAllowed = parseBoolean(dataset.nodeEditAllowed);
const store = new GeoNodesStore(dataset.primaryVersion, dataset.primaryRevision); const store = new GeoNodesStore(primaryVersion, primaryRevision);
const service = new GeoNodesService(); const service = new GeoNodesService();
return { return {
...@@ -34,6 +35,7 @@ export default () => { ...@@ -34,6 +35,7 @@ export default () => {
service, service,
nodeActionsAllowed, nodeActionsAllowed,
nodeEditAllowed, nodeEditAllowed,
geoTroubleshootingHelpPath,
}; };
}, },
render(createElement) { render(createElement) {
...@@ -43,6 +45,7 @@ export default () => { ...@@ -43,6 +45,7 @@ export default () => {
service: this.service, service: this.service,
nodeActionsAllowed: this.nodeActionsAllowed, nodeActionsAllowed: this.nodeActionsAllowed,
nodeEditAllowed: this.nodeEditAllowed, nodeEditAllowed: this.nodeEditAllowed,
geoTroubleshootingHelpPath: this.geoTroubleshootingHelpPath,
}, },
}); });
}, },
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
.node-health-message { .node-health-message {
margin-bottom: 0; margin-bottom: 0;
padding: 2px $gl-padding-8; padding: $gl-padding;
background-color: $red-100; background-color: $red-100;
color: $red-500; color: $red-500;
} }
......
...@@ -48,6 +48,7 @@ module EE ...@@ -48,6 +48,7 @@ module EE
def authenticate_user def authenticate_user
return super unless geo_request? return super unless geo_request?
return render_bad_geo_auth('Bad token') unless decoded_authorization return render_bad_geo_auth('Bad token') unless decoded_authorization
return render_bad_geo_auth('Unauthorized scope') unless jwt_scope_valid?
# grant access # grant access
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code, :push_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables @authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code, :push_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
...@@ -57,6 +58,14 @@ module EE ...@@ -57,6 +58,14 @@ module EE
render_bad_geo_auth("Invalid signature time ") render_bad_geo_auth("Invalid signature time ")
end end
def jwt_scope_valid?
decoded_authorization[:scope] == repository.full_path
end
def repository
wiki? ? project.wiki.repository : project.repository
end
def decoded_authorization def decoded_authorization
strong_memoize(:decoded_authorization) do strong_memoize(:decoded_authorization) do
::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode
......
...@@ -30,7 +30,8 @@ module EE ...@@ -30,7 +30,8 @@ module EE
primary_version: version.to_s, primary_version: version.to_s,
primary_revision: revision.to_s, primary_revision: revision.to_s,
node_actions_allowed: ::Gitlab::Database.db_read_write?.to_s, node_actions_allowed: ::Gitlab::Database.db_read_write?.to_s,
node_edit_allowed: ::Gitlab::Geo.license_allows?.to_s node_edit_allowed: ::Gitlab::Geo.license_allows?.to_s,
geo_troubleshooting_help_path: help_page_path('administration/geo/replication/troubleshooting.md')
} }
end end
......
...@@ -6,6 +6,8 @@ module EE ...@@ -6,6 +6,8 @@ module EE
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
prepended do prepended do
expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: ::RolloutStatusEntity
expose :logs_path, if: -> (*) { can_read_pod_logs? } do |environment| expose :logs_path, if: -> (*) { can_read_pod_logs? } do |environment|
logs_project_environment_path(environment.project, environment) logs_project_environment_path(environment.project, environment)
end end
...@@ -16,5 +18,9 @@ module EE ...@@ -16,5 +18,9 @@ module EE
def can_read_pod_logs? def can_read_pod_logs?
can?(current_user, :read_pod_logs, environment.project) can?(current_user, :read_pod_logs, environment.project)
end end
def can_read_deploy_board?
can?(current_user, :read_deploy_board, environment.project)
end
end end
end end
...@@ -6,13 +6,7 @@ module FeatureFlags ...@@ -6,13 +6,7 @@ module FeatureFlags
protected protected
def audit_enabled?
Feature.enabled?(:feature_flag_audit, project, default_enabled: true)
end
def audit_event(feature_flag) def audit_event(feature_flag)
return unless audit_enabled?
message = audit_message(feature_flag) message = audit_message(feature_flag)
return if message.blank? return if message.blank?
...@@ -33,7 +27,7 @@ module FeatureFlags ...@@ -33,7 +27,7 @@ module FeatureFlags
end end
def save_audit_event(audit_event) def save_audit_event(audit_event)
return unless audit_event # feature_flag_audit is disabled or audit_message is blank return unless audit_event
audit_event.security_event audit_event.security_event
end end
......
...@@ -101,7 +101,7 @@ module Geo ...@@ -101,7 +101,7 @@ module Geo
# Build a JWT header for authentication # Build a JWT header for authentication
def jwt_authentication_header def jwt_authentication_header
authorization = ::Gitlab::Geo::RepoSyncRequest.new( authorization = ::Gitlab::Geo::RepoSyncRequest.new(
scope: project.repository.full_path scope: repository.full_path
).authorization ).authorization
{ "http.#{remote_url}.extraHeader" => "Authorization: #{authorization}" } { "http.#{remote_url}.extraHeader" => "Authorization: #{authorization}" }
......
---
title: Enforce Geo JWT tokens scope for repository sync
merge_request: 10303
author:
type: changed
---
title: 'Geo: Help admins diagnose configuration problems'
merge_request: 9988
author:
type: added
...@@ -15,182 +15,120 @@ describe 'User creates feature flag', :js do ...@@ -15,182 +15,120 @@ describe 'User creates feature flag', :js do
end end
context 'when creates without changing scopes' do context 'when creates without changing scopes' do
shared_examples 'succesfully creates feature flag' do before do
before do visit(new_project_feature_flag_path(project))
visit(new_project_feature_flag_path(project)) set_feature_flag_info('ci_live_trace', 'For live trace')
set_feature_flag_info('ci_live_trace', 'For live trace') click_button 'Create feature flag'
click_button 'Create feature flag' expect(page).to have_current_path(project_feature_flags_path(project))
expect(page).to have_current_path(project_feature_flags_path(project)) end
end
it 'shows the created feature flag' do it 'shows the created feature flag' do
within_feature_flag_row(1) do within_feature_flag_row(1) do
expect(page.find('.feature-flag-name')).to have_content('ci_live_trace') expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
expect(page).to have_css('.js-feature-flag-status .badge-success') expect(page).to have_css('.js-feature-flag-status .badge-success')
within_feature_flag_scopes do within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*') expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active') expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active')
end
end end
end end
end end
context 'when feature flag audit enabled' do it 'records audit event' do
include_examples 'succesfully creates feature flag' visit(project_audit_events_path(project))
it 'records audit event' do
visit(project_audit_events_path(project))
expect(page).to have_text("Created feature flag ci live trace with description \"For live trace\".") expect(page).to have_text("Created feature flag ci live trace with description \"For live trace\".")
end
end
context 'when feature flag audit is disabled' do
before do
stub_feature_flags(feature_flag_audit: false)
end
include_examples 'succesfully creates feature flag'
it 'does not record audit event' do
visit(project_audit_events_path(project))
expect(page).to have_no_text("Created feature flag")
end
end end
end end
context 'when creates with disabling the default scope' do context 'when creates with disabling the default scope' do
shared_examples 'succesfully creates feature flag' do before do
before do visit(new_project_feature_flag_path(project))
visit(new_project_feature_flag_path(project)) set_feature_flag_info('ci_live_trace', 'For live trace')
set_feature_flag_info('ci_live_trace', 'For live trace')
within_scope_row(1) do
within_status { find('.project-feature-toggle').click }
end
click_button 'Create feature flag' within_scope_row(1) do
within_status { find('.project-feature-toggle').click }
end end
it 'shows the created feature flag' do click_button 'Create feature flag'
within_feature_flag_row(1) do
expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
expect(page).to have_css('.js-feature-flag-status .badge-danger')
within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-inactive')
end
end
end
end end
context 'when feature flag audit enabled' do it 'shows the created feature flag' do
include_examples 'succesfully creates feature flag' within_feature_flag_row(1) do
end expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
expect(page).to have_css('.js-feature-flag-status .badge-danger')
context 'when feature flag audit is disabled' do within_feature_flag_scopes do
before do expect(page.find('.badge:nth-child(1)')).to have_content('*')
stub_feature_flags(feature_flag_audit: false) expect(page.find('.badge:nth-child(1)')['class']).to include('badge-inactive')
end
end end
include_examples 'succesfully creates feature flag'
end end
end end
context 'when creates with an additional scope' do context 'when creates with an additional scope' do
shared_examples 'succesfully creates feature flag' do before do
before do visit(new_project_feature_flag_path(project))
visit(new_project_feature_flag_path(project)) set_feature_flag_info('mr_train', '')
set_feature_flag_info('mr_train', '')
within_scope_row(2) do
within_scope_row(2) do within_environment_spec do
within_environment_spec do find('.js-env-input').set("review/*")
find('.js-env-input').set("review/*") find('.js-create-button').click
find('.js-create-button').click
end
end
within_scope_row(2) do
within_status { find('.project-feature-toggle').click }
end end
click_button 'Create feature flag'
end end
it 'shows the created feature flag' do within_scope_row(2) do
within_feature_flag_row(1) do within_status { find('.project-feature-toggle').click }
expect(page.find('.feature-flag-name')).to have_content('mr_train')
expect(page).to have_css('.js-feature-flag-status .badge-success')
within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active')
expect(page.find('.badge:nth-child(2)')).to have_content('review/*')
expect(page.find('.badge:nth-child(2)')['class']).to include('badge-active')
end
end
end end
end
context 'when feature flag audit enabled' do click_button 'Create feature flag'
include_examples 'succesfully creates feature flag'
end end
context 'when feature flag audit is disabled' do it 'shows the created feature flag' do
before do within_feature_flag_row(1) do
stub_feature_flags(feature_flag_audit: false) expect(page.find('.feature-flag-name')).to have_content('mr_train')
end expect(page).to have_css('.js-feature-flag-status .badge-success')
include_examples 'succesfully creates feature flag' within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active')
expect(page.find('.badge:nth-child(2)')).to have_content('review/*')
expect(page.find('.badge:nth-child(2)')['class']).to include('badge-active')
end
end
end end
end end
context 'when searches an environment name for scope creation' do context 'when searches an environment name for scope creation' do
let!(:environment) { create(:environment, name: 'production', project: project) } let!(:environment) { create(:environment, name: 'production', project: project) }
shared_examples 'succesfully creates feature flag' do before do
before do visit(new_project_feature_flag_path(project))
visit(new_project_feature_flag_path(project)) set_feature_flag_info('mr_train', '')
set_feature_flag_info('mr_train', '')
within_scope_row(2) do within_scope_row(2) do
within_environment_spec do within_environment_spec do
find('.js-env-input').set('prod') find('.js-env-input').set('prod')
click_button 'production' click_button 'production'
end
end end
click_button 'Create feature flag'
end end
it 'shows the created feature flag' do click_button 'Create feature flag'
within_feature_flag_row(1) do
expect(page.find('.feature-flag-name')).to have_content('mr_train')
expect(page).to have_css('.js-feature-flag-status .badge-success')
within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active')
expect(page.find('.badge:nth-child(2)')).to have_content('production')
expect(page.find('.badge:nth-child(2)')['class']).to include('badge-inactive')
end
end
end
end end
context 'when feature flag audit enabled' do it 'shows the created feature flag' do
include_examples 'succesfully creates feature flag' within_feature_flag_row(1) do
end expect(page.find('.feature-flag-name')).to have_content('mr_train')
expect(page).to have_css('.js-feature-flag-status .badge-success')
context 'when feature flag audit is disabled' do within_feature_flag_scopes do
before do expect(page.find('.badge:nth-child(1)')).to have_content('*')
stub_feature_flags(feature_flag_audit: false) expect(page.find('.badge:nth-child(1)')['class']).to include('badge-active')
expect(page.find('.badge:nth-child(2)')).to have_content('production')
expect(page.find('.badge:nth-child(2)')['class']).to include('badge-inactive')
end
end end
include_examples 'succesfully creates feature flag'
end end
end end
......
...@@ -37,154 +37,94 @@ describe 'User updates feature flag', :js do ...@@ -37,154 +37,94 @@ describe 'User updates feature flag', :js do
end end
context 'when user updates a status of a scope' do context 'when user updates a status of a scope' do
shared_examples 'succesfully updates feature flag' do before do
before do within_scope_row(2) do
within_scope_row(2) do within_status { find('.project-feature-toggle').click }
within_status { find('.project-feature-toggle').click }
end
click_button 'Save changes'
expect(page).to have_current_path(project_feature_flags_path(project))
end end
it 'shows the updated feature flag' do click_button 'Save changes'
within_feature_flag_row(1) do expect(page).to have_current_path(project_feature_flags_path(project))
expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
expect(page).to have_css('.js-feature-flag-status .badge-danger')
within_feature_flag_scopes do
expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-inactive')
expect(page.find('.badge:nth-child(2)')).to have_content('review/*')
expect(page.find('.badge:nth-child(2)')['class']).to include('badge-inactive')
end
end
end
end end
context 'when feature flag audit enabled' do it 'shows the updated feature flag' do
include_examples 'succesfully updates feature flag' within_feature_flag_row(1) do
expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
expect(page).to have_css('.js-feature-flag-status .badge-danger')
it 'records audit event' do within_feature_flag_scopes do
visit(project_audit_events_path(project)) expect(page.find('.badge:nth-child(1)')).to have_content('*')
expect(page.find('.badge:nth-child(1)')['class']).to include('badge-inactive')
expect(page).to( expect(page.find('.badge:nth-child(2)')).to have_content('review/*')
have_text("Updated feature flag ci live trace. Updated rule review/* active state from true to false.") expect(page.find('.badge:nth-child(2)')['class']).to include('badge-inactive')
) end
end end
end end
context 'when feature flag audit is disabled' do it 'records audit event' do
before do visit(project_audit_events_path(project))
stub_feature_flags(feature_flag_audit: false)
end
include_examples 'succesfully updates feature flag'
it 'does not record audit event' do expect(page).to(
visit(project_audit_events_path(project)) have_text("Updated feature flag ci live trace. Updated rule review/* active state from true to false.")
)
expect(page).to have_no_text("Updated feature flag")
end
end end
end end
context 'when user adds a new scope' do context 'when user adds a new scope' do
shared_examples 'succesfully updates feature flag' do before do
before do within_scope_row(3) do
within_scope_row(3) do within_environment_spec do
within_environment_spec do find('.js-env-input').set('production')
find('.js-env-input').set('production') find('.js-create-button').click
find('.js-create-button').click
end
end end
click_button 'Save changes'
expect(page).to have_current_path(project_feature_flags_path(project))
end end
it 'shows the newly created scope' do click_button 'Save changes'
within_feature_flag_row(1) do expect(page).to have_current_path(project_feature_flags_path(project))
within_feature_flag_scopes do
expect(page.find('.badge:nth-child(3)')).to have_content('production')
expect(page.find('.badge:nth-child(3)')['class']).to include('badge-inactive')
end
end
end
end end
context 'when feature flag audit enabled' do it 'shows the newly created scope' do
include_examples 'succesfully updates feature flag' within_feature_flag_row(1) do
within_feature_flag_scopes do
it 'records audit event' do expect(page.find('.badge:nth-child(3)')).to have_content('production')
visit(project_audit_events_path(project)) expect(page.find('.badge:nth-child(3)')['class']).to include('badge-inactive')
end
expect(page).to(
have_text("Updated feature flag ci live trace")
)
end end
end end
context 'when feature flag audit is disabled' do it 'records audit event' do
before do visit(project_audit_events_path(project))
stub_feature_flags(feature_flag_audit: false)
end
include_examples 'succesfully updates feature flag'
it 'does not record audit event' do expect(page).to(
visit(project_audit_events_path(project)) have_text("Updated feature flag ci live trace")
)
expect(page).to have_no_text("Updated feature flag")
end
end end
end end
context 'when user deletes a scope' do context 'when user deletes a scope' do
shared_examples 'succesfully updates feature flag' do before do
before do within_scope_row(2) do
within_scope_row(2) do within_delete { find('.js-delete-scope').click }
within_delete { find('.js-delete-scope').click }
end
click_button 'Save changes'
expect(page).to have_current_path(project_feature_flags_path(project))
end end
it 'shows the updated feature flag' do click_button 'Save changes'
within_feature_flag_row(1) do expect(page).to have_current_path(project_feature_flags_path(project))
within_feature_flag_scopes do
expect(page).to have_css('.badge:nth-child(1)')
expect(page).not_to have_css('.badge:nth-child(2)')
end
end
end
end end
context 'when feature flag audit enabled' do it 'shows the updated feature flag' do
include_examples 'succesfully updates feature flag' within_feature_flag_row(1) do
within_feature_flag_scopes do
it 'records audit event' do expect(page).to have_css('.badge:nth-child(1)')
visit(project_audit_events_path(project)) expect(page).not_to have_css('.badge:nth-child(2)')
end
expect(page).to(
have_text("Updated feature flag ci live trace")
)
end end
end end
context 'when feature flag audit is disabled' do it 'records audit event' do
before do visit(project_audit_events_path(project))
stub_feature_flags(feature_flag_audit: false)
end
include_examples 'succesfully updates feature flag' expect(page).to(
have_text("Updated feature flag ci live trace")
it 'does not record audit event' do )
visit(project_audit_events_path(project))
expect(page).to have_no_text("Updated feature flag")
end
end end
end end
end end
require 'spec_helper'
describe GroupProjectsFinder do
include_context 'GroupProjectsFinder context'
subject { finder.execute }
describe 'with an auditor current user' do
let(:current_user) { create(:user, :auditor) }
context 'only shared' do
let(:options) { { only_shared: true } }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
end
context 'only owned' do
let(:options) { { only_owned: true } }
it { is_expected.to eq([private_project, public_project]) }
end
context 'all' do
subject { described_class.new(group: group, current_user: current_user).execute }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
end
end
end
...@@ -5,4 +5,93 @@ describe IssuesFinder do ...@@ -5,4 +5,93 @@ describe IssuesFinder do
let!(:subject) { create(:issue, project: project) } let!(:subject) { create(:issue, project: project) }
let(:project_params) { { project_id: project.id } } let(:project_params) { { project_id: project.id } }
end end
describe '#execute' do
include_context 'IssuesFinder context'
include_context 'IssuesFinder#execute context'
context 'scope: all' do
let(:scope) { 'all' }
describe 'filter by weight' do
set(:issue_with_weight_1) { create(:issue, project: project3, weight: 1) }
set(:issue_with_weight_42) { create(:issue, project: project3, weight: 42) }
context 'filter issues with no weight' do
let(:params) { { weight: Issue::WEIGHT_NONE } }
it 'returns all issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
end
end
context 'filter issues with any weight' do
let(:params) { { weight: Issue::WEIGHT_ANY } }
it 'returns all issues' do
expect(issues).to contain_exactly(issue_with_weight_1, issue_with_weight_42)
end
end
context 'filter issues with a specific weight' do
let(:params) { { weight: 42 } }
it 'returns all issues' do
expect(issues).to contain_exactly(issue_with_weight_42)
end
end
end
context 'filtering by assignee IDs' do
set(:user3) { create(:user) }
let(:params) { { assignee_ids: [user2.id, user3.id] } }
before do
project2.add_developer(user3)
issue3.assignees = [user2, user3]
end
it 'returns issues assigned to those users' do
expect(issues).to contain_exactly(issue3)
end
end
end
end
describe '#with_confidentiality_access_check' do
let(:guest) { create(:user) }
set(:authorized_user) { create(:user) }
set(:project) { create(:project, namespace: authorized_user.namespace) }
set(:public_issue) { create(:issue, project: project) }
set(:confidential_issue) { create(:issue, project: project, confidential: true) }
context 'when no project filter is given' do
let(:params) { {} }
context 'for an auditor' do
let(:auditor_user) { create(:user, :auditor) }
subject { described_class.new(auditor_user, params).with_confidentiality_access_check }
it 'returns all issues' do
expect(subject).to include(public_issue, confidential_issue)
end
end
end
context 'when searching within a specific project' do
let(:params) { { project_id: project.id } }
context 'for an auditor' do
let(:auditor_user) { create(:user, :auditor) }
subject { described_class.new(auditor_user, params).with_confidentiality_access_check }
it 'returns all issues' do
expect(subject).to include(public_issue, confidential_issue)
end
end
end
end
end end
...@@ -5,4 +5,16 @@ describe MergeRequestsFinder do ...@@ -5,4 +5,16 @@ describe MergeRequestsFinder do
let!(:subject) { create(:merge_request, source_project: project) } let!(:subject) { create(:merge_request, source_project: project) }
let(:project_params) { { project_id: project.id } } let(:project_params) { { project_id: project.id } }
end end
describe '#execute' do
include_context 'MergeRequestsFinder multiple projects with merge requests context'
it 'ignores filtering by weight' do
params = { project_id: project1.id, scope: 'authored', state: 'opened', weight: Issue::WEIGHT_ANY }
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(merge_request1)
end
end
end end
...@@ -33,4 +33,21 @@ describe SnippetsFinder do ...@@ -33,4 +33,21 @@ describe SnippetsFinder do
expect(results).to be_empty expect(results).to be_empty
end end
end end
context 'filter by project' do
set(:user) { create(:user) }
set(:group) { create(:group, :public) }
set(:project) { create(:project, :public, group: group) }
set(:private_project_snippet) { create(:project_snippet, :private, project: project) }
set(:internal_project_snippet) { create(:project_snippet, :internal, project: project) }
set(:public_project_snippet) { create(:project_snippet, :public, project: project) }
it 'returns all snippets for auditor users' do
user = create(:user, :auditor)
snippets = described_class.new(user, project: project).execute
expect(snippets).to include(private_project_snippet, internal_project_snippet, public_project_snippet)
end
end
end end
require 'spec_helper'
describe UsersFinder do
describe '#execute' do
include_context 'UsersFinder#execute filter by project context'
context 'with a normal user' do
context 'with LDAP users' do
let!(:ldap_user) { create(:omniauth_user, provider: 'ldap') }
it 'returns ldap users by default' do
users = described_class.new(normal_user).execute
expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user, ldap_user)
end
it 'returns only non-ldap users with skip_ldap: true' do
users = described_class.new(normal_user, skip_ldap: true).execute
expect(users).to contain_exactly(normal_user, blocked_user, omniauth_user)
end
end
end
end
end
...@@ -26,6 +26,7 @@ const createComponent = () => { ...@@ -26,6 +26,7 @@ const createComponent = () => {
service, service,
nodeActionsAllowed: true, nodeActionsAllowed: true,
nodeEditAllowed: true, nodeEditAllowed: true,
geoTroubleshootingHelpPath: '/foo/bar',
}); });
}; };
......
...@@ -17,6 +17,7 @@ const createComponent = ({ ...@@ -17,6 +17,7 @@ const createComponent = ({
nodeDetails, nodeDetails,
nodeActionsAllowed, nodeActionsAllowed,
nodeEditAllowed, nodeEditAllowed,
geoTroubleshootingHelpPath: '/foo/bar',
}); });
}; };
...@@ -80,5 +81,17 @@ describe('GeoNodeDetailsComponent', () => { ...@@ -80,5 +81,17 @@ describe('GeoNodeDetailsComponent', () => {
it('renders container elements correctly', () => { it('renders container elements correctly', () => {
expect(vm.$el.classList.contains('card-body')).toBe(true); expect(vm.$el.classList.contains('card-body')).toBe(true);
}); });
it('renders troubleshooting URL within error message section', done => {
vm.nodeDetails.healthy = false;
vm.errorMessage = 'Foobar';
vm.$nextTick(() => {
expect(vm.$el.querySelector('.node-health-message-container a').getAttribute('href')).toBe(
'/foo/bar',
);
done();
});
});
}); });
}); });
...@@ -13,6 +13,7 @@ const createComponent = (node = mockNode) => { ...@@ -13,6 +13,7 @@ const createComponent = (node = mockNode) => {
primaryNode: true, primaryNode: true,
nodeActionsAllowed: true, nodeActionsAllowed: true,
nodeEditAllowed: true, nodeEditAllowed: true,
geoTroubleshootingHelpPath: '/foo/bar',
}); });
}; };
...@@ -147,8 +148,11 @@ describe('GeoNodeItemComponent', () => { ...@@ -147,8 +148,11 @@ describe('GeoNodeItemComponent', () => {
vm.isNodeDetailsFailed = true; vm.isNodeDetailsFailed = true;
vm.errorMessage = err; vm.errorMessage = err;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(vm.$el.querySelectorAll('p.health-message').length).not.toBe(0); expect(vm.$el.querySelectorAll('p.node-health-message').length).not.toBe(0);
expect(vm.$el.querySelector('p.health-message').innerText.trim()).toBe(err); expect(vm.$el.querySelector('p.node-health-message').innerText.trim()).toContain(err);
expect(vm.$el.querySelector('p.node-health-message a').getAttribute('href')).toBe(
'/foo/bar',
);
done(); done();
}); });
}); });
......
...@@ -11,6 +11,7 @@ const createComponent = () => { ...@@ -11,6 +11,7 @@ const createComponent = () => {
nodes: mockNodes, nodes: mockNodes,
nodeActionsAllowed: true, nodeActionsAllowed: true,
nodeEditAllowed: true, nodeEditAllowed: true,
geoTroubleshootingHelpPath: '/foo/bar',
}); });
}; };
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ReferenceExtractor do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
before do
group.add_developer(project.creator)
end
subject { described_class.new(project, project.creator) }
it 'accesses valid epics' do
stub_licensed_features(epics: true)
@e0 = create(:epic, group: group)
@e1 = create(:epic, group: group)
@e2 = create(:epic, group: create(:group, :private))
text = "#{@e0.to_reference(group, full: true)}, &999, #{@e1.to_reference(group, full: true)}, #{@e2.to_reference(group, full: true)}"
subject.analyze(text, { group: group })
expect(subject.epics).to match_array([@e0, @e1])
end
end
...@@ -12,7 +12,7 @@ describe "Git HTTP requests (Geo)" do ...@@ -12,7 +12,7 @@ describe "Git HTTP requests (Geo)" do
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
# Ensure the token always comes from the real time of the request # Ensure the token always comes from the real time of the request
let!(:auth_token) { Gitlab::Geo::BaseRequest.new(scope: "repository-#{project.id}").authorization } let!(:auth_token) { Gitlab::Geo::BaseRequest.new(scope: project.full_path).authorization }
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:user_without_any_access) { create(:user) } let!(:user_without_any_access) { create(:user) }
let!(:user_without_push_access) { create(:user) } let!(:user_without_push_access) { create(:user) }
...@@ -21,6 +21,7 @@ describe "Git HTTP requests (Geo)" do ...@@ -21,6 +21,7 @@ describe "Git HTTP requests (Geo)" do
let!(:key_for_user_without_push_access) { create(:key, user: user_without_push_access) } let!(:key_for_user_without_push_access) { create(:key, user: user_without_push_access) }
let(:env) { valid_geo_env } let(:env) { valid_geo_env }
let(:auth_token_with_invalid_scope) { Gitlab::Geo::BaseRequest.new(scope: "invalid").authorization }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
...@@ -346,6 +347,53 @@ describe "Git HTTP requests (Geo)" do ...@@ -346,6 +347,53 @@ describe "Git HTTP requests (Geo)" do
end end
end end
end end
context 'invalid scope' do
subject do
make_request
response
end
def make_request
get "/#{repository_path}.git/info/refs", params: { service: 'git-upload-pack' }, headers: env
end
shared_examples_for 'unauthorized because of invalid scope' do
it { is_expected.to have_gitlab_http_status(:unauthorized) }
it 'returns correct error' do
expect(subject.parsed_body).to eq('Geo JWT authentication failed: Unauthorized scope')
end
end
context 'invalid scope of Geo JWT token' do
let(:repository_path) { project.full_path }
let(:env) { geo_env(auth_token_with_invalid_scope) }
include_examples 'unauthorized because of invalid scope'
end
context 'Geo JWT token scopes for wiki and repository are not interchangeable' do
context 'for a repository but using a wiki scope' do
let(:repository_path) { project.full_path }
let(:scope) { project.wiki.full_path }
let(:auth_token_with_valid_wiki_scope) { Gitlab::Geo::BaseRequest.new(scope: scope).authorization }
let(:env) { geo_env(auth_token_with_valid_wiki_scope) }
include_examples 'unauthorized because of invalid scope'
end
context 'for a wiki but using a repository scope' do
let(:project) { create(:project, :wiki_repo) }
let(:repository_path) { project.wiki.full_path }
let(:scope) { project.full_path }
let(:auth_token_with_valid_repository_scope) { Gitlab::Geo::BaseRequest.new(scope: scope).authorization }
let(:env) { geo_env(auth_token_with_valid_repository_scope) }
include_examples 'unauthorized because of invalid scope'
end
end
end
end end
def valid_geo_env def valid_geo_env
......
# frozen_string_literal: true
require 'spec_helper'
describe Emails::CreateService do
let(:user) { create(:user) }
let(:opts) { { email: 'new@email.com', user: user } }
subject(:service) { described_class.new(user, opts) }
describe '#execute' do
it 'registers a security event' do
stub_licensed_features(extended_audit_events: true)
expect { service.execute }.to change { SecurityEvent.count }.by(1)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Emails::DestroyService do
let!(:user) { create(:user) }
let!(:email) { create(:email, user: user) }
subject(:service) { described_class.new(user, user: user) }
describe '#execute' do
it 'registers a security event' do
stub_licensed_features(extended_audit_events: true)
expect { service.execute(email) }.to change { SecurityEvent.count }.by(1)
end
end
end
...@@ -55,22 +55,6 @@ describe FeatureFlags::CreateService do ...@@ -55,22 +55,6 @@ describe FeatureFlags::CreateService do
expect { subject }.to change { AuditEvent.count }.by(1) expect { subject }.to change { AuditEvent.count }.by(1)
expect(AuditEvent.last.present.action).to eq(expected_message) expect(AuditEvent.last.present.action).to eq(expected_message)
end end
context 'when feature flag audit is disabled' do
before do
stub_feature_flags(feature_flag_audit: false)
end
it { expect(subject[:status]).to eq(:success) }
it 'creates feature flag' do
expect { subject }.to change { Operations::FeatureFlag.count }.by(1)
end
it 'does not create audit log' do
expect { subject }.not_to change { AuditEvent.count }
end
end
end end
end end
end end
...@@ -24,20 +24,6 @@ describe FeatureFlags::DestroyService do ...@@ -24,20 +24,6 @@ describe FeatureFlags::DestroyService do
expect(audit_event_message).to eq("Deleted feature flag <strong>#{feature_flag.name.tr('_', ' ')}</strong>.") expect(audit_event_message).to eq("Deleted feature flag <strong>#{feature_flag.name.tr('_', ' ')}</strong>.")
end end
context 'when feature flag audit is disabled' do
before do
stub_feature_flags(feature_flag_audit: false)
end
it 'works successfully' do
expect(subject[:status]).to eq(:success)
end
it 'does not create audit event' do
expect { subject }.not_to change { AuditEvent.count }
end
end
context 'when feature flag can not be destroyed' do context 'when feature flag can not be destroyed' do
before do before do
allow(feature_flag).to receive(:destroy).and_return(false) allow(feature_flag).to receive(:destroy).and_return(false)
......
...@@ -29,24 +29,6 @@ describe FeatureFlags::UpdateService do ...@@ -29,24 +29,6 @@ describe FeatureFlags::UpdateService do
) )
end end
shared_examples 'disabled feature flag audit' do
context 'when feature flag audit is disabled' do
before do
stub_feature_flags(feature_flag_audit: false)
end
it 'returns success status' do
expect(subject[:status]).to eq(:success)
end
it 'does not create audit event' do
expect { subject }.not_to change { AuditEvent.count }
end
end
end
include_examples 'disabled feature flag audit'
context 'with invalid params' do context 'with invalid params' do
let(:params) { { name: nil } } let(:params) { { name: nil } }
...@@ -85,8 +67,6 @@ describe FeatureFlags::UpdateService do ...@@ -85,8 +67,6 @@ describe FeatureFlags::UpdateService do
" to <strong>\"new description\"</strong>.") " to <strong>\"new description\"</strong>.")
) )
end end
include_examples 'disabled feature flag audit'
end end
context 'when active state is changed' do context 'when active state is changed' do
...@@ -103,8 +83,6 @@ describe FeatureFlags::UpdateService do ...@@ -103,8 +83,6 @@ describe FeatureFlags::UpdateService do
"from <strong>true</strong> to <strong>false</strong>.") "from <strong>true</strong> to <strong>false</strong>.")
) )
end end
include_examples 'disabled feature flag audit'
end end
context 'when scope is renamed' do context 'when scope is renamed' do
...@@ -123,8 +101,6 @@ describe FeatureFlags::UpdateService do ...@@ -123,8 +101,6 @@ describe FeatureFlags::UpdateService do
) )
end end
include_examples 'disabled feature flag audit'
context 'when scope can not be updated' do context 'when scope can not be updated' do
let(:params) do let(:params) do
{ {
...@@ -159,8 +135,6 @@ describe FeatureFlags::UpdateService do ...@@ -159,8 +135,6 @@ describe FeatureFlags::UpdateService do
expect(audit_event_message).to include("Deleted rule <strong>review</strong>.") expect(audit_event_message).to include("Deleted rule <strong>review</strong>.")
end end
include_examples 'disabled feature flag audit'
context 'when scope can not be deleted' do context 'when scope can not be deleted' do
RSpec::Matchers.define_negated_matcher :not_change, :change RSpec::Matchers.define_negated_matcher :not_change, :change
...@@ -191,8 +165,6 @@ describe FeatureFlags::UpdateService do ...@@ -191,8 +165,6 @@ describe FeatureFlags::UpdateService do
) )
end end
include_examples 'disabled feature flag audit'
context 'when scope can not be created' do context 'when scope can not be created' do
let(:new_environment_scope) { '' } let(:new_environment_scope) { '' }
......
...@@ -5,6 +5,7 @@ module Gitlab ...@@ -5,6 +5,7 @@ module Gitlab
module Importer module Importer
class RepositoryImporter class RepositoryImporter
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include Gitlab::Utils::StrongMemoize
attr_reader :project, :client, :wiki_formatter attr_reader :project, :client, :wiki_formatter
...@@ -17,7 +18,7 @@ module Gitlab ...@@ -17,7 +18,7 @@ module Gitlab
# Returns true if we should import the wiki for the project. # Returns true if we should import the wiki for the project.
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def import_wiki? def import_wiki?
client.repository(project.import_source)&.has_wiki && client_repository&.has_wiki &&
!project.wiki_repository_exists? && !project.wiki_repository_exists? &&
Gitlab::GitalyClient::RemoteService.exists?(wiki_url) Gitlab::GitalyClient::RemoteService.exists?(wiki_url)
end end
...@@ -52,6 +53,7 @@ module Gitlab ...@@ -52,6 +53,7 @@ module Gitlab
refmap = Gitlab::GithubImport.refmap refmap = Gitlab::GithubImport.refmap
project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github') project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github')
project.change_head(default_branch) if default_branch
true true
rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e
fail_import("Failed to import the repository: #{e.message}") fail_import("Failed to import the repository: #{e.message}")
...@@ -82,6 +84,18 @@ module Gitlab ...@@ -82,6 +84,18 @@ module Gitlab
project.import_state.mark_as_failed(message) project.import_state.mark_as_failed(message)
false false
end end
private
def default_branch
client_repository&.default_branch
end
def client_repository
strong_memoize(:client_repository) do
client.repository(project.import_source)
end
end
end end
end end
end end
......
...@@ -84,6 +84,7 @@ module Gitlab ...@@ -84,6 +84,7 @@ module Gitlab
projects: count(Project), projects: count(Project),
projects_imported_from_github: count(Project.where(import_type: 'github')), projects_imported_from_github: count(Project.where(import_type: 'github')),
projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)), projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)),
projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
protected_branches: count(ProtectedBranch), protected_branches: count(ProtectedBranch),
releases: count(Release), releases: count(Release),
remote_mirrors: count(RemoteMirror), remote_mirrors: count(RemoteMirror),
......
...@@ -4938,6 +4938,9 @@ msgstr "" ...@@ -4938,6 +4938,9 @@ msgstr ""
msgid "Geo|Pending verification" msgid "Geo|Pending verification"
msgstr "" msgstr ""
msgid "Geo|Please refer to Geo Troubleshooting."
msgstr ""
msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk." msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is safe to remove this entry, as this will not remove any data on disk."
msgstr "" msgstr ""
...@@ -5444,6 +5447,9 @@ msgstr[1] "" ...@@ -5444,6 +5447,9 @@ msgstr[1] ""
msgid "Hide values" msgid "Hide values"
msgstr "" msgstr ""
msgid "Highest role:"
msgstr ""
msgid "History" msgid "History"
msgstr "" msgstr ""
......
...@@ -5,15 +5,6 @@ module QA ...@@ -5,15 +5,6 @@ module QA
describe 'Issue creation' do describe 'Issue creation' do
let(:issue_title) { 'issue title' } let(:issue_title) { 'issue title' }
def create_issue
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Resource::Issue.fabricate! do |issue|
issue.title = issue_title
end
end
it 'user creates an issue' do it 'user creates an issue' do
create_issue create_issue
...@@ -46,6 +37,15 @@ module QA ...@@ -46,6 +37,15 @@ module QA
end end
end end
end end
def create_issue
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Resource::Issue.fabricate! do |issue|
issue.title = issue_title
end
end
end end
end end
end end
require 'spec_helper' require 'spec_helper'
describe GroupProjectsFinder do describe GroupProjectsFinder do
let(:group) { create(:group) } include_context 'GroupProjectsFinder context'
let(:subgroup) { create(:group, parent: group) }
let(:current_user) { create(:user) }
let(:options) { {} }
let(:finder) { described_class.new(group: group, current_user: current_user, options: options) }
let!(:public_project) { create(:project, :public, group: group, path: '1') }
let!(:private_project) { create(:project, :private, group: group, path: '2') }
let!(:shared_project_1) { create(:project, :public, path: '3') }
let!(:shared_project_2) { create(:project, :private, path: '4') }
let!(:shared_project_3) { create(:project, :internal, path: '5') }
let!(:subgroup_project) { create(:project, :public, path: '6', group: subgroup) }
let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
before do
shared_project_1.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
shared_project_2.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
shared_project_3.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
end
subject { finder.execute } subject { finder.execute }
...@@ -162,25 +143,6 @@ describe GroupProjectsFinder do ...@@ -162,25 +143,6 @@ describe GroupProjectsFinder do
end end
end end
describe 'with an auditor current user' do
let(:current_user) { create(:user, :auditor) }
context "only shared" do
let(:options) { { only_shared: true } }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
end
context "only owned" do
let(:options) { { only_owned: true } }
it { is_expected.to eq([private_project, public_project]) }
end
context "all" do
subject { described_class.new(group: group, current_user: current_user).execute }
it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
end
end
describe "no user" do describe "no user" do
context "only shared" do context "only shared" do
let(:options) { { only_shared: true } } let(:options) { { only_shared: true } }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment