Commit 88e44726 authored by Rubén Dávila Santos's avatar Rubén Dávila Santos

Merge branch 'ce-to-ee' into 'master'

CE upstream

@iamphill can you please help me to resolve the below conflicts:

> app/views/projects/_merge_request_settings.html.haml

> spec/javascripts/datetime_utility_spec.js.coffee

See merge request !713
parents a294fe2f 95064218
...@@ -250,6 +250,21 @@ bundler:audit: ...@@ -250,6 +250,21 @@ bundler:audit:
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
migration paths:
stage: test
<<: *use-db
only:
- master@gitlab-org/gitlab-ce
script:
- git checkout HEAD .
- git fetch --tags
- git checkout v8.5.9
- 'echo test: unix:/var/opt/gitlab/redis/redis.socket > config/resque.yml'
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" --retry=3
- rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_BUILD_REF
- rake db:migrate
coverage: coverage:
stage: post-test stage: post-test
services: [] services: []
...@@ -265,7 +280,6 @@ coverage: ...@@ -265,7 +280,6 @@ coverage:
- coverage/index.html - coverage/index.html
- coverage/assets/ - coverage/assets/
# Notify slack in the end # Notify slack in the end
notify:slack: notify:slack:
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
(What you should see instead) (What you should see instead)
### Actual behaviour ### Actual behavior
(What actually happens) (What actually happens)
......
...@@ -6,6 +6,8 @@ v 8.11.1 ...@@ -6,6 +6,8 @@ v 8.11.1
- Fix file links on project page when default view is Files !5933 - Fix file links on project page when default view is Files !5933
- Fixed enter key in search input not working !5888 - Fixed enter key in search input not working !5888
v 8.12.0 (unreleased) v 8.12.0 (unreleased)
- Cleanup misalignments in Issue list view !6206
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Filter tags by name !6121 - Filter tags by name !6121
- Make push events have equal vertical spacing. - Make push events have equal vertical spacing.
- Add two-factor recovery endpoint to internal API !5510 - Add two-factor recovery endpoint to internal API !5510
...@@ -13,6 +15,7 @@ v 8.12.0 (unreleased) ...@@ -13,6 +15,7 @@ v 8.12.0 (unreleased)
- Add font color contrast to external label in admin area (ClemMakesApps) - Add font color contrast to external label in admin area (ClemMakesApps)
- Change logo animation to CSS (ClemMakesApps) - Change logo animation to CSS (ClemMakesApps)
- Instructions for enabling Git packfile bitmaps !6104 - Instructions for enabling Git packfile bitmaps !6104
- Fix pagination on user snippets page
- Change merge_error column from string to text type - Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps) - Reduce contributions calendar data payload (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel) - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
...@@ -20,11 +23,15 @@ v 8.12.0 (unreleased) ...@@ -20,11 +23,15 @@ v 8.12.0 (unreleased)
- Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling) - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps) - Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps)
- Center build stage columns in pipeline overview (ClemMakesApps) - Center build stage columns in pipeline overview (ClemMakesApps)
- Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps)
- Remove suggested colors hover underline (ClemMakesApps)
- Shorten task status phrase (ClemMakesApps) - Shorten task status phrase (ClemMakesApps)
- Add hover color to emoji icon (ClemMakesApps) - Add hover color to emoji icon (ClemMakesApps)
- Fix branches page dropdown sort alignment (ClemMakesApps) - Fix branches page dropdown sort alignment (ClemMakesApps)
- Add white background for no readme container (ClemMakesApps) - Add white background for no readme container (ClemMakesApps)
- API: Expose issue confidentiality flag. (Robert Schilling) - API: Expose issue confidentiality flag. (Robert Schilling)
- Fix markdown anchor icon interaction (ClemMakesApps)
- Test migration paths from 8.5 until current release !4874
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention) - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
- Add `wiki_page_events` to project hook APIs (Ben Boeckel) - Add `wiki_page_events` to project hook APIs (Ben Boeckel)
- Remove Gitorious import - Remove Gitorious import
...@@ -32,26 +39,33 @@ v 8.12.0 (unreleased) ...@@ -32,26 +39,33 @@ v 8.12.0 (unreleased)
- Remove prefixes from transition CSS property (ClemMakesApps) - Remove prefixes from transition CSS property (ClemMakesApps)
- Add Sentry logging to API calls - Add Sentry logging to API calls
- Add BroadcastMessage API - Add BroadcastMessage API
- Use 'git update-ref' for safer web commits !6130
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling) - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- Remove unused mixins (ClemMakesApps) - Remove unused mixins (ClemMakesApps)
- Add search to all issue board lists - Add search to all issue board lists
- Fix groups sort dropdown alignment (ClemMakesApps) - Fix groups sort dropdown alignment (ClemMakesApps)
- Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps) - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
- Use JavaScript tooltips for mentions !5301 (winniehell) - Use JavaScript tooltips for mentions !5301 (winniehell)
- Add hover state to todos !5361 (winniehell)
- Fix icon alignment of star and fork buttons !5451 (winniehell)
- Fix alignment of icon buttons !5887 (winniehell)
- Fix markdown help references (ClemMakesApps) - Fix markdown help references (ClemMakesApps)
- Add last commit time to repo view (ClemMakesApps) - Add last commit time to repo view (ClemMakesApps)
- Fix accessibility and visibility of project list dropdown button !6140 - Fix accessibility and visibility of project list dropdown button !6140
- Fix missing flash messages on service edit page (airatshigapov)
- Added project specific enable/disable setting for LFS !5997 - Added project specific enable/disable setting for LFS !5997
- Don't expose a user's token in the `/api/v3/user` API (!6047) - Don't expose a user's token in the `/api/v3/user` API (!6047)
- Remove redundant js-timeago-pending from user activity log (ClemMakesApps) - Remove redundant js-timeago-pending from user activity log (ClemMakesApps)
- Ability to manage project issues, snippets, wiki, merge requests and builds access level - Ability to manage project issues, snippets, wiki, merge requests and builds access level
- Remove inconsistent font weight for sidebar's labels (ClemMakesApps) - Remove inconsistent font weight for sidebar's labels (ClemMakesApps)
- Align add button on repository view (ClemMakesApps)
- Added tests for diff notes - Added tests for diff notes
- Add a button to download latest successful artifacts for branches and tags !5142 - Add a button to download latest successful artifacts for branches and tags !5142
- Remove redundant pipeline tooltips (ClemMakesApps) - Remove redundant pipeline tooltips (ClemMakesApps)
- Expire commit info views after one day, instead of two weeks, to allow for user email updates - Expire commit info views after one day, instead of two weeks, to allow for user email updates
- Add delimiter to project stars and forks count (ClemMakesApps) - Add delimiter to project stars and forks count (ClemMakesApps)
- Fix badge count alignment (ClemMakesApps) - Fix badge count alignment (ClemMakesApps)
- Remove green outline from `New branch unavailable` button on issue page !5858 (winniehell)
- Fix repo title alignment (ClemMakesApps) - Fix repo title alignment (ClemMakesApps)
- Fix branch title trailing space on hover (ClemMakesApps) - Fix branch title trailing space on hover (ClemMakesApps)
- Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison) - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
...@@ -63,22 +77,32 @@ v 8.12.0 (unreleased) ...@@ -63,22 +77,32 @@ v 8.12.0 (unreleased)
- Move to project dropdown with infinite scroll for better performance - Move to project dropdown with infinite scroll for better performance
- Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz) - Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz)
- Load branches asynchronously in Cherry Pick and Revert dialogs. - Load branches asynchronously in Cherry Pick and Revert dialogs.
- Convert datetime coffeescript spec to ES6 (ClemMakesApps)
- Add merge request versions !5467 - Add merge request versions !5467
- Change using size to use count and caching it for number of group members. !5935 - Change using size to use count and caching it for number of group members. !5935
- Replace play icon font with svg (ClemMakesApps) - Replace play icon font with svg (ClemMakesApps)
- Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck) - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck)
- Reduce number of database queries on builds tab - Reduce number of database queries on builds tab
- Wrap text in commit message containers
- Capitalize mentioned issue timeline notes (ClemMakesApps) - Capitalize mentioned issue timeline notes (ClemMakesApps)
- Fix inconsistent checkbox alignment (ClemMakesApps) - Fix inconsistent checkbox alignment (ClemMakesApps)
- Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger) - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- Adds response mime type to transaction metric action when it's not HTML - Adds response mime type to transaction metric action when it's not HTML
- Fix hover leading space bug in pipeline graph !5980 - Fix hover leading space bug in pipeline graph !5980
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496 - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
- Fix repository page ui issues
- Fixed invisible scroll controls on build page on iPhone - Fixed invisible scroll controls on build page on iPhone
- Fix error on raw build trace download for old builds stored in database !4822
- Refactor the triggers page and documentation !6217
- Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
- Use default clone protocol on "check out, review, and merge locally" help page URL
v 8.11.5 (unreleased) v 8.11.5 (unreleased)
- Optimize branch lookups and force a repository reload for Repository#find_branch - Optimize branch lookups and force a repository reload for Repository#find_branch
- Fix member expiration date picker after update
- Fix suggested colors options for new labels in the admin area. !6138 - Fix suggested colors options for new labels in the admin area. !6138
- Fix GitLab import button
- Remove gitorious from import_sources
v 8.11.4 v 8.11.4
- Fix resolving conflicts on forks. !6082 - Fix resolving conflicts on forks. !6082
...@@ -92,6 +116,10 @@ v 8.11.4 ...@@ -92,6 +116,10 @@ v 8.11.4
- Creating an issue through our API now emails label subscribers !5720 - Creating an issue through our API now emails label subscribers !5720
- Block concurrent updates for Pipeline - Block concurrent updates for Pipeline
- Don't create groups for unallowed users when importing projects - Don't create groups for unallowed users when importing projects
- Fix resolving conflicts on forks
- Fix diff commenting on merge requests created prior to 8.10
- Don't create groups for unallowed users when importing projects
- Scope webhooks/services that will run for confidential issues
- Fix issue boards leak private label names and descriptions - Fix issue boards leak private label names and descriptions
- Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner)
- Remove gitorious. !5866 - Remove gitorious. !5866
...@@ -175,8 +203,6 @@ v 8.11.0 ...@@ -175,8 +203,6 @@ v 8.11.0
- Update `timeago` plugin to use multiple string/locale settings - Update `timeago` plugin to use multiple string/locale settings
- Remove unused images (ClemMakesApps) - Remove unused images (ClemMakesApps)
- Get issue and merge request description templates from repositories - Get issue and merge request description templates from repositories
- Add hover state to todos !5361 (winniehell)
- Fix icon alignment of star and fork buttons !5451 (winniehell)
- Enforce 2FA restrictions on API authentication endpoints !5820 - Enforce 2FA restrictions on API authentication endpoints !5820
- Limit git rev-list output count to one in forced push check - Limit git rev-list output count to one in forced push check
- Show deployment status on merge requests with external URLs - Show deployment status on merge requests with external URLs
......
...@@ -57,7 +57,7 @@ gem 'browser', '~> 2.2' ...@@ -57,7 +57,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.5' gem 'gitlab_git', '~> 10.6.3'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
......
...@@ -301,7 +301,7 @@ GEM ...@@ -301,7 +301,7 @@ GEM
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab_git (10.5.0) gitlab_git (10.6.3)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
...@@ -889,7 +889,7 @@ DEPENDENCIES ...@@ -889,7 +889,7 @@ DEPENDENCIES
gitlab-elasticsearch-git (~> 0.0.17) gitlab-elasticsearch-git (~> 0.0.17)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
gitlab_git (~> 10.5) gitlab_git (~> 10.6.3)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
......
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="#333" fill-rule="evenodd" d="M9.683 6.676l-.047-.048C8.27 5.26 6.07 5.243 4.726 6.588l-2.29 2.29c-1.344 1.344-1.328 3.544.04 4.91 1.366 1.368 3.564 1.385 4.908.04l1.753-1.752c-.695.074-1.457-.078-2.176-.444L5.934 12.66c-.634.634-1.67.625-2.312-.017-.642-.643-.65-1.677-.017-2.312L6.035 7.9c.634-.634 1.67-.625 2.312.017.024.024.048.05.07.075l.003-.002c.36.36.943.366 1.3.01.355-.356.35-.938-.01-1.3l-.027-.024zM6.58 9.586l.048.05c1.367 1.366 3.565 1.384 4.91.04l2.29-2.292c1.344-1.343 1.328-3.542-.04-4.91-1.366-1.366-3.564-1.384-4.908-.04L7.127 4.187c.695-.074 1.457.078 2.176.444l1.028-1.027c.635-.634 1.67-.624 2.313.017.643.644.652 1.678.018 2.312l-2.43 2.432c-.635.634-1.67.624-2.313-.018-.024-.024-.048-.05-.07-.075l-.003.004c-.36-.362-.943-.367-1.3-.01-.355.355-.35.937.01 1.3.01.007.018.015.027.023z"/></svg>
\ No newline at end of file
Vue.http.interceptors.push((request, next) => { Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
Vue.nextTick(() => { next(function (response) {
setTimeout(() => { Vue.activeResources--;
Vue.activeResources--;
}, 500);
}); });
next();
}); });
...@@ -54,12 +54,14 @@ ...@@ -54,12 +54,14 @@
} }
Build.prototype.getInitialBuildTrace = function() { Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
return $.ajax({ return $.ajax({
url: this.build_url, url: this.build_url,
dataType: 'json', dataType: 'json',
success: function(build_data) { success: function(build_data) {
$('.js-build-output').html(build_data.trace_html); $('.js-build-output').html(build_data.trace_html);
if (build_data.status === 'success' || build_data.status === 'failed') { if (removeRefreshStatuses.indexOf(build_data.status) >= 0) {
return $('.js-build-refresh').remove(); return $('.js-build-refresh').remove();
} }
} }
......
$(function(){
$('.reveal-variables').off('click').on('click',function(){
$('.js-build').toggle().niceScroll();
$(this).hide();
});
});
...@@ -10,21 +10,24 @@ ...@@ -10,21 +10,24 @@
ImporterStatus.prototype.initStatusPage = function() { ImporterStatus.prototype.initStatusPage = function() {
$('.js-add-to-import').off('click').on('click', (function(_this) { $('.js-add-to-import').off('click').on('click', (function(_this) {
return function(e) { return function(e) {
var $btn, $namespace_input, $target_field, $tr, id, new_namespace; var $btn, $namespace_input, $target_field, $tr, id, target_namespace;
$btn = $(e.currentTarget); $btn = $(e.currentTarget);
$tr = $btn.closest('tr'); $tr = $btn.closest('tr');
$target_field = $tr.find('.import-target'); $target_field = $tr.find('.import-target');
$namespace_input = $target_field.find('input'); $namespace_input = $target_field.find('input');
id = $tr.attr('id').replace('repo_', ''); id = $tr.attr('id').replace('repo_', '');
new_namespace = null; target_namespace = null;
if ($namespace_input.length > 0) { if ($namespace_input.length > 0) {
new_namespace = $namespace_input.prop('value'); target_namespace = $namespace_input.prop('value');
$target_field.empty().append(new_namespace + "/" + ($target_field.data('project_name'))); $target_field.empty().append(target_namespace + "/" + ($target_field.data('project_name')));
} }
$btn.disable().addClass('is-loading'); $btn.disable().addClass('is-loading');
return $.post(_this.import_url, { return $.post(_this.import_url, {
repo_id: id, repo_id: id,
new_namespace: new_namespace target_namespace: target_namespace
}, { }, {
dataType: 'script' dataType: 'script'
}); });
...@@ -70,7 +73,7 @@ ...@@ -70,7 +73,7 @@
if ($('.js-importer-status').length) { if ($('.js-importer-status').length) {
var jobsImportPath = $('.js-importer-status').data('jobs-import-path'); var jobsImportPath = $('.js-importer-status').data('jobs-import-path');
var importPath = $('.js-importer-status').data('import-path'); var importPath = $('.js-importer-status').data('import-path');
new ImporterStatus(jobsImportPath, importPath); new ImporterStatus(jobsImportPath, importPath);
} }
}); });
......
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
Issue.prototype.initCanCreateBranch = function() { Issue.prototype.initCanCreateBranch = function() {
var $container; var $container;
$container = $('div#new-branch'); $container = $('#new-branch');
if ($container.length === 0) { if ($container.length === 0) {
return; return;
} }
...@@ -139,7 +139,6 @@ ...@@ -139,7 +139,6 @@
if (data.can_create_branch) { if (data.can_create_branch) {
$container.find('.checking').hide(); $container.find('.checking').hide();
$container.find('.available').show(); $container.find('.available').show();
return $container.find('a').attr('disabled', false);
} else { } else {
$container.find('.checking').hide(); $container.find('.checking').hide();
return $container.find('.unavailable').show(); return $container.find('.unavailable').show();
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
this.ProjectNew = (function() { this.ProjectNew = (function() {
function ProjectNew() { function ProjectNew() {
this.toggleSettings = bind(this.toggleSettings, this); this.toggleSettings = bind(this.toggleSettings, this);
this.$selects = $('.features select');
$('.project-edit-container').on('ajax:before', (function(_this) { $('.project-edit-container').on('ajax:before', (function(_this) {
return function() { return function() {
$('.project-edit-container').hide(); $('.project-edit-container').hide();
...@@ -15,19 +17,24 @@ ...@@ -15,19 +17,24 @@
} }
ProjectNew.prototype.toggleSettings = function() { ProjectNew.prototype.toggleSettings = function() {
this._showOrHide('#project_builds_enabled', '.builds-feature'); var self = this;
this._showOrHide('#project_merge_requests_enabled', '.merge-requests-feature');
return this._showOrHide('#project_issues_enabled', '.issues-feature'); this.$selects.each(function () {
var $select = $(this),
className = $select.data('field').replace(/_/g, '-')
.replace('access-level', 'feature');
self._showOrHide($select, '.' + className);
});
}; };
ProjectNew.prototype.toggleSettingsOnclick = function() { ProjectNew.prototype.toggleSettingsOnclick = function() {
return $('#project_builds_enabled, #project_merge_requests_enabled, #project_issues_enabled').on('click', this.toggleSettings); this.$selects.on('change', this.toggleSettings);
}; };
ProjectNew.prototype._showOrHide = function(checkElement, container) { ProjectNew.prototype._showOrHide = function(checkElement, container) {
var $container; var $container = $(container);
$container = $(container);
if ($(checkElement).prop('checked')) { if ($(checkElement).val() !== '0') {
return $container.show(); return $container.show();
} else { } else {
return $container.hide(); return $container.hide();
......
(global => {
global.gl = global.gl || {};
gl.SnippetsList = function() {
var $holder = $('.snippets-list-holder');
$holder.find('.pagination').on('ajax:success', (e, data) => {
$holder.replaceWith(data.html);
});
}
})(window);
...@@ -206,7 +206,9 @@ ...@@ -206,7 +206,9 @@
} }
svg, .fa { svg, .fa {
margin-right: 3px; &:not(:last-child) {
margin-right: 3px;
}
} }
} }
......
...@@ -53,7 +53,7 @@ pre { ...@@ -53,7 +53,7 @@ pre {
&.well-pre { &.well-pre {
border: 1px solid #eee; border: 1px solid #eee;
background: #f9f9f9; background: $gray-light;
border-radius: 0; border-radius: 0;
color: #555; color: #555;
} }
...@@ -225,7 +225,7 @@ li.note { ...@@ -225,7 +225,7 @@ li.note {
.milestone { .milestone {
&.milestone-closed { &.milestone-closed {
background: #f9f9f9; background: $gray-light;
} }
.progress { .progress {
margin-bottom: 0; margin-bottom: 0;
......
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
padding: 0; padding: 0;
} }
td.blame-commit { td.blame-commit {
background: #f9f9f9; background: $gray-light;
min-width: 350px; min-width: 350px;
.commit-author-link { .commit-author-link {
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
} }
&.active { &.active {
background: #f9f9f9; background: $gray-light;
a { a {
font-weight: 600; font-weight: 600;
} }
......
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
} }
li a { li a {
padding: 16px 10px 11px; padding: 16px 15px 11px;
} }
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
......
...@@ -125,7 +125,7 @@ $panel-inner-border: $border-color; ...@@ -125,7 +125,7 @@ $panel-inner-border: $border-color;
// //
//## //##
$well-bg: #f9f9f9; $well-bg: $gray-light;
$well-border: #eee; $well-border: #eee;
//== Code //== Code
......
...@@ -159,25 +159,18 @@ ...@@ -159,25 +159,18 @@
position: relative; position: relative;
a.anchor { a.anchor {
// Setting `display: none` would prevent the anchor being scrolled to, so left: -16px;
// instead we set the height to 0 and it gets updated on hover. position: absolute;
height: 0; text-decoration: none;
&:after {
content: url('icon_anchor.svg');
visibility: hidden;
}
} }
&:hover > a.anchor { &:hover > a.anchor:after {
$size: 14px; visibility: visible;
position: absolute;
right: 100%;
top: 50%;
margin-top: -11px;
margin-right: 0;
padding-right: 15px;
display: inline-block;
width: $size;
height: $size;
background-image: image-url("icon-link.png");
background-size: contain;
background-repeat: no-repeat;
} }
} }
} }
......
...@@ -9,13 +9,79 @@ $gutter_inner_width: 258px; ...@@ -9,13 +9,79 @@ $gutter_inner_width: 258px;
$sidebar-transition-duration: .15s; $sidebar-transition-duration: .15s;
$sidebar-breakpoint: 1024px; $sidebar-breakpoint: 1024px;
/*
* Color schema
*/
$white-light: #fff;
$white-normal: #ededed;
$white-dark: #ececec;
$gray-light: #fafafa;
$gray-normal: #f5f5f5;
$gray-dark: #ededed;
$gray-darkest: #c9c9c9;
$green-light: #38ae67;
$green-normal: #2faa60;
$green-dark: #2ca05b;
$blue-light: #2ea8e5;
$blue-normal: #2d9fd8;
$blue-dark: #2897ce;
$blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
$red-light: #e52c5a;
$red-normal: #d22852;
$red-dark: darken($red-normal, 5%);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4;
$border-white-normal: #d6dae2;
$border-white-dark: #c6cacf;
$border-gray-light: #dcdcdc;
$border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf;
$border-green-light: #2faa60;
$border-green-normal: #2ca05b;
$border-green-dark: #279654;
$border-blue-light: #2d9fd8;
$border-blue-normal: #2897ce;
$border-blue-dark: #258dc1;
$border-orange-light: #fc6d26;
$border-orange-normal: #ce5237;
$border-orange-dark: #c14e35;
$border-red-light: #d22852;
$border-red-normal: #ca264f;
$border-red-dark: darken($border-red-normal, 5%);
$help-well-bg: $gray-light;
$help-well-border: #e5e5e5;
$warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb;
/* /*
* UI elements * UI elements
*/ */
$border-color: #e5e5e5; $border-color: #e5e5e5;
$focus-border-color: #3aabf0; $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0; $table-border-color: #f0f0f0;
$background-color: #fafafa; $background-color: $gray-light;
$dark-background-color: #f5f5f5; $dark-background-color: #f5f5f5;
$table-text-gray: #8f8f8f; $table-text-gray: #8f8f8f;
...@@ -35,6 +101,7 @@ $gl-icon-color: $gl-placeholder-color; ...@@ -35,6 +101,7 @@ $gl-icon-color: $gl-placeholder-color;
$gl-grayish-blue: #7f8fa4; $gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color; $gl-gray: $gl-text-color;
$gl-gray-dark: #313236; $gl-gray-dark: #313236;
$gl-gray-light: $gl-placeholder-color;
$gl-header-color: $gl-title-color; $gl-header-color: $gl-title-color;
/* /*
...@@ -90,73 +157,6 @@ $btn-side-margin: 10px; ...@@ -90,73 +157,6 @@ $btn-side-margin: 10px;
$btn-sm-side-margin: 7px; $btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px; $btn-xs-side-margin: 5px;
/*
* Color schema
*/
$white-light: #fff;
$white-normal: #ededed;
$white-dark: #ececec;
$gray-light: #faf9f9;
$gray-normal: #f5f5f5;
$gray-dark: #ededed;
$gray-darkest: #c9c9c9;
$green-light: #38ae67;
$green-normal: #2faa60;
$green-dark: #2ca05b;
$blue-light: #2ea8e5;
$blue-normal: #2d9fd8;
$blue-dark: #2897ce;
$blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
$red-light: #e52c5a;
$red-normal: #d22852;
$red-dark: darken($red-normal, 5%);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4;
$border-white-normal: #d6dae2;
$border-white-dark: #c6cacf;
$border-gray-light: #dcdcdc;
$border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf;
$border-green-light: #2faa60;
$border-green-normal: #2ca05b;
$border-green-dark: #279654;
$border-blue-light: #2d9fd8;
$border-blue-normal: #2897ce;
$border-blue-dark: #258dc1;
$border-orange-light: #fc6d26;
$border-orange-normal: #ce5237;
$border-orange-dark: #c14e35;
$border-red-light: #d22852;
$border-red-normal: #ca264f;
$border-red-dark: darken($border-red-normal, 5%);
$help-well-bg: #fafafa;
$help-well-border: #e5e5e5;
$warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb;
/* tanuki logo colors */ /* tanuki logo colors */
$tanuki-red: #e24329; $tanuki-red: #e24329;
$tanuki-orange: #fc6d26; $tanuki-orange: #fc6d26;
...@@ -186,7 +186,7 @@ $line-removed-dark: #fac5cd; ...@@ -186,7 +186,7 @@ $line-removed-dark: #fac5cd;
$line-number-old: #f9d7dc; $line-number-old: #f9d7dc;
$line-number-new: #ddfbe6; $line-number-new: #ddfbe6;
$line-number-select: #fbf2da; $line-number-select: #fbf2da;
$match-line: #fafafa; $match-line: $gray-light;
$table-border-gray: #f0f0f0; $table-border-gray: #f0f0f0;
$line-target-blue: #eaf3fc; $line-target-blue: #eaf3fc;
$line-select-yellow: #fcf8e7; $line-select-yellow: #fcf8e7;
...@@ -267,7 +267,7 @@ $zen-control-hover-color: #111; ...@@ -267,7 +267,7 @@ $zen-control-hover-color: #111;
$calendar-header-color: #b8b8b8; $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe; $calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1); $calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9; $calendar-unselectable-bg: $gray-light;
/* /*
* Personal Access Tokens * Personal Access Tokens
......
...@@ -108,13 +108,23 @@ ...@@ -108,13 +108,23 @@
} }
.blocks-container { .blocks-container {
padding: $gl-padding; padding: 0 $gl-padding;
} }
.block { .block {
width: 100%; width: 100%;
} }
.js-build-variable {
color: $code-color;
}
.js-build-value {
padding: 2px 4px;
color: $black;
background-color: $white-light;
}
.build-sidebar-header { .build-sidebar-header {
padding: 0 $gl-padding $gl-padding; padding: 0 $gl-padding $gl-padding;
...@@ -123,6 +133,13 @@ ...@@ -123,6 +133,13 @@
} }
} }
.retry-link {
color: $gl-link-color;
&:hover {
text-decoration: underline;
}
}
.stage-item { .stage-item {
cursor: pointer; cursor: pointer;
...@@ -132,7 +149,7 @@ ...@@ -132,7 +149,7 @@
} }
.build-dropdown { .build-dropdown {
padding: 0 $gl-padding; padding: $gl-padding 0;
.dropdown-menu-toggle { .dropdown-menu-toggle {
margin-top: 8px; margin-top: 8px;
...@@ -146,7 +163,6 @@ ...@@ -146,7 +163,6 @@
} }
.builds-container { .builds-container {
margin-top: $gl-padding;
background-color: $white-light; background-color: $white-light;
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
......
...@@ -18,8 +18,7 @@ ...@@ -18,8 +18,7 @@
} }
.commit-row-title { .commit-row-title {
line-height: 1; line-height: 1.35;
margin-bottom: 7px;
.notes_count { .notes_count {
float: right; float: right;
...@@ -43,6 +42,7 @@ ...@@ -43,6 +42,7 @@
border: 1px solid $border-gray-dark; border: 1px solid $border-gray-dark;
border-radius: $border-radius-default; border-radius: $border-radius-default;
margin-left: 5px; margin-left: 5px;
line-height: 1;
&:hover { &:hover {
background-color: darken($gray-light, 10%); background-color: darken($gray-light, 10%);
...@@ -113,11 +113,13 @@ ...@@ -113,11 +113,13 @@
.commit-row-description { .commit-row-description {
font-size: 14px; font-size: 14px;
border-left: 1px solid #eee; border-left: 1px solid $btn-gray-hover;
padding: 10px 15px; padding: 10px 15px;
margin: 10px 0; margin: 10px 0;
background: #f9f9f9; background: $gray-light;
display: none; display: none;
white-space: pre-line;
word-break: normal;
pre { pre {
border: none; border: none;
...@@ -134,7 +136,7 @@ ...@@ -134,7 +136,7 @@
.commit-row-info { .commit-row-info {
color: $gl-gray; color: $gl-gray;
line-height: 1; line-height: 1.35;
a { a {
color: $gl-gray; color: $gl-gray;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
pre { pre {
border: none; border: none;
background: #f9f9f9; background: $gray-light;
border-radius: 0; border-radius: 0;
color: #777; color: #777;
margin: 0 20px; margin: 0 20px;
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
border: 1px solid #eee; border: 1px solid #eee;
padding: 5px; padding: 5px;
@include border-radius(5px); @include border-radius(5px);
background: #f9f9f9; background: $gray-light;
margin-left: 10px; margin-left: 10px;
top: -6px; top: -6px;
img { img {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.issue-check { .issue-check {
float: left; float: left;
padding-right: 8px; padding-right: 16px;
margin-bottom: 10px; margin-bottom: 10px;
min-width: 15px; min-width: 15px;
...@@ -72,12 +72,12 @@ form.edit-issue { ...@@ -72,12 +72,12 @@ form.edit-issue {
} }
&.closed { &.closed {
background: #f9f9f9; background: $gray-light;
border-color: #e5e5e5; border-color: #e5e5e5;
} }
&.merged { &.merged {
background: #f9f9f9; background: $gray-light;
border-color: #e5e5e5; border-color: #e5e5e5;
} }
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
margin-bottom: 10px; margin-bottom: 10px;
text-decoration: none;
} }
&.suggest-colors-dropdown { &.suggest-colors-dropdown {
......
...@@ -16,7 +16,7 @@ $colors: ( ...@@ -16,7 +16,7 @@ $colors: (
white_button_origin_chosen : #268ced, white_button_origin_chosen : #268ced,
white_header_not_chosen : #f0f0f0, white_header_not_chosen : #f0f0f0,
white_line_not_chosen : #f9f9f9, white_line_not_chosen : $gray-light,
dark_header_head_neutral : rgba(#3f3, .2), dark_header_head_neutral : rgba(#3f3, .2),
......
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
pre { pre {
border: none; border: none;
background: #f9f9f9; background: $gray-light;
border-radius: 0; border-radius: 0;
color: #777; color: #777;
margin: 0 20px; margin: 0 20px;
......
...@@ -11,14 +11,8 @@ ...@@ -11,14 +11,8 @@
} }
} }
.last-commit { .add-to-tree {
max-width: 506px; vertical-align: top;
.last-commit-content {
@include str-truncated;
width: calc(100% - 140px);
margin-left: 3px;
}
} }
.tree-table { .tree-table {
...@@ -32,6 +26,15 @@ ...@@ -32,6 +26,15 @@
line-height: 21px; line-height: 21px;
} }
.last-commit {
@include str-truncated(60%);
}
.commit-history-link-spacer {
margin: 0 10px;
color: $table-border-color;
}
&:hover { &:hover {
td { td {
background-color: $row-hover; background-color: $row-hover;
...@@ -87,11 +90,17 @@ ...@@ -87,11 +90,17 @@
} }
} }
.tree_commit { .tree-time-ago {
color: $gl-gray; min-width: 135px;
color: $gl-gray-light;
}
.tree-commit {
max-width: 320px;
color: $gl-gray-light;
.tree-commit-link { .tree-commit-link {
color: $gl-gray; color: $gl-gray-light;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
$l-cyan: #8abeb7; $l-cyan: #8abeb7;
$l-white: $ci-text-color; $l-white: $ci-text-color;
.term-bold {
font-weight: bold;
}
.term-italic { .term-italic {
font-style: italic; font-style: italic;
} }
......
...@@ -13,7 +13,7 @@ module ServiceParams ...@@ -13,7 +13,7 @@ module ServiceParams
# `issue_events` and `merge_request_events` (singular!) # `issue_events` and `merge_request_events` (singular!)
# See app/helpers/services_helper.rb for how we # See app/helpers/services_helper.rb for how we
# make those event names plural as special case. # make those event names plural as special case.
:issues_events, :merge_requests_events, :issues_events, :confidential_issues_events, :merge_requests_events,
:notify_only_broken_builds, :notify_only_broken_pipelines, :notify_only_broken_builds, :notify_only_broken_pipelines,
:add_pusher, :send_from_committer_email, :disable_diffs, :add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color, :external_wiki_url, :notify, :color,
......
class Import::BaseController < ApplicationController class Import::BaseController < ApplicationController
private private
def get_or_create_namespace def find_or_create_namespace(name, owner)
return current_user.namespace if name == owner
return current_user.namespace unless current_user.can_create_group?
begin begin
namespace = Group.create!(name: @target_namespace, path: @target_namespace, owner: current_user) name = params[:target_namespace].presence || name
namespace = Group.create!(name: name, path: name, owner: current_user)
namespace.add_owner(current_user) namespace.add_owner(current_user)
namespace
rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
namespace = Namespace.find_by_path_or_name(@target_namespace) Namespace.find_by_path_or_name(name)
unless current_user.can?(:create_projects, namespace)
@already_been_taken = true
return false
end
end end
namespace
end end
end end
...@@ -35,23 +35,20 @@ class Import::BitbucketController < Import::BaseController ...@@ -35,23 +35,20 @@ class Import::BitbucketController < Import::BaseController
end end
def create def create
@repo_id = params[:repo_id] || "" @repo_id = params[:repo_id].to_s
repo = client.project(@repo_id.gsub("___", "/")) repo = client.project(@repo_id.gsub('___', '/'))
@project_name = repo["slug"] @project_name = repo['slug']
@target_namespace = find_or_create_namespace(repo['owner'], client.user['user']['username'])
repo_owner = repo["owner"]
repo_owner = current_user.username if repo_owner == client.user["user"]["username"]
@target_namespace = params[:new_namespace].presence || repo_owner
namespace = get_or_create_namespace || (render and return)
unless Gitlab::BitbucketImport::KeyAdder.new(repo, current_user, access_params).execute unless Gitlab::BitbucketImport::KeyAdder.new(repo, current_user, access_params).execute
@access_denied = true render 'deploy_key' and return
render
return
end end
@project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user, access_params).execute if current_user.can?(:create_projects, @target_namespace)
@project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @target_namespace, current_user, access_params).execute
else
render 'unauthorized'
end
end end
private private
......
...@@ -41,14 +41,13 @@ class Import::GithubController < Import::BaseController ...@@ -41,14 +41,13 @@ class Import::GithubController < Import::BaseController
@repo_id = params[:repo_id].to_i @repo_id = params[:repo_id].to_i
repo = client.repo(@repo_id) repo = client.repo(@repo_id)
@project_name = repo.name @project_name = repo.name
@target_namespace = find_or_create_namespace(repo.owner.login, client.user.login)
repo_owner = repo.owner.login if current_user.can?(:create_projects, @target_namespace)
repo_owner = current_user.username if repo_owner == client.user.login @project = Gitlab::GithubImport::ProjectCreator.new(repo, @target_namespace, current_user, access_params).execute
@target_namespace = params[:new_namespace].presence || repo_owner else
render 'unauthorized'
namespace = get_or_create_namespace || (render and return) end
@project = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, current_user, access_params).execute
end end
private private
......
...@@ -26,15 +26,14 @@ class Import::GitlabController < Import::BaseController ...@@ -26,15 +26,14 @@ class Import::GitlabController < Import::BaseController
def create def create
@repo_id = params[:repo_id].to_i @repo_id = params[:repo_id].to_i
repo = client.project(@repo_id) repo = client.project(@repo_id)
@project_name = repo["name"] @project_name = repo['name']
@target_namespace = find_or_create_namespace(repo['namespace']['path'], client.user['username'])
repo_owner = repo["namespace"]["path"] if current_user.can?(:create_projects, @target_namespace)
repo_owner = current_user.username if repo_owner == client.user["username"] @project = Gitlab::GitlabImport::ProjectCreator.new(repo, @target_namespace, current_user, access_params).execute
@target_namespace = params[:new_namespace].presence || repo_owner else
render 'unauthorized'
namespace = get_or_create_namespace || (render and return) end
@project = Gitlab::GitlabImport::ProjectCreator.new(repo, namespace, current_user, access_params).execute
end end
private private
......
...@@ -78,8 +78,8 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -78,8 +78,8 @@ class Projects::BuildsController < Projects::ApplicationController
end end
def raw def raw
if @build.has_trace? if @build.has_trace_file?
send_file @build.path_to_trace, type: 'text/plain; charset=utf-8', disposition: 'inline' send_file @build.trace_file_path, type: 'text/plain; charset=utf-8', disposition: 'inline'
else else
render_404 render_404
end end
......
...@@ -59,6 +59,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -59,6 +59,7 @@ class Projects::HooksController < Projects::ApplicationController
:pipeline_events, :pipeline_events,
:enable_ssl_verification, :enable_ssl_verification,
:issues_events, :issues_events,
:confidential_issues_events,
:merge_requests_events, :merge_requests_events,
:note_events, :note_events,
:push_events, :push_events,
......
...@@ -20,9 +20,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -20,9 +20,8 @@ class Projects::ServicesController < Projects::ApplicationController
def update def update
if @service.update_attributes(service_params[:service]) if @service.update_attributes(service_params[:service])
redirect_to( redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, edit_namespace_project_service_path(@project.namespace, @project, @service.to_param),
@service.to_param, notice: notice: 'Successfully updated.'
'Successfully updated.')
) )
else else
render 'edit' render 'edit'
......
module ImportHelper module ImportHelper
def import_project_target(owner, name)
namespace = current_user.can_create_group? ? owner : current_user.namespace_path
"#{namespace}/#{name}"
end
def github_project_link(path_with_namespace) def github_project_link(path_with_namespace)
link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank'
end end
......
...@@ -448,6 +448,6 @@ module ProjectsHelper ...@@ -448,6 +448,6 @@ module ProjectsHelper
options.delete('Everyone with access') if @project.private? && level != ProjectFeature::ENABLED options.delete('Everyone with access') if @project.private? && level != ProjectFeature::ENABLED
options = options_for_select(options, selected: @project.project_feature.public_send(field) || ProjectFeature::ENABLED) options = options_for_select(options, selected: @project.project_feature.public_send(field) || ProjectFeature::ENABLED)
content_tag(:select, options, name: "project[project_feature_attributes][#{field.to_s}]", id: "project_project_feature_attributes_#{field.to_s}", class: "pull-right form-control").html_safe content_tag(:select, options, name: "project[project_feature_attributes][#{field.to_s}]", id: "project_project_feature_attributes_#{field.to_s}", class: "pull-right form-control", data: { field: field }).html_safe
end end
end end
...@@ -8,7 +8,9 @@ module ServicesHelper ...@@ -8,7 +8,9 @@ module ServicesHelper
when "note" when "note"
"Event will be triggered when someone adds a comment" "Event will be triggered when someone adds a comment"
when "issue" when "issue"
"Event will be triggered when an issue is created/updated/merged" "Event will be triggered when an issue is created/updated/closed"
when "confidential_issue"
"Event will be triggered when a confidential issue is created/updated/closed"
when "merge_request" when "merge_request"
"Event will be triggered when a merge request is created/updated/merged" "Event will be triggered when a merge request is created/updated/merged"
when "build" when "build"
...@@ -19,7 +21,7 @@ module ServicesHelper ...@@ -19,7 +21,7 @@ module ServicesHelper
end end
def service_event_field_name(event) def service_event_field_name(event)
event = event.pluralize if %w[merge_request issue].include?(event) event = event.pluralize if %w[merge_request issue confidential_issue].include?(event)
"#{event}_events" "#{event}_events"
end end
end end
...@@ -208,22 +208,31 @@ module Ci ...@@ -208,22 +208,31 @@ module Ci
end end
end end
def has_trace_file?
File.exist?(path_to_trace) || has_old_trace_file?
end
def has_trace? def has_trace?
raw_trace.present? raw_trace.present?
end end
def raw_trace def raw_trace
if File.file?(path_to_trace) if File.exist?(trace_file_path)
File.read(path_to_trace) File.read(trace_file_path)
elsif project.ci_id && File.file?(old_path_to_trace)
# Temporary fix for build trace data integrity
File.read(old_path_to_trace)
else else
# backward compatibility # backward compatibility
read_attribute :trace read_attribute :trace
end end
end end
##
# Deprecated
#
# This is a hotfix for CI build data integrity, see #4246
def has_old_trace_file?
project.ci_id && File.exist?(old_path_to_trace)
end
def trace def trace
trace = raw_trace trace = raw_trace
if project && trace.present? && project.runners_token.present? if project && trace.present? && project.runners_token.present?
...@@ -262,6 +271,14 @@ module Ci ...@@ -262,6 +271,14 @@ module Ci
end end
end end
def trace_file_path
if has_old_trace_file?
old_path_to_trace
else
path_to_trace
end
end
def dir_to_trace def dir_to_trace
File.join( File.join(
Settings.gitlab_ci.builds_path, Settings.gitlab_ci.builds_path,
......
...@@ -6,6 +6,7 @@ class ProjectHook < WebHook ...@@ -6,6 +6,7 @@ class ProjectHook < WebHook
belongs_to :project belongs_to :project
scope :issue_hooks, -> { where(issues_events: true) } scope :issue_hooks, -> { where(issues_events: true) }
scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) }
scope :note_hooks, -> { where(note_events: true) } scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :build_hooks, -> { where(build_events: true) } scope :build_hooks, -> { where(build_events: true) }
......
...@@ -4,6 +4,7 @@ class WebHook < ActiveRecord::Base ...@@ -4,6 +4,7 @@ class WebHook < ActiveRecord::Base
default_value_for :push_events, true default_value_for :push_events, true
default_value_for :issues_events, false default_value_for :issues_events, false
default_value_for :confidential_issues_events, false
default_value_for :note_events, false default_value_for :note_events, false
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false default_value_for :tag_push_events, false
......
...@@ -39,7 +39,7 @@ class HipchatService < Service ...@@ -39,7 +39,7 @@ class HipchatService < Service
end end
def supported_events def supported_events
%w(push issue merge_request note tag_push build) %w(push issue confidential_issue merge_request note tag_push build)
end end
def execute(data) def execute(data)
......
...@@ -44,7 +44,7 @@ class SlackService < Service ...@@ -44,7 +44,7 @@ class SlackService < Service
end end
def supported_events def supported_events
%w(push issue merge_request note tag_push build wiki_page) %w(push issue confidential_issue merge_request note tag_push build wiki_page)
end end
def execute(data) def execute(data)
......
...@@ -166,7 +166,7 @@ class Repository ...@@ -166,7 +166,7 @@ class Repository
return false unless target return false unless target
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do
rugged.branches.create(branch_name, target) update_ref!(ref, target, oldrev)
end end
after_create_branch after_create_branch
...@@ -202,7 +202,7 @@ class Repository ...@@ -202,7 +202,7 @@ class Repository
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do
rugged.branches.delete(branch_name) update_ref!(ref, newrev, oldrev)
end end
after_remove_branch after_remove_branch
...@@ -280,6 +280,21 @@ class Repository ...@@ -280,6 +280,21 @@ class Repository
rugged.references.exist?(ref) rugged.references.exist?(ref)
end end
def update_ref!(name, newrev, oldrev)
# We use 'git update-ref' because libgit2/rugged currently does not
# offer 'compare and swap' ref updates. Without compare-and-swap we can
# (and have!) accidentally reset the ref to an earlier state, clobbering
# commits. See also https://github.com/libgit2/libgit2/issues/1534.
command = %w[git update-ref --stdin -z]
_, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin|
stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00")
end
return if status.zero?
raise CommitError.new("Could not update branch #{name.sub('refs/heads/', '')}. Please refresh and try again.")
end
# Makes sure a commit is kept around when Git garbage collection runs. # Makes sure a commit is kept around when Git garbage collection runs.
# Git GC will delete commits from the repository that are no longer in any # Git GC will delete commits from the repository that are no longer in any
# branches or tags, but we want to keep some of these commits around, for # branches or tags, but we want to keep some of these commits around, for
...@@ -1221,15 +1236,10 @@ class Repository ...@@ -1221,15 +1236,10 @@ class Repository
def commit_with_hooks(current_user, branch) def commit_with_hooks(current_user, branch)
update_autocrlf_option update_autocrlf_option
oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
target_branch = find_branch(branch) target_branch = find_branch(branch)
was_empty = empty? was_empty = empty?
if !was_empty && target_branch
oldrev = target_branch.target.id
end
# Make commit # Make commit
newrev = yield(ref) newrev = yield(ref)
...@@ -1237,24 +1247,19 @@ class Repository ...@@ -1237,24 +1247,19 @@ class Repository
raise CommitError.new('Failed to create commit') raise CommitError.new('Failed to create commit')
end end
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
oldrev = Gitlab::Git::BLANK_SHA
else
oldrev = rugged.merge_base(newrev, target_branch.target.sha)
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
update_ref!(ref, newrev, oldrev)
if was_empty || !target_branch if was_empty || !target_branch
# Create branch
rugged.references.create(ref, newrev)
# If repo was empty expire cache # If repo was empty expire cache
after_create if was_empty after_create if was_empty
after_create_branch after_create_branch
else
# Update head
current_head = find_branch(branch).target.id
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
else
raise CommitError.new('Commit was rejected because branch received new push')
end
end end
end end
......
...@@ -7,6 +7,7 @@ class Service < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class Service < ActiveRecord::Base
default_value_for :active, false default_value_for :active, false
default_value_for :push_events, true default_value_for :push_events, true
default_value_for :issues_events, true default_value_for :issues_events, true
default_value_for :confidential_issues_events, true
default_value_for :merge_requests_events, true default_value_for :merge_requests_events, true
default_value_for :tag_push_events, true default_value_for :tag_push_events, true
default_value_for :note_events, true default_value_for :note_events, true
...@@ -33,6 +34,7 @@ class Service < ActiveRecord::Base ...@@ -33,6 +34,7 @@ class Service < ActiveRecord::Base
scope :push_hooks, -> { where(push_events: true, active: true) } scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) } scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
scope :issue_hooks, -> { where(issues_events: true, active: true) } scope :issue_hooks, -> { where(issues_events: true, active: true) }
scope :confidential_issue_hooks, -> { where(confidential_issues_events: true, active: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
scope :note_hooks, -> { where(note_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) } scope :build_hooks, -> { where(build_events: true, active: true) }
...@@ -100,7 +102,7 @@ class Service < ActiveRecord::Base ...@@ -100,7 +102,7 @@ class Service < ActiveRecord::Base
end end
def supported_events def supported_events
%w(push tag_push issue merge_request wiki_page) %w(push tag_push issue confidential_issue merge_request wiki_page)
end end
def execute(data) def execute(data)
......
...@@ -14,9 +14,10 @@ module Issues ...@@ -14,9 +14,10 @@ module Issues
end end
def execute_hooks(issue, action = 'open') def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action) issue_data = hook_data(issue, action)
issue.project.execute_hooks(issue_data, :issue_hooks) hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
issue.project.execute_services(issue_data, :issue_hooks) issue.project.execute_hooks(issue_data, hooks_scope)
issue.project.execute_services(issue_data, hooks_scope)
end end
end end
end end
...@@ -88,7 +88,7 @@ module MergeRequests ...@@ -88,7 +88,7 @@ module MergeRequests
closes_issue = "Closes ##{iid}" closes_issue = "Closes ##{iid}"
if merge_request.description.present? if merge_request.description.present?
merge_request.description += closes_issue.prepend("\n") merge_request.description += closes_issue.prepend("\n\n")
else else
merge_request.description = closes_issue merge_request.description = closes_issue
end end
......
:plain :plain
$("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
new MemberExpirationDate(); new gl.MemberExpirationDate();
- if @already_been_taken - if @project.persisted?
:plain
tr = $("tr#repo_#{@repo_id}")
target_field = tr.find(".import-target")
import_button = tr.find(".btn-import")
origin_target = target_field.text()
project_name = "#{@project_name}"
origin_namespace = "#{@target_namespace}"
target_field.empty()
target_field.append("<p class='alert alert-danger'>This namespace already been taken! Please choose another one</p>")
target_field.append("<input type='text' name='target_namespace' />")
target_field.append("/" + project_name)
target_field.data("project_name", project_name)
target_field.find('input').prop("value", origin_namespace)
import_button.enable().removeClass('is-loading')
- elsif @access_denied
:plain
job = $("tr#repo_#{@repo_id}")
job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>")
- elsif @project.persisted?
:plain :plain
job = $("tr#repo_#{@repo_id}") job = $("tr#repo_#{@repo_id}")
job.attr("id", "project_#{@project.id}") job.attr("id", "project_#{@project.id}")
......
:plain
tr = $("tr#repo_#{@repo_id}")
target_field = tr.find(".import-target")
import_button = tr.find(".btn-import")
origin_target = target_field.text()
project_name = "#{@project_name}"
origin_namespace = "#{@target_namespace.path}"
target_field.empty()
target_field.append("<p class='alert alert-danger'>This namespace has already been taken! Please choose another one.</p>")
target_field.append("<input type='text' name='target_namespace' />")
target_field.append("/" + project_name)
target_field.data("project_name", project_name)
target_field.find('input').prop("value", origin_namespace)
import_button.enable().removeClass('is-loading')
:plain
job = $("tr#repo_#{@repo_id}")
job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>")
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
%td %td
= link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
%td.import-target %td.import-target
= "#{repo["owner"]}/#{repo["slug"]}" = import_project_target(repo['owner'], repo['slug'])
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do = button_tag class: "btn btn-import js-add-to-import" do
Import Import
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
%td %td
= github_project_link(repo.full_name) = github_project_link(repo.full_name)
%td.import-target %td.import-target
= repo.full_name = import_project_target(repo.owner.login, repo.name)
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do = button_tag class: "btn btn-import js-add-to-import" do
Import Import
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
%td %td
= link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank"
%td.import-target %td.import-target
= repo["path_with_namespace"] = import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status %td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do = button_tag class: "btn btn-import js-add-to-import" do
Import Import
......
%fieldset.features.append-bottom-0.issue-feature %fieldset.features.append-bottom-0.issues-feature
%h5.prepend-top-0 %h5.prepend-top-0
Issues Issues
.form-group .form-group
......
- builds = @build.pipeline.builds.latest.to_a
- statuses = ["failed", "pending", "running", "canceled", "success", "skipped"]
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar %aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Build Build
...@@ -11,40 +14,6 @@ ...@@ -11,40 +14,6 @@
%p.build-detail-row %p.build-detail-row
#{@build.coverage}% #{@build.coverage}%
- builds = @build.pipeline.builds.latest.to_a
- statuses = ["failed", "pending", "running", "canceled", "success", "skipped"]
- if builds.size > 1
.dropdown.build-dropdown
.build-light-text Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More
= icon('caret-down')
%ul.dropdown-menu
- builds.map(&:stage).uniq.each do |stage|
%li
%a.stage-item= stage
.builds-container
- statuses.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{class: ('active' if build == @build), data: {stage: build.stage}}
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= icon('check')
= ci_icon_for_status(build.status)
%span
- if build.name
= build.name
- else
= build.id
- if @build.retried?
%li.active
%a
Build ##{@build.id}
&middot;
%i.fa.fa-warning
This build was retried.
.blocks-container .blocks-container
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?) - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block{ class: ("block-first" if !@build.coverage) } .block{ class: ("block-first" if !@build.coverage) }
...@@ -76,7 +45,7 @@ ...@@ -76,7 +45,7 @@
.title .title
Build details Build details
- if can?(current_user, :update_build, @build) && @build.retryable? - if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
- if @build.merge_request - if @build.merge_request
%p.build-detail-row %p.build-detail-row
%span.build-light-text Merge Request: %span.build-light-text Merge Request:
...@@ -100,7 +69,7 @@ ...@@ -100,7 +69,7 @@
- elsif @build.runner - elsif @build.runner
\##{@build.runner.id} \##{@build.runner.id}
.btn-group.btn-group-justified{ role: :group } .btn-group.btn-group-justified{ role: :group }
- if @build.has_trace? - if @build.has_trace_file?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
- if @build.active? - if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
...@@ -121,12 +90,13 @@ ...@@ -121,12 +90,13 @@
- if @build.trigger_request.variables - if @build.trigger_request.variables
%p %p
%span.build-light-text Variables: %button.btn.group.btn-group-justified.reveal-variables Reveal Variables
- @build.trigger_request.variables.each do |key, value| - @build.trigger_request.variables.each do |key, value|
%code .hide.js-build
#{key}=#{value} .js-build-variable= key
.js-build-value= value
.block .block
.title .title
...@@ -141,3 +111,35 @@ ...@@ -141,3 +111,35 @@
- @build.tag_list.each do |tag| - @build.tag_list.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
- if builds.size > 1
.dropdown.build-dropdown
.title Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More
= icon('caret-down')
%ul.dropdown-menu
- builds.map(&:stage).uniq.each do |stage|
%li
%a.stage-item= stage
.builds-container
- statuses.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{class: ('active' if build == @build), data: {stage: build.stage}}
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= icon('check')
= ci_icon_for_status(build.status)
%span
- if build.name
= build.name
- else
= build.id
- if @build.retried?
%li.active
%a
Build ##{@build.id}
&middot;
%i.fa.fa-warning
This build was retried.
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
Fork %span Fork
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
Fork %span Fork
%div.count-with-arrow %div.count-with-arrow
%span.arrow %span.arrow
= link_to namespace_project_forks_path(@project.namespace, @project), class: "count" do = link_to namespace_project_forks_path(@project.namespace, @project), class: "count" do
......
...@@ -32,11 +32,11 @@ ...@@ -32,11 +32,11 @@
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
Fork %span Fork
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
Fork %span Fork
= render 'projects', projects: @forks = render 'projects', projects: @forks
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.col-md-8.col-lg-7 .col-md-8.col-lg-7
%strong.light-header= hook.url %strong.light-header= hook.url
%div %div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events pipeline_events wiki_page_events).each do |trigger| - %w(push_events tag_push_events issues_events confidential_issues_events note_events merge_requests_events build_events pipeline_events wiki_page_events).each do |trigger|
- if hook.send(trigger) - if hook.send(trigger)
%span.label.label-gray.deploy-project-label= trigger.titleize %span.label.label-gray.deploy-project-label= trigger.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5 .col-md-4.col-lg-5.text-right-lg.prepend-top-5
......
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
.pull-right .pull-right
#new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)} #new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)}
= link_to '#', class: 'checking btn btn-grouped', disabled: 'disabled' do
= icon('spinner spin')
Checking branches
= link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid),
method: :post, class: 'btn btn-new btn-inverted has-tooltip', title: @issue.to_branch_name, disabled: 'disabled' do method: :post, class: 'btn btn-new btn-inverted btn-grouped has-tooltip available hide', title: @issue.to_branch_name do
.checking New branch
= icon('spinner spin') = link_to '#', class: 'unavailable btn btn-grouped hide', disabled: 'disabled' do
Checking branches = icon('exclamation-triangle')
.available.hide New branch unavailable
New branch
.unavailable.hide
= icon('exclamation-triangle')
New branch unavailable
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
%pre.dark#merge-info-1 %pre.dark#merge-info-1
- if @merge_request.for_fork? - if @merge_request.for_fork?
:preserve :preserve
git fetch #{h @merge_request.source_project.http_url_to_repo} #{h @merge_request.source_branch} git fetch #{h default_url_to_repo(@merge_request.source_project)} #{h @merge_request.source_branch}
git checkout -b #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} FETCH_HEAD git checkout -b #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} FETCH_HEAD
- else - else
:preserve :preserve
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
= render 'bitbucket_import_modal' = render 'bitbucket_import_modal'
%div %div
- if gitlab_import_enabled? - if gitlab_import_enabled?
= link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless bitbucket_import_configured?}" do = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do
= icon('gitlab', text: 'GitLab.com') = icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured? - unless gitlab_import_configured?
= render 'gitlab_import_modal' = render 'gitlab_import_modal'
......
:plain :plain
$("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}'); $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}');
new MemberExpirationDate(); new gl.MemberExpirationDate();
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
:plain :plain
var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}"); var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}");
row.find("td.tree_time_ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}'); row.find("td.tree-time-ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree_commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}'); row.find("td.tree-commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
- if lock_label - if lock_label
:plain :plain
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
- file_name = blob_item.name - file_name = blob_item.name
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do
%span.str-truncated= file_name %span.str-truncated= file_name
%td.tree_time_ago.cgray %td.hidden-xs.tree-commit
= render 'projects/tree/spinner' %td.tree-time-ago.cgray.text-right
%td.hidden-xs.tree_commit = render 'projects/tree/spinner'
\ No newline at end of file
...@@ -4,21 +4,21 @@ ...@@ -4,21 +4,21 @@
%thead %thead
%tr %tr
%th Name %th Name
%th Last Update %th.hidden-xs
%th.hidden-xs.last-commit .pull-left Last Commit
Last Commit .last-commit.hidden-sm.pull-left
.last-commit-content.hidden-sm &nbsp;
%i.fa.fa-angle-right %i.fa.fa-angle-right
&nbsp; &nbsp;
%small.light %small.light
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
&ndash; &ndash;
= time_ago_with_tooltip(@commit.committed_date) = time_ago_with_tooltip(@commit.committed_date)
&ndash;
= @commit.full_title = @commit.full_title
%small.commit-history-link-spacer &#124;
= lock_file_link(html_options: {class: 'pull-right prepend-left-10 path-lock'}) = lock_file_link(html_options: {class: 'pull-right prepend-left-10 path-lock'})
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'commit-history-link'
%th.text-right Last Update
- if @path.present? - if @path.present?
%tr.tree-item %tr.tree-item
%td.tree-item-file-name %td.tree-item-file-name
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
- path = flatten_tree(tree_item) - path = flatten_tree(tree_item)
= link_to namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)), title: path do = link_to namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)), title: path do
%span.str-truncated= path %span.str-truncated= path
%td.tree_time_ago.cgray %td.hidden-xs.tree-commit
= render 'projects/tree/spinner' %td.tree-time-ago.text-right
%td.hidden-xs.tree_commit = render 'projects/tree/spinner'
\ No newline at end of file
...@@ -4,65 +4,89 @@ ...@@ -4,65 +4,89 @@
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
= page_title = page_title
%p %p.prepend-top-20
Triggers can force a specific branch or tag to rebuild with an API call. Triggers can force a specific branch or tag to get rebuilt with an API call.
%p.append-bottom-0
= succeed '.' do
Learn more in the
= link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
.col-lg-9 .col-lg-9
%h5.prepend-top-0 .panel.panel-default
Your triggers .panel-heading
- if @triggers.any? %h4.panel-title
.table-responsive Manage your project's triggers
%table.table .panel-body
%thead - if @triggers.any?
%th Token .table-responsive
%th Last used %table.table
%th %thead
= render partial: 'trigger', collection: @triggers, as: :trigger %th
- else %strong Token
%p.settings-message.text-center.append-bottom-default %th
No triggers have been created yet. Add one using the button below. %strong Last used
%th
= render partial: 'trigger', collection: @triggers, as: :trigger
- else
%p.settings-message.text-center.append-bottom-default
No triggers have been created yet. Add one using the button below.
= form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f| = form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
= f.submit "Add Trigger", class: 'btn btn-success' = f.submit "Add trigger", class: 'btn btn-success'
%h5.prepend-top-default .panel-footer
Use CURL
%p.light %p
Copy the token above, set your branch or tag name, and that reference will be rebuilt. In the following examples, you can see the exact API call you need to
make in order to rebuild a specific
%code ref
(branch or tag) with a trigger token.
%p
All you need to do is replace the
%code TOKEN
and
%code REF_NAME
with the trigger token and the branch or tag name respectively.
%pre %h5.prepend-top-default
:plain Use cURL
curl -X POST \
-F token=TOKEN \
-F ref=REF_NAME \
#{builds_trigger_url(@project.id)}
%h5.prepend-top-default
Use .gitlab-ci.yml
%p.light %p.light
In the Copy one of the tokens above, set your branch or tag name, and that
%code .gitlab-ci.yml reference will be rebuilt.
of the dependent project, include the following snippet.
The project will rebuild at the end of the build.
%pre %pre
:plain :plain
trigger: curl -X POST \
type: deploy -F token=TOKEN \
script: -F ref=REF_NAME \
- "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}" #{builds_trigger_url(@project.id)}
%h5.prepend-top-default %h5.prepend-top-default
Pass build variables Use .gitlab-ci.yml
%p.light %p.light
Add In the
%code variables[VARIABLE]=VALUE %code .gitlab-ci.yml
to an API request. Variable values can be used to distinguish between triggered builds and normal builds. of another project, include the following snippet.
The project will be rebuilt at the end of the build.
%pre.append-bottom-0 %pre
:plain :plain
curl -X POST \ trigger_build:
-F token=TOKEN \ stage: deploy
-F "ref=REF_NAME" \ script:
-F "variables[RUN_NIGHTLY_BUILD]=true" \ - "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}"
#{builds_trigger_url(@project.id)} %h5.prepend-top-default
Pass build variables
%p.light
Add
%code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
%pre.append-bottom-0
:plain
curl -X POST \
-F token=TOKEN \
-F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \
#{builds_trigger_url(@project.id)}
...@@ -51,6 +51,13 @@ ...@@ -51,6 +51,13 @@
%strong Issues events %strong Issues events
%p.light %p.light
This URL will be triggered when an issue is created/updated/merged This URL will be triggered when an issue is created/updated/merged
%li
= f.check_box :confidential_issues_events, class: 'pull-left'
.prepend-left-20
= f.label :confidential_issues_events, class: 'list-label' do
%strong Confidential Issues events
%p.light
This URL will be triggered when a confidential issue is created/updated/merged
%li %li
= f.check_box :merge_requests_events, class: 'pull-left' = f.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20 .prepend-left-20
......
%ul.content-list .snippets-list-holder
= render partial: 'shared/snippets/snippet', collection: @snippets %ul.content-list
- if @snippets.empty? = render partial: 'shared/snippets/snippet', collection: @snippets
%li - if @snippets.empty?
.nothing-here-block Nothing here. %li
.nothing-here-block Nothing here.
= paginate @snippets, theme: 'gitlab' = paginate @snippets, theme: 'gitlab', remote: true
:javascript
gl.SnippetsList();
class AddConfidentialIssuesEventsToWebHooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default :web_hooks, :confidential_issues_events, :boolean, default: false, allow_null: false
end
def down
remove_column :web_hooks, :confidential_issues_events
end
end
class AddConfidentialIssuesEventsToServices < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default :services, :confidential_issues_events, :boolean, default: true, allow_null: false
end
def down
remove_column :services, :confidential_issues_events
end
end
class SetConfidentialIssuesEventsOnWebhooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
update_column_in_batches(:web_hooks, :confidential_issues_events, true) do |table, query|
query.where(table[:issues_events].eq(true))
end
end
def down
# noop
end
end
class DropGitoriousFieldFromApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# After the deploy the caches will be cold anyway
DOWNTIME = false
def up
require 'yaml'
import_sources = connection.execute('SELECT import_sources FROM application_settings;')
return unless import_sources.first # support empty databases
yaml = if Gitlab::Database.postgresql?
import_sources.values[0][0]
else
import_sources.first[0]
end
yaml = YAML.safe_load(yaml)
yaml.delete 'gitorious'
# No need for a WHERE clause as there is only one
connection.execute("UPDATE application_settings SET import_sources = #{update_yaml(yaml)}")
end
def down
# noop, gitorious still yields a 404 anyway
end
private
def connection
ActiveRecord::Base.connection
end
def update_yaml(yaml)
connection.quote(YAML.dump(yaml))
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160831223750) do ActiveRecord::Schema.define(version: 20160901141443) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1067,19 +1067,20 @@ ActiveRecord::Schema.define(version: 20160831223750) do ...@@ -1067,19 +1067,20 @@ ActiveRecord::Schema.define(version: 20160831223750) do
t.integer "project_id" t.integer "project_id"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.boolean "active", default: false, null: false t.boolean "active", default: false, null: false
t.text "properties" t.text "properties"
t.boolean "template", default: false t.boolean "template", default: false
t.boolean "push_events", default: true t.boolean "push_events", default: true
t.boolean "issues_events", default: true t.boolean "issues_events", default: true
t.boolean "merge_requests_events", default: true t.boolean "merge_requests_events", default: true
t.boolean "tag_push_events", default: true t.boolean "tag_push_events", default: true
t.boolean "note_events", default: true, null: false t.boolean "note_events", default: true, null: false
t.boolean "build_events", default: false, null: false t.boolean "build_events", default: false, null: false
t.string "category", default: "common", null: false t.string "category", default: "common", null: false
t.boolean "default", default: false t.boolean "default", default: false
t.boolean "wiki_page_events", default: true t.boolean "wiki_page_events", default: true
t.boolean "pipeline_events", default: false, null: false t.boolean "pipeline_events", default: false, null: false
t.boolean "confidential_issues_events", default: true, null: false
end end
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
...@@ -1281,11 +1282,11 @@ ActiveRecord::Schema.define(version: 20160831223750) do ...@@ -1281,11 +1282,11 @@ ActiveRecord::Schema.define(version: 20160831223750) do
add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
create_table "web_hooks", force: :cascade do |t| create_table "web_hooks", force: :cascade do |t|
t.string "url", limit: 2000 t.string "url", limit: 2000
t.integer "project_id" t.integer "project_id"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.string "type", default: "ProjectHook" t.string "type", default: "ProjectHook"
t.integer "service_id" t.integer "service_id"
t.boolean "push_events", default: true, null: false t.boolean "push_events", default: true, null: false
t.boolean "issues_events", default: false, null: false t.boolean "issues_events", default: false, null: false
...@@ -1298,6 +1299,7 @@ ActiveRecord::Schema.define(version: 20160831223750) do ...@@ -1298,6 +1299,7 @@ ActiveRecord::Schema.define(version: 20160831223750) do
t.string "token" t.string "token"
t.boolean "wiki_page_events", default: false, null: false t.boolean "wiki_page_events", default: false, null: false
t.boolean "pipeline_events", default: false, null: false t.boolean "pipeline_events", default: false, null: false
t.boolean "confidential_issues_events", default: false, null: false
end end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
......
...@@ -11,9 +11,10 @@ following locations: ...@@ -11,9 +11,10 @@ following locations:
- [Award Emoji](award_emoji.md) - [Award Emoji](award_emoji.md)
- [Branches](branches.md) - [Branches](branches.md)
- [Builds](builds.md) - [Builds](builds.md)
- [Build triggers](build_triggers.md) - [Build Triggers](build_triggers.md)
- [Build Variables](build_variables.md) - [Build Variables](build_variables.md)
- [Commits](commits.md) - [Commits](commits.md)
- [Deployments](deployments.md)
- [Deploy Keys](deploy_keys.md) - [Deploy Keys](deploy_keys.md)
- [Groups](groups.md) - [Groups](groups.md)
- [Group Access Requests](access_requests.md) - [Group Access Requests](access_requests.md)
......
...@@ -30,7 +30,8 @@ This is the universal solution which works with any type of executor ...@@ -30,7 +30,8 @@ This is the universal solution which works with any type of executor
## SSH keys when using the Docker executor ## SSH keys when using the Docker executor
You will first need to create an SSH key pair. For more information, follow the You will first need to create an SSH key pair. For more information, follow the
instructions to [generate an SSH key](../../ssh/README.md). instructions to [generate an SSH key](../../ssh/README.md). Do not add a comment
to the SSH key, or the `before_script` will prompt for a passphrase.
Then, create a new **Secret Variable** in your project settings on GitLab Then, create a new **Secret Variable** in your project settings on GitLab
following **Settings > Variables**. As **Key** add the name `SSH_PRIVATE_KEY` following **Settings > Variables**. As **Key** add the name `SSH_PRIVATE_KEY`
......
doc/ci/triggers/img/builds_page.png

32.5 KB | W: | H:

doc/ci/triggers/img/builds_page.png

74.4 KB | W: | H:

doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
doc/ci/triggers/img/builds_page.png
  • 2-up
  • Swipe
  • Onion skin
doc/ci/triggers/img/triggers_page.png

12.6 KB | W: | H:

doc/ci/triggers/img/triggers_page.png

11.7 KB | W: | H:

doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -6,50 +6,6 @@ GitLab Runner to manage your project's builds. ...@@ -6,50 +6,6 @@ GitLab Runner to manage your project's builds.
If you want a quick introduction to GitLab CI, follow our If you want a quick introduction to GitLab CI, follow our
[quick start guide](../quick_start/README.md). [quick start guide](../quick_start/README.md).
---
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [.gitlab-ci.yml](#gitlab-ci-yml)
- [image and services](#image-and-services)
- [before_script](#before_script)
- [after_script](#after_script)
- [stages](#stages)
- [types](#types)
- [variables](#variables)
- [cache](#cache)
- [cache:key](#cache-key)
- [Jobs](#jobs)
- [script](#script)
- [stage](#stage)
- [only and except](#only-and-except)
- [job variables](#job-variables)
- [tags](#tags)
- [allow_failure](#allow_failure)
- [when](#when)
- [Manual actions](#manual-actions)
- [environment](#environment)
- [artifacts](#artifacts)
- [artifacts:name](#artifacts-name)
- [artifacts:when](#artifacts-when)
- [artifacts:expire_in](#artifacts-expire_in)
- [dependencies](#dependencies)
- [before_script and after_script](#before_script-and-after_script)
- [Git Strategy](#git-strategy)
- [Shallow cloning](#shallow-cloning)
- [Hidden keys](#hidden-keys)
- [Special YAML features](#special-yaml-features)
- [Anchors](#anchors)
- [Validate the .gitlab-ci.yml](#validate-the-gitlab-ci-yml)
- [Skipping builds](#skipping-builds)
- [Examples](#examples)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
---
## .gitlab-ci.yml ## .gitlab-ci.yml
From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML) From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML)
...@@ -183,7 +139,7 @@ Alias for [stages](#stages). ...@@ -183,7 +139,7 @@ Alias for [stages](#stages).
Introduced in GitLab Runner v0.5.0. Introduced in GitLab Runner v0.5.0.
GitLab CI allows you to add variables to `.gitlab-ci.yml` that are set in the GitLab CI allows you to add variables to `.gitlab-ci.yml` that are set in the
build environment. The variables are stored in the git repository and are meant build environment. The variables are stored in the Git repository and are meant
to store non-sensitive project configuration, for example: to store non-sensitive project configuration, for example:
```yaml ```yaml
...@@ -198,6 +154,8 @@ thus allowing to fine tune them. ...@@ -198,6 +154,8 @@ thus allowing to fine tune them.
Variables can be also defined on [job level](#job-variables). Variables can be also defined on [job level](#job-variables).
[Learn more about variables.](../variables/README.md)
### cache ### cache
>**Note:** >**Note:**
...@@ -1086,6 +1044,13 @@ test:mysql: ...@@ -1086,6 +1044,13 @@ test:mysql:
You can see that the hidden keys are conveniently used as templates. You can see that the hidden keys are conveniently used as templates.
## Triggers
Triggers can be used to force a rebuild of a specific branch, tag or commit,
with an API call.
[Read more in the triggers documentation.](../triggers/README.md)
## Validate the .gitlab-ci.yml ## Validate the .gitlab-ci.yml
Each instance of GitLab CI has an embedded debug tool called Lint. Each instance of GitLab CI has an embedded debug tool called Lint.
......
...@@ -63,24 +63,24 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ...@@ -63,24 +63,24 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim
### Memory ### Memory
You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab! You need at least 4GB of addressable memory (RAM + swap) to install and use GitLab!
The operating system and any other running applications will also be using memory The operating system and any other running applications will also be using memory
so keep in mind that you need at least 2GB available before running GitLab. With so keep in mind that you need at least 4GB available before running GitLab. With
less memory GitLab will give strange errors during the reconfigure run and 500 less memory GitLab will give strange errors during the reconfigure run and 500
errors during usage. errors during usage.
- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. - 1GB RAM + 3GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice.
- 1GB RAM + 1GB swap supports up to 100 users but it will be very slow - 2GB RAM + 2GB swap supports up to 100 users but it will be very slow
- **2GB RAM** is the **recommended** memory size for all installations and supports up to 100 users - **4GB RAM** is the **recommended** memory size for all installations and supports up to 100 users
- 4GB RAM supports up to 1,000 users - 8GB RAM supports up to 1,000 users
- 8GB RAM supports up to 2,000 users - 16GB RAM supports up to 2,000 users
- 16GB RAM supports up to 4,000 users - 32GB RAM supports up to 4,000 users
- 32GB RAM supports up to 8,000 users - 64GB RAM supports up to 8,000 users
- 64GB RAM supports up to 16,000 users - 128GB RAM supports up to 16,000 users
- 128GB RAM supports up to 32,000 users - 256GB RAM supports up to 32,000 users
- More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/) - More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/)
We recommend having at least 1GB of swap on your server, even if you currently have We recommend having at least 2GB of swap on your server, even if you currently have
enough available RAM. Having swap will help reduce the chance of errors occurring enough available RAM. Having swap will help reduce the chance of errors occurring
if your available memory changes. if your available memory changes.
...@@ -113,10 +113,8 @@ It's possible to increase the amount of unicorn workers and this will usually he ...@@ -113,10 +113,8 @@ It's possible to increase the amount of unicorn workers and this will usually he
For most instances we recommend using: CPU cores + 1 = unicorn workers. For most instances we recommend using: CPU cores + 1 = unicorn workers.
So for a machine with 2 cores, 3 unicorn workers is ideal. So for a machine with 2 cores, 3 unicorn workers is ideal.
For all machines that have 1GB and up we recommend a minimum of three unicorn workers. For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check).
If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping.
To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
......
...@@ -171,11 +171,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -171,11 +171,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end end
step 'I should not see the issues settings' do step 'I should not see the issues settings' do
expect(find('.issue-feature')).not_to be_visible expect(find('.issues-feature')).not_to be_visible
end end
step 'I should see the issues settings' do step 'I should see the issues settings' do
expect(find('.issue-feature')).to be_visible expect(find('.issues-feature')).to be_visible
end end
step 'I create bare repo' do step 'I create bare repo' do
......
...@@ -117,15 +117,19 @@ module API ...@@ -117,15 +117,19 @@ module API
post '/two_factor_recovery_codes' do post '/two_factor_recovery_codes' do
status 200 status 200
key = Key.find(params[:key_id]) key = Key.find_by(id: params[:key_id])
user = key.user
unless key
return { 'success' => false, 'message' => 'Could not find the given key' }
end
# Make sure this isn't a deploy key if key.is_a?(DeployKey)
unless key.type.nil?
return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
end end
unless user.present? user = key.user
unless user
return { success: false, message: 'Could not find a user for the given key' } return { success: false, message: 'Could not find a user for the given key' }
end end
......
...@@ -22,9 +22,9 @@ module Gitlab ...@@ -22,9 +22,9 @@ module Gitlab
@cmd_status = 0 @cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
# We are not using stdin so we should close it, in case the command we yield(stdin) if block_given?
# are running waits for input.
stdin.close stdin.close
@cmd_output << stdout.read @cmd_output << stdout.read
@cmd_output << stderr.read @cmd_output << stderr.read
@cmd_status = wait_thr.value.exitstatus @cmd_status = wait_thr.value.exitstatus
......
...@@ -28,11 +28,6 @@ module Gitlab ...@@ -28,11 +28,6 @@ module Gitlab
end end
end end
def total_count
@total_count ||= issues_count + merge_requests_count + blobs_count +
notes_count + wiki_blobs_count + commits_count
end
def blobs_count def blobs_count
@blobs_count ||= blobs.count @blobs_count ||= blobs.count
end end
......
...@@ -27,11 +27,6 @@ module Gitlab ...@@ -27,11 +27,6 @@ module Gitlab
end end
end end
def total_count
@total_count ||= projects_count + issues_count + merge_requests_count +
milestones_count
end
def projects_count def projects_count
@projects_count ||= projects.count @projects_count ||= projects.count
end end
...@@ -48,10 +43,6 @@ module Gitlab ...@@ -48,10 +43,6 @@ module Gitlab
@milestones_count ||= milestones.count @milestones_count ||= milestones.count
end end
def empty?
total_count.zero?
end
private private
def projects def projects
......
...@@ -20,10 +20,6 @@ module Gitlab ...@@ -20,10 +20,6 @@ module Gitlab
end end
end end
def total_count
@total_count ||= snippet_titles_count + snippet_blobs_count
end
def snippet_titles_count def snippet_titles_count
@snippet_titles_count ||= snippet_titles.count @snippet_titles_count ||= snippet_titles.count
end end
......
...@@ -146,21 +146,42 @@ describe Import::BitbucketController do ...@@ -146,21 +146,42 @@ describe Import::BitbucketController do
end end
context "when a namespace with the Bitbucket user's username doesn't exist" do context "when a namespace with the Bitbucket user's username doesn't exist" do
it "creates the namespace" do context "when current user can create namespaces" do
expect(Gitlab::BitbucketImport::ProjectCreator). it "creates the namespace" do
to receive(:new).and_return(double(execute: true)) expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js expect { post :create, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, an_instance_of(Group), user, access_params).
and_return(double(execute: true))
expect(Namespace.where(name: other_username).first).not_to be_nil post :create, format: :js
end
end end
it "takes the new namespace" do context "when current user can't create namespaces" do
expect(Gitlab::BitbucketImport::ProjectCreator). before do
to receive(:new).with(bitbucket_repo, an_instance_of(Group), user, access_params). user.update_attribute(:can_create_group, false)
and_return(double(execute: true)) end
post :create, format: :js it "doesn't create the namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator).
to receive(:new).with(bitbucket_repo, user.namespace, user, access_params).
and_return(double(execute: true))
post :create, format: :js
end
end end
end end
end end
......
...@@ -181,21 +181,42 @@ describe Import::GithubController do ...@@ -181,21 +181,42 @@ describe Import::GithubController do
end end
context "when a namespace with the GitHub user's username doesn't exist" do context "when a namespace with the GitHub user's username doesn't exist" do
it "creates the namespace" do context "when current user can create namespaces" do
expect(Gitlab::GithubImport::ProjectCreator). it "creates the namespace" do
to receive(:new).and_return(double(execute: true)) expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js expect { post :create, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, an_instance_of(Group), user, access_params).
and_return(double(execute: true))
expect(Namespace.where(name: other_username).first).not_to be_nil post :create, format: :js
end
end end
it "takes the new namespace" do context "when current user can't create namespaces" do
expect(Gitlab::GithubImport::ProjectCreator). before do
to receive(:new).with(github_repo, an_instance_of(Group), user, access_params). user.update_attribute(:can_create_group, false)
and_return(double(execute: true)) end
post :create, format: :js it "doesn't create the namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(github_repo, user.namespace, user, access_params).
and_return(double(execute: true))
post :create, format: :js
end
end end
end end
end end
......
...@@ -136,21 +136,42 @@ describe Import::GitlabController do ...@@ -136,21 +136,42 @@ describe Import::GitlabController do
end end
context "when a namespace with the GitLab.com user's username doesn't exist" do context "when a namespace with the GitLab.com user's username doesn't exist" do
it "creates the namespace" do context "when current user can create namespaces" do
expect(Gitlab::GitlabImport::ProjectCreator). it "creates the namespace" do
to receive(:new).and_return(double(execute: true)) expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
post :create, format: :js expect { post :create, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params).
and_return(double(execute: true))
expect(Namespace.where(name: other_username).first).not_to be_nil post :create, format: :js
end
end end
it "takes the new namespace" do context "when current user can't create namespaces" do
expect(Gitlab::GitlabImport::ProjectCreator). before do
to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params). user.update_attribute(:can_create_group, false)
and_return(double(execute: true)) end
post :create, format: :js it "doesn't create the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator).
to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
and_return(double(execute: true))
post :create, format: :js
end
end end
end end
end end
......
...@@ -49,4 +49,20 @@ describe Projects::ServicesController do ...@@ -49,4 +49,20 @@ describe Projects::ServicesController do
let!(:referrer) { nil } let!(:referrer) { nil }
end end
end end
describe 'PUT #update' do
context 'on successful update' do
it 'sets the flash' do
expect(service).to receive(:to_param).and_return('hipchat')
put :update,
namespace_id: project.namespace.id,
project_id: project.id,
id: service.id,
service: { active: false }
expect(flash[:notice]).to eq 'Successfully updated.'
end
end
end
end end
...@@ -211,6 +211,13 @@ feature 'Expand and collapse diffs', js: true, feature: true do ...@@ -211,6 +211,13 @@ feature 'Expand and collapse diffs', js: true, feature: true do
context 'expanding all diffs' do context 'expanding all diffs' do
before do before do
click_link('Expand all') click_link('Expand all')
# Wait for elements to appear to ensure full page reload
expect(page).to have_content('This diff was suppressed by a .gitattributes entry')
expect(page).to have_content('This diff could not be displayed because it is too large.')
expect(page).to have_content('too_large_image.jpg')
find('.note-textarea')
wait_for_ajax wait_for_ajax
execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });') execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });')
end end
......
require 'spec_helper' require 'spec_helper'
require 'tempfile'
describe "Builds" do describe "Builds" do
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
...@@ -6,7 +7,7 @@ describe "Builds" do ...@@ -6,7 +7,7 @@ describe "Builds" do
before do before do
login_as(:user) login_as(:user)
@commit = FactoryGirl.create :ci_pipeline @commit = FactoryGirl.create :ci_pipeline
@build = FactoryGirl.create :ci_build, pipeline: @commit @build = FactoryGirl.create :ci_build, :trace, pipeline: @commit
@build2 = FactoryGirl.create :ci_build @build2 = FactoryGirl.create :ci_build
@project = @commit.project @project = @commit.project
@project.team << [@user, :developer] @project.team << [@user, :developer]
...@@ -156,7 +157,6 @@ describe "Builds" do ...@@ -156,7 +157,6 @@ describe "Builds" do
context 'Build raw trace' do context 'Build raw trace' do
before do before do
@build.run! @build.run!
@build.trace = 'BUILD TRACE'
visit namespace_project_build_path(@project.namespace, @project, @build) visit namespace_project_build_path(@project.namespace, @project, @build)
end end
...@@ -164,6 +164,26 @@ describe "Builds" do ...@@ -164,6 +164,26 @@ describe "Builds" do
expect(page).to have_link 'Raw' expect(page).to have_link 'Raw'
end end
end end
describe 'Variables' do
before do
@trigger_request = create :ci_trigger_request_with_variables
@build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request
visit namespace_project_build_path(@project.namespace, @project, @build)
end
it 'shows variable key and value after click', js: true do
expect(page).to have_css('.reveal-variables')
expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value')
click_button 'Reveal Variables'
expect(page).not_to have_css('.reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
end
end
end end
describe "POST /:project/builds/:id/cancel" do describe "POST /:project/builds/:id/cancel" do
...@@ -255,35 +275,101 @@ describe "Builds" do ...@@ -255,35 +275,101 @@ describe "Builds" do
end end
end end
describe "GET /:project/builds/:id/raw" do describe 'GET /:project/builds/:id/raw' do
context "Build from project" do context 'access source' do
before do context 'build from project' do
Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') before do
@build.run! Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
@build.trace = 'BUILD TRACE' @build.run!
visit namespace_project_build_path(@project.namespace, @project, @build) visit namespace_project_build_path(@project.namespace, @project, @build)
page.within('.js-build-sidebar') { click_link 'Raw' } page.within('.js-build-sidebar') { click_link 'Raw' }
end
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace)
end
end end
it 'sends the right headers' do context 'build from other project' do
expect(page.status_code).to eq(200) before do
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) @build2.run!
visit raw_namespace_project_build_path(@project.namespace, @project, @build2)
end
it 'sends the right headers' do
expect(page.status_code).to eq(404)
end
end end
end end
context "Build from other project" do context 'storage form' do
before do let(:existing_file) { Tempfile.new('existing-trace-file').path }
Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') let(:non_existing_file) do
@build2.run! file = Tempfile.new('non-existing-trace-file')
@build2.trace = 'BUILD TRACE' path = file.path
visit raw_namespace_project_build_path(@project.namespace, @project, @build2) file.unlink
puts page.status_code path
puts current_url
end end
it 'sends the right headers' do context 'when build has trace in file' do
expect(page.status_code).to eq(404) before do
Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
@build.run!
visit namespace_project_build_path(@project.namespace, @project, @build)
allow_any_instance_of(Project).to receive(:ci_id).and_return(nil)
allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(existing_file)
allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file)
page.within('.js-build-sidebar') { click_link 'Raw' }
end
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
expect(page.response_headers['X-Sendfile']).to eq(existing_file)
end
end
context 'when build has trace in old file' do
before do
Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
@build.run!
visit namespace_project_build_path(@project.namespace, @project, @build)
allow_any_instance_of(Project).to receive(:ci_id).and_return(999)
allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file)
allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(existing_file)
page.within('.js-build-sidebar') { click_link 'Raw' }
end
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
expect(page.response_headers['X-Sendfile']).to eq(existing_file)
end
end
context 'when build has trace in DB' do
before do
Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
@build.run!
visit namespace_project_build_path(@project.namespace, @project, @build)
allow_any_instance_of(Project).to receive(:ci_id).and_return(nil)
allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file)
allow_any_instance_of(Ci::Build).to receive(:old_path_to_trace).and_return(non_existing_file)
page.within('.js-build-sidebar') { click_link 'Raw' }
end
it 'sends the right headers' do
expect(page.status_code).to eq(404)
end
end end
end end
end end
......
require 'rails_helper'
feature 'Project edit', feature: true, js: true do
include WaitForAjax
let(:user) { create(:user) }
let(:project) { create(:project) }
before do
project.team << [user, :master]
login_as(user)
visit edit_namespace_project_path(project.namespace, project)
end
context 'feature visibility' do
context 'merge requests select' do
it 'hides merge requests section' do
select('Disabled', from: 'project_project_feature_attributes_merge_requests_access_level')
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'
wait_for_ajax
expect(page).to have_selector('.merge-requests-feature', visible: false)
end
end
context 'builds select' do
it 'hides merge requests 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)
click_button 'Save changes'
wait_for_ajax
expect(page).to have_selector('.builds-feature', visible: false)
end
end
end
end
...@@ -12,7 +12,7 @@ describe 'Triggers' do ...@@ -12,7 +12,7 @@ describe 'Triggers' do
context 'create a trigger' do context 'create a trigger' do
before do before do
click_on 'Add Trigger' click_on 'Add trigger'
expect(@project.triggers.count).to eq(1) expect(@project.triggers.count).to eq(1)
end end
......
require 'spec_helper'
describe 'Snippets tab on a user profile', feature: true, js: true do
include WaitForAjax
let(:user) { create(:user) }
context 'when the user has snippets' do
before do
create_list(:snippet, 25, :public, author: user)
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
wait_for_ajax
end
it 'is limited to 20 items per page' do
expect(page.all('.snippets-list-holder .snippet-row').count).to eq(20)
end
context 'clicking on the link to the second page' do
before { click_link('2') }
it 'shows the remaining snippets' do
expect(page.all('.snippets-list-holder .snippet-row').count).to eq(5)
end
end
end
end
require 'rails_helper' require 'rails_helper'
describe ImportHelper do describe ImportHelper do
describe '#import_project_target' do
let(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when current user can create namespaces' do
it 'returns project namespace' do
user.update_attribute(:can_create_group, true)
expect(helper.import_project_target('asd', 'vim')).to eq 'asd/vim'
end
end
context 'when current user can not create namespaces' do
it "takes the current user's namespace" do
user.update_attribute(:can_create_group, false)
expect(helper.import_project_target('asd', 'vim')).to eq "#{user.namespace_path}/vim"
end
end
end
describe '#github_project_link' do describe '#github_project_link' do
context 'when provider does not specify a custom URL' do context 'when provider does not specify a custom URL' do
it 'uses default GitHub URL' do it 'uses default GitHub URL' do
......
#= require lib/utils/datetime_utility
describe 'Date time utils', ->
describe 'get day name', ->
it 'returns Sunday', ->
day = gl.utils.getDayName(new Date('07/17/2016'))
expect(day).toBe('Sunday')
it 'returns Monday', ->
day = gl.utils.getDayName(new Date('07/18/2016'))
expect(day).toBe('Monday')
it 'returns Tuesday', ->
day = gl.utils.getDayName(new Date('07/19/2016'))
expect(day).toBe('Tuesday')
it 'returns Wednesday', ->
day = gl.utils.getDayName(new Date('07/20/2016'))
expect(day).toBe('Wednesday')
it 'returns Thursday', ->
day = gl.utils.getDayName(new Date('07/21/2016'))
expect(day).toBe('Thursday')
it 'returns Friday', ->
day = gl.utils.getDayName(new Date('07/22/2016'))
expect(day).toBe('Friday')
it 'returns Saturday', ->
day = gl.utils.getDayName(new Date('07/23/2016'))
expect(day).toBe('Saturday')
describe 'get day difference', ->
it 'should return 7', ->
firstDay = new Date('07/01/2016')
secondDay = new Date('07/08/2016')
difference = gl.utils.getDayDifference(firstDay, secondDay)
expect(difference).toBe(7)
it 'should return 31', ->
firstDay = new Date('07/01/2016')
secondDay = new Date('08/01/2016')
difference = gl.utils.getDayDifference(firstDay, secondDay)
expect(difference).toBe(31)
it 'should return 365', ->
firstDay = new Date('07/02/2015')
secondDay = new Date('07/01/2016')
difference = gl.utils.getDayDifference(firstDay, secondDay)
expect(difference).toBe(365)
\ No newline at end of file
//= require lib/utils/datetime_utility
(() => {
describe('Date time utils', () => {
describe('get day name', () => {
it('should return Sunday', () => {
const day = gl.utils.getDayName(new Date('07/17/2016'));
expect(day).toBe('Sunday');
});
it('should return Monday', () => {
const day = gl.utils.getDayName(new Date('07/18/2016'));
expect(day).toBe('Monday');
});
it('should return Tuesday', () => {
const day = gl.utils.getDayName(new Date('07/19/2016'));
expect(day).toBe('Tuesday');
});
it('should return Wednesday', () => {
const day = gl.utils.getDayName(new Date('07/20/2016'));
expect(day).toBe('Wednesday');
});
it('should return Thursday', () => {
const day = gl.utils.getDayName(new Date('07/21/2016'));
expect(day).toBe('Thursday');
});
it('should return Friday', () => {
const day = gl.utils.getDayName(new Date('07/22/2016'));
expect(day).toBe('Friday');
});
it('should return Saturday', () => {
const day = gl.utils.getDayName(new Date('07/23/2016'));
expect(day).toBe('Saturday');
});
});
describe('get day difference', () => {
it('should return 7', () => {
const firstDay = new Date('07/01/2016');
const secondDay = new Date('07/08/2016');
const difference = gl.utils.getDayDifference(firstDay, secondDay);
expect(difference).toBe(7);
});
it('should return 31', () => {
const firstDay = new Date('07/01/2016');
const secondDay = new Date('08/01/2016');
const difference = gl.utils.getDayDifference(firstDay, secondDay);
expect(difference).toBe(31);
});
it('should return 365', () => {
const firstDay = new Date('07/02/2015');
const secondDay = new Date('07/01/2016');
const difference = gl.utils.getDayDifference(firstDay, secondDay);
expect(difference).toBe(365);
});
});
});
})();
...@@ -40,4 +40,13 @@ describe 'Gitlab::Popen', lib: true, no_db: true do ...@@ -40,4 +40,13 @@ describe 'Gitlab::Popen', lib: true, no_db: true do
it { expect(@status).to be_zero } it { expect(@status).to be_zero }
it { expect(@output).to include('spec') } it { expect(@output).to include('spec') }
end end
context 'use stdin' do
before do
@output, @status = @klass.new.popen(%w[cat]) { |stdin| stdin.write 'hello' }
end
it { expect(@status).to be_zero }
it { expect(@output).to eq('hello') }
end
end end
...@@ -12,12 +12,6 @@ describe Gitlab::SearchResults do ...@@ -12,12 +12,6 @@ describe Gitlab::SearchResults do
let!(:milestone) { create(:milestone, project: project, title: 'foo') } let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(user, Project.all, 'foo') } let(:results) { described_class.new(user, Project.all, 'foo') }
describe '#total_count' do
it 'returns the total amount of search hits' do
expect(results.total_count).to eq(4)
end
end
describe '#projects_count' do describe '#projects_count' do
it 'returns the total amount of projects' do it 'returns the total amount of projects' do
expect(results.projects_count).to eq(1) expect(results.projects_count).to eq(1)
...@@ -42,18 +36,6 @@ describe Gitlab::SearchResults do ...@@ -42,18 +36,6 @@ describe Gitlab::SearchResults do
end end
end end
describe '#empty?' do
it 'returns true when there are no search results' do
allow(results).to receive(:total_count).and_return(0)
expect(results.empty?).to eq(true)
end
it 'returns false when there are search results' do
expect(results.empty?).to eq(false)
end
end
describe 'confidential issues' do describe 'confidential issues' do
let(:project_1) { create(:empty_project) } let(:project_1) { create(:empty_project) }
let(:project_2) { create(:empty_project) } let(:project_2) { create(:empty_project) }
......
...@@ -5,12 +5,6 @@ describe Gitlab::SnippetSearchResults do ...@@ -5,12 +5,6 @@ describe Gitlab::SnippetSearchResults do
let(:results) { described_class.new(Snippet.all, 'foo') } let(:results) { described_class.new(Snippet.all, 'foo') }
describe '#total_count' do
it 'returns the total amount of search hits' do
expect(results.total_count).to eq(2)
end
end
describe '#snippet_titles_count' do describe '#snippet_titles_count' do
it 'returns the amount of matched snippet titles' do it 'returns the amount of matched snippet titles' do
expect(results.snippet_titles_count).to eq(1) expect(results.snippet_titles_count).to eq(1)
......
...@@ -19,4 +19,64 @@ describe Ci::Build, models: true do ...@@ -19,4 +19,64 @@ describe Ci::Build, models: true do
expect(build.trace).to eq(test_trace) expect(build.trace).to eq(test_trace)
end end
end end
describe '#has_trace_file?' do
context 'when there is no trace' do
it { expect(build.has_trace_file?).to be_falsey }
it { expect(build.trace).to be_nil }
end
context 'when there is a trace' do
context 'when trace is stored in file' do
let(:build_with_trace) { create(:ci_build, :trace) }
it { expect(build_with_trace.has_trace_file?).to be_truthy }
it { expect(build_with_trace.trace).to eq('BUILD TRACE') }
end
context 'when trace is stored in old file' do
before do
allow(build.project).to receive(:ci_id).and_return(999)
allow(File).to receive(:exist?).with(build.path_to_trace).and_return(false)
allow(File).to receive(:exist?).with(build.old_path_to_trace).and_return(true)
allow(File).to receive(:read).with(build.old_path_to_trace).and_return(test_trace)
end
it { expect(build.has_trace_file?).to be_truthy }
it { expect(build.trace).to eq(test_trace) }
end
context 'when trace is stored in DB' do
before do
allow(build.project).to receive(:ci_id).and_return(nil)
allow(build).to receive(:read_attribute).with(:trace).and_return(test_trace)
allow(File).to receive(:exist?).with(build.path_to_trace).and_return(false)
allow(File).to receive(:exist?).with(build.old_path_to_trace).and_return(false)
end
it { expect(build.has_trace_file?).to be_falsey }
it { expect(build.trace).to eq(test_trace) }
end
end
end
describe '#trace_file_path' do
context 'when trace is stored in file' do
before do
allow(build).to receive(:has_trace_file?).and_return(true)
allow(build).to receive(:has_old_trace_file?).and_return(false)
end
it { expect(build.trace_file_path).to eq(build.path_to_trace) }
end
context 'when trace is stored in old file' do
before do
allow(build).to receive(:has_trace_file?).and_return(true)
allow(build).to receive(:has_old_trace_file?).and_return(true)
end
it { expect(build.trace_file_path).to eq(build.old_path_to_trace) }
end
end
end end
...@@ -443,31 +443,32 @@ describe Repository, models: true do ...@@ -443,31 +443,32 @@ describe Repository, models: true do
describe '#commit_with_hooks' do describe '#commit_with_hooks' do
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
let(:new_rev) { 'a74ae73c1ccde9b974a70e82b901588071dc142a' } # commit whose parent is old_rev
context 'when pre hooks were successful' do context 'when pre hooks were successful' do
before do before do
expect_any_instance_of(GitHooksService).to receive(:execute). expect_any_instance_of(GitHooksService).to receive(:execute).
with(user, repository.path_to_repo, old_rev, sample_commit.id, 'refs/heads/feature'). with(user, repository.path_to_repo, old_rev, new_rev, 'refs/heads/feature').
and_yield.and_return(true) and_yield.and_return(true)
end end
it 'runs without errors' do it 'runs without errors' do
expect do expect do
repository.commit_with_hooks(user, 'feature') { sample_commit.id } repository.commit_with_hooks(user, 'feature') { new_rev }
end.not_to raise_error end.not_to raise_error
end end
it 'ensures the autocrlf Git option is set to :input' do it 'ensures the autocrlf Git option is set to :input' do
expect(repository).to receive(:update_autocrlf_option) expect(repository).to receive(:update_autocrlf_option)
repository.commit_with_hooks(user, 'feature') { sample_commit.id } repository.commit_with_hooks(user, 'feature') { new_rev }
end end
context "when the branch wasn't empty" do context "when the branch wasn't empty" do
it 'updates the head' do it 'updates the head' do
expect(repository.find_branch('feature').target.id).to eq(old_rev) expect(repository.find_branch('feature').target.id).to eq(old_rev)
repository.commit_with_hooks(user, 'feature') { sample_commit.id } repository.commit_with_hooks(user, 'feature') { new_rev }
expect(repository.find_branch('feature').target.id).to eq(sample_commit.id) expect(repository.find_branch('feature').target.id).to eq(new_rev)
end end
end end
end end
...@@ -477,7 +478,7 @@ describe Repository, models: true do ...@@ -477,7 +478,7 @@ describe Repository, models: true do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
expect do expect do
repository.commit_with_hooks(user, 'feature') { sample_commit.id } repository.commit_with_hooks(user, 'feature') { new_rev }
end.to raise_error(GitHooksService::PreReceiveError) end.to raise_error(GitHooksService::PreReceiveError)
end end
end end
...@@ -485,6 +486,7 @@ describe Repository, models: true do ...@@ -485,6 +486,7 @@ describe Repository, models: true do
context 'when target branch is different from source branch' do context 'when target branch is different from source branch' do
before do before do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, '']) allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
allow(repository).to receive(:update_ref!)
end end
it 'expires branch cache' do it 'expires branch cache' do
...@@ -495,7 +497,7 @@ describe Repository, models: true do ...@@ -495,7 +497,7 @@ describe Repository, models: true do
expect(repository).to receive(:expire_has_visible_content_cache) expect(repository).to receive(:expire_has_visible_content_cache)
expect(repository).to receive(:expire_branch_count_cache) expect(repository).to receive(:expire_branch_count_cache)
repository.commit_with_hooks(user, 'new-feature') { sample_commit.id } repository.commit_with_hooks(user, 'new-feature') { new_rev }
end end
end end
...@@ -748,32 +750,10 @@ describe Repository, models: true do ...@@ -748,32 +750,10 @@ describe Repository, models: true do
end end
describe '#ff_merge' do describe '#ff_merge' do
it 'merges the code and return the commit id' do before { repository.add_branch(user, 'ff-target', 'feature~5') }
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
merge_commit_id = repository.ff_merge(user,
merge_request.diff_head_sha,
merge_request.target_branch,
merge_request: merge_request)
merge_commit = repository.commit(merge_commit_id)
expect(merge_commit).to be_present
expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
end
it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
merge_commit_id = repository.ff_merge(user,
merge_request.diff_head_sha,
merge_request.target_branch,
merge_request: merge_request)
expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
end
end
describe '#ff_merge' do
it 'merges the code and return the commit id' do it 'merges the code and return the commit id' do
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'ff-target', source_project: project)
merge_commit_id = repository.ff_merge(user, merge_commit_id = repository.ff_merge(user,
merge_request.diff_head_sha, merge_request.diff_head_sha,
merge_request.target_branch, merge_request.target_branch,
...@@ -785,7 +765,7 @@ describe Repository, models: true do ...@@ -785,7 +765,7 @@ describe Repository, models: true do
end end
it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'ff-target', source_project: project)
merge_commit_id = repository.ff_merge(user, merge_commit_id = repository.ff_merge(user,
merge_request.diff_head_sha, merge_request.diff_head_sha,
merge_request.target_branch, merge_request.target_branch,
...@@ -1440,6 +1420,20 @@ describe Repository, models: true do ...@@ -1440,6 +1420,20 @@ describe Repository, models: true do
end end
end end
describe '#update_ref!' do
it 'can create a ref' do
repository.update_ref!('refs/heads/foobar', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
expect(repository.find_branch('foobar')).not_to be_nil
end
it 'raises CommitError when the ref update fails' do
expect do
repository.update_ref!('refs/heads/master', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
end.to raise_error(Repository::CommitError)
end
end
def create_remote_branch(remote_name, branch_name, target) def create_remote_branch(remote_name, branch_name, target)
rugged = repository.rugged rugged = repository.rugged
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
......
...@@ -44,8 +44,8 @@ describe API::API, api: true do ...@@ -44,8 +44,8 @@ describe API::API, api: true do
secret_token: secret_token, secret_token: secret_token,
key_id: 12345 key_id: 12345
expect(response).to have_http_status(404) expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('404 Not found') expect(json_response['message']).to eq('Could not find the given key')
end end
it 'returns an error message when the key is a deploy key' do it 'returns an error message when the key is a deploy key' do
......
...@@ -18,12 +18,12 @@ describe Issues::CloseService, services: true do ...@@ -18,12 +18,12 @@ describe Issues::CloseService, services: true do
context "valid params" do context "valid params" do
before do before do
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = described_class.new(project, user, {}).execute(issue) described_class.new(project, user).execute(issue)
end end
end end
it { expect(@issue).to be_valid } it { expect(issue).to be_valid }
it { expect(@issue).to be_closed } it { expect(issue).to be_closed }
it 'sends email to user2 about assign of new issue' do it 'sends email to user2 about assign of new issue' do
email = ActionMailer::Base.deliveries.last email = ActionMailer::Base.deliveries.last
...@@ -32,7 +32,7 @@ describe Issues::CloseService, services: true do ...@@ -32,7 +32,7 @@ describe Issues::CloseService, services: true do
end end
it 'creates system note about issue reassign' do it 'creates system note about issue reassign' do
note = @issue.notes.last note = issue.notes.last
expect(note.note).to include "Status changed to closed" expect(note.note).to include "Status changed to closed"
end end
...@@ -44,23 +44,43 @@ describe Issues::CloseService, services: true do ...@@ -44,23 +44,43 @@ describe Issues::CloseService, services: true do
context 'current user is not authorized to close issue' do context 'current user is not authorized to close issue' do
before do before do
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = described_class.new(project, guest).execute(issue) described_class.new(project, guest).execute(issue)
end end
end end
it 'does not close the issue' do it 'does not close the issue' do
expect(@issue).to be_open expect(issue).to be_open
end end
end end
context "external issue tracker" do context 'when issue is not confidential' do
it 'executes issue hooks' do
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :issue_hooks)
described_class.new(project, user).execute(issue)
end
end
context 'when issue is confidential' do
it 'executes confidential issue hooks' do
issue = create(:issue, :confidential, project: project)
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :confidential_issue_hooks)
described_class.new(project, user).execute(issue)
end
end
context 'external issue tracker' do
before do before do
allow(project).to receive(:default_issues_tracker?).and_return(false) allow(project).to receive(:default_issues_tracker?).and_return(false)
@issue = described_class.new(project, user, {}).execute(issue) described_class.new(project, user).execute(issue)
end end
it { expect(@issue).to be_valid } it { expect(issue).to be_valid }
it { expect(@issue).to be_opened } it { expect(issue).to be_opened }
it { expect(todo.reload).to be_pending } it { expect(todo.reload).to be_pending }
end end
end end
......
...@@ -72,6 +72,24 @@ describe Issues::CreateService, services: true do ...@@ -72,6 +72,24 @@ describe Issues::CreateService, services: true do
expect(issue.milestone).not_to eq milestone expect(issue.milestone).not_to eq milestone
end end
end end
it 'executes issue hooks when issue is not confidential' do
opts = { title: 'Title', description: 'Description', confidential: false }
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :issue_hooks)
described_class.new(project, user, opts).execute
end
it 'executes confidential issue hooks when issue is confidential' do
opts = { title: 'Title', description: 'Description', confidential: true }
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :confidential_issue_hooks)
described_class.new(project, user, opts).execute
end
end end
it_behaves_like 'new issuable record that supports slash commands' it_behaves_like 'new issuable record that supports slash commands'
......
require 'spec_helper' require 'spec_helper'
describe Issues::ReopenService, services: true do describe Issues::ReopenService, services: true do
let(:guest) { create(:user) } let(:project) { create(:empty_project) }
let(:issue) { create(:issue, :closed) } let(:issue) { create(:issue, :closed, project: project) }
let(:project) { issue.project }
before do
project.team << [guest, :guest]
end
describe '#execute' do describe '#execute' do
context 'current user is not authorized to reopen issue' do context 'when user is not authorized to reopen issue' do
before do before do
guest = create(:user)
project.team << [guest, :guest]
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = described_class.new(project, guest).execute(issue) described_class.new(project, guest).execute(issue)
end end
end end
it 'does not reopen the issue' do it 'does not reopen the issue' do
expect(@issue).to be_closed expect(issue).to be_closed
end
end
context 'when user is authrized to reopen issue' do
let(:user) { create(:user) }
before do
project.team << [user, :master]
end
context 'when issue is not confidential' do
it 'executes issue hooks' do
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :issue_hooks)
described_class.new(project, user).execute(issue)
end
end
context 'when issue is confidential' do
it 'executes confidential issue hooks' do
issue = create(:issue, :confidential, :closed, project: project)
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
expect(project).to receive(:execute_services).with(an_instance_of(Hash), :confidential_issue_hooks)
described_class.new(project, user).execute(issue)
end
end end
end end
end end
......
...@@ -23,11 +23,15 @@ describe Issues::UpdateService, services: true do ...@@ -23,11 +23,15 @@ describe Issues::UpdateService, services: true do
describe 'execute' do describe 'execute' do
def find_note(starting_with) def find_note(starting_with)
@issue.notes.find do |note| issue.notes.find do |note|
note && note.note.start_with?(starting_with) note && note.note.start_with?(starting_with)
end end
end end
def update_issue(opts)
described_class.new(project, user, opts).execute(issue)
end
context "valid params" do context "valid params" do
before do before do
opts = { opts = {
...@@ -35,23 +39,20 @@ describe Issues::UpdateService, services: true do ...@@ -35,23 +39,20 @@ describe Issues::UpdateService, services: true do
description: 'Also please fix', description: 'Also please fix',
assignee_id: user2.id, assignee_id: user2.id,
state_event: 'close', state_event: 'close',
label_ids: [label.id], label_ids: [label.id]
confidential: true
} }
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = Issues::UpdateService.new(project, user, opts).execute(issue) update_issue(opts)
end end
@issue.reload
end end
it { expect(@issue).to be_valid } it { expect(issue).to be_valid }
it { expect(@issue.title).to eq('New title') } it { expect(issue.title).to eq('New title') }
it { expect(@issue.assignee).to eq(user2) } it { expect(issue.assignee).to eq(user2) }
it { expect(@issue).to be_closed } it { expect(issue).to be_closed }
it { expect(@issue.labels.count).to eq(1) } it { expect(issue.labels.count).to eq(1) }
it { expect(@issue.labels.first.title).to eq(label.name) } it { expect(issue.labels.first.title).to eq(label.name) }
it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do
deliveries = ActionMailer::Base.deliveries deliveries = ActionMailer::Base.deliveries
...@@ -81,18 +82,35 @@ describe Issues::UpdateService, services: true do ...@@ -81,18 +82,35 @@ describe Issues::UpdateService, services: true do
expect(note).not_to be_nil expect(note).not_to be_nil
expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**' expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
end end
end
context 'when issue turns confidential' do
let(:opts) do
{
title: 'New title',
description: 'Also please fix',
assignee_id: user2.id,
state_event: 'close',
label_ids: [label.id],
confidential: true
}
end
it 'creates system note about confidentiality change' do it 'creates system note about confidentiality change' do
update_issue(confidential: true)
note = find_note('Made the issue confidential') note = find_note('Made the issue confidential')
expect(note).not_to be_nil expect(note).not_to be_nil
expect(note.note).to eq 'Made the issue confidential' expect(note.note).to eq 'Made the issue confidential'
end end
end
def update_issue(opts) it 'executes confidential issue hooks' do
@issue = Issues::UpdateService.new(project, user, opts).execute(issue) expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
@issue.reload expect(project).to receive(:execute_services).with(an_instance_of(Hash), :confidential_issue_hooks)
update_issue(confidential: true)
end
end end
context 'todos' do context 'todos' do
...@@ -100,7 +118,7 @@ describe Issues::UpdateService, services: true do ...@@ -100,7 +118,7 @@ describe Issues::UpdateService, services: true do
context 'when the title change' do context 'when the title change' do
before do before do
update_issue({ title: 'New title' }) update_issue(title: 'New title')
end end
it 'marks pending todos as done' do it 'marks pending todos as done' do
...@@ -110,7 +128,7 @@ describe Issues::UpdateService, services: true do ...@@ -110,7 +128,7 @@ describe Issues::UpdateService, services: true do
context 'when the description change' do context 'when the description change' do
before do before do
update_issue({ description: 'Also please fix' }) update_issue(description: 'Also please fix')
end end
it 'marks todos as done' do it 'marks todos as done' do
...@@ -120,7 +138,7 @@ describe Issues::UpdateService, services: true do ...@@ -120,7 +138,7 @@ describe Issues::UpdateService, services: true do
context 'when is reassigned' do context 'when is reassigned' do
before do before do
update_issue({ assignee: user2 }) update_issue(assignee: user2)
end end
it 'marks previous assignee todos as done' do it 'marks previous assignee todos as done' do
...@@ -144,7 +162,7 @@ describe Issues::UpdateService, services: true do ...@@ -144,7 +162,7 @@ describe Issues::UpdateService, services: true do
context 'when the milestone change' do context 'when the milestone change' do
before do before do
update_issue({ milestone: create(:milestone) }) update_issue(milestone: create(:milestone))
end end
it 'marks todos as done' do it 'marks todos as done' do
...@@ -154,7 +172,7 @@ describe Issues::UpdateService, services: true do ...@@ -154,7 +172,7 @@ describe Issues::UpdateService, services: true do
context 'when the labels change' do context 'when the labels change' do
before do before do
update_issue({ label_ids: [label.id] }) update_issue(label_ids: [label.id])
end end
it 'marks todos as done' do it 'marks todos as done' do
...@@ -165,6 +183,7 @@ describe Issues::UpdateService, services: true do ...@@ -165,6 +183,7 @@ describe Issues::UpdateService, services: true do
context 'when the issue is relabeled' do context 'when the issue is relabeled' do
let!(:non_subscriber) { create(:user) } let!(:non_subscriber) { create(:user) }
let!(:subscriber) do let!(:subscriber) do
create(:user).tap do |u| create(:user).tap do |u|
label.toggle_subscription(u) label.toggle_subscription(u)
...@@ -176,7 +195,7 @@ describe Issues::UpdateService, services: true do ...@@ -176,7 +195,7 @@ describe Issues::UpdateService, services: true do
opts = { label_ids: [label.id] } opts = { label_ids: [label.id] }
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = Issues::UpdateService.new(project, user, opts).execute(issue) @issue = described_class.new(project, user, opts).execute(issue)
end end
should_email(subscriber) should_email(subscriber)
...@@ -190,7 +209,7 @@ describe Issues::UpdateService, services: true do ...@@ -190,7 +209,7 @@ describe Issues::UpdateService, services: true do
opts = { label_ids: [label.id, label2.id] } opts = { label_ids: [label.id, label2.id] }
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = Issues::UpdateService.new(project, user, opts).execute(issue) @issue = described_class.new(project, user, opts).execute(issue)
end end
should_not_email(subscriber) should_not_email(subscriber)
...@@ -201,7 +220,7 @@ describe Issues::UpdateService, services: true do ...@@ -201,7 +220,7 @@ describe Issues::UpdateService, services: true do
opts = { label_ids: [label2.id] } opts = { label_ids: [label2.id] }
perform_enqueued_jobs do perform_enqueued_jobs do
@issue = Issues::UpdateService.new(project, user, opts).execute(issue) @issue = described_class.new(project, user, opts).execute(issue)
end end
should_not_email(subscriber) should_not_email(subscriber)
...@@ -210,13 +229,15 @@ describe Issues::UpdateService, services: true do ...@@ -210,13 +229,15 @@ describe Issues::UpdateService, services: true do
end end
end end
context 'when Issue has tasks' do context 'when issue has tasks' do
before { update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) } before do
update_issue(description: "- [ ] Task 1\n- [ ] Task 2")
end
it { expect(@issue.tasks?).to eq(true) } it { expect(issue.tasks?).to eq(true) }
context 'when tasks are marked as completed' do context 'when tasks are marked as completed' do
before { update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) } before { update_issue(description: "- [x] Task 1\n- [X] Task 2") }
it 'creates system note about task status change' do it 'creates system note about task status change' do
note1 = find_note('Marked the task **Task 1** as completed') note1 = find_note('Marked the task **Task 1** as completed')
...@@ -229,8 +250,8 @@ describe Issues::UpdateService, services: true do ...@@ -229,8 +250,8 @@ describe Issues::UpdateService, services: true do
context 'when tasks are marked as incomplete' do context 'when tasks are marked as incomplete' do
before do before do
update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) update_issue(description: "- [x] Task 1\n- [X] Task 2")
update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) update_issue(description: "- [ ] Task 1\n- [ ] Task 2")
end end
it 'creates system note about task status change' do it 'creates system note about task status change' do
...@@ -244,8 +265,8 @@ describe Issues::UpdateService, services: true do ...@@ -244,8 +265,8 @@ describe Issues::UpdateService, services: true do
context 'when tasks position has been modified' do context 'when tasks position has been modified' do
before do before do
update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) update_issue(description: "- [x] Task 1\n- [X] Task 2")
update_issue({ description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2" }) update_issue(description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2")
end end
it 'does not create a system note' do it 'does not create a system note' do
...@@ -257,8 +278,8 @@ describe Issues::UpdateService, services: true do ...@@ -257,8 +278,8 @@ describe Issues::UpdateService, services: true do
context 'when a Task list with a completed item is totally replaced' do context 'when a Task list with a completed item is totally replaced' do
before do before do
update_issue({ description: "- [ ] Task 1\n- [X] Task 2" }) update_issue(description: "- [ ] Task 1\n- [X] Task 2")
update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) update_issue(description: "- [ ] One\n- [ ] Two\n- [ ] Three")
end end
it 'does not create a system note referencing the position the old item' do it 'does not create a system note referencing the position the old item' do
...@@ -269,7 +290,7 @@ describe Issues::UpdateService, services: true do ...@@ -269,7 +290,7 @@ describe Issues::UpdateService, services: true do
it 'does not generate a new note at all' do it 'does not generate a new note at all' do
expect do expect do
update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) update_issue(description: "- [ ] One\n- [ ] Two\n- [ ] Three")
end.not_to change { Note.count } end.not_to change { Note.count }
end end
end end
...@@ -277,7 +298,7 @@ describe Issues::UpdateService, services: true do ...@@ -277,7 +298,7 @@ describe Issues::UpdateService, services: true do
context 'updating labels' do context 'updating labels' do
let(:label3) { create(:label, project: project) } let(:label3) { create(:label, project: project) }
let(:result) { Issues::UpdateService.new(project, user, params).execute(issue).reload } let(:result) { described_class.new(project, user, params).execute(issue).reload }
context 'when add_label_ids and label_ids are passed' do context 'when add_label_ids and label_ids are passed' do
let(:params) { { label_ids: [label.id], add_label_ids: [label3.id] } } let(:params) { { label_ids: [label.id], add_label_ids: [label3.id] } }
......
...@@ -99,14 +99,14 @@ describe MergeRequests::BuildService, services: true do ...@@ -99,14 +99,14 @@ describe MergeRequests::BuildService, services: true do
let(:source_branch) { "#{issue.iid}-fix-issue" } let(:source_branch) { "#{issue.iid}-fix-issue" }
it 'appends "Closes #$issue-iid" to the description' do it 'appends "Closes #$issue-iid" to the description' do
expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\nCloses ##{issue.iid}") expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\n\nCloses ##{issue.iid}")
end end
context 'merge request already has a description set' do context 'merge request already has a description set' do
let(:description) { 'Merge request description' } let(:description) { 'Merge request description' }
it 'appends "Closes #$issue-iid" to the description' do it 'appends "Closes #$issue-iid" to the description' do
expect(merge_request.description).to eq("#{description}\nCloses ##{issue.iid}") expect(merge_request.description).to eq("#{description}\n\nCloses ##{issue.iid}")
end end
end end
......
...@@ -59,14 +59,10 @@ describe 'projects/builds/show' do ...@@ -59,14 +59,10 @@ describe 'projects/builds/show' do
end end
it 'shows trigger variables in separate lines' do it 'shows trigger variables in separate lines' do
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_1', 'TRIGGER_VALUE_1')) expect(rendered).to have_css('.js-build-variable', visible: false, text: 'TRIGGER_KEY_1')
expect(rendered).to have_css('code', text: variable_regexp('TRIGGER_KEY_2', 'TRIGGER_VALUE_2')) expect(rendered).to have_css('.js-build-variable', visible: false, text: 'TRIGGER_KEY_2')
expect(rendered).to have_css('.js-build-value', visible: false, text: 'TRIGGER_VALUE_1')
expect(rendered).to have_css('.js-build-value', visible: false, text: 'TRIGGER_VALUE_2')
end end
end end
private
def variable_regexp(key, value)
/\A#{Regexp.escape("#{key}=#{value}")}\Z/
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