Commit a3e92224 authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into ce-to-ee-2018-09-07

parents 6b2a3fd2 bef4c774
...@@ -48,6 +48,16 @@ export const dasherize = str => str.replace(/[_\s]+/g, '-'); ...@@ -48,6 +48,16 @@ export const dasherize = str => str.replace(/[_\s]+/g, '-');
*/ */
export const slugify = str => str.trim().toLowerCase(); export const slugify = str => str.trim().toLowerCase();
/**
* Replaces whitespaces with hyphens and converts to lower case
* @param {String} str
* @returns {String}
*/
export const slugifyWithHyphens = str => {
const regex = new RegExp(/\s+/, 'g');
return str.toLowerCase().replace(regex, '-');
};
/** /**
* Truncates given text * Truncates given text
* *
......
import $ from 'jquery'; import $ from 'jquery';
import { getParameterValues } from '../lib/utils/url_utility'; import { getParameterValues } from '../lib/utils/url_utility';
import projectNew from './project_new';
export default () => { export default () => {
const path = getParameterValues('path')[0]; const pathParam = getParameterValues('path')[0];
const nameParam = getParameterValues('name')[0];
const $projectPath = $('.js-path-name');
const $projectName = $('.js-project-name');
// get the path url and append it in the inputS // get the path url and append it in the input
$('.js-path-name').val(path); $projectPath.val(pathParam);
// get the project name from the URL and set it as input value
$projectName.val(nameParam);
// generate slug when project name changes
$projectName.keyup(() => projectNew.onProjectNameChange($projectName, $projectPath));
}; };
import $ from 'jquery'; import $ from 'jquery';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils'; import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import { slugifyWithHyphens } from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false; let hasUserDefinedProjectPath = false;
...@@ -29,18 +30,23 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => { ...@@ -29,18 +30,23 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => {
} }
}; };
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugifyWithHyphens($projectNameInput.val());
$projectPathInput.val(slug);
};
const bindEvents = () => { const bindEvents = () => {
const $newProjectForm = $('#new_project'); const $newProjectForm = $('#new_project');
const $projectImportUrl = $('#project_import_url'); const $projectImportUrl = $('#project_import_url');
const $projectPath = $('#project_path'); const $projectPath = $('.tab-pane.active #project_path');
const $useTemplateBtn = $('.template-button > input'); const $useTemplateBtn = $('.template-button > input');
const $projectFieldsForm = $('.project-fields-form'); const $projectFieldsForm = $('.project-fields-form');
const $selectedTemplateText = $('.selected-template'); const $selectedTemplateText = $('.selected-template');
const $changeTemplateBtn = $('.change-template'); const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon'); const $selectedIcon = $('.selected-icon');
const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip'); const $pushNewProjectTipTrigger = $('.push-new-project-tip');
const $projectTemplateButtons = $('.project-templates-buttons'); const $projectTemplateButtons = $('.project-templates-buttons');
const $projectName = $('.tab-pane.active #project_name');
if ($newProjectForm.length !== 1) { if ($newProjectForm.length !== 1) {
return; return;
...@@ -57,7 +63,8 @@ const bindEvents = () => { ...@@ -57,7 +63,8 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').on('click', () => { $('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href'); const importHref = $('a.btn_import_gitlab_project').attr('href');
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`); $('.btn_import_gitlab_project')
.attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&name=${$projectName.val()}&path=${$projectPath.val()}`);
}); });
if ($pushNewProjectTipTrigger) { if ($pushNewProjectTipTrigger) {
...@@ -111,7 +118,15 @@ const bindEvents = () => { ...@@ -111,7 +118,15 @@ const bindEvents = () => {
const selectedTemplate = templates[value]; const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text); $selectedTemplateText.text(selectedTemplate.text);
$(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon); $(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon);
$templateProjectNameInput.focus();
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
$activeTabProjectName
.keyup(() => {
onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
});
} }
$useTemplateBtn.on('change', chooseTemplate); $useTemplateBtn.on('change', chooseTemplate);
...@@ -137,9 +152,15 @@ const bindEvents = () => { ...@@ -137,9 +152,15 @@ const bindEvents = () => {
$projectMirror.attr('disabled', !$projectMirror.attr('disabled')); $projectMirror.attr('disabled', !$projectMirror.attr('disabled'));
}); });
$projectName.keyup(() => {
onProjectNameChange($projectName, $projectPath);
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
}; };
export default { export default {
bindEvents, bindEvents,
deriveProjectPathFromUrl, deriveProjectPathFromUrl,
onProjectNameChange,
}; };
...@@ -221,6 +221,7 @@ module ApplicationSettingsHelper ...@@ -221,6 +221,7 @@ module ApplicationSettingsHelper
:recaptcha_enabled, :recaptcha_enabled,
:recaptcha_private_key, :recaptcha_private_key,
:recaptcha_site_key, :recaptcha_site_key,
:receive_max_input_size,
:repository_checks_enabled, :repository_checks_enabled,
:repository_storages, :repository_storages,
:require_two_factor_authentication, :require_two_factor_authentication,
......
...@@ -17,7 +17,10 @@ ...@@ -17,7 +17,10 @@
= render 'repository_size_limit_setting', form: f = render 'repository_size_limit_setting', form: f
.form-group .form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-bold' = f.label :receive_max_input_size, 'Maximum push size (MB)', class: 'label-light'
= f.number_field :receive_max_input_size, class: 'form-control'
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
= f.number_field :session_expire_delay, class: 'form-control' = f.number_field :session_expire_delay, class: 'form-control'
%span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes %span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group .form-group
......
...@@ -8,8 +8,11 @@ ...@@ -8,8 +8,11 @@
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do = form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
.row .row
.form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold'
= text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control input-lg", autofocus: true, required: true
.form-group.col-12.col-sm-6 .form-group.col-12.col-sm-6
= label_tag :namespace_id, 'Project path', class: 'label-bold' = label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group .form-group
.input-group .input-group
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
...@@ -24,8 +27,8 @@ ...@@ -24,8 +27,8 @@
#{user_url(current_user.username)}/ #{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id = hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path .form-group.col-12.col-sm-6.project-path
= label_tag :path, _('Project name'), class: 'label-bold' = label_tag :path, _('Project slug'), class: 'label-bold'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, required: true
.row .row
.form-group.col-md-12 .form-group.col-md-12
......
...@@ -3,10 +3,13 @@ ...@@ -3,10 +3,13 @@
.row{ id: project_name_id } .row{ id: project_name_id }
= f.hidden_field :ci_cd_only, value: ci_cd_only = f.hidden_field :ci_cd_only, value: ci_cd_only
.form-group.project-name.col-sm-12
= f.label :name, class: 'label-bold' do
%span= _("Project name")
= f.text_field :name, placeholder: "My awesome project", class: "form-control input-lg", autofocus: true, required: true
.form-group.project-path.col-sm-6 .form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do = f.label :namespace_id, class: 'label-bold' do
%span %span= s_("Project URL")
Project path
.input-group .input-group
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
.input-group-prepend.has-tooltip{ title: root_url } .input-group-prepend.has-tooltip{ title: root_url }
...@@ -27,13 +30,12 @@ ...@@ -27,13 +30,12 @@
= f.hidden_field :namespace_id, value: current_user.namespace_id = f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6 .form-group.project-path.col-sm-6
= f.label :path, class: 'label-bold' do = f.label :path, class: 'label-bold' do
%span %span= _("Project slug")
Project name = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, required: true
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
- if current_user.can_create_group? - if current_user.can_create_group?
.form-text.text-muted .form-text.text-muted
Want to house several dependent projects under the same namespace? Want to house several dependent projects under the same namespace?
= link_to "Create a group", new_group_path = link_to "Create a group.", new_group_path
.form-group .form-group
= f.label :description, class: 'label-bold' do = f.label :description, class: 'label-bold' do
......
---
title: Re-add project name field on "Create new project" page
merge_request: 21386
author:
type: other
---
title: Allow admins to configure the maximum Git push size
merge_request: 20758
author:
type: added
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddReceiveMaxInputSizeToApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :application_settings, :receive_max_input_size, :integer
end
end
...@@ -219,6 +219,7 @@ ActiveRecord::Schema.define(version: 20180906101639) do ...@@ -219,6 +219,7 @@ ActiveRecord::Schema.define(version: 20180906101639) do
t.boolean "user_show_add_ssh_key_message", default: true, null: false t.boolean "user_show_add_ssh_key_message", default: true, null: false
t.integer "custom_project_templates_group_id" t.integer "custom_project_templates_group_id"
t.integer "usage_stats_set_by_user_id" t.integer "usage_stats_set_by_user_id"
t.integer "receive_max_input_size"
end end
create_table "approvals", force: :cascade do |t| create_table "approvals", force: :cascade do |t|
......
# Custom Git Hooks # Custom Git Hooks
> **Note:** Custom Git hooks must be configured on the filesystem of the GitLab > **Note:** Custom Git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks. > server. Only GitLab server administrators will be able to complete these tasks.
Please explore [webhooks] and [CI] as an option if you do not > Please explore [webhooks] and [CI] as an option if you do not
have filesystem access. For a user configurable Git hook interface, see > have filesystem access. For a user configurable Git hook interface, see
[Push Rules](https://docs.gitlab.com/ee/push_rules/push_rules.html), > [Push Rules](https://docs.gitlab.com/ee/push_rules/push_rules.html),
available in GitLab Enterprise Edition. > available in GitLab Enterprise Edition.
> >
**Note:** Custom Git hooks won't be replicated to secondary nodes if you use [GitLab Geo][gitlab-geo] > **Note:** Custom Git hooks won't be replicated to secondary nodes if you use [GitLab Geo][gitlab-geo]
Git natively supports hooks that are executed on different actions. Git natively supports hooks that are executed on different actions.
Examples of server-side git hooks include pre-receive, post-receive, and update. Examples of server-side git hooks include pre-receive, post-receive, and update.
......
...@@ -25,16 +25,16 @@ To bring the former primary up to date: ...@@ -25,16 +25,16 @@ To bring the former primary up to date:
sudo gitlab-ctl start sudo gitlab-ctl start
``` ```
>**Note 1:** If you [disabled primary permanently][disaster-recovery-disable-primary], > **Note 1:** If you [disabled primary permanently][disaster-recovery-disable-primary],
you need to undo those steps now. For Debian/Ubuntu you just need to run > you need to undo those steps now. For Debian/Ubuntu you just need to run
`sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install > `sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install
the GitLab instance from scratch and setup it as a secondary node by > the GitLab instance from scratch and setup it as a secondary node by
following [Setup instructions][setup-geo]. In this case you don't need to follow the next step. > following [Setup instructions][setup-geo]. In this case you don't need to follow the next step.
>
>**Note 2:** If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domains-dns-record) > **Note 2:** If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domains-dns-record)
for this node during disaster recovery procedure you may need to [block > for this node during disaster recovery procedure you may need to [block
all the writes to this node](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/planned-failover.md#block-primary-traffic) > all the writes to this node](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/planned-failover.md#block-primary-traffic)
during this procedure. > during this procedure.
1. [Setup database replication][database-replication]. Note that in this 1. [Setup database replication][database-replication]. Note that in this
case, primary refers to the current primary, and secondary refers to the case, primary refers to the current primary, and secondary refers to the
......
...@@ -151,7 +151,6 @@ be specified. ...@@ -151,7 +151,6 @@ be specified.
gitlab_rails['redis_host'] = '10.1.2.1' gitlab_rails['redis_host'] = '10.1.2.1'
gitlab_rails['redis_password'] = 'Redis password' gitlab_rails['redis_password'] = 'Redis password'
## ##
## Enable the geo secondary role and configure the ## Enable the geo secondary role and configure the
## geo tracking database ## geo tracking database
......
# Geo (Geo Replication) **[PREMIUM ONLY]** # Geo (Geo Replication) **[PREMIUM ONLY]**
> **Notes:** > **Notes:**
- Geo is part of [GitLab Premium][ee] > - Geo is part of [GitLab Premium][ee]
- Introduced in GitLab Enterprise Edition 8.9 > - Introduced in GitLab Enterprise Edition 8.9
We recommend you use it with at least GitLab Enterprise Edition 10.0 for > We recommend you use it with at least GitLab Enterprise Edition 10.0 for
basic Geo features, or latest version for a better experience > basic Geo features, or latest version for a better experience
- You should make sure that all nodes run the same GitLab version > - You should make sure that all nodes run the same GitLab version
- Geo requires PostgreSQL 9.6 and Git 2.9 in addition to GitLab's usual > - Geo requires PostgreSQL 9.6 and Git 2.9 in addition to GitLab's usual
[minimum requirements][install-requirements] > [minimum requirements][install-requirements]
- Using Geo in combination with High Availability (HA) is considered **Generally Available** (GA) in GitLab Enterprise Edition 10.4 > - Using Geo in combination with High Availability (HA) is considered **Generally Available** (GA) in GitLab Enterprise Edition 10.4
>
>**Note:** > **Note:**
Geo changes significantly from release to release. Upgrades **are** > Geo changes significantly from release to release. Upgrades **are**
supported and [documented](#updating-the-geo-nodes), but you should ensure that > supported and [documented](#updating-the-geo-nodes), but you should ensure that
you're following the right version of the documentation for your installation! > you're following the right version of the documentation for your installation!
The best way to do this is to follow the documentation from the `/help` endpoint > The best way to do this is to follow the documentation from the `/help` endpoint
on your **primary** node, but you can also navigate to [this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/README.md) > on your **primary** node, but you can also navigate to [this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/README.md)
and choose the appropriate release from the `tags` dropdown, e.g., `v10.0.0-ee`. > and choose the appropriate release from the `tags` dropdown, e.g., `v10.0.0-ee`.
Geo allows you to replicate your GitLab instance to other geographical Geo allows you to replicate your GitLab instance to other geographical
locations as a read-only fully operational version. locations as a read-only fully operational version.
...@@ -202,9 +202,8 @@ Read how to [replicate the Container Registry][docker-registry]. ...@@ -202,9 +202,8 @@ Read how to [replicate the Container Registry][docker-registry].
extra limitations may be in place. extra limitations may be in place.
- Pushing code to a secondary redirects the request to the primary instead of handling it directly [gitlab-ee#1381](https://gitlab.com/gitlab-org/gitlab-ee/issues/1381): - Pushing code to a secondary redirects the request to the primary instead of handling it directly [gitlab-ee#1381](https://gitlab.com/gitlab-org/gitlab-ee/issues/1381):
* Only push via HTTP is currently supported * Push via HTTP and SSH supported
* Git LFS is supported * Git LFS also supported
* Pushing via SSH is currently not supported: [gitlab-ee#5387](https://gitlab.com/gitlab-org/gitlab-ee/issues/5387)
- The primary node has to be online for OAuth login to happen (existing sessions and Git are not affected) - The primary node has to be online for OAuth login to happen (existing sessions and Git are not affected)
- The installation takes multiple manual steps that together can take about an hour depending on circumstances; we are - The installation takes multiple manual steps that together can take about an hour depending on circumstances; we are
working on improving this experience, see [gitlab-org/omnibus-gitlab#2978] for details. working on improving this experience, see [gitlab-org/omnibus-gitlab#2978] for details.
......
...@@ -49,7 +49,8 @@ authentication method. ...@@ -49,7 +49,8 @@ authentication method.
# Every node that runs Unicorn or Sidekiq needs to have the database # Every node that runs Unicorn or Sidekiq needs to have the database
# password specified as below. If you have a high-availability setup, this # password specified as below. If you have a high-availability setup, this
# must be present in all application nodes. # must be present in all application nodes.
gitlab_rails['db_password'] = 'mypassword' ``` gitlab_rails['db_password'] = 'mypassword'
```
Still in the configuration file, locate and remove the `trust_auth_cidr_address`: Still in the configuration file, locate and remove the `trust_auth_cidr_address`:
......
...@@ -6,13 +6,12 @@ PostgreSQL. This is the database that will be installed if you use the ...@@ -6,13 +6,12 @@ PostgreSQL. This is the database that will be installed if you use the
Omnibus package to manage your database. Omnibus package to manage your database.
> Important notes: > Important notes:
- This document will focus only on configuration supported with [GitLab Premium](https://about.gitlab.com/pricing/), using the Omnibus GitLab package. > - This document will focus only on configuration supported with [GitLab Premium](https://about.gitlab.com/pricing/), using the Omnibus GitLab package.
- If you are a Community Edition or Starter user, consider using a cloud hosted solution. > - If you are a Community Edition or Starter user, consider using a cloud hosted solution.
- This document will not cover installations from source. > - This document will not cover installations from source.
> >
- If HA setup is not what you were looking for, see the [database configuration document](http://docs.gitlab.com/omnibus/settings/database.html) > - If HA setup is not what you were looking for, see the [database configuration document](http://docs.gitlab.com/omnibus/settings/database.html)
for the Omnibus GitLab packages. > for the Omnibus GitLab packages.
## Configure your own database server ## Configure your own database server
...@@ -31,12 +30,10 @@ If you use a cloud-managed service, or provide your own PostgreSQL: ...@@ -31,12 +30,10 @@ If you use a cloud-managed service, or provide your own PostgreSQL:
## Configure using Omnibus for High Availability ## Configure using Omnibus for High Availability
> Please read this document fully before attempting to configure PostgreSQL HA
> for GitLab.
> >
Please read this document fully before attempting to configure PostgreSQL HA > This configuration is GA in EE 10.2.
for GitLab.
>
This configuration is GA in EE 10.2.
The recommended configuration for a PostgreSQL HA requires: The recommended configuration for a PostgreSQL HA requires:
...@@ -94,12 +91,12 @@ Similarly, PostgreSQL access is controlled based on the network source. ...@@ -94,12 +91,12 @@ Similarly, PostgreSQL access is controlled based on the network source.
This is why you will need: This is why you will need:
> IP address of each nodes network interface > IP address of each nodes network interface
- This can be set to `0.0.0.0` to listen on all interfaces. It cannot > - This can be set to `0.0.0.0` to listen on all interfaces. It cannot
be set to the loopack address `127.0.0.1` > be set to the loopack address `127.0.0.1`
>
> Network Address > Network Address
- This can be in subnet (i.e. `192.168.0.0/255.255.255.0`) or CIDR (i.e. > - This can be in subnet (i.e. `192.168.0.0/255.255.255.0`) or CIDR (i.e.
`192.168.0.0/24`) form. > `192.168.0.0/24`) form.
#### User information #### User information
...@@ -115,10 +112,12 @@ When using default setup, minimum configuration requires: ...@@ -115,10 +112,12 @@ When using default setup, minimum configuration requires:
- `CONSUL_USERNAME`. Defaults to `gitlab-consul` - `CONSUL_USERNAME`. Defaults to `gitlab-consul`
- `CONSUL_DATABASE_PASSWORD`. Password for the database user. - `CONSUL_DATABASE_PASSWORD`. Password for the database user.
- `CONSUL_PASSWORD_HASH`. This is a hash generated out of consul username/password pair. - `CONSUL_PASSWORD_HASH`. This is a hash generated out of consul username/password pair.
Can be generated with: Can be generated with:
```sh ```sh
sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME
``` ```
- `CONSUL_SERVER_NODES`. The IP addresses or DNS records of the Consul server nodes. - `CONSUL_SERVER_NODES`. The IP addresses or DNS records of the Consul server nodes.
Few notes on the service itself: Few notes on the service itself:
...@@ -141,7 +140,7 @@ This is used to prevent replication from using up all of the ...@@ -141,7 +140,7 @@ This is used to prevent replication from using up all of the
available database connections. available database connections.
> Note: > Note:
- In this document we are assuming 3 database nodes, which makes this configuration: > - In this document we are assuming 3 database nodes, which makes this configuration:
``` ```
postgresql['max_wal_senders'] = 4 postgresql['max_wal_senders'] = 4
...@@ -157,7 +156,8 @@ We will need the following password information for the application's database u ...@@ -157,7 +156,8 @@ We will need the following password information for the application's database u
- `POSTGRESQL_USERNAME`. Defaults to `gitlab` - `POSTGRESQL_USERNAME`. Defaults to `gitlab`
- `POSTGRESQL_USER_PASSWORD`. The password for the database user - `POSTGRESQL_USER_PASSWORD`. The password for the database user
- `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair. - `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair.
Can be generated with: Can be generated with:
```sh ```sh
sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
``` ```
...@@ -169,10 +169,12 @@ When using default setup, minimum configuration requires: ...@@ -169,10 +169,12 @@ When using default setup, minimum configuration requires:
- `PGBOUNCER_USERNAME`. Defaults to `pgbouncer` - `PGBOUNCER_USERNAME`. Defaults to `pgbouncer`
- `PGBOUNCER_PASSWORD`. This is a password for pgbouncer service. - `PGBOUNCER_PASSWORD`. This is a password for pgbouncer service.
- `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of pgbouncer username/password pair. - `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of pgbouncer username/password pair.
Can be generated with: Can be generated with:
```sh ```sh
sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME
``` ```
- `PGBOUNCER_NODE`, is the IP address or a FQDN of the node running Pgbouncer. - `PGBOUNCER_NODE`, is the IP address or a FQDN of the node running Pgbouncer.
Few notes on the service itself: Few notes on the service itself:
...@@ -321,11 +323,11 @@ check the [Troubleshooting section](#troubleshooting) before proceeding. ...@@ -321,11 +323,11 @@ check the [Troubleshooting section](#troubleshooting) before proceeding.
1. [Reconfigure GitLab] for the changes to take effect. 1. [Reconfigure GitLab] for the changes to take effect.
> Please note: > Please note:
- If you want your database to listen on a specific interface, change the config: > - If you want your database to listen on a specific interface, change the config:
`postgresql['listen_address'] = '0.0.0.0'` > `postgresql['listen_address'] = '0.0.0.0'`
- If your Pgbouncer service runs under a different user account, > - If your Pgbouncer service runs under a different user account,
you also need to specify: `postgresql['pgbouncer_user'] = PGBOUNCER_USERNAME` in > you also need to specify: `postgresql['pgbouncer_user'] = PGBOUNCER_USERNAME` in
your configuration > your configuration
#### Database nodes post-configuration #### Database nodes post-configuration
...@@ -374,6 +376,7 @@ Select one node as a primary node. ...@@ -374,6 +376,7 @@ Select one node as a primary node.
```sh ```sh
gitlab-ctl repmgr standby setup MASTER_NODE_NAME gitlab-ctl repmgr standby setup MASTER_NODE_NAME
``` ```
Do note that this will remove the existing data on the node. The command Do note that this will remove the existing data on the node. The command
has a wait time. has a wait time.
......
...@@ -91,7 +91,7 @@ for each GitLab application server in your environment. ...@@ -91,7 +91,7 @@ for each GitLab application server in your environment.
certificates are not present, Nginx will fail to start. See certificates are not present, Nginx will fail to start. See
[Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) [Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
for more information. for more information.
>
> **Note:** It is best to set the `uid` and `gid`s prior to the initial reconfigure of GitLab. Omnibus will not recursively `chown` directories if set after the initial reconfigure. > **Note:** It is best to set the `uid` and `gid`s prior to the initial reconfigure of GitLab. Omnibus will not recursively `chown` directories if set after the initial reconfigure.
## First GitLab application server ## First GitLab application server
......
...@@ -22,6 +22,7 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin ...@@ -22,6 +22,7 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin
1. Generate SQL_USER_PASSWORD_HASH with the command `gitlab-ctl pg-password-md5 gitlab`. We'll also need to enter the plaintext SQL_USER_PASSWORD later 1. Generate SQL_USER_PASSWORD_HASH with the command `gitlab-ctl pg-password-md5 gitlab`. We'll also need to enter the plaintext SQL_USER_PASSWORD later
1. On your database node, ensure the following is set in your `/etc/gitlab/gitlab.rb` 1. On your database node, ensure the following is set in your `/etc/gitlab/gitlab.rb`
```ruby ```ruby
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH' postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH'
postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH' postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH'
......
# GitLab private Maven repository administration # GitLab private Maven repository administration
> **Notes:** > **Notes:**
- [Introduced][ee-5811] in GitLab 11.3. > - [Introduced][ee-5811] in GitLab 11.3.
- This document is about the admin guide. Learn how to use GitLab Maven > - This document is about the admin guide. Learn how to use GitLab Maven
repository from [user documentation](../user/project/maven_packages.md). > repository from [user documentation](../user/project/maven_packages.md).
When enabled, every project in GitLab will have its own space to store Maven packages. When enabled, every project in GitLab will have its own space to store Maven packages.
......
# GitLab ChatOps **[ULTIMATE]** # GitLab ChatOps **[ULTIMATE]**
> **Notes:**
> **Notes:**
>
> * [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. > * [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
>
> * ChatOps is currently in alpha, with some important features missing like access control. > * ChatOps is currently in alpha, with some important features missing like access control.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow. GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
......
...@@ -9,7 +9,7 @@ you may need to enable pipeline triggering in your project's ...@@ -9,7 +9,7 @@ you may need to enable pipeline triggering in your project's
## Pipelines ## Pipelines
A pipeline is a group of [jobs][] that get executed in [stages][](batches). A pipeline is a group of [jobs] that get executed in [stages].
All of the jobs in a stage are executed in parallel (if there are enough All of the jobs in a stage are executed in parallel (if there are enough
concurrent [Runners]), and if they all succeed, the pipeline moves on to the concurrent [Runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the jobs fails, the next stage is not (usually) next stage. If one of the jobs fails, the next stage is not (usually)
...@@ -29,17 +29,17 @@ There are three types of pipelines that often use the single shorthand of "pipel ...@@ -29,17 +29,17 @@ There are three types of pipelines that often use the single shorthand of "pipel
![Types of Pipelines](img/types-of-pipelines.svg) ![Types of Pipelines](img/types-of-pipelines.svg)
1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml` 1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`.
2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production 1. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production.
3. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. 1. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
## Development workflows ## Development workflows
Pipelines accommodate several development workflows: Pipelines accommodate several development workflows:
1. **Branch Flow** (e.g. different branch for dev, qa, staging, production) 1. **Branch Flow** (e.g. different branch for dev, qa, staging, production).
2. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases) 1. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases).
3. **Fork-based Flow** (e.g. merge requests come from forks) 1. **Fork-based Flow** (e.g. merge requests come from forks).
Example continuous delivery flow: Example continuous delivery flow:
...@@ -57,6 +57,16 @@ Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in ...@@ -57,6 +57,16 @@ Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
See the reference [documentation for jobs](yaml/README.md#jobs). See the reference [documentation for jobs](yaml/README.md#jobs).
## Manually executing pipelines
Pipelines can be manually executed, with predefined or manually-specified [variables](variables/README.md).
To execute a pipeline manually:
1. Navigate to your project's **CI/CD > Pipelines**.
1. Click on the **Run Pipeline** button.
1. Select the branch to run the pipeline for and enter any environment variables required for the pipeline run.
## Seeing pipeline status ## Seeing pipeline status
You can find the current and historical pipeline runs under your project's You can find the current and historical pipeline runs under your project's
...@@ -112,9 +122,9 @@ Then, there is the pipeline mini graph which takes less space and can give you a ...@@ -112,9 +122,9 @@ Then, there is the pipeline mini graph which takes less space and can give you a
quick glance if all jobs pass or something failed. The pipeline mini graph can quick glance if all jobs pass or something failed. The pipeline mini graph can
be found when you visit: be found when you visit:
- the pipelines index page - The pipelines index page.
- a single commit page - A single commit page.
- a merge request page - A merge request page.
That way, you can see all related jobs for a single commit and the net result That way, you can see all related jobs for a single commit and the net result
of each stage of your pipeline. This allows you to quickly see what failed and of each stage of your pipeline. This allows you to quickly see what failed and
...@@ -142,9 +152,9 @@ jobs. Click to expand them. ...@@ -142,9 +152,9 @@ jobs. Click to expand them.
The basic requirements is that there are two numbers separated with one of The basic requirements is that there are two numbers separated with one of
the following (you can even use them interchangeably): the following (you can even use them interchangeably):
- a space - A space (` `)
- a slash (`/`) - A slash (`/`)
- a colon (`:`) - A colon (`:`)
>**Note:** >**Note:**
More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`. More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`.
...@@ -257,11 +267,12 @@ A strict security model is enforced when pipelines are executed on ...@@ -257,11 +267,12 @@ A strict security model is enforced when pipelines are executed on
The following actions are allowed on protected branches only if the user is The following actions are allowed on protected branches only if the user is
[allowed to merge or push](../user/project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings) [allowed to merge or push](../user/project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings)
on that specific branch: on that specific branch:
- run **manual pipelines** (using Web UI or Pipelines API)
- run **scheduled pipelines** - Run **manual pipelines** (using [Web UI](#manually-executing-pipelines) or Pipelines API).
- run pipelines using **triggers** - Run **scheduled pipelines**.
- trigger **manual actions** on existing pipelines - Run pipelines using **triggers**.
- **retry/cancel** existing jobs (using Web UI or Pipelines API) - Trigger **manual actions** on existing pipelines.
- **Retry/cancel** existing jobs (using Web UI or Pipelines API).
**Variables** marked as **protected** are accessible only to jobs that **Variables** marked as **protected** are accessible only to jobs that
run on protected branches, avoiding untrusted users to get unintended access to run on protected branches, avoiding untrusted users to get unintended access to
......
...@@ -34,7 +34,7 @@ Some of the predefined environment variables are available only if a minimum ...@@ -34,7 +34,7 @@ Some of the predefined environment variables are available only if a minimum
version of [GitLab Runner][runner] is used. Consult the table below to find the version of [GitLab Runner][runner] is used. Consult the table below to find the
version of Runner required. version of Runner required.
>**Note:** NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated some variables. Read the Starting with GitLab 9.0, we have deprecated some variables. Read the
[9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are [9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are
strongly advised to use the new variables as we will remove the old ones in strongly advised to use the new variables as we will remove the old ones in
...@@ -112,7 +112,7 @@ To follow conventions of naming across GitLab, and to further move away from the ...@@ -112,7 +112,7 @@ To follow conventions of naming across GitLab, and to further move away from the
`build` term and toward `job` CI variables have been renamed for the 9.0 `build` term and toward `job` CI variables have been renamed for the 9.0
release. release.
>**Note:** NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated the `$CI_BUILD_*` variables. **You are Starting with GitLab 9.0, we have deprecated the `$CI_BUILD_*` variables. **You are
strongly advised to use the new variables as we will remove the old ones in strongly advised to use the new variables as we will remove the old ones in
future GitLab releases.** future GitLab releases.**
...@@ -134,7 +134,7 @@ future GitLab releases.** ...@@ -134,7 +134,7 @@ future GitLab releases.**
## `.gitlab-ci.yml` defined variables ## `.gitlab-ci.yml` defined variables
>**Note:** NOTE **Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher. This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in the GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in the
...@@ -236,9 +236,15 @@ Wildcards (`*`) can be used along with the environment name, therefore if the ...@@ -236,9 +236,15 @@ Wildcards (`*`) can be used along with the environment name, therefore if the
environment scope is `review/*` then any jobs with environment names starting environment scope is `review/*` then any jobs with environment names starting
with `review/` would have that particular variable. with `review/` would have that particular variable.
### Manually-specified variables
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/44059) in GitLab 10.8.
Variables can be specified for a single pipeline run when a [manual pipeline](../pipelines.md#manually-executing-pipelines) is created.
## Deployment variables ## Deployment variables
>**Note:** NOTE: **Note:**
This feature requires GitLab CI 8.15 or higher. This feature requires GitLab CI 8.15 or higher.
[Project services](../../user/project/integrations/project_services.md) that are [Project services](../../user/project/integrations/project_services.md) that are
......
...@@ -8,10 +8,10 @@ This document describes where and how the different types of variables can be us ...@@ -8,10 +8,10 @@ This document describes where and how the different types of variables can be us
## Variables usage ## Variables usage
There are basically two places where you can use any defined variables: There are two places defined variables can be used. On the:
1. On GitLab's side there's `.gitlab-ci.yml` 1. GitLab side, in `.gitlab-ci.yml`.
1. On the Runner's side there's `config.toml` 1. The runner side, in `config.toml`.
### `.gitlab-ci.yml` file ### `.gitlab-ci.yml` file
...@@ -56,8 +56,8 @@ since the expansion is done in GitLab before any Runner will get the job. ...@@ -56,8 +56,8 @@ since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism ### GitLab Runner internal variable expansion mechanism
- **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and - **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
variables from triggers and pipeline schedules variables from triggers, pipeline schedules, and manual pipelines.
- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`) - **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that only variables defined as `$variable` and `${variable}`. What's also important, is that
...@@ -80,11 +80,10 @@ are using a different variables syntax. ...@@ -80,11 +80,10 @@ are using a different variables syntax.
`.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules). `.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules).
- The `script` may also use all variables defined in the lines before. So, for example, if you define - The `script` may also use all variables defined in the lines before. So, for example, if you define
a variable `export MY_VARIABLE="test"`: a variable `export MY_VARIABLE="test"`:
- In `before_script`, it will work in the following lines of `before_script` and
- in `before_script`, it will work in the following lines of `before_script` and all lines of the related `script`.
all lines of the related `script` - In `script`, it will work in the following lines of `script`.
- in `script`, it will work in the following lines of `script` - In `after_script`, it will work in following lines of `after_script`.
- in `after_script`, it will work in following lines of `after_script`
## Persisted variables ## Persisted variables
...@@ -107,10 +106,10 @@ The following variables are known as "persisted": ...@@ -107,10 +106,10 @@ The following variables are known as "persisted":
They are: They are:
- **supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner" - **Supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner".
- **not supported:** - **Not supported:**
- by the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab" - By the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab".
- in the `only` and `except` [variables expressions](README.md#variables-expressions) - In the `only` and `except` [variables expressions](README.md#variables-expressions).
## Variables with an environment scope ## Variables with an environment scope
......
# Elasticsearch integration **[STARTER ONLY]** # Elasticsearch integration **[STARTER ONLY]**
> > [Introduced][ee-109] in GitLab [Starter][ee] 8.4. Support
[Introduced][ee-109] in GitLab [Starter][ee] 8.4. Support > for [Amazon Elasticsearch][aws-elastic] was [introduced][ee-1305] in GitLab
for [Amazon Elasticsearch][aws-elastic] was [introduced][ee-1305] in GitLab > [Starter][ee] 9.0.
[Starter][ee] 9.0.
This document describes how to set up Elasticsearch with GitLab. Once enabled, This document describes how to set up Elasticsearch with GitLab. Once enabled,
you'll have the benefit of fast search response times and the advantage of two you'll have the benefit of fast search response times and the advantage of two
......
...@@ -93,11 +93,11 @@ from the left navigation menu. Click `Link GitHub account` to start creating a n ...@@ -93,11 +93,11 @@ from the left navigation menu. Click `Link GitHub account` to start creating a n
for all the projects in the GitLab group you specified in the previous step. These are refreshed for all the projects in the GitLab group you specified in the previous step. These are refreshed
every 60 minutes. every 60 minutes.
>**Note:** > **Note:**
In the future, we plan on implementating real-time integration. If you need > In the future, we plan on implementating real-time integration. If you need
to refresh the data manually, you can do this from the `Applications -> DVCS > to refresh the data manually, you can do this from the `Applications -> DVCS
accounts` screen where you initially set up the integration: > accounts` screen where you initially set up the integration:
>
> ![Refresh GitLab information in JIRA](img/jira_dev_panel_manual_refresh.png) > ![Refresh GitLab information in JIRA](img/jira_dev_panel_manual_refresh.png)
To connect additional GitLab projects from other GitLab top-level groups (or personal namespaces), repeat the above To connect additional GitLab projects from other GitLab top-level groups (or personal namespaces), repeat the above
......
# External authorization control **[PREMIUM]** # External authorization control **[PREMIUM]**
> > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in
[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in > [GitLab Premium](https://about.gitlab.com/pricing) 10.6.
[GitLab Premium](https://about.gitlab.com/pricing) 10.6.
In highly controlled environments, it may be necessary for access policy to be In highly controlled environments, it may be necessary for access policy to be
controlled by an external service that permits access based on project controlled by an external service that permits access based on project
......
...@@ -83,14 +83,14 @@ To add an existing Kubernetes cluster to your project: ...@@ -83,14 +83,14 @@ To add an existing Kubernetes cluster to your project:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster. - **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- **Environment scope** (required)- The - **Environment scope** (required)- The
[associated environment](#setting-the-environment-scope) to this cluster. [associated environment](#setting-the-environment-scope) to this cluster.
* **API URL** (required) - - **API URL** (required) -
It's the URL that GitLab uses to access the Kubernetes API. Kubernetes It's the URL that GitLab uses to access the Kubernetes API. Kubernetes
exposes several APIs, we want the "base" URL that is common to all of them, exposes several APIs, we want the "base" URL that is common to all of them,
e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`. e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
* **CA certificate** (optional) - - **CA certificate** (optional) -
If the API is using a self-signed TLS certificate, you'll also need to include If the API is using a self-signed TLS certificate, you'll also need to include
the `ca.crt` contents here. the `ca.crt` contents here.
* **Token** - - **Token** -
GitLab authenticates against Kubernetes using service tokens, which are GitLab authenticates against Kubernetes using service tokens, which are
scoped to a particular `namespace`. If you don't have a service token yet, scoped to a particular `namespace`. If you don't have a service token yet,
you can follow the you can follow the
...@@ -104,10 +104,11 @@ To add an existing Kubernetes cluster to your project: ...@@ -104,10 +104,11 @@ To add an existing Kubernetes cluster to your project:
- Each project should have a unique namespace. - Each project should have a unique namespace.
- The project namespace is not necessarily the namespace of the secret, if - The project namespace is not necessarily the namespace of the secret, if
you're using a secret with broader permissions, like the secret from `default`. you're using a secret with broader permissions, like the secret from `default`.
* You should **not** use `default` as the project namespace. - You should **not** use `default` as the project namespace.
* If you or someone created a secret specifically for the project, usually - If you or someone created a secret specifically for the project, usually
with limited permissions, the secret's namespace and project namespace may with limited permissions, the secret's namespace and project namespace may
be the same. be the same.
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
......
...@@ -57,13 +57,12 @@ changes you made after picking the template and return it to its initial status. ...@@ -57,13 +57,12 @@ changes you made after picking the template and return it to its initial status.
## Setting a default template for issues and merge requests **[STARTER]** ## Setting a default template for issues and merge requests **[STARTER]**
> > **Notes:**
**Notes:** > - This feature was introduced before [description templates](#overview) and is
- This feature was introduced before [description templates](#overview) and is > available in [GitLab Starter][products]. It can be enabled
available in [GitLab Starter][products]. It can be enabled > in the project's settings.
in the project's settings. > - Templates for issues were [introduced][ee-28] in GitLab EE 8.1.
- Templates for issues were [introduced][ee-28] in GitLab EE 8.1. > - Templates for merge requests were [introduced][ee-7478ece] in GitLab EE 6.9.
- Templates for merge requests were [introduced][ee-7478ece] in GitLab EE 6.9.
The visibility of issues and/or merge requests should be set to either "Everyone The visibility of issues and/or merge requests should be set to either "Everyone
with access" or "Only team members" in your project's **Settings** otherwise the with access" or "Only team members" in your project's **Settings** otherwise the
......
# File Locking **[PREMIUM]** # File Locking **[PREMIUM]**
>**Notes:** > **Notes:**
- [Introduced][ee-440] in [GitLab Premium][ee] 8.9. > - [Introduced][ee-440] in [GitLab Premium][ee] 8.9.
- This feature needs to have a license with the "File Lock" option enabled. If > - This feature needs to have a license with the "File Lock" option enabled. If
you are using Premium but you don't see the "Lock" button, > you are using Premium but you don't see the "Lock" button,
ask your GitLab administrator. > ask your GitLab administrator.
File Locking helps you avoid merge conflicts and better manage your binary files. File Locking helps you avoid merge conflicts and better manage your binary files.
Lock any file or directory, make your changes, and then unlock it so another Lock any file or directory, make your changes, and then unlock it so another
......
...@@ -289,7 +289,7 @@ Each security vulnerability in the report is actionable. Clicking on an entry, ...@@ -289,7 +289,7 @@ Each security vulnerability in the report is actionable. Clicking on an entry,
a detailed information will pop up with two different possible options: a detailed information will pop up with two different possible options:
- **Dismiss vulnerability** - Dismissing a vulnerability will place a - **Dismiss vulnerability** - Dismissing a vulnerability will place a
~~strikethrough~~ styling on it. <s>strikethrough</s> styling on it.
- **Create issue** - The new issue will have the title and description - **Create issue** - The new issue will have the title and description
pre-populated with the information of the vulnerability report. pre-populated with the information of the vulnerability report.
......
...@@ -75,17 +75,16 @@ the following is possible: ...@@ -75,17 +75,16 @@ the following is possible:
- **They are not an eligible approver**: They cannot do anything with respect - **They are not an eligible approver**: They cannot do anything with respect
to approving this merge request. to approving this merge request.
- **They have not approved this merge request**: - **They have not approved this merge request**:
- If the required number of approvals has _not_ been yet met, they can approve - If the required number of approvals has _not_ been yet met, they can approve
it by clicking the displayed **Approve** button. it by clicking the displayed **Approve** button.
![Approve](img/approve.png) ![Approve](img/approve.png)
- If the required number of approvals has already been met, they can still - If the required number of approvals has already been met, they can still
approve it by clicking the displayed **Add approval** button. approve it by clicking the displayed **Add approval** button.
![Add approval](img/approve_additionally.png) ![Add approval](img/approve_additionally.png)
---
- **They have already approved this merge request**: They can remove their approval. - **They have already approved this merge request**: They can remove their approval.
![Remove approval](img/remove_approval.png) ![Remove approval](img/remove_approval.png)
......
# Burndown Charts **[STARTER]** # Burndown Charts **[STARTER]**
>**Notes:** > **Notes:**
- [Introduced][ee-1540] in [GitLab Starter 9.1][ee-9.1] for project milestones. > - [Introduced][ee-1540] in [GitLab Starter 9.1][ee-9.1] for project milestones.
- [Introduced][ee-5354] in [GitLab Premium 10.8][ee-10.8] for group milestones. > - [Introduced][ee-5354] in [GitLab Premium 10.8][ee-10.8] for group milestones.
- [Added][ee-6495] to [GitLab Starter 11.2][ee-11.2] for group milestones. > - [Added][ee-6495] to [GitLab Starter 11.2][ee-11.2] for group milestones.
- Closed or reopened issues prior to GitLab 9.1 won't have a `closed_at` > - Closed or reopened issues prior to GitLab 9.1 won't have a `closed_at`
value, so the burndown chart considers them as closed on the milestone > value, so the burndown chart considers them as closed on the milestone
`start_date`. In that case, a warning will be displayed. > `start_date`. In that case, a warning will be displayed.
## Overview ## Overview
...@@ -51,9 +51,8 @@ Find your project's **Burndown Chart** under **Project > Issues > Milestones**, ...@@ -51,9 +51,8 @@ Find your project's **Burndown Chart** under **Project > Issues > Milestones**,
and select a milestone from your current ones, while for group's, access the **Groups** dashboard, and select a milestone from your current ones, while for group's, access the **Groups** dashboard,
select a group, and go through **Issues > Milestones** on the sidebar. select a group, and go through **Issues > Milestones** on the sidebar.
> > **Note:** You're able to [promote project][promote-milestone] to group milestones and still
**Note:** You're able to [promote project][promote-milestone] to group milestones and still > see the **Burndown Chart** for them, respecting license limitations.
see the **Burndown Chart** for them, respecting license limitations.
The chart indicates the project's progress throughout that milestone (for issues assigned to it). The chart indicates the project's progress throughout that milestone (for issues assigned to it).
......
...@@ -157,6 +157,10 @@ into your `README.md`: ...@@ -157,6 +157,10 @@ into your `README.md`:
![coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage) ![coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
``` ```
### Environment Variables
[Environment variables](../../../ci/variables/README.html#variables) can be set in an environment to be available to a runner.
[var]: ../../../ci/yaml/README.md#git-strategy [var]: ../../../ci/yaml/README.md#git-strategy
[coverage report]: #test-coverage-parsing [coverage report]: #test-coverage-parsing
[timeout overriding]: ../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner [timeout overriding]: ../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner
......
# Advanced Global Search **[STARTER]** # Advanced Global Search **[STARTER]**
> > - [Introduced][ee-109] in GitLab [Starter][ee] 8.4.
- [Introduced][ee-109] in GitLab [Starter][ee] 8.4. > - This is the user documentation. To install and configure Elasticsearch,
- This is the user documentation. To install and configure Elasticsearch, > visit the [admin docs](../../integration/elasticsearch.md).
visit the [admin docs](../../integration/elasticsearch.md).
Leverage Elasticsearch for faster, more advanced code search across your entire Leverage Elasticsearch for faster, more advanced code search across your entire
GitLab instance. GitLab instance.
......
# Advanced Syntax Search **[STARTER]** # Advanced Syntax Search **[STARTER]**
>**Notes:** > **Notes:**
- Introduced in [GitLab Enterprise Starter][ee] 9.2 > - Introduced in [GitLab Enterprise Starter][ee] 9.2
- This is the user documentation. To install and configure Elasticsearch, > - This is the user documentation. To install and configure Elasticsearch,
visit the [admin docs](../../integration/elasticsearch.md). > visit the [admin docs](../../integration/elasticsearch.md).
Use advanced queries for more targeted search results. Use advanced queries for more targeted search results.
......
...@@ -86,9 +86,8 @@ one is located in `config.yml` of gitlab-shell. ...@@ -86,9 +86,8 @@ one is located in `config.yml` of gitlab-shell.
## Using GitLab git-annex ## Using GitLab git-annex
> > **Note:**
**Note:** > Your Git remotes must be using the SSH protocol, not HTTP(S).
Your Git remotes must be using the SSH protocol, not HTTP(S).
Here is an example workflow of uploading a very large file and then checking it Here is an example workflow of uploading a very large file and then checking it
into your Git repository: into your Git repository:
......
...@@ -152,6 +152,7 @@ if the server also has Git Annex 6 installed. Read more in the ...@@ -152,6 +152,7 @@ if the server also has Git Annex 6 installed. Read more in the
indirect ok indirect ok
ok ok
``` ```
--- ---
At this point, you have two options. Either add, commit and push the files At this point, you have two options. Either add, commit and push the files
......
import $ from 'jquery'; import $ from 'jquery';
import projectNew from '~/projects/project_new';
const bindEvents = () => { const bindEvents = () => {
const $newProjectForm = $('#new_project'); const $newProjectForm = $('#new_project');
...@@ -41,6 +42,12 @@ const bindEvents = () => { ...@@ -41,6 +42,12 @@ const bindEvents = () => {
$templateProjectNameInput.focus(); $templateProjectNameInput.focus();
enableCustomTemplate(); enableCustomTemplate();
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
$activeTabProjectName
.keyup(() => projectNew.onProjectNameChange($activeTabProjectName, $activeTabProjectPath));
} }
$useCustomTemplateBtn.on('change', chooseTemplate); $useCustomTemplateBtn.on('change', chooseTemplate);
......
...@@ -6,11 +6,24 @@ module EE ...@@ -6,11 +6,24 @@ module EE
override :render_ok override :render_ok
def render_ok def render_ok
set_workhorse_internal_api_content_type set_workhorse_internal_api_content_type
render json: ::Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name, show_all_refs: geo_request?) render json: ::Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name, show_all_refs: geo_request?)
end end
private private
def user
super || geo_push_user&.user
end
def geo_push_user
@geo_push_user ||= ::Geo::PushUser.new_from_headers(request.headers)
end
def geo_push_user_headers_provided?
::Geo::PushUser.needed_headers_provided?(request.headers)
end
def geo_request? def geo_request?
::Gitlab::Geo::JwtRequestDecoder.geo_auth_attempt?(request.headers['Authorization']) ::Gitlab::Geo::JwtRequestDecoder.geo_auth_attempt?(request.headers['Authorization'])
end end
...@@ -21,9 +34,11 @@ module EE ...@@ -21,9 +34,11 @@ module EE
override :access_actor override :access_actor
def access_actor def access_actor
return :geo if geo? return super unless geo?
return :geo unless geo_push_user_headers_provided?
return geo_push_user.user if geo_push_user.user
super raise ::Gitlab::GitAccess::UnauthorizedError, 'Geo push user is invalid.'
end end
override :authenticate_user override :authenticate_user
...@@ -32,7 +47,7 @@ module EE ...@@ -32,7 +47,7 @@ module EE
payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode
if payload if payload
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables @authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code, :push_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
return # grant access return # grant access
end end
......
# frozen_string_literal: true
class Geo::PushUser
include ::Gitlab::Identifier
def initialize(gl_id)
@gl_id = gl_id
end
def self.needed_headers_provided?(headers)
headers['Geo-GL-Id'].present?
end
def self.new_from_headers(headers)
return nil unless needed_headers_provided?(headers)
new(headers['Geo-GL-Id'])
end
def user
@user ||= identify_using_ssh_key(gl_id)
end
private
attr_reader :gl_id
end
---
title: Allow push_code when auth'd via Geo JWT
merge_request: 6455
author:
type: changed
---
title: 'Geo: SSH git push to secondary -> proxy to Primary'
merge_request: 6456
author:
type: added
require 'base64'
module API module API
class Geo < Grape::API class Geo < Grape::API
resource :geo do resource :geo do
...@@ -40,6 +42,51 @@ module API ...@@ -40,6 +42,51 @@ module API
render_validation_error!(db_status) render_validation_error!(db_status)
end end
end end
# git push over SSH secondary -> primary related proxying logic
#
resource 'proxy_git_push_ssh' do
format :json
# Responsible for making HTTP GET /repo.git/info/refs?service=git-receive-pack
# request *from* secondary gitlab-shell to primary
#
params do
requires :secret_token, type: String
requires :data, type: Hash do
requires :gl_id, type: String
requires :primary_repo, type: String
end
end
post 'info_refs' do
authenticate_by_gitlab_shell_token!
params.delete(:secret_token)
resp = Gitlab::Geo::GitPushSSHProxy.new(params['data']).info_refs
status(resp.code.to_i)
{ status: true, message: nil, result: Base64.encode64(resp.body.to_s) }
end
# Responsible for making HTTP POST /repo.git/git-receive-pack
# request *from* secondary gitlab-shell to primary
#
params do
requires :secret_token, type: String
requires :data, type: Hash do
requires :gl_id, type: String
requires :primary_repo, type: String
end
requires :output, type: String, desc: 'Output from git-receive-pack'
end
post 'push' do
authenticate_by_gitlab_shell_token!
params.delete(:secret_token)
resp = Gitlab::Geo::GitPushSSHProxy.new(params['data']).push(Base64.decode64(params['output']))
status(resp.code.to_i)
{ status: true, message: nil, result: Base64.encode64(resp.body.to_s) }
end
end
end end
end end
end end
...@@ -3,9 +3,19 @@ module EE ...@@ -3,9 +3,19 @@ module EE
module GeoGitAccess module GeoGitAccess
include ::Gitlab::ConfigHelper include ::Gitlab::ConfigHelper
include ::EE::GitlabRoutingHelper include ::EE::GitlabRoutingHelper
include GrapePathHelpers::NamedRouteMatcher
extend ::Gitlab::Utils::Override
GEO_SERVER_DOCS_URL = 'https://docs.gitlab.com/ee/administration/geo/replication/using_a_geo_server.html'.freeze GEO_SERVER_DOCS_URL = 'https://docs.gitlab.com/ee/administration/geo/replication/using_a_geo_server.html'.freeze
override :check_custom_action
def check_custom_action(cmd)
custom_action = custom_action_for(cmd)
return custom_action if custom_action
super
end
protected protected
def project_or_wiki def project_or_wiki
...@@ -14,6 +24,27 @@ module EE ...@@ -14,6 +24,27 @@ module EE
private private
def custom_action_for?(cmd)
return unless receive_pack?(cmd)
return unless ::Gitlab::Database.read_only?
::Gitlab::Geo.secondary_with_primary?
end
def custom_action_for(cmd)
return unless custom_action_for?(cmd)
payload = {
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => [api_v4_geo_proxy_git_push_ssh_info_refs_path, api_v4_geo_proxy_git_push_ssh_push_path],
'primary_repo' => geo_primary_http_url_to_repo(project_or_wiki)
}
}
::Gitlab::GitAccessResult::CustomAction.new(payload, 'Attempting to proxy to primary.')
end
def push_to_read_only_message def push_to_read_only_message
message = super message = super
......
...@@ -17,7 +17,7 @@ module EE ...@@ -17,7 +17,7 @@ module EE
override :whitelisted_routes override :whitelisted_routes
def whitelisted_routes def whitelisted_routes
super || geo_node_update_route super || geo_node_update_route || geo_proxy_git_push_ssh_route
end end
def geo_node_update_route def geo_node_update_route
...@@ -33,6 +33,14 @@ module EE ...@@ -33,6 +33,14 @@ module EE
WHITELISTED_GEO_ROUTES_TRACKING_DB[controller]&.include?(action) WHITELISTED_GEO_ROUTES_TRACKING_DB[controller]&.include?(action)
end end
end end
def geo_proxy_git_push_ssh_route
routes = ::Gitlab::Middleware::ReadOnly::API_VERSIONS.map do |version|
["/api/v#{version}/geo/proxy_git_push_ssh/info_refs",
"/api/v#{version}/geo/proxy_git_push_ssh/push"]
end
routes.flatten.include?(request.path)
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Geo
class GitPushSSHProxy
HTTP_READ_TIMEOUT = 10
HTTP_SUCCESS_CODE = '200'.freeze
MustBeASecondaryNode = Class.new(StandardError)
def initialize(data)
@data = data
end
def info_refs
ensure_secondary!
url = "#{primary_repo}/info/refs?service=git-receive-pack"
headers = {
'Content-Type' => 'application/x-git-upload-pack-request'
}
resp = get(url, headers)
return resp unless resp.code == HTTP_SUCCESS_CODE
resp.body = remove_http_service_fragment_from(resp.body)
resp
end
def push(info_refs_response)
ensure_secondary!
url = "#{primary_repo}/git-receive-pack"
headers = {
'Content-Type' => 'application/x-git-receive-pack-request',
'Accept' => 'application/x-git-receive-pack-result'
}
post(url, info_refs_response, headers)
end
private
attr_reader :data
def primary_repo
@primary_repo ||= data['primary_repo']
end
def gl_id
@gl_id ||= data['gl_id']
end
def base_headers
@base_headers ||= {
'Geo-GL-Id' => gl_id,
'Authorization' => Gitlab::Geo::BaseRequest.new.authorization
}
end
def get(url, headers)
request(url, Net::HTTP::Get, headers)
end
def post(url, body, headers)
request(url, Net::HTTP::Post, headers, body: body)
end
def request(url, klass, headers, body: nil)
headers = base_headers.merge(headers)
uri = URI.parse(url)
req = klass.new(uri, headers)
req.body = body if body
http = Net::HTTP.new(uri.hostname, uri.port)
http.read_timeout = HTTP_READ_TIMEOUT
http.use_ssl = true if uri.is_a?(URI::HTTPS)
http.start { http.request(req) }
end
def remove_http_service_fragment_from(body)
# HTTP(S) and SSH responses are very similar, except for the fragment below.
# As we're performing a git HTTP(S) request here, we'll get a HTTP(s)
# suitable git response. However, we're executing in the context of an
# SSH session so we need to make the response suitable for what git over
# SSH expects.
#
# See Downloading Data > HTTP(S) section at:
# https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols
body.gsub(/\A001f# service=git-receive-pack\n0000/, '')
end
def ensure_secondary!
raise MustBeASecondaryNode, 'Node is not a secondary' unless Gitlab::Geo.secondary_with_primary?
end
end
end
end
...@@ -48,6 +48,7 @@ describe 'Project' do ...@@ -48,6 +48,7 @@ describe 'Project' do
page.within '.project-fields-form' do page.within '.project-fields-form' do
fill_in("project_path", with: new_name) fill_in("project_path", with: new_name)
fill_in("project_name", with: new_name)
Sidekiq::Testing.inline! do Sidekiq::Testing.inline! do
click_button "Create project" click_button "Create project"
......
...@@ -90,6 +90,7 @@ describe 'New project' do ...@@ -90,6 +90,7 @@ describe 'New project' do
first('.js-import-git-toggle-button').click first('.js-import-git-toggle-button').click
fill_in 'project_import_url', with: 'http://foo.git' fill_in 'project_import_url', with: 'http://foo.git'
fill_in 'project_name', with: 'import-project-with-features1'
fill_in 'project_path', with: 'import-project-with-features1' fill_in 'project_path', with: 'import-project-with-features1'
choose 'project_visibility_level_20' choose 'project_visibility_level_20'
click_button 'Create project' click_button 'Create project'
...@@ -114,6 +115,7 @@ describe 'New project' do ...@@ -114,6 +115,7 @@ describe 'New project' do
find('.js-import-git-toggle-button').click find('.js-import-git-toggle-button').click
fill_in 'project_import_url', with: 'http://foo.git' fill_in 'project_import_url', with: 'http://foo.git'
fill_in 'project_name', with: 'CI CD Project1'
fill_in 'project_path', with: 'ci-cd-project1' fill_in 'project_path', with: 'ci-cd-project1'
choose 'project_visibility_level_20' choose 'project_visibility_level_20'
click_button 'Create project' click_button 'Create project'
......
...@@ -18,6 +18,7 @@ describe 'User creates a project', :js do ...@@ -18,6 +18,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do it 'creates a new project' do
visit(new_project_path) visit(new_project_path)
fill_in :project_name, with: 'a-new-project'
fill_in :project_path, with: 'a-new-project' fill_in :project_path, with: 'a-new-project'
page.find('.js-select-namespace').click page.find('.js-select-namespace').click
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Geo::GitPushSSHProxy, :geo do
include ::EE::GeoHelpers
set(:primary_node) { create(:geo_node, :primary) }
set(:secondary_node) { create(:geo_node) }
let(:current_node) { nil }
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let(:key) { create(:key, user: user) }
let(:base_request) { double(Gitlab::Geo::BaseRequest.new.authorization) }
let(:info_refs_body_short) do
"008f43ba78b7912f7bf7ef1d7c3b8a0e5ae14a759dfa refs/heads/masterreport-status delete-refs side-band-64k quiet atomic ofs-delta agent=git/2.18.0
0000"
end
let(:base_headers) do
{
'Geo-GL-Id' => "key-#{key.id}",
'Authorization' => 'secret'
}
end
let(:data) do
{
'gl_id' => "key-#{key.id}",
'primary_repo' => "#{primary_node.url}#{project.repository.full_path}.git"
}
end
subject { described_class.new(data) }
before do
stub_current_geo_node(current_node)
allow(Gitlab::Geo::BaseRequest).to receive(:new).and_return(base_request)
allow(base_request).to receive(:authorization).and_return('secret')
end
describe '#info_refs' do
context 'against primary node' do
let(:current_node) { primary_node }
it 'raises an exception' do
expect do
subject.info_refs
end.to raise_error(described_class::MustBeASecondaryNode)
end
end
context 'against secondary node' do
let(:current_node) { secondary_node }
let(:full_info_refs_url) { "#{primary_node.url}#{project.full_path}.git/info/refs?service=git-receive-pack" }
let(:info_refs_headers) { base_headers.merge('Content-Type' => 'application/x-git-upload-pack-request') }
let(:info_refs_http_body_full) do
"001f# service=git-receive-pack
0000#{info_refs_body_short}"
end
before do
stub_request(:get, full_info_refs_url).to_return(status: 200, body: info_refs_http_body_full, headers: info_refs_headers)
end
it 'returns a Net::HTTPOK' do
expect(subject.info_refs).to be_a(Net::HTTPOK)
end
it 'returns a modified body' do
expect(subject.info_refs.body).to eql(info_refs_body_short)
end
end
end
describe '#push' do
context 'against primary node' do
let(:current_node) { primary_node }
it 'raises an exception' do
expect do
subject.push(info_refs_body_short)
end.to raise_error(described_class::MustBeASecondaryNode)
end
end
context 'against secondary node' do
let(:current_node) { secondary_node }
let(:full_git_receive_pack_url) { "#{primary_node.url}#{project.full_path}.git/git-receive-pack" }
let(:push_headers) do
base_headers.merge(
'Content-Type' => 'application/x-git-receive-pack-request',
'Accept' => 'application/x-git-receive-pack-result'
)
end
before do
stub_request(:post, full_git_receive_pack_url).to_return(status: 201, body: info_refs_body_short, headers: push_headers)
end
it 'returns a Net::HTTPCreated' do
expect(subject.push(info_refs_body_short)).to be_a(Net::HTTPCreated)
end
end
end
end
...@@ -18,25 +18,9 @@ describe Gitlab::GitAccess do ...@@ -18,25 +18,9 @@ describe Gitlab::GitAccess do
allow(Gitlab::Database).to receive(:read_only?) { true } allow(Gitlab::Database).to receive(:read_only?) { true }
end end
it 'denies push access' do let(:primary_repo_url) { "https://localhost:3000/gitlab/#{project.full_path}.git" }
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized("You can't push code to a read-only GitLab instance.") it_behaves_like 'a read-only GitLab instance'
end
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance."\
"\nPlease use the primary node URL instead: https://localhost:3000/gitlab/#{project.full_path}.git.
For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized(error_message)
end
end end
describe "push_rule_check" do describe "push_rule_check" do
...@@ -264,12 +248,23 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}" ...@@ -264,12 +248,23 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
end end
end end
describe 'Geo system permissions' do
let(:actor) { :geo }
it { expect { pull_changes }.not_to raise_error }
it { expect { push_changes }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:push_code]) }
end
private private
def push_changes(changes = '_any') def push_changes(changes = '_any')
access.check('git-receive-pack', changes) access.check('git-receive-pack', changes)
end end
def pull_changes(changes = '_any')
access.check('git-upload-pack', changes)
end
def raise_unauthorized(message) def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message) raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::GitAccessWiki do describe Gitlab::GitAccessWiki do
let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
let(:project) { create(:project, :repository) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] } let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] }
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil } let(:redirected_path) { nil }
let(:authentication_abilities) do
[ let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
:read_project,
:download_code,
:push_code
]
end
context "when in a read-only GitLab instance" do context "when in a read-only GitLab instance" do
subject { access.check('git-receive-pack', changes) } subject { access.check('git-receive-pack', changes) }
...@@ -22,26 +17,9 @@ describe Gitlab::GitAccessWiki do ...@@ -22,26 +17,9 @@ describe Gitlab::GitAccessWiki do
allow(Gitlab::Database).to receive(:read_only?) { true } allow(Gitlab::Database).to receive(:read_only?) { true }
end end
it 'denies push access' do let(:primary_repo_url) { "https://localhost:3000/gitlab/#{project.full_path}.wiki.git" }
project.add_maintainer(user)
expect { subject }.to raise_unauthorized("You can't push code to a read-only GitLab instance.")
end
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance.
Please use the primary node URL instead: "\
"https://localhost:3000/gitlab/#{project.full_path}.wiki.git.
For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
project.add_maintainer(user)
expect { subject }.to raise_unauthorized(error_message) it_behaves_like 'a read-only GitLab instance'
end
end end
context 'when wiki is disabled' do context 'when wiki is disabled' do
...@@ -58,6 +36,10 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}" ...@@ -58,6 +36,10 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
private private
def push_changes(changes = '_any')
access.check('git-receive-pack', changes)
end
def raise_unauthorized(message) def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message) raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Geo::PushUser do
let!(:user) { create(:user) }
let!(:key) { create(:key, user: user) }
let(:gl_id) { "key-#{key.id}" }
subject { described_class.new(gl_id) }
describe '.needed_headers_provided?' do
where(:headers) do
[
{},
{ 'Geo-GL-Id' => nil },
{ 'Geo-GL-Id' => '' }
]
end
with_them do
it 'returns false' do
expect(described_class.needed_headers_provided?(headers)).to be(false)
end
end
context 'where gl_id is not nil' do
let(:headers) do
{ 'Geo-GL-Id' => gl_id }
end
it 'returns true' do
expect(described_class.needed_headers_provided?(headers)).to be(true)
end
end
end
describe '.new_from_headers' do
where(:headers) do
[
{},
{ 'Geo-GL-Id' => nil },
{ 'Geo-GL-Id' => '' }
]
end
with_them do
it 'returns false' do
expect(described_class.new_from_headers(headers)).to be_nil
end
end
context 'where gl_id is not nil' do
let(:headers) do
{ 'Geo-GL-Id' => gl_id }
end
it 'returns an instance of Geo::PushUser' do
expect(described_class.new_from_headers(headers)).to be_a(described_class)
end
end
end
describe '#user' do
context 'with a junk gl_id' do
let(:gl_id) { "test" }
it 'returns nil' do
expect(subject.user).to be_nil
end
end
context 'with an unsupported gl_id type' do
let(:gl_id) { "user-#{user.id}" }
it 'returns nil' do
expect(subject.user).to be_nil
end
end
context 'when the User associated to gl_id matches the User associated to gl_username' do
it 'returns a User' do
expect(subject.user).to be_a(User)
end
end
end
end
...@@ -25,7 +25,7 @@ describe API::Geo do ...@@ -25,7 +25,7 @@ describe API::Geo do
end end
end end
describe '/geo/transfers' do describe 'GET /geo/transfers' do
before do before do
stub_current_geo_node(secondary_node) stub_current_geo_node(secondary_node)
end end
...@@ -287,4 +287,120 @@ describe API::Geo do ...@@ -287,4 +287,120 @@ describe API::Geo do
it_behaves_like 'with terms enforced' it_behaves_like 'with terms enforced'
end end
end end
describe '/geo/proxy_git_push_ssh' do
let(:secret_token) { Gitlab::Shell.secret_token }
let(:data) { { primary_repo: 'http://localhost:3001/testuser/repo.git', gl_id: 'key-1', gl_username: 'testuser' } }
before do
stub_current_geo_node(secondary_node)
end
describe 'POST /geo/proxy_git_push_ssh/info_refs' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_push_ssh/info_refs'), nil
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing')
end
end
context 'with all required params' do
let(:git_push_ssh_proxy) { double(Gitlab::Geo::GitPushSSHProxy) }
before do
allow(Gitlab::Geo::GitPushSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
context 'with an invalid secret_token' do
it 'responds with 401' do
post(api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: 'invalid', data: data })
expect(response).to have_gitlab_http_status(401)
expect(json_response['error']).to be_nil
end
end
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:info_refs).and_raise('deliberate exception raised')
post api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: secret_token, data: data }
expect(response).to have_gitlab_http_status(500)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
expect(json_response['result']).to be_nil
end
end
context 'with a valid secret token' do
let(:http_response) { double(Net::HTTPResponse, code: 200, body: 'something here') }
it 'responds with 200' do
expect(git_push_ssh_proxy).to receive(:info_refs).and_return(http_response)
post api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: secret_token, data: data }
expect(response).to have_gitlab_http_status(200)
expect(Base64.decode64(json_response['result'])).to eql('something here')
end
end
end
end
describe 'POST /geo/proxy_git_push_ssh/push' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_push_ssh/push'), nil
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing, output is missing')
end
end
context 'with all required params' do
let(:text) { 'output text' }
let(:output) { Base64.encode64(text) }
let(:git_push_ssh_proxy) { double(Gitlab::Geo::GitPushSSHProxy) }
before do
allow(Gitlab::Geo::GitPushSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
context 'with an invalid secret_token' do
it 'responds with 401' do
post(api('/geo/proxy_git_push_ssh/push'), { secret_token: 'invalid', data: data, output: output })
expect(response).to have_gitlab_http_status(401)
expect(json_response['error']).to be_nil
end
end
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:push).and_raise('deliberate exception raised')
post api('/geo/proxy_git_push_ssh/push'), { secret_token: secret_token, data: data, output: output }
expect(response).to have_gitlab_http_status(500)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
expect(json_response['result']).to be_nil
end
end
context 'with a valid secret token' do
let(:http_response) { double(Net::HTTPResponse, code: 201, body: 'something here') }
it 'responds with 201' do
expect(git_push_ssh_proxy).to receive(:push).with(text).and_return(http_response)
post api('/geo/proxy_git_push_ssh/push'), { secret_token: secret_token, data: data, output: output }
expect(response).to have_gitlab_http_status(201)
expect(Base64.decode64(json_response['result'])).to eql('something here')
end
end
end
end
end
end end
...@@ -13,15 +13,24 @@ describe "Git HTTP requests (Geo)" do ...@@ -13,15 +13,24 @@ describe "Git HTTP requests (Geo)" do
# 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.authorization } let!(:auth_token) { Gitlab::Geo::BaseRequest.new.authorization }
let!(:user) { create(:user) }
let!(:user_without_any_access) { create(:user) }
let!(:user_without_push_access) { create(:user) }
let!(:key) { create(:key, user: user) }
let!(:key_for_user_without_any_access) { create(:key, user: user_without_any_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 }
before do before do
project.add_maintainer(user)
project.add_guest(user_without_push_access)
stub_licensed_features(geo: true) stub_licensed_features(geo: true)
stub_current_geo_node(secondary) stub_current_geo_node(current_node)
end end
shared_examples_for 'Geo sync request' do shared_examples_for 'Geo request' do
subject do subject do
make_request make_request
response response
...@@ -64,20 +73,25 @@ describe "Git HTTP requests (Geo)" do ...@@ -64,20 +73,25 @@ describe "Git HTTP requests (Geo)" do
end end
end end
context 'when current node is a secondary' do
let(:current_node) { secondary }
set(:project) { create(:project, :repository, :private) }
describe 'GET info_refs' do describe 'GET info_refs' do
context 'git pull' do context 'git pull' do
def make_request def make_request
get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env
end end
it_behaves_like 'Geo sync request' it_behaves_like 'Geo request'
context 'when terms are enforced' do context 'when terms are enforced' do
before do before do
enforce_terms enforce_terms
end end
it_behaves_like 'Geo sync request' it_behaves_like 'Geo request'
end end
end end
...@@ -101,19 +115,19 @@ describe "Git HTTP requests (Geo)" do ...@@ -101,19 +115,19 @@ describe "Git HTTP requests (Geo)" do
end end
end end
describe 'POST upload_pack' do describe 'POST git_upload_pack' do
def make_request def make_request
post "/#{project.full_path}.git/git-upload-pack", {}, env post "/#{project.full_path}.git/git-upload-pack", {}, env
end end
it_behaves_like 'Geo sync request' it_behaves_like 'Geo request'
context 'when terms are enforced' do context 'when terms are enforced' do
before do before do
enforce_terms enforce_terms
end end
it_behaves_like 'Geo sync request' it_behaves_like 'Geo request'
end end
end end
...@@ -243,6 +257,96 @@ describe "Git HTTP requests (Geo)" do ...@@ -243,6 +257,96 @@ describe "Git HTTP requests (Geo)" do
end end
end end
end end
end
context 'when current node is the primary' do
let(:current_node) { primary }
describe 'POST git_receive_pack' do
def make_request
post url, {}, env
end
let(:url) { "/#{project.full_path}.git/git-receive-pack" }
before do
env['Geo-GL-Id'] = geo_gl_id
end
subject do
make_request
response
end
context 'when gl_id is incorrectly provided via HTTP headers' do
where(:geo_gl_id) do
[
nil,
''
]
end
with_them do
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to upload code for this project.')
end
end
end
context 'when gl_id is provided via HTTP headers' do
context 'but is invalid' do
where(:geo_gl_id) do
[
'key-999',
'key-1',
'key-999',
'junk',
'junk-1',
'kkey-1'
]
end
with_them do
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('Geo push user is invalid.')
end
end
end
context 'and is valid' do
context 'but the user has no access' do
let(:geo_gl_id) { "key-#{key_for_user_without_any_access.id}" }
it 'returns a 404' do
is_expected.to have_gitlab_http_status(:not_found)
expect(response.body).to eql('The project you were looking for could not be found.')
end
end
context 'but the user does not have push access' do
let(:geo_gl_id) { "key-#{key_for_user_without_push_access.id}" }
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to push code to this project.')
end
end
context 'and the user has push access' do
let(:geo_gl_id) { "key-#{key.id}" }
it 'returns a 200' do
is_expected.to have_gitlab_http_status(:ok)
expect(json_response['GL_ID']).to match("user-#{user.id}")
expect(json_response['GL_REPOSITORY']).to match(Gitlab::GlRepository.gl_repository(project, false))
end
end
end
end
end
end
def valid_geo_env def valid_geo_env
geo_env(auth_token) geo_env(auth_token)
......
# frozen_string_literal: true
shared_examples 'a read-only GitLab instance' do
it 'denies push access' do
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized("You can't push code to a read-only GitLab instance.")
end
context 'for a Geo setup' do
before do
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(secondary_with_primary)
end
context 'that is incorrectly setup' do
let(:secondary_with_primary) { false }
let(:error_message) { "You can't push code to a read-only GitLab instance." }
it 'denies push access with primary present' do
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized(error_message)
end
end
context 'that is correctly setup' do
let(:secondary_with_primary) { true }
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => %w{/api/v4/geo/proxy_git_push_ssh/info_refs /api/v4/geo/proxy_git_push_ssh/push},
'primary_repo' => primary_repo_url
}
}
end
it 'attempts to proxy to the primary' do
project.add_maintainer(user)
expect(push_changes).to be_a(Gitlab::GitAccessResult::CustomAction)
expect(push_changes.message).to eql('Attempting to proxy to primary.')
expect(push_changes.payload).to eql(payload)
end
end
end
end
...@@ -74,6 +74,7 @@ module API ...@@ -74,6 +74,7 @@ module API
gl_repository: gl_repository, gl_repository: gl_repository,
gl_id: Gitlab::GlId.gl_id(user), gl_id: Gitlab::GlId.gl_id(user),
gl_username: user&.username, gl_username: user&.username,
git_config_options: [],
# This repository_path is a bogus value but gitlab-shell still requires # This repository_path is a bogus value but gitlab-shell still requires
# its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135 # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135
...@@ -81,6 +82,13 @@ module API ...@@ -81,6 +82,13 @@ module API
gitaly: gitaly_payload(params[:action]) gitaly: gitaly_payload(params[:action])
} }
# Custom option for git-receive-pack command
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
response_with_status(**payload) response_with_status(**payload)
when ::Gitlab::GitAccessResult::CustomAction when ::Gitlab::GitAccessResult::CustomAction
response_with_status(code: 300, message: check_result.message, payload: check_result.payload) response_with_status(code: 300, message: check_result.message, payload: check_result.payload)
......
...@@ -50,6 +50,7 @@ module Gitlab ...@@ -50,6 +50,7 @@ module Gitlab
def push_checks def push_checks
unless can_push? unless can_push?
# You are not allowed to push code to this project.
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code] raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code]
end end
end end
......
...@@ -236,11 +236,13 @@ module Gitlab ...@@ -236,11 +236,13 @@ module Gitlab
# TODO: please clean this up # TODO: please clean this up
def check_push_access! def check_push_access!
if project.repository_read_only? if project.repository_read_only?
# The repository is temporarily read-only. Please try again later.
raise UnauthorizedError, ERROR_MESSAGES[:read_only] raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end end
if deploy_key? if deploy_key?
unless deploy_key.can_push_to?(project) unless deploy_key.can_push_to?(project)
# This deploy key does not have write access to this project.
raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload] raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload]
end end
elsif user elsif user
...@@ -248,6 +250,7 @@ module Gitlab ...@@ -248,6 +250,7 @@ module Gitlab
elsif authed_via_jwt? elsif authed_via_jwt?
# Authenticated via JWT # Authenticated via JWT
else else
# You are not allowed to upload code for this project.
raise UnauthorizedError, ERROR_MESSAGES[:upload] raise UnauthorizedError, ERROR_MESSAGES[:upload]
end end
......
...@@ -22,18 +22,27 @@ module Gitlab ...@@ -22,18 +22,27 @@ module Gitlab
project = repository.project project = repository.project
{ attrs = {
GL_ID: Gitlab::GlId.gl_id(user), GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki), GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
GL_USERNAME: user&.username, GL_USERNAME: user&.username,
ShowAllRefs: show_all_refs, ShowAllRefs: show_all_refs,
Repository: repository.gitaly_repository.to_h, Repository: repository.gitaly_repository.to_h,
RepoPath: 'ignored but not allowed to be empty in gitlab-workhorse', RepoPath: 'ignored but not allowed to be empty in gitlab-workhorse',
GitConfigOptions: [],
GitalyServer: { GitalyServer: {
address: Gitlab::GitalyClient.address(project.repository_storage), address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage) token: Gitlab::GitalyClient.token(project.repository_storage)
} }
} }
# Custom option for git-receive-pack command
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
attrs[:GitConfigOptions] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
attrs
end end
def send_git_blob(repository, blob) def send_git_blob(repository, blob)
......
...@@ -5819,6 +5819,9 @@ msgstr "" ...@@ -5819,6 +5819,9 @@ msgstr ""
msgid "Project Badges" msgid "Project Badges"
msgstr "" msgstr ""
msgid "Project URL"
msgstr ""
msgid "Project access must be granted explicitly to each user." msgid "Project access must be granted explicitly to each user."
msgstr "" msgstr ""
...@@ -5846,6 +5849,9 @@ msgstr "" ...@@ -5846,6 +5849,9 @@ msgstr ""
msgid "Project name" msgid "Project name"
msgstr "" msgstr ""
msgid "Project slug"
msgstr ""
msgid "ProjectActivityRSS|Subscribe" msgid "ProjectActivityRSS|Subscribe"
msgstr "" msgstr ""
......
...@@ -140,5 +140,12 @@ describe Admin::ApplicationSettingsController do ...@@ -140,5 +140,12 @@ describe Admin::ApplicationSettingsController do
expect(body).to include('counts') expect(body).to include('counts')
expect(response.status).to eq(200) expect(response.status).to eq(200)
end end
it 'updates the receive_max_input_size setting' do
put :update, application_setting: { receive_max_input_size: "1024" }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
end end
end end
...@@ -20,7 +20,7 @@ describe 'Top Plus Menu', :js do ...@@ -20,7 +20,7 @@ describe 'Top Plus Menu', :js do
click_topmenuitem("New project") click_topmenuitem("New project")
expect(page).to have_content('Project path') expect(page).to have_content('Project URL')
expect(page).to have_content('Project name') expect(page).to have_content('Project name')
end end
...@@ -92,7 +92,7 @@ describe 'Top Plus Menu', :js do ...@@ -92,7 +92,7 @@ describe 'Top Plus Menu', :js do
find('.header-new-group-project a').click find('.header-new-group-project a').click
end end
expect(page).to have_content('Project path') expect(page).to have_content('Project URL')
expect(page).to have_content('Project name') expect(page).to have_content('Project name')
end end
end end
......
...@@ -3,6 +3,8 @@ require 'rails_helper' ...@@ -3,6 +3,8 @@ require 'rails_helper'
describe 'Cohorts page' do describe 'Cohorts page' do
before do before do
sign_in(create(:admin)) sign_in(create(:admin))
stub_application_setting(usage_ping_enabled: true)
end end
it 'See users count per month' do it 'See users count per month' do
......
...@@ -20,6 +20,7 @@ describe 'Import/Export - project import integration test', :js do ...@@ -20,6 +20,7 @@ describe 'Import/Export - project import integration test', :js do
context 'when selecting the namespace' do context 'when selecting the namespace' do
let(:user) { create(:admin) } let(:user) { create(:admin) }
let!(:namespace) { user.namespace } let!(:namespace) { user.namespace }
let(:project_name) { 'Test Project Name' + SecureRandom.hex }
let(:project_path) { 'test-project-path' + SecureRandom.hex } let(:project_path) { 'test-project-path' + SecureRandom.hex }
context 'prefilled the path' do context 'prefilled the path' do
...@@ -27,12 +28,13 @@ describe 'Import/Export - project import integration test', :js do ...@@ -27,12 +28,13 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path visit new_project_path
select2(namespace.id, from: '#project_namespace_id') select2(namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project_name, visible: true
fill_in :project_path, with: project_path, visible: true fill_in :project_path, with: project_path, visible: true
click_import_project_tab click_import_project_tab
click_link 'GitLab export' click_link 'GitLab export'
expect(page).to have_content('Import an exported GitLab project') expect(page).to have_content('Import an exported GitLab project')
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}") expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&name=#{ERB::Util.url_encode(project_name)}&path=#{project_path}")
attach_file('file', file) attach_file('file', file)
click_on 'Import project' click_on 'Import project'
...@@ -56,6 +58,7 @@ describe 'Import/Export - project import integration test', :js do ...@@ -56,6 +58,7 @@ describe 'Import/Export - project import integration test', :js do
click_import_project_tab click_import_project_tab
click_link 'GitLab export' click_link 'GitLab export'
fill_in :name, with: 'Test Project Name', visible: true
fill_in :path, with: 'test-project-path', visible: true fill_in :path, with: 'test-project-path', visible: true
attach_file('file', file) attach_file('file', file)
...@@ -74,7 +77,8 @@ describe 'Import/Export - project import integration test', :js do ...@@ -74,7 +77,8 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path visit new_project_path
select2(user.namespace.id, from: '#project_namespace_id') select2(user.namespace.id, from: '#project_namespace_id')
fill_in :project_path, with: project.name, visible: true fill_in :project_name, with: project.name, visible: true
fill_in :project_path, with: project.path, visible: true
click_import_project_tab click_import_project_tab
click_link 'GitLab export' click_link 'GitLab export'
attach_file('file', file) attach_file('file', file)
......
...@@ -12,8 +12,9 @@ describe 'New project' do ...@@ -12,8 +12,9 @@ describe 'New project' do
it 'shows "New project" page', :js do it 'shows "New project" page', :js do
visit new_project_path visit new_project_path
expect(page).to have_content('Project path')
expect(page).to have_content('Project name') expect(page).to have_content('Project name')
expect(page).to have_content('Project URL')
expect(page).to have_content('Project slug')
find('#import-project-tab').click find('#import-project-tab').click
...@@ -187,7 +188,7 @@ describe 'New project' do ...@@ -187,7 +188,7 @@ describe 'New project' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace) collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
fill_in 'project_import_url', with: collision_project.http_url_to_repo fill_in 'project_import_url', with: collision_project.http_url_to_repo
fill_in 'project_path', with: collision_project.path fill_in 'project_name', with: collision_project.name
click_on 'Create project' click_on 'Create project'
......
...@@ -11,7 +11,7 @@ describe 'User creates a project', :js do ...@@ -11,7 +11,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do it 'creates a new project' do
visit(new_project_path) visit(new_project_path)
fill_in(:project_path, with: 'Empty') fill_in(:project_name, with: 'Empty')
page.within('#content-body') do page.within('#content-body') do
click_button('Create project') click_button('Create project')
...@@ -37,6 +37,7 @@ describe 'User creates a project', :js do ...@@ -37,6 +37,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do it 'creates a new project' do
visit(new_project_path) visit(new_project_path)
fill_in :project_name, with: 'A Subgroup Project'
fill_in :project_path, with: 'a-subgroup-project' fill_in :project_path, with: 'a-subgroup-project'
page.find('.js-select-namespace').click page.find('.js-select-namespace').click
...@@ -46,7 +47,7 @@ describe 'User creates a project', :js do ...@@ -46,7 +47,7 @@ describe 'User creates a project', :js do
click_button('Create project') click_button('Create project')
end end
expect(page).to have_content("Project 'a-subgroup-project' was successfully created") expect(page).to have_content("Project 'A Subgroup Project' was successfully created")
project = Project.last project = Project.last
......
...@@ -16,7 +16,7 @@ describe 'Project' do ...@@ -16,7 +16,7 @@ describe 'Project' do
it "allows creation from templates", :js do it "allows creation from templates", :js do
find('#create-from-template-tab').click find('#create-from-template-tab').click
find("label[for=#{template.name}]").click find("label[for=#{template.name}]").click
fill_in("project_path", with: template.name) fill_in("project_name", with: template.name)
page.within '#content-body' do page.within '#content-body' do
click_button "Create project" click_button "Create project"
......
...@@ -61,6 +61,12 @@ describe('text_utility', () => { ...@@ -61,6 +61,12 @@ describe('text_utility', () => {
}); });
}); });
describe('slugifyWithHyphens', () => {
it('should replaces whitespaces with hyphens and convert to lower case', () => {
expect(textUtils.slugifyWithHyphens('My Input String')).toEqual('my-input-string');
});
});
describe('stripHtml', () => { describe('stripHtml', () => {
it('replaces html tag with the default replacement', () => { it('replaces html tag with the default replacement', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual( expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual(
......
...@@ -4,12 +4,14 @@ import projectNew from '~/projects/project_new'; ...@@ -4,12 +4,14 @@ import projectNew from '~/projects/project_new';
describe('New Project', () => { describe('New Project', () => {
let $projectImportUrl; let $projectImportUrl;
let $projectPath; let $projectPath;
let $projectName;
beforeEach(() => { beforeEach(() => {
setFixtures(` setFixtures(`
<div class='toggle-import-form'> <div class='toggle-import-form'>
<div class='import-url-data'> <div class='import-url-data'>
<input id="project_import_url" /> <input id="project_import_url" />
<input id="project_name" />
<input id="project_path" /> <input id="project_path" />
</div> </div>
</div> </div>
...@@ -17,6 +19,7 @@ describe('New Project', () => { ...@@ -17,6 +19,7 @@ describe('New Project', () => {
$projectImportUrl = $('#project_import_url'); $projectImportUrl = $('#project_import_url');
$projectPath = $('#project_path'); $projectPath = $('#project_path');
$projectName = $('#project_name');
}); });
describe('deriveProjectPathFromUrl', () => { describe('deriveProjectPathFromUrl', () => {
...@@ -129,4 +132,31 @@ describe('New Project', () => { ...@@ -129,4 +132,31 @@ describe('New Project', () => {
}); });
}); });
}); });
describe('deriveSlugFromProjectName', () => {
beforeEach(() => {
projectNew.bindEvents();
$projectName.val('').keyup();
});
it('converts project name to lower case and dash-limited slug', () => {
const dummyProjectName = 'My Awesome Project';
$projectName.val(dummyProjectName);
projectNew.onProjectNameChange($projectName, $projectPath);
expect($projectPath.val()).toEqual('my-awesome-project');
});
it('does not add additional dashes in the slug if the project name already contains dashes', () => {
const dummyProjectName = 'My-Dash-Delimited Awesome Project';
$projectName.val(dummyProjectName);
projectNew.onProjectNameChange($projectName, $projectPath);
expect($projectPath.val()).toEqual('my-dash-delimited-awesome-project');
});
});
}); });
...@@ -1165,13 +1165,6 @@ describe Gitlab::GitAccess do ...@@ -1165,13 +1165,6 @@ describe Gitlab::GitAccess do
end end
end end
describe 'Geo system permissions' do
let(:actor) { :geo }
it { expect { pull_access_check }.not_to raise_error }
it { expect { push_access_check }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:upload]) }
end
context 'terms are enforced' do context 'terms are enforced' do
before do before do
enforce_terms enforce_terms
......
...@@ -336,6 +336,22 @@ describe Gitlab::Workhorse do ...@@ -336,6 +336,22 @@ describe Gitlab::Workhorse do
it { expect { subject }.to raise_exception('Unsupported action: download') } it { expect { subject }.to raise_exception('Unsupported action: download') }
end end
end end
context 'when receive_max_input_size has been updated' do
it 'returns custom git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
expect(subject[:GitConfigOptions]).to be_present
end
end
context 'when receive_max_input_size is empty' do
it 'returns an empty git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
expect(subject[:GitConfigOptions]).to be_empty
end
end
end end
describe '.set_key_and_notify' do describe '.set_key_and_notify' do
......
...@@ -369,6 +369,26 @@ describe API::Internal do ...@@ -369,6 +369,26 @@ describe API::Internal do
expect(user.reload.last_activity_on).to be_nil expect(user.reload.last_activity_on).to be_nil
end end
end end
context 'when receive_max_input_size has been updated' do
it 'returns custom git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
push(key, project)
expect(json_response["git_config_options"]).to be_present
end
end
context 'when receive_max_input_size is empty' do
it 'returns an empty git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
push(key, project)
expect(json_response["git_config_options"]).to be_empty
end
end
end end
end end
......
...@@ -10,9 +10,9 @@ shared_examples 'label note created from events' do ...@@ -10,9 +10,9 @@ shared_examples 'label note created from events' do
end end
def label_refs(events) def label_refs(events)
sorted_labels = events.map(&:label).compact.sort_by(&:title) labels = events.map(&:label).compact
sorted_labels.map { |l| l.to_reference}.join(' ') labels.map { |l| l.to_reference}.sort.join(' ')
end end
let(:time) { Time.now } let(:time) { Time.now }
......
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