Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--app/assets/javascripts/awards_handler.js30
-rw-r--r--app/assets/javascripts/dispatcher.js3
-rw-r--r--app/assets/javascripts/merge_request_widget.js5
-rw-r--r--app/assets/javascripts/mini_pipeline_graph_dropdown.js155
-rw-r--r--app/assets/javascripts/notes.js10
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss9
-rw-r--r--app/controllers/autocomplete_controller.rb3
-rw-r--r--app/controllers/projects/services_controller.rb3
-rw-r--r--app/helpers/issuables_helper.rb19
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/ci/stage.rb6
-rw-r--r--app/models/project_services/issue_tracker_service.rb11
-rw-r--r--app/views/dashboard/issues.atom.builder2
-rw-r--r--app/views/devise/shared/_signup_box.html.haml2
-rw-r--r--app/views/events/_event.atom.builder2
-rw-r--r--app/views/groups/issues.atom.builder2
-rw-r--r--app/views/issues/_issue.atom.builder2
-rw-r--r--app/views/projects/deploy_keys/_index.html.haml2
-rw-r--r--app/views/projects/issues/index.atom.builder2
-rw-r--r--app/views/projects/notes/_note.html.haml19
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml34
-rw-r--r--app/workers/authorized_projects_worker.rb2
-rw-r--r--changelogs/unreleased/24137-issuable-permalink.yml4
-rw-r--r--changelogs/unreleased/24166-close-builds-dropdown.yml4
-rw-r--r--changelogs/unreleased/29046-fix-github-importer-open-prs.yml4
-rw-r--r--changelogs/unreleased/29137-bulk-perform-async-should-specify-queue.yml4
-rw-r--r--changelogs/unreleased/29189-discussion-button.yml4
-rw-r--r--changelogs/unreleased/29209-sign-up-form-name.yml4
-rw-r--r--changelogs/unreleased/29263-merge-button-color.yml4
-rw-r--r--changelogs/unreleased/29328-fix-transient-failure-in-model-user-spec.yml4
-rw-r--r--changelogs/unreleased/adam-prevent-two-issue-trackers.yml4
-rw-r--r--changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml4
-rw-r--r--changelogs/unreleased/enable-snippets-by-default.yml4
-rw-r--r--changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml4
-rw-r--r--changelogs/unreleased/fix_updated_field_in_issues-atom.yml4
-rw-r--r--changelogs/unreleased/tc-fix-project-create-500.yml4
-rw-r--r--config/gitlab.yml.example12
-rw-r--r--config/initializers/0_inflections.rb (renamed from config/initializers/inflections.rb)0
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/fix_local_cache_middleware.rb24
-rw-r--r--doc/administration/pages/source.md20
-rw-r--r--doc/development/frontend.md22
-rw-r--r--doc/development/ux_guide/copy.md9
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/update/8.17-to-9.0.md234
-rw-r--r--lib/gitlab/etag_caching/middleware.rb2
-rw-r--r--lib/gitlab/github_import/importer.rb2
-rw-r--r--lib/gitlab/github_import/pull_request_formatter.rb4
-rw-r--r--lib/gitlab/url_sanitizer.rb2
-rw-r--r--lib/tasks/gitlab/db.rake2
-rw-r--r--spec/features/issues/form_spec.rb10
-rw-r--r--spec/features/merge_requests/form_spec.rb11
-rw-r--r--spec/features/projects/edit_spec.rb30
-rw-r--r--spec/javascripts/awards_handler_spec.js50
-rw-r--r--spec/javascripts/mini_pipeline_graph_dropdown_spec.js35
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb120
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb8
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb8
-rw-r--r--spec/models/ci/pipeline_spec.rb18
-rw-r--r--spec/models/ci/stage_spec.rb17
-rw-r--r--spec/models/project_services/issue_tracker_service_spec.rb32
-rw-r--r--spec/models/user_spec.rb5
-rw-r--r--spec/requests/api/projects_spec.rb8
-rw-r--r--spec/workers/authorized_projects_worker_spec.rb25
-rw-r--r--vendor/gitlab-ci-yml/Docker.gitlab-ci.yml4
-rw-r--r--vendor/gitlab-ci-yml/Maven.gitlab-ci.yml8
-rw-r--r--vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml12
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml16
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml18
72 files changed, 900 insertions, 266 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 57d94dad672..1fd29fef4f0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -94,6 +94,10 @@ look for [issues with the label `Accepting Merge Requests` and weight < 5][accep
These issues will be of reasonable size and challenge, for anyone to start
contributing to GitLab.
+## Workflow labels
+
+Labelling issues is described in the [GitLab Inc engineering workflow].
+
## Implement design & UI elements
Please see the [UX Guide for GitLab].
@@ -535,6 +539,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
[license-finder-doc]: doc/development/licensing.md
+[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
[^1]: Specs other than JavaScript specs are considered backend code. Haml
changes are considered backend code if they include Ruby code other than just
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 54836efdf29..8a077f0081a 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -45,12 +45,12 @@ function buildCategoryMap() {
});
}
-function renderCategory(name, emojiList) {
+function renderCategory(name, emojiList, opts = {}) {
return `
<h5 class="emoji-menu-title">
${name}
</h5>
- <ul class="clearfix emoji-menu-list">
+ <ul class="clearfix emoji-menu-list ${opts.menuListClass}">
${emojiList.map(emojiName => `
<li class="emoji-menu-list-item">
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
@@ -140,9 +140,6 @@ AwardsHandler.prototype.showEmojiMenu = function showEmojiMenu($addBtn) {
const $createdMenu = $('.emoji-menu');
$addBtn.removeClass('is-loading');
this.positionMenu($createdMenu, $addBtn);
- if (!this.frequentEmojiBlockRendered) {
- this.renderFrequentlyUsedBlock();
- }
return setTimeout(() => {
$createdMenu.addClass('is-visible');
$('#emoji_search').focus();
@@ -165,11 +162,21 @@ AwardsHandler.prototype.createEmojiMenu = function createEmojiMenu(callback) {
const emojisInCategory = categoryMap[categoryNameKey];
const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
+ // Render the frequently used
+ const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
+ let frequentlyUsedCatgegory = '';
+ if (frequentlyUsedEmojis.length > 0) {
+ frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
+ menuListClass: 'frequent-emojis',
+ });
+ }
+
const emojiMenuMarkup = `
<div class="emoji-menu">
<input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" placeholder="Search emoji" />
<div class="emoji-menu-content">
+ ${frequentlyUsedCatgegory}
${firstCategory}
</div>
</div>
@@ -457,19 +464,6 @@ AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmoj
return _.compact(_.uniq(frequentlyUsedEmojis));
};
-AwardsHandler.prototype.renderFrequentlyUsedBlock = function renderFrequentlyUsedBlock() {
- if (Cookies.get('frequently_used_emojis')) {
- const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
- const ul = $('<ul class="clearfix emoji-menu-list frequent-emojis">');
- for (let i = 0, len = frequentlyUsedEmojis.length; i < len; i += 1) {
- const emoji = frequentlyUsedEmojis[i];
- $(`.emoji-menu-content [data-name="${emoji}"]`).closest('li').clone().appendTo(ul);
- }
- $('.emoji-menu-content').prepend(ul).prepend($('<h5>').text('Frequently used'));
- }
- this.frequentEmojiBlockRendered = true;
-};
-
AwardsHandler.prototype.setupSearch = function setupSearch() {
this.registerEventListener('on', $('input.emoji-search'), 'input', (e) => {
const term = $(e.target).val().trim();
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 017980271b1..7b9b9123c31 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -39,6 +39,7 @@ import Issue from './issue';
import BindInOut from './behaviors/bind_in_out';
import GroupsList from './groups_list';
import ProjectsList from './projects_list';
+import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout');
@@ -181,7 +182,7 @@ const UserCallout = require('./user_callout');
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:commit:pipelines':
- new gl.MiniPipelineGraph({
+ new MiniPipelineGraph({
container: '.js-pipeline-table',
}).bindEvents();
break;
diff --git a/app/assets/javascripts/merge_request_widget.js b/app/assets/javascripts/merge_request_widget.js
index 5f1bd474a0c..66cc270ab4d 100644
--- a/app/assets/javascripts/merge_request_widget.js
+++ b/app/assets/javascripts/merge_request_widget.js
@@ -3,7 +3,8 @@
/* global notifyPermissions */
/* global merge_request_widget */
-require('./smart_interval');
+import './smart_interval';
+import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
@@ -285,7 +286,7 @@ require('./smart_interval');
};
MergeRequestWidget.prototype.initMiniPipelineGraph = function() {
- new gl.MiniPipelineGraph({
+ new MiniPipelineGraph({
container: '.js-pipeline-inline-mr-widget-graph:visible',
}).bindEvents();
};
diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
index 2145e531331..9c58c465001 100644
--- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js
+++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js
@@ -15,81 +15,96 @@
* <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div>
*/
-(() => {
- class MiniPipelineGraph {
- constructor(opts = {}) {
- this.container = opts.container || '';
- this.dropdownListSelector = '.js-builds-dropdown-container';
- this.getBuildsList = this.getBuildsList.bind(this);
- }
- /**
- * Adds the event listener when the dropdown is opened.
- * All dropdown events are fired at the .dropdown-menu's parent element.
- */
- bindEvents() {
- $(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
- }
+export default class MiniPipelineGraph {
+ constructor(opts = {}) {
+ this.container = opts.container || '';
+ this.dropdownListSelector = '.js-builds-dropdown-container';
+ this.getBuildsList = this.getBuildsList.bind(this);
+ }
+
+ /**
+ * Adds the event listener when the dropdown is opened.
+ * All dropdown events are fired at the .dropdown-menu's parent element.
+ */
+ bindEvents() {
+ $(document).off('shown.bs.dropdown', this.container).on('shown.bs.dropdown', this.container, this.getBuildsList);
+ }
- /**
- * For the clicked stage, renders the given data in the dropdown list.
- *
- * @param {HTMLElement} stageContainer
- * @param {Object} data
- */
- renderBuildsList(stageContainer, data) {
- const dropdownContainer = stageContainer.parentElement.querySelector(
- `${this.dropdownListSelector} .js-builds-dropdown-list`,
- );
+ /**
+ * When the user right clicks or cmd/ctrl + click in the job name
+ * the dropdown should not be closed and the link should open in another tab,
+ * so we stop propagation of the click event inside the dropdown.
+ *
+ * Since this component is rendered multiple times per page we need to guarantee we only
+ * target the click event of this component.
+ */
+ stopDropdownClickPropagation() {
+ $(document).on(
+ 'click',
+ `${this.container} .js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item`,
+ (e) => {
+ e.stopPropagation();
+ },
+ );
+ }
- dropdownContainer.innerHTML = data;
- }
+ /**
+ * For the clicked stage, renders the given data in the dropdown list.
+ *
+ * @param {HTMLElement} stageContainer
+ * @param {Object} data
+ */
+ renderBuildsList(stageContainer, data) {
+ const dropdownContainer = stageContainer.parentElement.querySelector(
+ `${this.dropdownListSelector} .js-builds-dropdown-list`,
+ );
- /**
- * For the clicked stage, gets the list of builds.
- *
- * All dropdown events have a relatedTarget property,
- * whose value is the toggling anchor element.
- *
- * @param {Object} e bootstrap dropdown event
- * @return {Promise}
- */
- getBuildsList(e) {
- const button = e.relatedTarget;
- const endpoint = button.dataset.stageEndpoint;
+ dropdownContainer.innerHTML = data;
+ }
- return $.ajax({
- dataType: 'json',
- type: 'GET',
- url: endpoint,
- beforeSend: () => {
- this.renderBuildsList(button, '');
- this.toggleLoading(button);
- },
- success: (data) => {
- this.toggleLoading(button);
- this.renderBuildsList(button, data.html);
- },
- error: () => {
- this.toggleLoading(button);
- new Flash('An error occurred while fetching the builds.', 'alert');
- },
- });
- }
+ /**
+ * For the clicked stage, gets the list of builds.
+ *
+ * All dropdown events have a relatedTarget property,
+ * whose value is the toggling anchor element.
+ *
+ * @param {Object} e bootstrap dropdown event
+ * @return {Promise}
+ */
+ getBuildsList(e) {
+ const button = e.relatedTarget;
+ const endpoint = button.dataset.stageEndpoint;
- /**
- * Toggles the visibility of the loading icon.
- *
- * @param {HTMLElement} stageContainer
- * @return {type}
- */
- toggleLoading(stageContainer) {
- stageContainer.parentElement.querySelector(
- `${this.dropdownListSelector} .js-builds-dropdown-loading`,
- ).classList.toggle('hidden');
- }
+ return $.ajax({
+ dataType: 'json',
+ type: 'GET',
+ url: endpoint,
+ beforeSend: () => {
+ this.renderBuildsList(button, '');
+ this.toggleLoading(button);
+ },
+ success: (data) => {
+ this.toggleLoading(button);
+ this.renderBuildsList(button, data.html);
+ this.stopDropdownClickPropagation();
+ },
+ error: () => {
+ this.toggleLoading(button);
+ new Flash('An error occurred while fetching the builds.', 'alert');
+ },
+ });
}
- window.gl = window.gl || {};
- window.gl.MiniPipelineGraph = MiniPipelineGraph;
-})();
+ /**
+ * Toggles the visibility of the loading icon.
+ *
+ * @param {HTMLElement} stageContainer
+ * @return {type}
+ */
+ toggleLoading(stageContainer) {
+ stageContainer.parentElement.querySelector(
+ `${this.dropdownListSelector} .js-builds-dropdown-loading`,
+ ).classList.toggle('hidden');
+ }
+}
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index eeab69da941..47cc34e7a20 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,12 +1,14 @@
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */
/* global Flash */
/* global Autosave */
+/* global Cookies */
/* global ResolveService */
/* global mrRefreshWidgetUrl */
require('./autosave');
window.autosize = require('vendor/autosize');
window.Dropzone = require('dropzone');
+window.Cookies = require('js-cookie');
require('./dropzone_input');
require('./gfm_auto_complete');
require('vendor/jquery.caret'); // required by jquery.atwho
@@ -42,7 +44,6 @@ require('./task_list');
this.notes_url = notes_url;
this.note_ids = note_ids;
this.last_fetched_at = last_fetched_at;
- this.view = view;
this.noteable_url = document.URL;
this.notesCountBadge || (this.notesCountBadge = $(".issuable-details").find(".notes-tab .badge"));
this.basePollingInterval = 15000;
@@ -57,6 +58,7 @@ require('./task_list');
selector: '.notes'
});
this.collapseLongCommitList();
+ this.setViewType(view);
// We are in the Merge Requests page so we need another edit form for Changes tab
if (gl.utils.getPagePath(1) === 'merge_requests') {
@@ -65,6 +67,10 @@ require('./task_list');
}
}
+ Notes.prototype.setViewType = function(view) {
+ this.view = Cookies.get('diff_view') || view;
+ };
+
Notes.prototype.addBinding = function() {
// add note to UI after creation
$(document).on("ajax:success", ".js-main-target-form", this.addNote);
@@ -302,7 +308,7 @@ require('./task_list');
};
Notes.prototype.isParallelView = function() {
- return this.view === 'parallel';
+ return Cookies.get('diff_view') === 'parallel';
};
/*
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index fe8b37d2c6e..186bb9ac616 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -43,7 +43,7 @@
white-space: nowrap;
&[disabled] {
- background-color: $input-bg-disabled;
+ opacity: .65;
cursor: not-allowed;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index d3496e19dde..7c3172421c1 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -24,10 +24,6 @@
color: inherit;
}
- .btn-success.dropdown-toggle:disabled {
- background-color: $gl-success;
- }
-
.accept-merge-request {
&.ci-pending,
&.ci-running {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index dc79de19d48..c8ad88e9351 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -384,7 +384,7 @@ ul.notes {
top: 0;
.note-action-button {
- margin-left: 10px;
+ margin-left: 8px;
}
}
@@ -400,8 +400,7 @@ ul.notes {
}
.note-action-button {
- display: inline-block;
- margin-left: 0;
+ display: inline;
line-height: 20px;
@media (min-width: $screen-sm-min) {
@@ -540,7 +539,6 @@ ul.notes {
}
.line-resolve-btn {
- display: inline-block;
position: relative;
top: 2px;
padding: 0;
@@ -563,8 +561,9 @@ ul.notes {
}
svg {
- position: relative;
fill: $gray-darkest;
+ height: 15px;
+ width: 15px;
}
}
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index d7a45bacd35..b79ca034c5b 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -18,8 +18,7 @@ class AutocompleteController < ApplicationController
if params[:search].blank?
# Include current user if available to filter by "Me"
if params[:current_user].present? && current_user
- @users = @users.where.not(id: current_user.id)
- @users = [current_user, *@users]
+ @users = [current_user, *@users].uniq
end
if params[:author_id].present?
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 17cb1d5be24..f9d798d0455 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -13,7 +13,8 @@ class Projects::ServicesController < Projects::ApplicationController
end
def update
- if @service.update_attributes(service_params[:service])
+ @service.assign_attributes(service_params[:service])
+ if @service.save(context: :manual_change)
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, @service.to_param),
notice: 'Successfully updated.'
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index c2b399041c6..aad83731b87 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -1,4 +1,6 @@
module IssuablesHelper
+ include GitlabRoutingHelper
+
def sidebar_gutter_toggle_icon
sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' })
end
@@ -95,8 +97,23 @@ module IssuablesHelper
h(milestone_title.presence || default_label)
end
+ def to_url_reference(issuable)
+ case issuable
+ when Issue
+ link_to issuable.to_reference, issue_url(issuable)
+ when MergeRequest
+ link_to issuable.to_reference, merge_request_url(issuable)
+ else
+ issuable.to_reference
+ end
+ end
+
def issuable_meta(issuable, project, text)
- output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
+ output = content_tag(:strong, class: "identifier") do
+ concat("#{text} ")
+ concat(to_url_reference(issuable))
+ end
+
output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 67206415f7b..8a5a9aa4adb 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -144,7 +144,7 @@ module Ci
status_sql = statuses.latest.where('stage=sg.stage').status_sql
- warnings_sql = statuses.latest.select('COUNT(*) > 0')
+ warnings_sql = statuses.latest.select('COUNT(*)')
.where('stage=sg.stage').failed_but_allowed.to_sql
stages_with_statuses = CommitStatus.from(stages_query, :sg)
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index ca74c91b062..e7d6b17d445 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -46,10 +46,10 @@ module Ci
end
def has_warnings?
- if @warnings.nil?
- statuses.latest.failed_but_allowed.any?
+ if @warnings.is_a?(Integer)
+ @warnings > 0
else
- @warnings
+ statuses.latest.failed_but_allowed.any?
end
end
end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 9e65fdbf9d6..50435b67eda 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,4 +1,6 @@
class IssueTrackerService < Service
+ validate :one_issue_tracker, if: :activated?, on: :manual_change
+
default_value_for :category, 'issue_tracker'
# Pattern used to extract links from comments
@@ -92,4 +94,13 @@ class IssueTrackerService < Service
def issues_tracker
Gitlab.config.issues_tracker[to_param]
end
+
+ def one_issue_tracker
+ return if template?
+ return if project.blank?
+
+ if project.services.external_issue_trackers.where.not(id: id).any?
+ errors.add(:base, 'Another issue tracker is already in use. Only one issue tracker service can be active at a time')
+ end
+ end
end
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index bdea1064096..06fb531b546 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html"
xml.id issues_dashboard_url
- xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
+ xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
end
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 30e63d991bb..a2f6a7ab1cb 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -4,7 +4,7 @@
.devise-errors
= devise_error_messages!
.form-group
- = f.label :name
+ = f.label :name, 'Full name'
= f.text_field :name, class: "form-control top", required: true, title: "This field is required."
.username.form-group
= f.label :username
diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder
index 7890e717aa7..43a52cf3002 100644
--- a/app/views/events/_event.atom.builder
+++ b/app/views/events/_event.atom.builder
@@ -4,7 +4,7 @@ xml.entry do
xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
xml.link href: event_feed_url(event)
xml.title truncate(event_feed_title(event), length: 80)
- xml.updated event.created_at.xmlschema
+ xml.updated event.updated_at.xmlschema
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
xml.author do
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index 0cc6466d34e..469768d83f2 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: issues_group_url, rel: "alternate", type: "text/html"
xml.id issues_group_url
- xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
+ xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
end
diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder
index 96831874144..fcd30c8c765 100644
--- a/app/views/issues/_issue.atom.builder
+++ b/app/views/issues/_issue.atom.builder
@@ -2,7 +2,7 @@ xml.entry do
xml.id namespace_project_issue_url(issue.project.namespace, issue.project, issue)
xml.link href: namespace_project_issue_url(issue.project.namespace, issue.project, issue)
xml.title truncate(issue.title, length: 80)
- xml.updated issue.created_at.xmlschema
+ xml.updated issue.updated_at.xmlschema
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
xml.author do
diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml
index 0cbe9b3275a..4cfbd9add00 100644
--- a/app/views/projects/deploy_keys/_index.html.haml
+++ b/app/views/projects/deploy_keys/_index.html.haml
@@ -3,7 +3,7 @@
%h4.prepend-top-0
Deploy Keys
%p
- Deploy keys allow read-only access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
+ Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
.col-lg-9
%h5.prepend-top-0
Create a new deploy key for this project
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index a0df0db77c5..4feec09bb5d 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: url_for(params), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_issues_url(@project.namespace, @project)
- xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
+ xml.updated @issues.first.updated_at.xmlschema if @issues.reorder(nil).any?
xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
end
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index a7618370a5d..5552086bc50 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -43,18 +43,17 @@
"inline-template" => true,
"ref" => "note_#{note.id}" }
- .note-action-button
+ %button.note-action-button.line-resolve-btn{ type: "button",
+ class: ("is-disabled" unless can_resolve),
+ ":class" => "{ 'is-active': isResolved }",
+ ":aria-label" => "buttonText",
+ "@click" => "resolve",
+ ":title" => "buttonText",
+ "v-show" => "!loading",
+ ":ref" => "'button'" }
= icon("spin spinner", "v-show" => "loading")
- %button.line-resolve-btn{ type: "button",
- class: ("is-disabled" unless can_resolve),
- ":class" => "{ 'is-active': isResolved }",
- ":aria-label" => "buttonText",
- "@click" => "resolve",
- ":title" => "buttonText",
- "v-show" => "!loading",
- ":ref" => "'button'" }
- = render "shared/icons/icon_status_success.svg"
+ = render "shared/icons/icon_status_success.svg"
- if current_user
- if note.emoji_awardable?
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index afd37e76c79..f8123846596 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -14,18 +14,18 @@
.scroll-container
%ul.tokens-container.list-unstyled
%li.input-token
- %input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id, 'data-username-params' => @users.to_json(only: [:id, :username]), 'data-base-endpoint' => namespace_project_path(@project.namespace, @project) }
+ %input.form-control.filtered-search{ placeholder: 'Search or filter results...', data: { id: 'filtered-search', 'project-id' => @project.id, 'username-params' => @users.to_json(only: [:id, :username]), 'base-endpoint' => namespace_project_path(@project.namespace, @project) } }
= icon('filter')
%button.clear-search.hidden{ type: 'button' }
= icon('times')
#js-dropdown-hint.dropdown-menu.hint-dropdown
- %ul{ 'data-dropdown' => true }
- %li.filter-dropdown-item{ 'data-action' => 'submit' }
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { action: 'submit' } }
%button.btn.btn-link
= icon('search')
%span
Keep typing and press Enter
- %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link
-# Encapsulate static class name `{{icon}}` inside #{} to bypass
@@ -36,50 +36,50 @@
%span.js-filter-tag.dropdown-light-content
{{tag}}
#js-dropdown-author.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } }
- %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
- %img.avatar{ 'data-src' => '{{avatar_url}}', alt: '{{name}}\'s avatar', width: '30' }
+ %img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } }
.dropdown-user-details
%span
{{name}}
%span.dropdown-light-content
@{{username}}
#js-dropdown-assignee.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } }
- %ul{ 'data-dropdown' => true }
- %li.filter-dropdown-item{ 'data-value' => 'none' }
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
No Assignee
%li.divider
- %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
- %img.avatar{ 'data-src' => '{{avatar_url}}', alt: '{{name}}\'s avatar', width: '30' }
+ %img.avatar{ alt: '{{name}}\'s avatar', width: '30', data: { src: '{{avatar_url}}' } }
.dropdown-user-details
%span
{{name}}
%span.dropdown-light-content
@{{username}}
#js-dropdown-milestone.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } }
- %ul{ 'data-dropdown' => true }
- %li.filter-dropdown-item{ 'data-value' => 'none' }
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
No Milestone
- %li.filter-dropdown-item{ 'data-value' => 'upcoming' }
+ %li.filter-dropdown-item{ data: { value: 'upcoming' } }
%button.btn.btn-link
Upcoming
%li.divider
- %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value
{{title}}
#js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label' } }
- %ul{ 'data-dropdown' => true }
- %li.filter-dropdown-item{ 'data-value' => 'none' }
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
No Label
%li.divider
- %ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link
%span.dropdown-label-box{ style: 'background: {{color}}' }
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 0e20df506a3..13207a8bc71 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -10,7 +10,7 @@ class AuthorizedProjectsWorker
end
def self.bulk_perform_async(args_list)
- Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
+ Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
end
def perform(user_id)
diff --git a/changelogs/unreleased/24137-issuable-permalink.yml b/changelogs/unreleased/24137-issuable-permalink.yml
new file mode 100644
index 00000000000..bcc6c6957a1
--- /dev/null
+++ b/changelogs/unreleased/24137-issuable-permalink.yml
@@ -0,0 +1,4 @@
+---
+title: Link issuable reference to itself in meta-header
+merge_request: 9641
+author: mhasbini
diff --git a/changelogs/unreleased/24166-close-builds-dropdown.yml b/changelogs/unreleased/24166-close-builds-dropdown.yml
new file mode 100644
index 00000000000..c57ffed6b45
--- /dev/null
+++ b/changelogs/unreleased/24166-close-builds-dropdown.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent builds dropdown to close when the user clicks in a build
+merge_request:
+author:
diff --git a/changelogs/unreleased/29046-fix-github-importer-open-prs.yml b/changelogs/unreleased/29046-fix-github-importer-open-prs.yml
new file mode 100644
index 00000000000..d279c269f94
--- /dev/null
+++ b/changelogs/unreleased/29046-fix-github-importer-open-prs.yml
@@ -0,0 +1,4 @@
+---
+title: Fix GitHub Import deleting branches for open PRs from a fork
+merge_request: 9758
+author:
diff --git a/changelogs/unreleased/29137-bulk-perform-async-should-specify-queue.yml b/changelogs/unreleased/29137-bulk-perform-async-should-specify-queue.yml
new file mode 100644
index 00000000000..0de7754badc
--- /dev/null
+++ b/changelogs/unreleased/29137-bulk-perform-async-should-specify-queue.yml
@@ -0,0 +1,4 @@
+---
+title: Make authorized projects worker use a specific queue instead of the default one
+merge_request: 9813
+author:
diff --git a/changelogs/unreleased/29189-discussion-button.yml b/changelogs/unreleased/29189-discussion-button.yml
new file mode 100644
index 00000000000..eea96362117
--- /dev/null
+++ b/changelogs/unreleased/29189-discussion-button.yml
@@ -0,0 +1,4 @@
+---
+title: Fix alignment of resolve button
+merge_request:
+author:
diff --git a/changelogs/unreleased/29209-sign-up-form-name.yml b/changelogs/unreleased/29209-sign-up-form-name.yml
new file mode 100644
index 00000000000..e8e3a71f875
--- /dev/null
+++ b/changelogs/unreleased/29209-sign-up-form-name.yml
@@ -0,0 +1,4 @@
+---
+title: Change label for name on sign up form
+merge_request:
+author:
diff --git a/changelogs/unreleased/29263-merge-button-color.yml b/changelogs/unreleased/29263-merge-button-color.yml
new file mode 100644
index 00000000000..2d0625483a4
--- /dev/null
+++ b/changelogs/unreleased/29263-merge-button-color.yml
@@ -0,0 +1,4 @@
+---
+title: ensure MR widget dropdown is same color as button
+merge_request:
+author:
diff --git a/changelogs/unreleased/29328-fix-transient-failure-in-model-user-spec.yml b/changelogs/unreleased/29328-fix-transient-failure-in-model-user-spec.yml
new file mode 100644
index 00000000000..dabf9968c5b
--- /dev/null
+++ b/changelogs/unreleased/29328-fix-transient-failure-in-model-user-spec.yml
@@ -0,0 +1,4 @@
+---
+title: Add custom attributes in factories
+merge_request: 9892
+author: George Andrinopoulos
diff --git a/changelogs/unreleased/adam-prevent-two-issue-trackers.yml b/changelogs/unreleased/adam-prevent-two-issue-trackers.yml
new file mode 100644
index 00000000000..307b7ec7359
--- /dev/null
+++ b/changelogs/unreleased/adam-prevent-two-issue-trackers.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent more than one issue tracker to be active for the same project
+merge_request:
+author: luisdgs19
diff --git a/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml b/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml
new file mode 100644
index 00000000000..66d5bb63734
--- /dev/null
+++ b/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml
@@ -0,0 +1,4 @@
+---
+title: Add frequently used emojis back to awards menu
+merge_request:
+author:
diff --git a/changelogs/unreleased/enable-snippets-by-default.yml b/changelogs/unreleased/enable-snippets-by-default.yml
new file mode 100644
index 00000000000..04fa3f7bdae
--- /dev/null
+++ b/changelogs/unreleased/enable-snippets-by-default.yml
@@ -0,0 +1,4 @@
+---
+title: Enable snippets for new projects by default
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml b/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml
new file mode 100644
index 00000000000..6365b1a1910
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml
@@ -0,0 +1,4 @@
+---
+title: Fix "passed with warnings" stage status on MySQL installations
+merge_request: 9802
+author:
diff --git a/changelogs/unreleased/fix_updated_field_in_issues-atom.yml b/changelogs/unreleased/fix_updated_field_in_issues-atom.yml
new file mode 100644
index 00000000000..414facdf779
--- /dev/null
+++ b/changelogs/unreleased/fix_updated_field_in_issues-atom.yml
@@ -0,0 +1,4 @@
+---
+title: Fix xml.updated field in rss/atom feeds
+merge_request: 9889
+author: blackst0ne
diff --git a/changelogs/unreleased/tc-fix-project-create-500.yml b/changelogs/unreleased/tc-fix-project-create-500.yml
new file mode 100644
index 00000000000..1b746a41eab
--- /dev/null
+++ b/changelogs/unreleased/tc-fix-project-create-500.yml
@@ -0,0 +1,4 @@
+---
+title: Fix for creating a project through API when import_url is nil
+merge_request: 9841
+author:
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 720df0cac2d..2bc39ea3f65 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -89,7 +89,7 @@ production: &base
issues: true
merge_requests: true
wiki: true
- snippets: false
+ snippets: true
builds: true
container_registry: true
@@ -441,6 +441,16 @@ production: &base
shared:
# path: /mnt/gitlab # Default: shared
+ # Gitaly settings
+ gitaly:
+ # The socket_path setting is optional and obsolete. When this is set
+ # GitLab assumes it can reach a Gitaly services via a Unix socket at
+ # this path. When this is commented out GitLab will not use Gitaly.
+ #
+ # This setting is obsolete because we expect it to be moved under
+ # repositories/storages in GitLab 9.1.
+ #
+ # socket_path: tmp/sockets/gitaly.socket
#
# 4. Advanced settings
diff --git a/config/initializers/inflections.rb b/config/initializers/0_inflections.rb
index d4197da3fa9..d4197da3fa9 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/0_inflections.rb
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index b45d0e23080..d049ae9476f 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -221,7 +221,7 @@ Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
-Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
+Settings.gitlab.default_projects_features['snippets'] = true if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil?
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
diff --git a/config/initializers/fix_local_cache_middleware.rb b/config/initializers/fix_local_cache_middleware.rb
new file mode 100644
index 00000000000..cb37f9ed22c
--- /dev/null
+++ b/config/initializers/fix_local_cache_middleware.rb
@@ -0,0 +1,24 @@
+module LocalCacheRegistryCleanupWithEnsure
+ LocalCacheRegistry =
+ ActiveSupport::Cache::Strategy::LocalCache::LocalCacheRegistry
+ LocalStore =
+ ActiveSupport::Cache::Strategy::LocalCache::LocalStore
+
+ def call(env)
+ LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
+ response = @app.call(env)
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil)
+ end
+ cleanup_after_response = true # ADDED THIS LINE
+ response
+ rescue Rack::Utils::InvalidParameterError
+ [400, {}, []]
+ ensure # ADDED ensure CLAUSE to cleanup when something is thrown
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless
+ cleanup_after_response
+ end
+end
+
+ActiveSupport::Cache::Strategy::LocalCache::Middleware
+ .prepend(LocalCacheRegistryCleanupWithEnsure)
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 463715e48ca..f6f50e2c571 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -17,14 +17,17 @@ Pages to the latest supported version.
## Prerequisites
-Before proceeding with the Pages configuration, you will need to:
-
-1. Have a separate domain under which the GitLab Pages will be served. In this
- document we assume that to be `example.io`.
-1. Configure a **wildcard DNS record**.
-1. (Optional) Have a **wildcard certificate** for that domain if you decide to
- serve Pages under HTTPS.
-1. (Optional but recommended) Enable [Shared runners](../../ci/runners/README.md)
+Before proceeding with the Pages configuration, make sure that:
+
+1. You have a separate domain under which GitLab Pages will be served. In
+ this document we assume that to be `example.io`.
+1. You have configured a **wildcard DNS record** for that domain.
+1. You have installed the `zip` and `unzip` packages in the same server that
+ GitLab is installed since they are needed to compress/uncompress the
+ Pages artifacts.
+1. (Optional) You have a **wildcard certificate** for the Pages domain if you
+ decide to serve Pages (`*.example.io`) under HTTPS.
+1. (Optional but recommended) You have configured and enabled the [Shared Runners][]
so that your users don't have to bring their own.
### DNS configuration
@@ -390,3 +393,4 @@ than GitLab to prevent XSS attacks.
[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: ../restart_gitlab.md#installations-from-source
[gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4
+[shared runners]: ../../ci/runners/README.md
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index 9ba820eaee5..d646de7c54a 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -16,6 +16,22 @@ minification, and compression of our assets.
[jQuery][jquery] is used throughout the application's JavaScript, with
[Vue.js][vue] for particularly advanced, dynamic elements.
+### Architecture
+
+The Frontend Architect is an expert who makes high-level frontend design choices
+and decides on technical standards, including coding standards, and frameworks.
+
+When you are assigned a new feature that requires architectural design,
+make sure it is discussed with one of the Frontend Architecture Experts.
+
+This rule also applies if you plan to change the architecture of an existing feature.
+
+These decisions should be accessible to everyone, so please document it on the Merge Request.
+
+You can find the Frontend Architecture experts on the [team page][team-page].
+
+You can find documentation about the desired architecture for a new feature built with Vue.js in [here][vue-section].
+
### Vue
For more complex frontend features, we recommend using Vue.js. It shares
@@ -238,8 +254,8 @@ readability.
See the relevant style guides for our guidelines and for information on linting:
- [SCSS][scss-style-guide]
-- JavaScript - We defer to [AirBnb][airbnb-js-style-guide] on most style-related
-conventions and enforce them with eslint. See [our current .eslintrc][eslistrc]
+- JavaScript - We defer to [AirBnb][airbnb-js-style-guide] on most style-related
+conventions and enforce them with eslint. See [our current .eslintrc][eslintrc]
for specific rules and patterns.
## Testing
@@ -439,3 +455,5 @@ Scenario: Developer can approve merge request
[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
[airbnb-js-style-guide]: https://github.com/airbnb/javascript
[eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc
+[team-page]: https://about.gitlab.com/team
+[vue-section]: https://docs.gitlab.com/ce/development/frontend.html#how-to-build-a-new-feature-with-vue-js
diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md
index ead79ba6a10..794c8eb6bfe 100644
--- a/doc/development/ux_guide/copy.md
+++ b/doc/development/ux_guide/copy.md
@@ -167,6 +167,15 @@ A **comment** is a written piece of text that users of GitLab can create. Commen
#### Discussion
A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved.
+## Confirmation dialogs
+
+- Destruction buttons should be clear and always say what they are destroying.
+ E.g., `Delete page` instead of just `Delete`.
+- If the copy describes another action the user can take instead of the
+ destructive one, provide a way for them to do that as a secondary button.
+- Avoid the word `cancel` or `canceled` in the descriptive copy. It can be
+ confusing when you then see the `Cancel` button.
+
---
Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons].
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 8e74970b8e9..177e1a9378b 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -288,9 +288,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-17-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-0-stable gitlab
-**Note:** You can change `8-17-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `9-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/update/8.17-to-9.0.md b/doc/update/8.17-to-9.0.md
index 4cc8be752c4..1fe38cf8d2a 100644
--- a/doc/update/8.17-to-9.0.md
+++ b/doc/update/8.17-to-9.0.md
@@ -1,3 +1,162 @@
+# From 8.17 to 9.0
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+We will continue supporting Ruby < 2.3 for the time being but we recommend you
+upgrade to Ruby 2.3 if you're running a source installation, as this is the same
+version that ships with our Omnibus package.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
+echo '1014ee699071aa2ddd501907d18cbe15399c997d ruby-2.3.3.tar.gz' | shasum -c - && tar xzf ruby-2.3.3.tar.gz
+cd ruby-2.3.3
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
+it has a minimum requirement of node v4.3.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v4.3.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+
+Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
+JavaScript dependencies.
+
+```bash
+curl --location https://yarnpkg.com/install.sh | bash -
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 9-0-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 9-0-stable-ee
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Install/update frontend asset dependencies
+sudo -u git -H npm install --production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 7. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
+```
+
+### 8. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v5.0.0
+```
+
+### 9. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/8-17-stable:config/gitlab.yml.example origin/9-0-stable:config/gitlab.yml.example
+```
+
#### Configuration changes for repository storages
This version introduces a new configuration structure for repository storages.
@@ -85,3 +244,78 @@ via [/etc/default/gitlab].
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-0-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 10. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 11. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.17)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.16 to 8.17](8.16-to-8.17.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-0-stable/config/gitlab.yml.example
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index 0f24f9bbfde..ffbc6e17dc5 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -37,7 +37,7 @@ module Gitlab
def get_etag(env)
cache_key = env['PATH_INFO']
- store = Store.new
+ store = Gitlab::EtagCaching::Store.new
current_value = store.get(cache_key)
cached_value_present = current_value.present?
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index dc73cad93a5..eea4a91f17d 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -171,6 +171,8 @@ module Gitlab
end
def clean_up_restored_branches(pull_request)
+ return if pull_request.opened?
+
remove_branch(pull_request.source_branch_name) unless pull_request.source_branch_exists?
remove_branch(pull_request.target_branch_name) unless pull_request.target_branch_exists?
end
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index 28812fd0cb9..add7236e339 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -60,6 +60,10 @@ module Gitlab
source_branch.repo.id != target_branch.repo.id
end
+ def opened?
+ state == 'opened'
+ end
+
private
def state
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 1f0d96088cf..c81dc7e30d0 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -9,6 +9,8 @@ module Gitlab
end
def self.valid?(url)
+ return false unless url
+
Addressable::URI.parse(url.strip)
true
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index ecf6b6e068b..5476438b8fa 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -62,7 +62,7 @@ namespace :gitlab do
ref = Shellwords.escape(args[:ref])
- migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines
+ migrations = `git diff #{ref}.. --diff-filter=A --name-only -- db/migrate`.lines
.map { |file| Rails.root.join(file.strip).to_s }
.select { |file| File.file?(file) }
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index d4e0ef91856..755992069ff 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe 'New/edit issue', feature: true, js: true do
+ include GitlabRoutingHelper
+
let!(:project) { create(:project) }
let!(:user) { create(:user)}
let!(:user2) { create(:user)}
@@ -78,6 +80,14 @@ describe 'New/edit issue', feature: true, js: true do
expect(page).to have_content label2.title
end
end
+
+ page.within '.issuable-meta' do
+ issue = Issue.find_by(title: 'title')
+
+ expect(page).to have_text("Issue #{issue.to_reference}")
+ # compare paths because the host differ in test
+ expect(find_link(issue.to_reference)[:href]).to end_with(issue_path(issue))
+ end
end
it 'correctly updates the dropdown toggle when removing a label' do
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index 1ecdb8b5983..f8518f450dc 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe 'New/edit merge request', feature: true, js: true do
+ include GitlabRoutingHelper
+
let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:fork_project) { create(:project, forked_from_project: project) }
let!(:user) { create(:user)}
@@ -84,6 +86,15 @@ describe 'New/edit merge request', feature: true, js: true do
expect(page).to have_content label2.title
end
end
+
+ page.within '.issuable-meta' do
+ merge_request = MergeRequest.find_by(source_branch: 'fix')
+
+ expect(page).to have_text("Merge Request #{merge_request.to_reference}")
+ # compare paths because the host differ in test
+ expect(find_link(merge_request.to_reference)[:href])
+ .to end_with(merge_request_path(merge_request))
+ end
end
end
diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb
index a1643fd1f43..7c319af893b 100644
--- a/spec/features/projects/edit_spec.rb
+++ b/spec/features/projects/edit_spec.rb
@@ -21,36 +21,28 @@ feature 'Project edit', feature: true, js: true do
expect(page).to have_selector('.merge-requests-feature', visible: false)
end
- it 'hides merge requests section after save' do
- select('Disabled', from: 'project_project_feature_attributes_merge_requests_access_level')
-
- expect(page).to have_selector('.merge-requests-feature', visible: false)
-
- click_button 'Save changes'
+ context 'given project with merge_requests_disabled access level' do
+ let(:project) { create(:project, :merge_requests_disabled) }
- wait_for_ajax
-
- expect(page).to have_selector('.merge-requests-feature', visible: false)
+ it 'hides merge requests section' do
+ expect(page).to have_selector('.merge-requests-feature', visible: false)
+ end
end
end
context 'builds select' do
- it 'hides merge requests section' do
+ it 'hides builds select section' do
select('Disabled', from: 'project_project_feature_attributes_builds_access_level')
expect(page).to have_selector('.builds-feature', visible: false)
end
- it 'hides merge requests section after save' do
- select('Disabled', from: 'project_project_feature_attributes_builds_access_level')
-
- expect(page).to have_selector('.builds-feature', visible: false)
+ context 'given project with builds_disabled access level' do
+ let(:project) { create(:project, :builds_disabled) }
- click_button 'Save changes'
-
- wait_for_ajax
-
- expect(page).to have_selector('.builds-feature', visible: false)
+ it 'hides builds select section' do
+ expect(page).to have_selector('.builds-feature', visible: false)
+ end
end
end
end
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index dc0a62ade50..9a2978006aa 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,6 +1,7 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */
import promisePolyfill from 'es6-promise';
+import Cookies from 'js-cookie';
import AwardsHandler from '~/awards_handler';
promisePolyfill.polyfill();
@@ -208,8 +209,8 @@ promisePolyfill.polyfill();
expect($('[data-name=alien]').is(':visible')).toBe(true);
})
.then(done)
- .catch(() => {
- done.fail('Failed to open and build emoji menu');
+ .catch((err) => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
});
});
@@ -232,8 +233,8 @@ promisePolyfill.polyfill();
it('should add selected emoji to awards block', function(done) {
return openEmojiMenuAndAddEmoji()
.then(done)
- .catch(() => {
- done.fail('Failed to open and build emoji menu');
+ .catch((err) => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
});
it('should remove already selected emoji', function(done) {
@@ -247,7 +248,46 @@ promisePolyfill.polyfill();
})
.then(done)
.catch((err) => {
- done.fail('Failed to open and build emoji menu');
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
+ });
+
+ describe('frequently used emojis', function() {
+ beforeEach(() => {
+ // Clear it out
+ Cookies.set('frequently_used_emojis', '');
+ });
+
+ it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) {
+ return openAndWaitForEmojiMenu()
+ .then(() => {
+ const emojiMenu = document.querySelector('.emoji-menu');
+ Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), (title) => {
+ expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used');
+ });
+ })
+ .then(done)
+ .catch((err) => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
+ });
+ });
+
+ it('should have any frequently used section when there are frequently used emojis', function(done) {
+ awardsHandler.addEmojiToFrequentlyUsedList('8ball');
+
+ return openAndWaitForEmojiMenu()
+ .then(() => {
+ const emojiMenu = document.querySelector('.emoji-menu');
+ const hasFrequentlyUsedHeading = Array.prototype.some.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title =>
+ title.textContent.trim().toLowerCase() === 'frequently used'
+ );
+
+ expect(hasFrequentlyUsedHeading).toBe(true);
+ })
+ .then(done)
+ .catch((err) => {
+ done.fail(`Failed to open and build emoji menu: ${err.message}`);
});
});
});
diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
index 7cdade01e00..e504d41d4d4 100644
--- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
+++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
-require('~/flash');
-require('~/mini_pipeline_graph_dropdown');
+import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
+import '~/flash';
(() => {
describe('Mini Pipeline Graph Dropdown', () => {
@@ -13,7 +13,7 @@ require('~/mini_pipeline_graph_dropdown');
describe('When is initialized', () => {
it('should initialize without errors when no options are given', () => {
- const miniPipelineGraph = new window.gl.MiniPipelineGraph();
+ const miniPipelineGraph = new MiniPipelineGraph();
expect(miniPipelineGraph.dropdownListSelector).toEqual('.js-builds-dropdown-container');
});
@@ -21,7 +21,7 @@ require('~/mini_pipeline_graph_dropdown');
it('should set the container as the given prop', () => {
const container = '.foo';
- const miniPipelineGraph = new window.gl.MiniPipelineGraph({ container });
+ const miniPipelineGraph = new MiniPipelineGraph({ container });
expect(miniPipelineGraph.container).toEqual(container);
});
@@ -29,9 +29,9 @@ require('~/mini_pipeline_graph_dropdown');
describe('When dropdown is clicked', () => {
it('should call getBuildsList', () => {
- const getBuildsListSpy = spyOn(gl.MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(function () {});
+ const getBuildsListSpy = spyOn(MiniPipelineGraph.prototype, 'getBuildsList').and.callFake(function () {});
- new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
+ new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
@@ -41,11 +41,32 @@ require('~/mini_pipeline_graph_dropdown');
it('should make a request to the endpoint provided in the html', () => {
const ajaxSpy = spyOn($, 'ajax').and.callFake(function () {});
- new gl.MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
+ new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
document.querySelector('.js-builds-dropdown-button').click();
expect(ajaxSpy.calls.allArgs()[0][0].url).toEqual('foobar');
});
+
+ it('should not close when user uses cmd/ctrl + click', () => {
+ spyOn($, 'ajax').and.callFake(function (params) {
+ params.success({
+ html: `<li>
+ <a class="mini-pipeline-graph-dropdown-item" href="#">
+ <span class="ci-status-icon ci-status-icon-failed"></span>
+ <span class="ci-build-text">build</span>
+ </a>
+ <a class="ci-action-icon-wrapper js-ci-action-icon" href="#"></a>
+ </li>`,
+ });
+ });
+ new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents();
+
+ document.querySelector('.js-builds-dropdown-button').click();
+
+ document.querySelector('a.mini-pipeline-graph-dropdown-item').click();
+
+ expect($('.js-builds-dropdown-list').is(':visible')).toEqual(true);
+ });
});
});
})();
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 3f080de99dd..8b867fbe322 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -55,9 +55,6 @@ describe Gitlab::GithubImport::Importer, lib: true do
allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
end
- let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
- let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
- let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:label1) do
double(
name: 'Bug',
@@ -127,32 +124,6 @@ describe Gitlab::GithubImport::Importer, lib: true do
)
end
- let!(:user) { create(:user, email: octocat.email) }
- let(:repository) { double(id: 1, fork: false) }
- let(:source_sha) { create(:commit, project: project).id }
- let(:source_branch) { double(ref: 'branch-merged', repo: repository, sha: source_sha) }
- let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
- let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) }
- let(:pull_request) do
- double(
- number: 1347,
- milestone: nil,
- state: 'open',
- title: 'New feature',
- body: 'Please pull these awesome changes',
- head: source_branch,
- base: target_branch,
- assignee: nil,
- user: octocat,
- created_at: created_at,
- updated_at: updated_at,
- closed_at: nil,
- merged_at: nil,
- url: "#{api_root}/repos/octocat/Hello-World/pulls/1347",
- labels: [double(name: 'Label #2')]
- )
- end
-
let(:release1) do
double(
tag_name: 'v1.0.0',
@@ -177,12 +148,14 @@ describe Gitlab::GithubImport::Importer, lib: true do
)
end
+ subject { described_class.new(project) }
+
it 'returns true' do
- expect(described_class.new(project).execute).to eq true
+ expect(subject.execute).to eq true
end
it 'does not raise an error' do
- expect { described_class.new(project).execute }.not_to raise_error
+ expect { subject.execute }.not_to raise_error
end
it 'stores error messages' do
@@ -205,15 +178,93 @@ describe Gitlab::GithubImport::Importer, lib: true do
end
end
+ shared_examples 'Gitlab::GithubImport unit-testing' do
+ describe '#clean_up_restored_branches' do
+ subject { described_class.new(project) }
+
+ before do
+ allow(gh_pull_request).to receive(:source_branch_exists?).at_least(:once) { false }
+ allow(gh_pull_request).to receive(:target_branch_exists?).at_least(:once) { false }
+ end
+
+ context 'when pull request stills open' do
+ let(:gh_pull_request) { Gitlab::GithubImport::PullRequestFormatter.new(project, pull_request) }
+
+ it 'does not remove branches' do
+ expect(subject).not_to receive(:remove_branch)
+ subject.send(:clean_up_restored_branches, gh_pull_request)
+ end
+ end
+
+ context 'when pull request is closed' do
+ let(:gh_pull_request) { Gitlab::GithubImport::PullRequestFormatter.new(project, closed_pull_request) }
+
+ it 'does remove branches' do
+ expect(subject).to receive(:remove_branch).at_least(2).times
+ subject.send(:clean_up_restored_branches, gh_pull_request)
+ end
+ end
+ end
+ end
+
let(:project) { create(:project, :wiki_disabled, import_url: "#{repo_root}/octocat/Hello-World.git") }
+ let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:credentials) { { user: 'joe' } }
+ let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
+ let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
+ let(:repository) { double(id: 1, fork: false) }
+ let(:source_sha) { create(:commit, project: project).id }
+ let(:source_branch) { double(ref: 'branch-merged', repo: repository, sha: source_sha) }
+ let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
+ let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) }
+ let(:pull_request) do
+ double(
+ number: 1347,
+ milestone: nil,
+ state: 'open',
+ title: 'New feature',
+ body: 'Please pull these awesome changes',
+ head: source_branch,
+ base: target_branch,
+ assignee: nil,
+ user: octocat,
+ created_at: created_at,
+ updated_at: updated_at,
+ closed_at: nil,
+ merged_at: nil,
+ url: "#{api_root}/repos/octocat/Hello-World/pulls/1347",
+ labels: [double(name: 'Label #2')]
+ )
+ end
+ let(:closed_pull_request) do
+ double(
+ number: 1347,
+ milestone: nil,
+ state: 'closed',
+ title: 'New feature',
+ body: 'Please pull these awesome changes',
+ head: source_branch,
+ base: target_branch,
+ assignee: nil,
+ user: octocat,
+ created_at: created_at,
+ updated_at: updated_at,
+ closed_at: updated_at,
+ merged_at: nil,
+ url: "#{api_root}/repos/octocat/Hello-World/pulls/1347",
+ labels: [double(name: 'Label #2')]
+ )
+ end
+
context 'when importing a GitHub project' do
let(:api_root) { 'https://api.github.com' }
let(:repo_root) { 'https://github.com' }
+ subject { described_class.new(project) }
it_behaves_like 'Gitlab::GithubImport::Importer#execute'
it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs'
+ it_behaves_like 'Gitlab::GithubImport unit-testing'
describe '#client' do
it 'instantiates a Client' do
@@ -223,7 +274,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
{}
)
- described_class.new(project).client
+ subject.client
end
end
end
@@ -231,6 +282,8 @@ describe Gitlab::GithubImport::Importer, lib: true do
context 'when importing a Gitea project' do
let(:api_root) { 'https://try.gitea.io/api/v1' }
let(:repo_root) { 'https://try.gitea.io' }
+ subject { described_class.new(project) }
+
before do
project.update(import_type: 'gitea', import_url: "#{repo_root}/foo/group/project.git")
end
@@ -239,6 +292,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
let(:expected_not_called) { [:import_releases] }
end
it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs'
+ it_behaves_like 'Gitlab::GithubImport unit-testing'
describe '#client' do
it 'instantiates a Client' do
@@ -248,7 +302,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
{ host: "#{repo_root}:443/foo", api_version: 'v1' }
)
- described_class.new(project).client
+ subject.client
end
end
end
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index 951cbea7857..44423917944 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -306,4 +306,12 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.url).to eq 'https://api.github.com/repos/octocat/Hello-World/pulls/1347'
end
end
+
+ describe '#opened?' do
+ let(:raw_data) { double(base_data.merge(state: 'open')) }
+
+ it 'returns true when state is "open"' do
+ expect(pull_request.opened?).to be_truthy
+ end
+ end
end
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index 2cb74629da8..3fd361de458 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -70,4 +70,12 @@ describe Gitlab::UrlSanitizer, lib: true do
expect(sanitizer.full_url).to eq('user@server:project.git')
end
end
+
+ describe '.valid?' do
+ it 'validates url strings' do
+ expect(described_class.valid?(nil)).to be(false)
+ expect(described_class.valid?('valid@project:url.git')).to be(true)
+ expect(described_class.valid?('123://invalid:url')).to be(false)
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 3ea62df62f2..9962c987110 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -197,6 +197,24 @@ describe Ci::Pipeline, models: true do
end
end
end
+
+ context 'when there is a stage with warnings' do
+ before do
+ create(:commit_status, pipeline: pipeline,
+ stage: 'deploy',
+ name: 'prod:2',
+ stage_idx: 2,
+ status: 'failed',
+ allow_failure: true)
+ end
+
+ it 'populates stage with correct number of warnings' do
+ deploy_stage = pipeline.stages.third
+
+ expect(deploy_stage).not_to receive(:statuses)
+ expect(deploy_stage).to have_warnings
+ end
+ end
end
describe '#stages_count' do
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index c4a9743a4e2..c38faf32f7d 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -170,22 +170,31 @@ describe Ci::Stage, models: true do
context 'when stage has warnings' do
context 'when using memoized warnings flag' do
context 'when there are warnings' do
- let(:stage) { build(:ci_stage, warnings: true) }
+ let(:stage) { build(:ci_stage, warnings: 2) }
- it 'has memoized warnings' do
+ it 'returns true using memoized value' do
expect(stage).not_to receive(:statuses)
expect(stage).to have_warnings
end
end
context 'when there are no warnings' do
- let(:stage) { build(:ci_stage, warnings: false) }
+ let(:stage) { build(:ci_stage, warnings: 0) }
- it 'has memoized warnings' do
+ it 'returns false using memoized value' do
expect(stage).not_to receive(:statuses)
expect(stage).not_to have_warnings
end
end
+
+ context 'when number of warnings is not a valid value' do
+ let(:stage) { build(:ci_stage, warnings: true) }
+
+ it 'calculates statuses using database queries' do
+ expect(stage).to receive(:statuses).and_call_original
+ expect(stage).not_to have_warnings
+ end
+ end
end
context 'when calculating warnings from statuses' do
diff --git a/spec/models/project_services/issue_tracker_service_spec.rb b/spec/models/project_services/issue_tracker_service_spec.rb
new file mode 100644
index 00000000000..fbe6f344a98
--- /dev/null
+++ b/spec/models/project_services/issue_tracker_service_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe IssueTrackerService, models: true do
+ describe 'Validations' do
+ let(:project) { create :project }
+
+ describe 'only one issue tracker per project' do
+ let(:service) { RedmineService.new(project: project, active: true) }
+
+ before do
+ create(:service, project: project, active: true, category: 'issue_tracker')
+ end
+
+ context 'when service is changed manually by user' do
+ it 'executes the validation' do
+ valid = service.valid?(:manual_change)
+
+ expect(valid).to be_falsey
+ expect(service.errors[:base]).to include(
+ 'Another issue tracker is already in use. Only one issue tracker service can be active at a time'
+ )
+ end
+ end
+
+ context 'when service is changed internally' do
+ it 'does not execute the validation' do
+ expect(service.valid?).to be_truthy
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e1dafe3aa72..9da4140f3ce 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -697,8 +697,9 @@ describe User, models: true do
describe '.search_with_secondary_emails' do
delegate :search_with_secondary_emails, to: :described_class
- let!(:user) { create(:user) }
- let!(:email) { create(:email) }
+ let!(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'john.doe@example.com' ) }
+ let!(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'albert.smith@example.com' ) }
+ let!(:email) { create(:email, user: another_user) }
it 'returns users with a matching name' do
expect(search_with_secondary_emails(user.name)).to eq([user])
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 77f79cd5bc7..b4b23617498 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -424,6 +424,14 @@ describe API::Projects, api: true do
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
end
+ it 'ignores import_url when it is nil' do
+ project = attributes_for(:project, { import_url: nil })
+
+ post api('/projects', user), project
+
+ expect(response).to have_http_status(201)
+ end
+
context 'when a visibility level is restricted' do
let(:project_param) { attributes_for(:project, visibility: 'public') }
diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb
index 97c4bfcd248..bd5cc651c2b 100644
--- a/spec/workers/authorized_projects_worker_spec.rb
+++ b/spec/workers/authorized_projects_worker_spec.rb
@@ -1,12 +1,10 @@
require 'spec_helper'
describe AuthorizedProjectsWorker do
- let(:worker) { described_class.new }
+ let(:project) { create(:empty_project) }
describe '.bulk_perform_and_wait' do
it 'schedules the ids and waits for the jobs to complete' do
- project = create(:project)
-
project.owner.project_authorizations.delete_all
described_class.bulk_perform_and_wait([[project.owner.id]])
@@ -15,20 +13,37 @@ describe AuthorizedProjectsWorker do
end
end
+ describe '.bulk_perform_async' do
+ it "uses it's respective sidekiq queue" do
+ args = [[project.owner.id]]
+ push_bulk_args = {
+ 'class' => described_class,
+ 'queue' => described_class.sidekiq_options['queue'],
+ 'args' => args
+ }
+
+ expect(Sidekiq::Client).to receive(:push_bulk).with(push_bulk_args).once
+
+ described_class.bulk_perform_async(args)
+ end
+ end
+
describe '#perform' do
+ subject { described_class.new }
+
it "refreshes user's authorized projects" do
user = create(:user)
expect_any_instance_of(User).to receive(:refresh_authorized_projects)
- worker.perform(user.id)
+ subject.perform(user.id)
end
context "when the user is not found" do
it "does nothing" do
expect_any_instance_of(User).not_to receive(:refresh_authorized_projects)
- described_class.new.perform(-1)
+ subject.perform(-1)
end
end
end
diff --git a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
index 8c590579934..40648bcd3de 100644
--- a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml
@@ -7,7 +7,7 @@ services:
build:
stage: build
script:
- - export IMAGE_TAG=$(echo -en $CI_BUILD_REF_NAME | tr -c '[:alnum:]_.-' '-')
- - docker login -u "gitlab-ci-token" -p "$CI_BUILD_TOKEN" $CI_REGISTRY
+ - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-')
+ - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY
- docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" .
- docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG"
diff --git a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
index b75f0665bee..91b096654d1 100644
--- a/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Maven.gitlab-ci.yml
@@ -3,9 +3,9 @@
# For docker image tags see https://hub.docker.com/_/maven/
#
# For general lifecycle information see https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
-#
+#
# This template will build and test your projects as well as create the documentation.
-#
+#
# * Caches downloaded dependencies and plugins between invocation.
# * Does only verify merge requests but deploy built artifacts of the
# master branch.
@@ -24,12 +24,12 @@ variables:
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"
# Cache downloaded dependencies and plugins between builds.
-# To keep cache across branches add 'key: "$CI_BUILD_REF_NAME"'
+# To keep cache across branches add 'key: "$CI_JOB_REF_NAME"'
cache:
paths:
- .m2/repository
-# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
+# This will only validate and compile stuff and run e.g. maven-enforcer-plugin.
# Because some enforcer rules might check dependency convergence and class duplications
# we use `test-compile` here instead of `validate`, so the correct classpath is picked up.
.validate: &validate
diff --git a/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml
index 6b6c405a507..d3bb388a1e7 100644
--- a/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/OpenShift.gitlab-ci.yml
@@ -38,11 +38,11 @@ review:
<<: *deploy
stage: review
variables:
- APP: $CI_BUILD_REF_NAME
- APP_HOST: $CI_PROJECT_NAME-$CI_BUILD_REF_NAME.$OPENSHIFT_DOMAIN
+ APP: $CI_COMMIT_REF_NAME
+ APP_HOST: $CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$OPENSHIFT_DOMAIN
environment:
- name: review/$CI_BUILD_REF_NAME
- url: http://$CI_PROJECT_NAME-$CI_BUILD_REF_NAME.$OPENSHIFT_DOMAIN
+ name: review/$CI_COMMIT_REF_SLUG
+ url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$OPENSHIFT_DOMAIN
on_stop: stop-review
only:
- branches
@@ -56,10 +56,10 @@ stop-review:
- oc delete all -l "app=$APP"
when: manual
variables:
- APP: $CI_BUILD_REF_NAME
+ APP: $CI_COMMIT_REF_NAME
GIT_STRATEGY: none
environment:
- name: review/$CI_BUILD_REF_NAME
+ name: review/$CI_COMMIT_REF_SLUG
action: stop
only:
- branches
diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
index 574f9365f14..c644560647f 100644
--- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
@@ -24,12 +24,12 @@ build:
production:
stage: production
variables:
- CI_ENVIRONMENT_URL: http://production.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME.$KUBE_DOMAIN
script:
- command deploy
environment:
name: production
- url: http://production.$KUBE_DOMAIN
+ url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN
when: manual
only:
- master
@@ -37,24 +37,24 @@ production:
staging:
stage: staging
variables:
- CI_ENVIRONMENT_URL: http://staging.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN
script:
- command deploy
environment:
name: staging
- url: http://staging.$KUBE_DOMAIN
+ url: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN
only:
- master
review:
stage: review
variables:
- CI_ENVIRONMENT_URL: http://$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
script:
- command deploy
environment:
- name: review/$CI_BUILD_REF_NAME
- url: http://$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
+ name: review/$CI_COMMIT_REF_NAME
+ url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
on_stop: stop_review
only:
- branches
@@ -68,7 +68,7 @@ stop_review:
script:
- command destroy
environment:
- name: review/$CI_BUILD_REF_NAME
+ name: review/$CI_COMMIT_REF_NAME
action: stop
when: manual
only:
diff --git a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml
index 4d6f4e00ebb..27c9107e0d7 100644
--- a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml
@@ -1,4 +1,4 @@
-# Explaination on the scripts:
+# Explanation on the scripts:
# https://gitlab.com/gitlab-examples/openshift-deploy/blob/master/README.md
image: registry.gitlab.com/gitlab-examples/openshift-deploy
@@ -24,12 +24,12 @@ build:
production:
stage: production
variables:
- CI_ENVIRONMENT_URL: http://production.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME.$KUBE_DOMAIN
script:
- command deploy
environment:
name: production
- url: http://production.$KUBE_DOMAIN
+ url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN
when: manual
only:
- master
@@ -37,24 +37,24 @@ production:
staging:
stage: staging
variables:
- CI_ENVIRONMENT_URL: http://staging.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN
script:
- command deploy
environment:
name: staging
- url: http://staging.$KUBE_DOMAIN
+ url: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN
only:
- master
review:
stage: review
variables:
- CI_ENVIRONMENT_URL: http://$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
+ CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
script:
- command deploy
environment:
- name: review/$CI_BUILD_REF_NAME
- url: http://$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
+ name: review/$CI_COMMIT_REF_NAME
+ url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
on_stop: stop_review
only:
- branches
@@ -68,7 +68,7 @@ stop_review:
script:
- command destroy
environment:
- name: review/$CI_BUILD_REF_NAME
+ name: review/$CI_COMMIT_REF_NAME
action: stop
when: manual
only: