Commit 3efcb46a authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-09-22

* upstream/master: (26 commits)
  Resolve "Better SVG Usage in the Frontend"
  Replace the 'project/service.feature' spinach test with an rspec analog
  Removed two legacy config options
  Fix rendering double note issue.
  IssueNotes: Switch back to Write pane when note cancel or submit.
  Upgrade Nokogiri because of CVE-2017-9050
  Bump VERSION to 10.1.0-pre
  Standardize access to CSRF token in JavaScript
  Do not clone the repo when running the review-docs jobs
  Don't memoize storage configuration on `FsShardsCheck`
  Document that group Owners can always create subgroups
  Auto DevOps docs cleanup
  Display full pre-receive and post-receive hook output in GitLab UI
  Simplify a test
  Adds EE tag detection to remove_old in gitlab backup.
  Correctly detect multiple issue URLs after 'Closes...' in MR descriptions
  new sharing permissions
  IssueNotes: Resize comment form after note submit and discard.
  [skip ci] Add changelog
  update spacing
  ...
parents fc805edf c8e60d63
......@@ -183,11 +183,20 @@ build-package:
image: ruby:2.4-alpine
before_script:
- gem install gitlab --no-doc
# We need to download the script rather than clone the repo since the
# review-docs-cleanup job will not be able to run when the branch gets
# deleted (when merging the MR).
- apk add --update openssl
- wget https://gitlab.com/gitlab-org/gitlab-ce/raw/master/scripts/trigger-build-docs
- chmod 755 trigger-build-docs
services: []
cache: {}
dependencies: []
artifacts: {}
variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
cache: {}
GIT_STRATEGY: none
when: manual
only:
- branches
......@@ -204,7 +213,7 @@ review-docs-deploy:
url: http://preview-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup
script:
- scripts/trigger-build-docs deploy
- ./trigger-build-docs deploy
# Cleanup remote environment of gitlab-docs
review-docs-cleanup:
......@@ -214,7 +223,7 @@ review-docs-cleanup:
name: review-docs/$CI_COMMIT_REF_NAME
action: stop
script:
- scripts/trigger-build-docs cleanup
- ./trigger-build-docs cleanup
# Retrieve knapsack and rspec_flaky reports
retrieve-tests-metadata:
......
9.6.0-pre
10.1.0-pre
......@@ -3,6 +3,7 @@
import '../lib/utils/url_utility';
import { HIDDEN_CLASS } from '../lib/utils/constants';
import csrf from '../lib/utils/csrf';
function toggleLoading($el, $icon, loading) {
if (loading) {
......@@ -36,9 +37,7 @@ export default class BlobFileDropzone {
maxFiles: 1,
addRemoveLinks: true,
previewsContainer: '.dropzone-previews',
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
},
headers: csrf.headers,
init: function () {
this.on('addedfile', function () {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
......
......@@ -11,14 +11,22 @@
function ImageFile(file) {
this.file = file;
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
// Determine if old and new file has same dimensions, if not show 'two-up' view
return function(deletedWidth, deletedHeight) {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
if (width === deletedWidth && height === deletedHeight) {
return _this.initViewModes();
} else {
return _this.initView('two-up');
}
_this.initViewModes();
// Load two-up view after images are loaded
// so that we can display the correct width and height information
const images = $('.two-up.view img', _this.file);
let loadedCount = 0;
images.on('load', () => {
loadedCount += 1;
if (loadedCount === images.length) {
_this.initView('two-up');
}
});
});
};
})(this));
......
......@@ -2,6 +2,7 @@
/* global Dropzone */
import _ from 'underscore';
import './preview_markdown';
import csrf from './lib/utils/csrf';
window.DropzoneInput = (function() {
function DropzoneInput(form) {
......@@ -50,9 +51,7 @@ window.DropzoneInput = (function() {
paramName: 'file',
maxFilesize: maxFileSize,
uploadMultiple: false,
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
headers: csrf.headers,
previewContainer: false,
processing: function() {
return $('.div-dropzone-alert').alert('close');
......@@ -260,9 +259,7 @@ window.DropzoneInput = (function() {
dataType: 'json',
processData: false,
contentType: false,
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
headers: csrf.headers,
beforeSend: function() {
showSpinner();
return closeAlertMessage();
......
/*
This module provides easy access to the CSRF token and caches
it for re-use. It also exposes some values commonly used in relation
to the CSRF token (header key and headers object).
If you need to refresh the csrfToken for some reason, just call `init` and
then use the accessors as you would normally.
If you need to compose a headers object, use the spread operator:
```
headers: {
...csrf.headers,
someOtherHeader: '12345',
}
```
*/
const csrf = {
init() {
const tokenEl = document.querySelector('meta[name=csrf-token]');
if (tokenEl !== null) {
this.csrfToken = tokenEl.getAttribute('content');
} else {
this.csrfToken = null;
}
},
get token() {
return this.csrfToken;
},
get headerKey() {
return 'X-CSRF-Token';
},
get headers() {
if (this.csrfToken !== null) {
return {
[this.headerKey]: this.token,
};
}
return {};
},
};
csrf.init();
// use our cached token for any $.rails-generated AJAX requests
if ($.rails) {
$.rails.csrfToken = () => csrf.token;
}
export default csrf;
......@@ -2,6 +2,7 @@
/* global Flash, Autosave */
import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import autosize from 'vendor/autosize';
import '../../autosave';
import TaskList from '../../task_list';
import * as constants from '../constants';
......@@ -96,6 +97,8 @@
methods: {
...mapActions([
'saveNote',
'stopPolling',
'restartPolling',
'removePlaceholderNotes',
]),
setIsSubmitButtonDisabled(note, isSubmitting) {
......@@ -124,10 +127,14 @@
}
this.isSubmitting = true;
this.note = ''; // Empty textarea while being requested. Repopulate in catch
this.resizeTextarea();
this.stopPolling();
this.saveNote(noteData)
.then((res) => {
this.isSubmitting = false;
this.restartPolling();
if (res.errors) {
if (res.errors.commands_only) {
this.discard();
......@@ -174,6 +181,8 @@
if (shouldClear) {
this.note = '';
this.resizeTextarea();
this.$refs.markdownField.previewMarkdown = false;
}
// reset autostave
......@@ -205,6 +214,11 @@
selector: '.notes',
});
},
resizeTextarea() {
this.$nextTick(() => {
autosize.update(this.$refs.textarea);
});
},
},
mounted() {
// jQuery is needed here because it is a custom event being dispatched with jQuery.
......@@ -247,7 +261,8 @@
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false"
:is-confidential-issue="isConfidentialIssue">
:is-confidential-issue="isConfidentialIssue"
ref="markdownField">
<textarea
id="note-body"
name="note[note]"
......
......@@ -187,6 +187,14 @@ export const poll = ({ commit, state, getters }) => {
});
};
export const stopPolling = () => {
eTagPoll.stop();
};
export const restartPolling = () => {
eTagPoll.restart();
};
export const fetchData = ({ commit, state, getters }) => {
const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt };
......
......@@ -5,15 +5,19 @@ import * as constants from '../constants';
export default {
[types.ADD_NEW_NOTE](state, note) {
const { discussion_id, type } = note;
const noteData = {
expanded: true,
id: discussion_id,
individual_note: !(type === constants.DISCUSSION_NOTE),
notes: [note],
reply_id: discussion_id,
};
state.notes.push(noteData);
const [exists] = state.notes.filter(n => n.id === note.discussion_id);
if (!exists) {
const noteData = {
expanded: true,
id: discussion_id,
individual_note: !(type === constants.DISCUSSION_NOTE),
notes: [note],
reply_id: discussion_id,
};
state.notes.push(noteData);
}
},
[types.ADD_NEW_REPLY_TO_DISCUSSION](state, note) {
......
import Vue from 'vue';
import VueResource from 'vue-resource';
import csrf from '../lib/utils/csrf';
Vue.use(VueResource);
......@@ -18,9 +19,7 @@ Vue.http.interceptors.push((request, next) => {
// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
// and polling.
Vue.http.interceptors.push((request, next) => {
if ($.rails) {
request.headers.set('X-CSRF-Token', $.rails.csrfToken());
}
request.headers.set(csrf.headerKey, csrf.token);
next((response) => {
// Headers object has a `forEach` property that iterates through all values.
......
......@@ -3,8 +3,13 @@ class HelpController < ApplicationController
layout 'help'
# Taken from Jekyll
# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13
YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
def index
@help_index = File.read(Rails.root.join('doc', 'README.md'))
# Remove YAML frontmatter so that it doesn't look weird
@help_index = File.read(Rails.root.join('doc', 'README.md')).sub(YAML_FRONT_MATTER_REGEXP, '')
# Prefix Markdown links with `help/` unless they are external links
# See http://rubular.com/r/X3baHTbPO2
......@@ -22,7 +27,8 @@ class HelpController < ApplicationController
path = File.join(Rails.root, 'doc', "#{@path}.md")
if File.exist?(path)
@markdown = File.read(path)
# Remove YAML frontmatter so that it doesn't look weird
@markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
render 'show.html.haml'
else
......
......@@ -176,13 +176,15 @@ module CommitsHelper
end
end
def view_file_button(commit_sha, diff_new_path, project)
def view_file_button(commit_sha, diff_new_path, project, replaced: false)
title = replaced ? _('View replaced file @ ') : _('View file @ ')
link_to(
project_blob_path(project,
tree_join(commit_sha, diff_new_path)),
class: 'btn view-file js-view-file'
) do
raw('View file @ ') + content_tag(:span, Commit.truncate_sha(commit_sha),
raw(title) + content_tag(:span, Commit.truncate_sha(commit_sha),
class: 'commit-sha')
end
end
......
......@@ -19,11 +19,15 @@ module SystemNoteHelper
'discussion' => 'comment',
'moved' => 'arrow-right',
'outdated' => 'pencil',
<<<<<<< HEAD
'duplicate' => 'issue-duplicate',
'approved' => 'approval',
'unapproved' => 'unapproval',
'relate' => 'link',
'unrelate' => 'unlink'
=======
'duplicate' => 'issue-duplicate'
>>>>>>> upstream/master
}.freeze
def system_note_icon_name(note)
......
- environment = local_assigns.fetch(:environment, nil)
- file_hash = hexdigest(diff_file.file_path)
- image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image'
- image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha
.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_file.content_sha) }
.js-file-title.file-title-flex-parent
.file-header-content
......@@ -17,6 +20,9 @@
= edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
blob: blob, link_opts: link_opts)
- if image_diff && image_replaced
= view_file_button(diff_file.old_content_sha, diff_file.old_path, project, replaced: true)
= view_file_button(diff_file.content_sha, diff_file.file_path, project)
= view_on_environment_button(diff_file.content_sha, diff_file.file_path, environment) if environment
......
......@@ -15,8 +15,7 @@
.two-up.view
%span.wrap
.frame.deleted
%a{ href: project_blob_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path)) }
= image_tag(old_blob_raw_path, alt: diff_file.old_path)
= image_tag(old_blob_raw_path, alt: diff_file.old_path)
%p.image-info.hide
%span.meta-filesize= number_to_human_size(old_blob.size)
|
......@@ -27,8 +26,7 @@
%span.meta-height
%span.wrap
.frame.added
%a{ href: project_blob_path(@project, tree_join(diff_file.content_sha, diff_file.new_path)) }
= image_tag(blob_raw_path, alt: diff_file.new_path)
= image_tag(blob_raw_path, alt: diff_file.new_path)
%p.image-info.hide
%span.meta-filesize= number_to_human_size(blob.size)
|
......
......@@ -36,6 +36,7 @@
":board-id" => "boardId",
":key" => "_uid" }
= render "shared/boards/components/sidebar"
<<<<<<< HEAD
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
......@@ -44,3 +45,12 @@
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
":project-id" => @project.try(:id) }
=======
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
"label-path" => labels_filter_path,
"empty-state-svg" => image_path('illustrations/issues.svg'),
":issue-link-base" => "issueLinkBase",
":root-path" => "rootPath",
":project-id" => @project.try(:id) }
>>>>>>> upstream/master
---
title: Add view replaced file link for image diffs
merge_request:
author:
type: changed
---
title: Correctly detect multiple issue URLs after 'Closes...' in MR descriptions
merge_request:
author:
type: fixed
---
title: Display full pre-receive and post-receive hook output in GitLab UI
merge_request: 14222
author: Robin Bobbitt
type: fixed
---
title: Force two up view to load by default for image diffs
merge_request:
author:
type: fixed
---
title: Replace the 'project/service.feature' spinach test with an rspec analog
merge_request: 14432
author: Vitaliy @blackst0ne Klachkov
type: other
---
title: Removed two legacy config options
merge_request:
author: Daniel Voogsgerd
type: deprecated
......@@ -681,12 +681,6 @@ production: &base
# Use the default values unless you really know what you are doing
git:
bin_path: /usr/bin/git
# The next value is the maximum memory size grit can use
# Given in number of bytes per git object (e.g. a commit)
# This value can be increased if you have very large commits
max_size: 20971520 # 20.megabytes
# Git timeout to read a commit, in seconds
timeout: 10
## Webpack settings
# If enabled, this will tell rails to serve frontend assets from the webpack-dev-server running
......
......@@ -47,8 +47,11 @@ var config = {
groups: './groups/index.js',
groups_list: './groups_list.js',
help: './help/help.js',
<<<<<<< HEAD
issuable: './issuable/issuable_bundle.js',
issues: './issues/issues_bundle.js',
=======
>>>>>>> upstream/master
how_to_merge: './how_to_merge.js',
issue_show: './issue_show/index.js',
integrations: './integrations',
......
......@@ -32,19 +32,17 @@ This is the typeface used for code blocks and references to commits, branches, a
---
## Icons
GitLab uses Font Awesome icons throughout our interface.
| | |
| :-----------: | :---- |
| ![Trash icon](img/icon-trash.png) | The trash icon is used for destructive actions that deletes information. |
| ![Edit icon](img/icon-edit.png) | The pencil icon is used for editing content such as comments.|
| ![Notification icon](img/icon-notification.png) | The bell icon is for notifications, such as Todos. |
| ![Subscribe icon](img/icon-subscribe.png) | The eye icon is for subscribing to updates. For example, you can subscribe to a label and get updated on issues with that label. |
| ![RSS icon](img/icon-rss.png) | The standard RSS icon is used for linking to RSS/atom feeds. |
| ![Close icon](img/icon-close.png) | An 'x' is used for closing UI elements such as dropdowns. |
| ![Add icon](img/icon-add.png) | A plus is used when creating new objects, such as issues, projects, etc. |
> TODO: update this section, add more general guidance to icon usage and personality, etc.
GitLab has a strong, unique personality. When you look at any screen, you should know immediately know that it is GitLab.
Iconography is a powerful visual cue to the user and is a great way for us to reflect our particular sense of style.
- **Standard size:** 16px * 16px
- **Border thickness:** 2px
- **Border radius:** 3px
![Icon sampler](img/icon-spec.png)
> TODO: List all icons, proper usage, hover, and active states.
---
......
......@@ -149,31 +149,19 @@ helm install --name gitlab --set baseDomain=gitlab.io,baseIP=1.1.1.1,gitlab=ee,g
## Updating GitLab using the Helm Chart
Once your GitLab Chart is installed, configuration changes and chart updates
should we done using `helm upgrade`
should we done using `helm upgrade`:
```bash
helm upgrade -f <CONFIG_VALUES_FILE> <RELEASE-NAME> gitlab/gitlab
helm upgrade -f values.yaml gitlab gitlab/gitlab-omnibus
```
where:
- `<CONFIG_VALUES_FILE>` is the path to values file containing your custom
[configuration] (#configuring-and-installing-gitlab).
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
In the [Install section](#installing-gitlab-using-the-helm-chart) we called it `gitlab`.
## Uninstalling GitLab using the Helm Chart
To uninstall the GitLab Chart, run the following:
```bash
helm delete <RELEASE-NAME>
helm delete gitlab
```
where:
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
In the [Install section](#installing) we called it `gitlab`.
[kube-srv]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
[storageclass]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#storageclasses
......@@ -188,7 +188,11 @@ static analysis and other code checks on the current code. The report is
created, and is uploaded as an artifact which you can later download and check
out. In GitLab Enterprise Edition Starter, differences between the source and
target branches are
<<<<<<< HEAD
[shown in the merge request widget](../../user/project/merge_requests/code_quality_diff.md).
=======
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
>>>>>>> upstream/master
### Auto Review Apps
......@@ -367,7 +371,11 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain); by default set automatically by the [Auto DevOps setting](#enabling-auto-devops). |
| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app). |
| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment; defaults to 1. |
<<<<<<< HEAD
| `CANARY_PRODUCTION_REPLICAS`| The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. |
=======
| `CANARY_PRODUCTION_REPLICAS`| The number of canary replicas to deploy for [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html) in the production environment. |
>>>>>>> upstream/master
| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled; defaults to `"true"`. Set to `false` to disable the automatic deployment of PostgreSQL. |
| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. |
| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
......
Feature: Project Services
Background:
Given I sign in as a user
And I own project "Shop"
Scenario: I should see project services
When I visit project "Shop" services page
Then I should see list of available services
Scenario: Activate hipchat service
When I visit project "Shop" services page
And I click hipchat service link
And I fill hipchat settings
Then I should see the Hipchat success message
Scenario: Activate hipchat service with custom server
When I visit project "Shop" services page
And I click hipchat service link
And I fill hipchat settings with custom server
Then I should see the Hipchat success message
Scenario: Activate pivotaltracker service
When I visit project "Shop" services page
And I click pivotaltracker service link
And I fill pivotaltracker settings
Then I should see the Pivotaltracker success message
Scenario: Activate Flowdock service
When I visit project "Shop" services page
And I click Flowdock service link
And I fill Flowdock settings
Then I should see the Flowdock success message
Scenario: Activate Assembla service
When I visit project "Shop" services page
And I click Assembla service link
And I fill Assembla settings
Then I should see the Assembla success message
Scenario: Activate Slack notifications service
When I visit project "Shop" services page
And I click Slack notifications service link
And I fill Slack notifications settings
Then I should see the Slack notifications success message
Scenario: Activate Pushover service
When I visit project "Shop" services page
And I click Pushover service link
And I fill Pushover settings
Then I should see the Pushover success message
Scenario: Activate email on push service
When I visit project "Shop" services page
And I click email on push service link
And I fill email on push settings
Then I should see the Emails on push success message
Scenario: Activate JIRA service
When I visit project "Shop" services page
And I click jira service link
And I fill jira settings
Then I should see the JIRA success message
Scenario: Activate Irker (IRC Gateway) service
When I visit project "Shop" services page
And I click Irker service link
And I fill Irker settings
Then I should see the Irker success message
Scenario: Activate Atlassian Bamboo CI service
When I visit project "Shop" services page
And I click Atlassian Bamboo CI service link
And I fill Atlassian Bamboo CI settings
Then I should see the Bamboo success message
And I should see empty field Change Password
Scenario: Activate jetBrains TeamCity CI service
When I visit project "Shop" services page
And I click jetBrains TeamCity CI service link
And I fill jetBrains TeamCity CI settings
Then I should see the JetBrains success message
Scenario: Activate Asana service
When I visit project "Shop" services page
And I click Asana service link
And I fill Asana settings
Then I should see the Asana success message
......@@ -139,7 +139,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'The diff links to both the previous and current image' do
links = page.all('.two-up span div a')
links = page.all('.file-actions a')
expect(links[0]['href']).to match %r{blob/#{sample_image_commit.old_blob_id}}
expect(links[1]['href']).to match %r{blob/#{sample_image_commit.new_blob_id}}
end
......
class Spinach::Features::ProjectServices < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
step 'I visit project "Shop" services page' do
visit project_settings_integrations_path(@project)
end
step 'I should see list of available services' do
expect(page).to have_content 'Project services'
expect(page).to have_content 'Campfire'
expect(page).to have_content 'HipChat'
expect(page).to have_content 'Assembla'
expect(page).to have_content 'Pushover'
expect(page).to have_content 'Atlassian Bamboo'
expect(page).to have_content 'JetBrains TeamCity'
expect(page).to have_content 'Asana'
expect(page).to have_content 'Irker (IRC gateway)'
end
step 'I should see service settings saved' do
expect(find_field('Active').value).to eq '1'
end
step 'I click hipchat service link' do
click_link 'HipChat'
end
step 'I fill hipchat settings' do
check 'Active'
fill_in 'Room', with: 'gitlab'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see the Hipchat success message' do
expect(page).to have_content 'HipChat activated.'
end
step 'I fill hipchat settings with custom server' do
check 'Active'
fill_in 'Room', with: 'gitlab_custom'
fill_in 'Token', with: 'secretCustom'
fill_in 'Server', with: 'https://chat.example.com'
click_button 'Save'
end
step 'I click pivotaltracker service link' do
click_link 'PivotalTracker'
end
step 'I fill pivotaltracker settings' do
check 'Active'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see the Pivotaltracker success message' do
expect(page).to have_content 'PivotalTracker activated.'
end
step 'I click Flowdock service link' do
click_link 'Flowdock'
end
step 'I fill Flowdock settings' do
check 'Active'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see the Flowdock success message' do
expect(page).to have_content 'Flowdock activated.'
end
step 'I click Assembla service link' do
click_link 'Assembla'
end
step 'I fill Assembla settings' do
check 'Active'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see the Assembla success message' do
expect(page).to have_content 'Assembla activated.'
end
step 'I click Asana service link' do
click_link 'Asana'
end
step 'I fill Asana settings' do
check 'Active'
fill_in 'Api key', with: 'verySecret'
fill_in 'Restrict to branch', with: 'master'
click_button 'Save'
end
step 'I should see the Asana success message' do
expect(page).to have_content 'Asana activated.'
end
step 'I click email on push service link' do
click_link 'Emails on push'
end
step 'I fill email on push settings' do
check 'Active'
fill_in 'Recipients', with: 'qa@company.name'
click_button 'Save'
end
step 'I should see the Emails on push success message' do
expect(page).to have_content 'Emails on push activated.'
end
step 'I click Irker service link' do
click_link 'Irker (IRC gateway)'
end
step 'I fill Irker settings' do
check 'Active'
fill_in 'Recipients', with: 'irc://chat.freenode.net/#commits'
check 'Colorize messages'
click_button 'Save'
end
step 'I should see the Irker success message' do
expect(page).to have_content 'Irker (IRC gateway) activated.'
end
step 'I click Slack notifications service link' do
click_link 'Slack notifications'
end
step 'I fill Slack notifications settings' do
check 'Active'
fill_in 'Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685'
click_button 'Save'
end
step 'I should see the Slack notifications success message' do
expect(page).to have_content 'Slack notifications activated.'
end
step 'I click Pushover service link' do
click_link 'Pushover'
end
step 'I fill Pushover settings' do
check 'Active'
fill_in 'Api key', with: 'verySecret'
fill_in 'User key', with: 'verySecret'
fill_in 'Device', with: 'myDevice'
select 'High Priority', from: 'Priority'
select 'Bike', from: 'Sound'
click_button 'Save'
end
step 'I should see the Pushover success message' do
expect(page).to have_content 'Pushover activated.'
end
step 'I click jira service link' do
click_link 'JIRA'
end
step 'I fill jira settings' do
check 'Active'
fill_in 'Web URL', with: 'http://jira.example'
fill_in 'JIRA API URL', with: 'http://jira.example/api'
fill_in 'Username', with: 'gitlab'
fill_in 'Password', with: 'gitlab'
click_button 'Save'
end
step 'I should see the JIRA success message' do
expect(page).to have_content 'JIRA activated.'
end
step 'I click Atlassian Bamboo CI service link' do
click_link 'Atlassian Bamboo CI'
end
step 'I fill Atlassian Bamboo CI settings' do
check 'Active'
fill_in 'Bamboo url', with: 'http://bamboo.example.com'
fill_in 'Build key', with: 'KEY'
fill_in 'Username', with: 'user'
fill_in 'Password', with: 'verySecret'
click_button 'Save'
end
step 'I should see the Bamboo success message' do
expect(page).to have_content 'Atlassian Bamboo CI activated.'
end
step 'I should see empty field Change Password' do
click_link 'Atlassian Bamboo CI'
expect(find_field('Enter new password').value).to be_nil
end
step 'I click JetBrains TeamCity CI service link' do
click_link 'JetBrains TeamCity CI'
end
step 'I fill JetBrains TeamCity CI settings' do
check 'Active'
fill_in 'Teamcity url', with: 'http://teamcity.example.com'
fill_in 'Build type', with: 'GitlabTest_Build'
fill_in 'Username', with: 'user'
fill_in 'Password', with: 'verySecret'
click_button 'Save'
end
step 'I should see the JetBrains success message' do
expect(page).to have_content 'JetBrains TeamCity CI activated.'
end
end
......@@ -79,7 +79,7 @@ module Backup
# - 1495527122_gitlab_backup.tar
# - 1495527068_2017_05_23_gitlab_backup.tar
# - 1495527097_2017_05_23_9.3.0-pre_gitlab_backup.tar
next unless file =~ /^(\d{10})(?:_\d{4}_\d{2}_\d{2}(_\d+\.\d+\.\d+((-|\.)(pre|rc\d))?)?)?_gitlab_backup\.tar$/
next unless file =~ /^(\d{10})(?:_\d{4}_\d{2}_\d{2}(_\d+\.\d+\.\d+((-|\.)(pre|rc\d))?(-ee)?)?)?_gitlab_backup\.tar$/
timestamp = $1.to_i
......
module Gitlab
class ClosingIssueExtractor
ISSUE_CLOSING_REGEX = begin
link_pattern = URI.regexp(%w(http https))
link_pattern = Banzai::Filter::AutolinkFilter::LINK_PATTERN
pattern = Gitlab.config.gitlab.issue_closing_pattern
pattern = pattern.sub('%{issue_ref}', "(?:(?:#{link_pattern})|(?:#{Issue.reference_pattern}))")
......
......@@ -83,13 +83,14 @@ module Gitlab
def call_update_hook(gl_id, oldrev, newrev, ref)
Dir.chdir(repo_path) do
stdout, stderr, status = Open3.capture3({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev)
[status.success?, stderr.presence || stdout]
[status.success?, (stderr.presence || stdout).gsub(/\R/, "<br>").html_safe]
end
end
def retrieve_error_message(stderr, stdout)
err_message = stderr.gets
err_message.blank? ? stdout.gets : err_message
err_message = stderr.read
err_message = err_message.blank? ? stdout.read : err_message
err_message.gsub(/\R/, "<br>").html_safe
end
end
end
......
......@@ -58,11 +58,11 @@ module Gitlab
end
def repository_storages
@repository_storage ||= storages_paths.keys
storages_paths.keys
end
def storages_paths
@storage_paths ||= Gitlab.config.repositories.storages
Gitlab.config.repositories.storages
end
def exec_with_timeout(cmd_args, *args, &block)
......
......@@ -62,13 +62,43 @@ feature 'Diff file viewer', :js do
end
context 'Image file' do
before do
visit_commit('2f63565e7aac07bcdadb654e253078b727143ec4')
context 'Replaced' do
before do
visit_commit('2f63565e7aac07bcdadb654e253078b727143ec4')
end
it 'shows a rendered image' do
within('.diff-file[id="e986451b8f7397b617dbb6fffcb5539328c56921"]') do
expect(page).to have_css('img[alt="files/images/6049019_460s.jpg"]')
end
end
it 'shows view replaced and view file links' do
expect(page.all('.file-actions a').length).to eq 2
expect(page.all('.file-actions a')[0]).to have_content 'View replaced file @'
expect(page.all('.file-actions a')[1]).to have_content 'View file @'
end
end
context 'Added' do
before do
visit_commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9')
end
it 'shows view file link' do
expect(page.all('.file-actions a').length).to eq 1
expect(page.all('.file-actions a')[0]).to have_content 'View file @'
end
end
it 'shows a rendered image' do
within('.diff-file[id="e986451b8f7397b617dbb6fffcb5539328c56921"]') do
expect(page).to have_css('img[alt="files/images/6049019_460s.jpg"]')
context 'Deleted' do
before do
visit_commit('7fd7a459706ee87be6f855fd98ce8c552b15529a')
end
it 'shows view file link' do
expect(page.all('.file-actions a').length).to eq 1
expect(page.all('.file-actions a')[0]).to have_content 'View file @'
end
end
end
......
require 'spec_helper'
feature 'Projects > Slack service > Setup events' do
let(:user) { create(:user) }
let(:service) { SlackService.new }
let(:project) { create(:project, slack_service: service) }
background do
service.fields
service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, pipeline_channel: 6, wiki_page_channel: 7)
project.team << [user, :master]
sign_in(user)
end
scenario 'user can filter events by channel' do
visit edit_project_service_path(project, service)
expect(page.find_field("service_push_channel").value).to have_content '1'
expect(page.find_field("service_issue_channel").value).to have_content '2'
expect(page.find_field("service_merge_request_channel").value).to have_content '3'
expect(page.find_field("service_note_channel").value).to have_content '4'
expect(page.find_field("service_tag_push_channel").value).to have_content '5'
expect(page.find_field("service_pipeline_channel").value).to have_content '6'
expect(page.find_field("service_wiki_page_channel").value).to have_content '7'
end
end
require 'spec_helper'
describe 'User activates Asana' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Asana')
end
it 'activates service' do
check('Active')
fill_in('Api key', with: 'verySecret')
fill_in('Restrict to branch', with: 'verySecret')
click_button('Save')
expect(page).to have_content('Asana activated.')
end
end
require 'spec_helper'
describe 'User activates Assembla' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Assembla')
end
it 'activates service' do
check('Active')
fill_in('Token', with: 'verySecret')
click_button('Save')
expect(page).to have_content('Assembla activated.')
end
end
require 'spec_helper'
describe 'User activates Atlassian Bamboo CI' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Atlassian Bamboo CI')
end
it 'activates service' do
check('Active')
fill_in('Bamboo url', with: 'http://bamboo.example.com')
fill_in('Build key', with: 'KEY')
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
click_button('Save')
expect(page).to have_content('Atlassian Bamboo CI activated.')
# Password field should not be filled in.
click_link('Atlassian Bamboo CI')
expect(find_field('Enter new password').value).to be_nil
end
end
require 'spec_helper'
describe 'User activates Emails on push' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Emails on push')
end
it 'activates service' do
check('Active')
fill_in('Recipients', with: 'qa@company.name')
click_button('Save')
expect(page).to have_content('Emails on push activated.')
end
end
require 'spec_helper'
describe 'User activates Flowdock' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Flowdock')
end
it 'activates service' do
check('Active')
fill_in('Token', with: 'verySecret')
click_button('Save')
expect(page).to have_content('Flowdock activated.')
end
end
require 'spec_helper'
describe 'User activates HipChat' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('HipChat')
end
context 'with standart settings' do
it 'activates service' do
check('Active')
fill_in('Room', with: 'gitlab')
fill_in('Token', with: 'verySecret')
click_button('Save')
expect(page).to have_content('HipChat activated.')
end
end
context 'with custom settings' do
it 'activates service' do
check('Active')
fill_in('Room', with: 'gitlab_custom')
fill_in('Token', with: 'secretCustom')
fill_in('Server', with: 'https://chat.example.com')
click_button('Save')
expect(page).to have_content('HipChat activated.')
end
end
end
require 'spec_helper'
describe 'User activates Irker (IRC gateway)' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Irker (IRC gateway)')
end
it 'activates service' do
check('Active')
check('Colorize messages')
fill_in('Recipients', with: 'irc://chat.freenode.net/#commits')
click_button('Save')
expect(page).to have_content('Irker (IRC gateway) activated.')
end
end
require 'spec_helper'
describe 'User activates JetBrains TeamCity CI' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('JetBrains TeamCity CI')
end
it 'activates service' do
check('Active')
fill_in('Teamcity url', with: 'http://teamcity.example.com')
fill_in('Build type', with: 'GitlabTest_Build')
fill_in('Username', with: 'user')
fill_in('Password', with: 'verySecret')
click_button('Save')
expect(page).to have_content('JetBrains TeamCity CI activated.')
end
end
require 'spec_helper'
feature 'Setup Jira service', :js do
describe 'User activates Jira', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:service) { project.create_jira_service }
......
require 'spec_helper'
describe 'User activates PivotalTracker' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('PivotalTracker')
end
it 'activates service' do
check('Active')
fill_in('Token', with: 'verySecret')
click_button('Save')
expect(page).to have_content('PivotalTracker activated.')
end
end
require 'spec_helper'
describe 'User activates Pushover' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
click_link('Pushover')
end
it 'activates service' do
check('Active')
fill_in('Api key', with: 'verySecret')
fill_in('User key', with: 'verySecret')
fill_in('Device', with: 'myDevice')
select('High Priority', from: 'Priority')
select('Bike', from: 'Sound')
click_button('Save')
expect(page).to have_content('Pushover activated.')
end
end
require 'spec_helper'
describe 'User activates Slack notifications' do
let(:user) { create(:user) }
let(:service) { SlackService.new }
let(:project) { create(:project, slack_service: service) }
before do
project.add_master(user)
sign_in(user)
end
context 'when service is not configured yet' do
before do
visit(project_settings_integrations_path(project))
click_link('Slack notifications')
end
it 'activates service' do
check('Active')
fill_in('Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685')
click_button('Save')
expect(page).to have_content('Slack notifications activated.')
end
end
context 'when service is already configured' do
before do
service.fields
service.update_attributes(
push_channel: 1,
issue_channel: 2,
merge_request_channel: 3,
note_channel: 4,
tag_push_channel: 5,
pipeline_channel: 6,
wiki_page_channel: 7)
visit(edit_project_service_path(project, service))
end
it 'filters events by channel' do
expect(page.find_field('service_push_channel').value).to have_content('1')
expect(page.find_field('service_issue_channel').value).to have_content('2')
expect(page.find_field('service_merge_request_channel').value).to have_content('3')
expect(page.find_field('service_note_channel').value).to have_content('4')
expect(page.find_field('service_tag_push_channel').value).to have_content('5')
expect(page.find_field('service_pipeline_channel').value).to have_content('6')
expect(page.find_field('service_wiki_page_channel').value).to have_content('7')
end
end
end
require 'spec_helper'
describe 'User views services' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
visit(project_settings_integrations_path(project))
end
it 'shows the list of available services' do
expect(page).to have_content('Project services')
expect(page).to have_content('Campfire')
expect(page).to have_content('HipChat')
expect(page).to have_content('Assembla')
expect(page).to have_content('Pushover')
expect(page).to have_content('Atlassian Bamboo')
expect(page).to have_content('JetBrains TeamCity')
expect(page).to have_content('Asana')
expect(page).to have_content('Irker (IRC gateway)')
end
end
import csrf from '~/lib/utils/csrf';
describe('csrf', () => {
beforeEach(() => {
this.tokenKey = 'X-CSRF-Token';
this.token = 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
});
it('returns the correct headerKey', () => {
expect(csrf.headerKey).toBe(this.tokenKey);
});
describe('when csrf token is in the DOM', () => {
beforeEach(() => {
setFixtures(`
<meta name="csrf-token" content="${this.token}">
`);
csrf.init();
});
it('returns the csrf token', () => {
expect(csrf.token).toBe(this.token);
});
it('returns the csrf headers object', () => {
expect(csrf.headers[this.tokenKey]).toBe(this.token);
});
});
describe('when csrf token is not in the DOM', () => {
beforeEach(() => {
setFixtures(`
<meta name="some-other-token">
`);
csrf.init();
});
it('returns null for token', () => {
expect(csrf.token).toBeNull();
});
it('returns empty object for headers', () => {
expect(typeof csrf.headers).toBe('object');
expect(Object.keys(csrf.headers).length).toBe(0);
});
});
});
import Vue from 'vue';
import autosize from 'vendor/autosize';
import store from '~/notes/stores';
import issueCommentForm from '~/notes/components/issue_comment_form.vue';
import { loggedOutIssueData, notesDataMock, userDataMock, issueDataMock } from '../mock_data';
......@@ -55,6 +56,19 @@ describe('issue_comment_form component', () => {
expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions');
});
it('should resize textarea after note discarded', (done) => {
spyOn(autosize, 'update');
spyOn(vm, 'discard').and.callThrough();
vm.note = 'foo';
vm.discard();
Vue.nextTick(() => {
expect(autosize.update).toHaveBeenCalled();
done();
});
});
describe('edit mode', () => {
it('should enter edit mode when arrow up is pressed', () => {
spyOn(vm, 'editCurrentUserLastNote').and.callThrough();
......
import * as actions from '~/notes/stores/actions';
import testAction from './helpers';
import { discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
......
......@@ -3,19 +3,31 @@ import { note, discussionMock, notesDataMock, userDataMock, issueDataMock, indiv
describe('Mutation Notes Store', () => {
describe('ADD_NEW_NOTE', () => {
it('should add a new note to an array of notes', () => {
const state = { notes: [] };
let state;
let noteData;
beforeEach(() => {
state = { notes: [] };
noteData = {
expanded: true,
id: note.discussion_id,
individual_note: true,
notes: [note],
reply_id: note.discussion_id,
};
mutations.ADD_NEW_NOTE(state, note);
});
it('should add a new note to an array of notes', () => {
expect(state).toEqual({
notes: [{
expanded: true,
id: note.discussion_id,
individual_note: true,
notes: [note],
reply_id: note.discussion_id,
}],
notes: [noteData],
});
expect(state.notes.length).toBe(1);
});
it('should not add the same note to the notes array', () => {
mutations.ADD_NEW_NOTE(state, note);
expect(state.notes.length).toBe(1);
});
});
......
......@@ -28,6 +28,7 @@ describe Backup::Manager do
'1451520000_2015_12_31_4.5.6_gitlab_backup.tar',
'1451520000_2015_12_31_4.5.6-pre_gitlab_backup.tar',
'1451520000_2015_12_31_4.5.6-rc1_gitlab_backup.tar',
'1451520000_2015_12_31_4.5.6-pre-ee_gitlab_backup.tar',
'1451510000_2015_12_30_gitlab_backup.tar',
'1450742400_2015_12_22_gitlab_backup.tar',
'1449878400_gitlab_backup.tar',
......@@ -114,14 +115,18 @@ describe Backup::Manager do
expect(FileUtils).to have_received(:rm).with(files[3])
end
it 'removes matching files with a human-readable non-versioned timestamp' do
it 'removes matching files with a human-readable versioned timestamp with tagged EE' do
expect(FileUtils).to have_received(:rm).with(files[4])
end
it 'removes matching files with a human-readable non-versioned timestamp' do
expect(FileUtils).to have_received(:rm).with(files[5])
expect(FileUtils).to have_received(:rm).with(files[6])
end
it 'removes matching files without a human-readable timestamp' do
expect(FileUtils).to have_received(:rm).with(files[6])
expect(FileUtils).to have_received(:rm).with(files[7])
expect(FileUtils).to have_received(:rm).with(files[8])
end
it 'does not remove files that are not old enough' do
......@@ -129,11 +134,11 @@ describe Backup::Manager do
end
it 'does not remove non-matching files' do
expect(FileUtils).not_to have_received(:rm).with(files[8])
expect(FileUtils).not_to have_received(:rm).with(files[9])
end
it 'prints a done message' do
expect(progress).to have_received(:puts).with('done. (7 removed)')
expect(progress).to have_received(:puts).with('done. (8 removed)')
end
end
......@@ -153,10 +158,11 @@ describe Backup::Manager do
expect(FileUtils).to have_received(:rm).with(files[5])
expect(FileUtils).to have_received(:rm).with(files[6])
expect(FileUtils).to have_received(:rm).with(files[7])
expect(FileUtils).to have_received(:rm).with(files[8])
end
it 'sets the correct removed count' do
expect(progress).to have_received(:puts).with('done. (6 removed)')
expect(progress).to have_received(:puts).with('done. (7 removed)')
end
it 'prints the error from file that could not be removed' do
......
......@@ -347,10 +347,10 @@ describe Gitlab::ClosingIssueExtractor do
end
it "fetches cross-project URL references" do
message = "Closes #{urls.project_issue_url(issue2.project, issue2)} and #{reference}"
message = "Closes #{urls.project_issue_url(issue2.project, issue2)}, #{reference} and #{urls.project_issue_url(other_issue.project, other_issue)}"
expect(subject.closed_by_message(message))
.to match_array([issue, issue2])
.to match_array([issue, issue2, other_issue])
end
it "ignores invalid cross-project URL references" do
......
......@@ -28,6 +28,7 @@ describe Gitlab::Git::Hook do
f.write(<<-HOOK)
echo 'regular message from the hook'
echo 'error message from the hook' 1>&2
echo 'error message from the hook line 2' 1>&2
exit 1
HOOK
end
......@@ -73,7 +74,7 @@ describe Gitlab::Git::Hook do
status, errors = hook.trigger(gl_id, blank, blank, ref)
expect(status).to be false
expect(errors).to eq("error message from the hook\n")
expect(errors).to eq("error message from the hook<br>error message from the hook line 2<br>")
end
end
end
......
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