Commit 12f07300 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into fix-git-hooks-when-creating-file

* upstream/master: (141 commits)
  Log mv_namespace parameters
  Remove header ids from University docs
  Added test that checks the correct select box is there for the LFS enabled setting.
  Simplify copy on "Create a new list" dropdown in Issue Boards
  Fix `LFS enabled` select box.
  Use Commit#author so we share logic and cache
  Move admin abuse report spinach test to rspec
  fixes non-retina shadow and browser zoom issue
  Use default `closest` if available!
  Adds polyfill for CustomEvent
  Move abuse report spinach test to rspec
  Add support of Chrome/Chromium in requirements.md
  Fixed dragging issues on issue boards
  Grapify the sidekiq metrics API
  Add nested groups support to the routing
  Correctly determine mergeability of MR with no discussions
  API: Add endpoint to delete a group share
  Add a starting date to milestones
  Update ProjectTeam#fetch_members to use project authorizations
  Update ProjectTeam#max_member_access_for_user_ids to use project authorizations
  ...
parents b82f415f 3943e632
{
"env": {
"browser": true,
"es6": true
},
"extends": "airbnb",
"globals": {
"$": false,
"_": false,
"gl": false,
"gon": false,
"jQuery": false
},
"plugins": [
"filenames"
],
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"]
},
"globals": {
"$": false,
"_": false,
"beforeEach": false,
"d3": false,
"define": false,
"describe": false,
"document": false,
"expect": false,
"fixture": false,
"gl": false,
"it": false,
"jQuery": false,
"Mousetrap": false,
"spyOn": false,
"spyOnEvent": false,
"Turbolinks": false,
"window": false,
"Vue": false,
"Flash": false,
"Cookies": false
}
}
......@@ -5,6 +5,7 @@
.chef
.directory
/.envrc
eslint-report.html
/.gitlab_shell_secret
.idea
/.rbenv-version
......
......@@ -20,7 +20,7 @@ before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) $FLAGS'
- retry gem install knapsack
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
......@@ -271,12 +271,17 @@ rake db:seed_fu:
- log/development.log
teaspoon:
cache:
paths:
- vendor/ruby
- node_modules/
stage: test
<<: *use-db
script:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install --global istanbul
- npm install
- npm link istanbul
- rake teaspoon
artifacts:
name: coverage-javascript
......@@ -323,7 +328,7 @@ migration paths:
- git checkout -f FETCH_HEAD
- cp config/resque.yml.example config/resque.yml
- sed -i 's/localhost/redis/g' config/resque.yml
- bundle install --without postgres production --jobs $(nproc) ${FLAGS[@]} --retry=3
- 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
- source scripts/prepare_build.sh
......@@ -344,13 +349,33 @@ coverage:
- coverage/index.html
- coverage/assets/
lint-javascript:
lint:javascript:
cache:
paths:
- node_modules/
stage: test
image: "node:latest"
image: "node:7.1"
before_script:
- npm install
script:
- npm --silent run eslint
lint:javascript:report:
cache:
paths:
- node_modules/
stage: post-test
image: "node:7.1"
before_script:
- npm install
script:
- npm run eslint
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- npm --silent run eslint-report || true # ignore exit code
artifacts:
name: eslint-report
expire_in: 31d
paths:
- eslint-report.html
# Trigger docs build
# https://gitlab.com/gitlab-com/doc-gitlab-com/blob/master/README.md#deployment-process
......@@ -376,7 +401,7 @@ notify:slack:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
- ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure
only:
- master@gitlab-org/gitlab-ce
......@@ -390,11 +415,13 @@ pages:
dependencies:
- coverage
- teaspoon
- lint:javascript:report
script:
- mv public/ .public/
- mkdir public/
- mv coverage public/coverage-ruby
- mv coverage-javascript/default/ public/coverage-javascript/
- mv eslint-report.html public/
artifacts:
paths:
- public
......
......@@ -4,6 +4,131 @@ entry.
## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- API: allow recursive tree request. !6088 (Rebeca Mendez)
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
- Add button to delete all merged branches. !6449 (Toon Claes)
- Finer-grained Git gargage collection. !6588
- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
- Centralize LDAP config/filter logic. !6606
- Make system notes less intrusive. !6755
- Process commits using a dedicated Sidekiq worker. !6802
- Show random messages when the To Do list is empty. !6818 (Josep Llaneras)
- Precalculate user's authorized projects in database. !6839
- Fix record not found error on NewNoteWorker processing. !6863 (Oswaldo Ferreira)
- Show avatars in mention dropdown. !6865
- Fix expanding a collapsed diff when converting a symlink to a regular file. !6953
- Defer saving project services to the database if there are no user changes. !6958
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
- Display "folders" for environments. !7015
- Make it possible to trigger builds from webhooks. !7022 (Dmitry Poray)
- Fix showing pipeline status for a given commit from correct branch. !7034
- Add link to build pipeline within individual build pages. !7082
- Add api endpoint `/groups/owned`. !7103 (Borja Aparicio)
- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
- Fix trace patching feature - update the updated_at value. !7146
- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
- Add api endpoint for creating a pipeline. !7209 (Ido Leibovich)
- Allow users to subscribe to group labels. !7215
- Reduce API calls needed when importing issues and pull requests from GitHub. !7241 (Andrew Smith (EspadaV8))
- Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266
- Ensure merge request's "remove branch" accessors return booleans. !7267
- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
- Expose label IDs in API. !7275 (Rares Sfirlogea)
- Fix invalid filename validation on eslint. !7281
- API: Ability to retrieve version information. !7286 (Robert Schilling)
- Added ability to throttle Sidekiq Jobs. !7292
- Set default Sidekiq retries to 3. !7294
- Fix double event and ajax request call on MR page. !7298 (YarNayar)
- Unify anchor link format for MR diff files. !7298 (YarNayar)
- Require projects before creating milestone. !7301 (gfyoung)
- Fix error when using invalid branch name when creating a new pipeline. !7324
- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
- Auto-close environment when branch is deleted. !7355
- Rework cache invalidation so only changed data is refreshed. !7360
- Navigation bar issuables counters reflects dashboard issuables counters. !7368 (Lucas Deschamps)
- Fix cache for commit status in commits list to respect branches. !7372
- fixes 500 error on project show when user is not logged in and project is still empty. !7376
- Removed gray button styling from todo buttons in sidebars. !7387
- Fix project records with invalid visibility_level values. !7391
- Use 'Forking in progress' title when appropriate. !7394 (Philip Karpiak)
- Fix error links in help index page. !7396 (Fu Xu)
- Add support for reply-by-email when the email only contains HTML. !7397
- [Fix] Extra divider issue in dropdown. !7398
- Project download buttons always show. !7405 (Philip Karpiak)
- Give search-input correct padding-right value. !7407 (Philip Karpiak)
- Remove additional padding on right-aligned items in MR widget. !7411 (Didem Acet)
- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
- Allow mail_room idle_timeout option to be configurable. !7423
- Fix misaligned buttons on admin builds page. !7424 (Didem Acet)
- Disable "Request Access" functionality by default for new projects and groups. !7425
- fix shibboleth misconfigurations resulting in authentication bypass. !7428
- Added Mattermost slash command. !7438
- Allow to connect Chat account with GitLab. !7450
- Make New Group form respect default visibility application setting. !7454 (Jacopo Beschi @jacopo-beschi)
- Fix Error 500 when creating a merge request that contains an image that was deleted and added. !7457
- Fix labels API by adding missing current_user parameter. !7458 (Francesco Coda Zabetta)
- Changed restricted visibility admin buttons to checkboxes. !7463
- Send credentials (currently for registry only) with build data to GitLab Runner. !7474
- Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths. !7480
- Adds es6-promise Polyfill. !7482
- Added colored labels to related MR list. !7486 (Didem Acet)
- Use setter for key instead AR callback. !7488 (Semyon Pupkov)
- Limit labels returned for a specific project as an administrator. !7496
- Change slack notification comment link. !7498 (Herbert Kagumba)
- Allow registering users whose username contains dots. !7500 (Timothy Andrew)
- Fix race condition during group deletion and remove stale records present due to this bug. !7528 (Timothy Andrew)
- Check all namespaces on validation of new username. !7537
- Pass correct tag target to post-receive hook when creating tag via UI. !7556
- Add help message for configuring Mattermost slash commands. !7558
- Fix typo in Build page JavaScript. !7563 (winniehell)
- Make job script a required configuration entry. !7566
- Fix errors happening when source branch of merge request is removed and then restored. !7568
- Fix a wrong "The build for this merge request failed" message. !7579
- Fix Margins look weird in Project page with pinned sidebar in project stats bar. !7580
- Fix regression causing bad error message to appear on Merge Request form. !7599 (Alex Sanford)
- Fix activity page endless scroll on large viewports. !7608
- Fix 404 on some group pages when name contains dot. !7614
- Do not create a new TODO when failed build is allowed to fail. !7618
- Add deployment command to ChatOps. !7619
- Fix 500 error when group name ends with git. !7630
- Fix undefined error in CI linter. !7650
- Show events per stage on Cycle Analytics page. !23449
- Add JIRA remotelinks and prevent duplicated closing messages.
- Fixed issue boards counter border when unauthorized.
- Add placeholder for the example text for custom hex color on label creation popup. (Luis Alonso Chavez Armendariz)
- Add an index for project_id in project_import_data to improve performance.
- Fix broken commits search.
- Assignee dropdown now searches author of issue or merge request.
- Clicking "force remove source branch" label now toggles the checkbox again.
- More aggressively preload on merge request and issue index pages.
- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
- Fixing the issue of the project fork url giving 500 when not signed instead of being redirected to sign in page. (Cagdas Gerede)
- Fix: Guest sees some repository details and gets 404.
- Add logging for rack attack events to production.log.
- Add environment info to builds page.
- Allow commit note to be visible if repo is visible.
- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
- Redesign pipelines page.
- Faster search inside Project.
- Search for a filename in a project.
- Allow sorting groups in the API.
- Fix: Todos Filter Shows All Users.
- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
- Fixed multiple requests sent when opening dropdowns.
- Added permissions per stage to cycle analytics endpoint.
- Fix project Visibility Level selector not using default values.
- Add events per stage to cycle analytics.
- Allow to test JIRA service settings without having a repository.
- Fix JIRA references for project snippets.
- Allow enabling and disabling commit and MR events for JIRA.
- simplify url generation. (Jarka Kadlecova)
- Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing.
......
8.14.0-pre
8.15.0-pre
/* eslint-disable no-param-reassign, class-methods-use-this */
/* global Pager, Cookies */
/* global Pager */
/* global Cookies */
((global) => {
class Activities {
......
......@@ -54,6 +54,9 @@
mouseDown () {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue (e) {
const targetTagName = e.target.tagName.toLowerCase();
......
......@@ -94,12 +94,10 @@
gl.issueBoards.onStart();
},
onAdd: (e) => {
// Add the element back to original list to allow Vue to handle DOM updates
e.from.appendChild(e.item);
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
this.$nextTick(() => {
// Update the issues once we know the element has been moved
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
e.item.remove();
});
},
});
......
......@@ -12,7 +12,7 @@
this.pageUrl = options.pageUrl;
this.buildUrl = options.buildUrl;
this.buildStatus = options.buildStatus;
this.state = options.state1;
this.state = options.logState;
this.buildStage = options.buildStage;
this.updateDropdown = bind(this.updateDropdown, this);
this.$document = $(document);
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* eslint-disable no-param-reassign */
/* global Vue */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
......
/* global Vue */
/* global Cookies */
/* global Flash */
//= require vue
//= require_tree ./svg
//= require_tree .
......
......@@ -145,25 +145,19 @@
class DueDateSelectors {
constructor() {
this.initMilestoneDueDate();
this.initMilestoneDatePicker();
this.initIssuableSelect();
}
initMilestoneDueDate() {
const $datePicker = $('.datepicker');
initMilestoneDatePicker() {
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd'
});
if ($datePicker.length) {
const $dueDate = $('#milestone_due_date');
$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
onSelect: (dateText, inst) => {
$dueDate.val(dateText);
}
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()));
}
$('.js-clear-due-date').on('click', (e) => {
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
e.preventDefault();
$.datepicker._clearDate($datePicker);
const datepicker = $(e.target).siblings('.datepicker');
$.datepicker._clearDate(datepicker);
});
}
......
/* eslint-disable no-param-reassign */
/* global Vue */
/* global EnvironmentsService */
//= require vue
//= require vue-resource
//= require_tree ../services/
//= require ./environment_item
/* globals Vue, EnvironmentsService */
/* eslint-disable no-param-reassign */
(() => { // eslint-disable-line
(() => {
window.gl = window.gl || {};
/**
......@@ -209,12 +210,12 @@
<table class="table ci-table environments">
<thead>
<tr>
<th>Environment</th>
<th>Last deployment</th>
<th>Build</th>
<th>Commit</th>
<th></th>
<th class="hidden-xs"></th>
<th class="environments-name">Environment</th>
<th class="environments-deploy">Last deployment</th>
<th class="environments-build">Build</th>
<th class="environments-commit">Commit</th>
<th class="environments-date"></th>
<th class="hidden-xs environments-actions"></th>
</tr>
</thead>
<tbody>
......
/*= require lib/utils/timeago */
/* global Vue */
/* global timeago */
/*= require timeago */
/*= require lib/utils/text_utility */
/*= require vue_common_component/commit */
/*= require ./environment_actions */
......@@ -6,8 +9,6 @@
/*= require ./environment_stop */
/*= require ./environment_rollback */
/* globals Vue, timeago */
(() => {
/**
* Envrionment Item Component
......
......@@ -3,7 +3,7 @@
Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
Element.prototype.closest = function closest(selector, selectedElement = this) {
Element.prototype.closest = Element.prototype.closest || function closest(selector, selectedElement = this) {
if (!selectedElement) return;
return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement);
};
/**
* CustomEvent support for IE
*/
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function CustomEvent(e, params) {
const options = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(e, options.bubbles, options.cancelable, options.detail);
return evt;
};
window.CustomEvent.prototype = window.Event.prototype;
}
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-undef, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */
/* global timeago */
/* global dateFormat */
/*= require timeago */
/*= require date.format */
(function() {
(function(w) {
var base;
......
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, padded-blocks */
/* global Turbolinks */
(function() {
Turbolinks.enableProgressBar();
......
......@@ -67,7 +67,7 @@
MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages;
allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes'];
return $(document).on('page:change.merge_request', (function(_this) {
$(document).on('page:change.merge_request', (function(_this) {
return function() {
var page;
page = $('body').data('page').split(':').last();
......@@ -245,7 +245,7 @@
case "not_found":
return this.setMergeButtonClass('btn-danger');
case "running":
return this.setMergeButtonClass('btn-warning');
return this.setMergeButtonClass('btn-info');
case "success":
case "success_with_warnings":
return this.setMergeButtonClass('btn-create');
......@@ -263,7 +263,7 @@
};
MergeRequestWidget.prototype.setMergeButtonClass = function(css_class) {
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-warning btn-create').addClass(css_class);
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
};
return MergeRequestWidget;
......
......@@ -113,6 +113,7 @@
$(document).off("click", ".js-note-discard");
$(document).off("keydown", ".js-note-text");
$(document).off('click', '.js-comment-resolve-button');
$(document).off("click", '.system-note-commit-list-toggler');
$('.note .js-task-list-container').taskList('disable');
return $(document).off('tasklist:changed', '.note .js-task-list-container');
};
......
......@@ -35,7 +35,6 @@
}
onSubmitForm(e) {
e.preventDefault();
return this.saveForm();
}
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
/* global Turbolinks */
(function() {
this.TreeView = (function() {
function TreeView() {
......
......@@ -206,6 +206,7 @@
}
});
} else {
this.currentSelectedDate = '';
return $('.user-calendar-activities').html('');
}
};
......
......@@ -299,6 +299,10 @@ table {
.well {
margin-bottom: $gl-padding;
hr {
border-color: $gray-darker;
}
}
.search_box {
......
......@@ -68,6 +68,46 @@ label {
}
}
.help-form .form-group {
margin-left: 0;
margin-right: 0;
.control-label {
font-weight: bold;
padding-top: 4px;
}
.form-control {
height: 29px;
background: $white-light;
font-family: $monospace_font;
}
.input-group-btn .btn {
padding: 3px $gl-btn-padding;
background-color: $gray-light;
border: 1px solid $border-color;
}
.text-block {
line-height: 0.8;
padding-top: 9px;
code {
line-height: 1.8;
}
}
@media(max-width: $screen-sm-min) {
padding: 0 $gl-padding;
.control-label,
.text-block {
padding-left: 0;
}
}
}
.fieldset-form fieldset {
margin-bottom: 20px;
}
......@@ -167,4 +207,3 @@ label {
color: $gl-text-color;
}
}
......@@ -39,4 +39,8 @@
&.status-box-expired {
background: #cea61b;
}
&.status-box-upcoming {
background: #8f8f8f;
}
}
......@@ -243,7 +243,7 @@
}
.issue-boards-search {
width: 335px;
width: 290px;
.form-control {
display: inline-block;
......
......@@ -18,6 +18,31 @@
.environments {
table-layout: fixed;
.environments-commit,
.environments-actions,
.environments-deploy,
.environments-build,
.environments-date {
position: static;
float: none;
display: table-cell;
}
.environments-commit,
.environments-actions {
width: 20%;
}
.environments-deploy,
.environments-build,
.environments-date {
width: 10%;
}
.environments-name {
width: 30%;
}
.deployment-column {
.avatar {
float: none;
......
// CI icon colors
.ci-status-icon-success {
color: $gl-success;
.ci-status-icon {
&-created {
fill: $gray-darkest;
svg {
fill: $gl-success;
}
}
.ci-status-icon-failed {
color: $gl-danger;
svg {
fill: $gl-danger;
}
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warnings {
color: $gl-warning;
svg {
fill: $gl-warning;
}
}
.ci-status-icon-running {
color: $blue-normal;
svg {
fill: $blue-normal;
}
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found {
color: $gl-gray;
svg {
fill: $gl-gray;
}
}
&-skipped,
&-canceled {
fill: $gl-text-color;
.ci-status-icon-created,
.ci-status-icon-skipped {
color: $gray-darkest;
svg {
fill: $gray-darkest;
}
}
......@@ -24,7 +24,7 @@
.accept_merge_request {
&.ci-pending,
&.ci-running {
@include btn-orange;
@include btn-blue;
}
&.ci-skipped,
......@@ -62,6 +62,7 @@
.ci_widget {
border-bottom: 1px solid $well-inner-border;
color: $gl-gray;
svg {
margin-right: 4px;
......@@ -70,48 +71,12 @@
overflow: visible;
}
&.ci-success {
color: $gl-success;
a.environment,
a.pipeline {
color: inherit;
}
}
&.ci-success_with_warnings {
color: $gl-success;
i {
color: $gl-warning;
}
}
&.ci-skipped {
background-color: #eee;
color: #888;
}
&.ci-pending {
color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
}
&.ci-failed,
&.ci-error {
color: $gl-danger;
}
&.ci-canceled {
color: $gl-gray;
}
a.monospace {
color: inherit;
}
}
.mr-widget-body,
......
......@@ -43,12 +43,25 @@ ul.notes {
}
.system-note-message {
text-transform: lowercase;
display: inline-block;
&::first-letter {
text-transform: lowercase;
}
a {
color: $gl-link-color;
text-decoration: none;
}
p {
display: inline-block;
margin: 0;
&::first-letter {
text-transform: lowercase;
}
}
}
.timeline-content {
......@@ -62,6 +75,13 @@ ul.notes {
display: none;
padding: 10px 0 0;
cursor: pointer;
position: relative;
z-index: 2;
&:hover {
color: $gl-link-color;
text-decoration: underline;
}
}
.note-text {
......@@ -87,14 +107,24 @@ ul.notes {
display: none;
}
p:last-child {
a {
color: $gl-text-color;
&:hover {
color: $gl-link-color;
}
}
}
&::after {
content: '';
width: 100%;
height: 20px;
height: 67px;
position: absolute;
left: 0;
bottom: 50px;
background: linear-gradient(rgba($gray-light, .3) 0, $white-light);
bottom: 0;
background: linear-gradient(rgba($gray-light, 0.1) -100px, $white-light 100%);
}
&.hide-shade {
......@@ -188,11 +218,6 @@ ul.notes {
padding-bottom: 3px;
padding-right: 20px;
p {
display: inline;
margin: 0;
}
@media (min-width: $screen-sm-min) {
padding-right: 0;
}
......
......@@ -91,14 +91,6 @@
}
}
.ci-status {
svg {
top: 1px;
margin-right: 0;
}
}
a:hover {
text-decoration: none;
}
......@@ -195,7 +187,7 @@
width: 8px;
position: absolute;
right: -7px;
bottom: 8px;
bottom: 9px;
border-bottom: 2px solid $border-color;
}
}
......@@ -691,10 +683,3 @@
}
}
}
.ci-status-icon-created {
svg {
fill: $gray-darkest;
}
}
......@@ -20,3 +20,7 @@
.danger-title {
color: $gl-danger;
}
.service-settings .control-label {
padding-top: 0;
}
......@@ -11,80 +11,108 @@
text-decoration: none;
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 1px;
margin-right: 3px;
overflow: visible;
}
&.ci-failed {
color: $gl-danger;
border-color: $gl-danger;
&:not(span):hover {
background-color: rgba( $gl-danger, .07);
}
svg {
fill: $gl-danger;
}
}
&.ci-success,
&.ci-success_with_warnings {
color: $gl-success;
border-color: $gl-success;
&:not(span):hover {
background-color: rgba( $gl-success, .07);
}
svg {
fill: $gl-success;
}
}
&.ci-info {
color: $gl-info;
border-color: $gl-info;
&:not(span):hover {
background-color: rgba( $gl-info, .07);
}
svg {
fill: $gl-info;
}
}
&.ci-canceled,
&.ci-skipped,
&.ci-disabled {
color: $gl-gray;
border-color: $gl-gray;
&:not(span):hover {
background-color: rgba( $gl-gray, .07);
}
svg {
fill: $gl-gray;
}
}
&.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
&:not(span):hover {
background-color: rgba( $gl-warning, .07);
}
svg {
fill: $gl-warning;
}
}
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
&:not(span):hover {
background-color: rgba( $blue-normal, .07);
}
svg {
fill: $blue-normal;
}
}
&.ci-created {
&.ci-created,
&.ci-skipped {
color: $table-text-gray;
border-color: $table-text-gray;
&:not(span):hover {
background-color: rgba( $table-text-gray, .07);
}
svg {
fill: $table-text-gray;
}
}
svg {
height: 13px;
width: 13px;
position: relative;
top: 1px;
margin: 0 3px;
overflow: visible;
}
}
.ci-status-icon-success {
color: $gl-success;
}
.ci-status-icon-failed {
color: $gl-danger;
}
.ci-status-icon-pending,
.ci-status-icon-success_with_warning {
color: $gl-warning;
}
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
.ci-status-icon-skipped {
color: $gl-gray;
}
}
......
......@@ -49,6 +49,14 @@ class ApplicationController < ActionController::Base
render_404
end
def route_not_found
if current_user
not_found
else
redirect_to new_user_session_path
end
end
protected
# This filter handles both private tokens and personal access tokens
......@@ -224,7 +232,7 @@ class ApplicationController < ActionController::Base
end
def require_email
if current_user && current_user.temp_oauth_email?
if current_user && current_user.temp_oauth_email? && session[:impersonator_id].nil?
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end
end
......
......@@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
def milestone_path(title)
......
......@@ -563,11 +563,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars
@pipelines = @merge_request.all_pipelines
if @pipelines.present? && @merge_request.commits.present?
@pipeline = @pipelines.first
@statuses = @pipeline.statuses.relevant
end
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses.relevant if @pipeline.present?
end
def define_new_vars
......
......@@ -112,6 +112,6 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
end
......@@ -17,7 +17,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
else
render 'index'
render 'show'
end
end
......
......@@ -67,14 +67,14 @@ module ApplicationSettingsHelper
def import_sources_checkboxes(help_block_id)
Gitlab::ImportSources.options.map do |name, source|
checked = current_application_settings.import_sources.include?(source)
css_class = 'btn'
css_class += ' active' if checked
css_class = checked ? 'active' : ''
checkbox_name = 'application_setting[import_sources][]'
label_tag(checkbox_name, class: css_class) do
label_tag(name, class: css_class) do
check_box_tag(checkbox_name, source, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id) + name
'aria-describedby' => help_block_id,
id: name.tr(' ', '_')) + name
end
end
end
......
......@@ -12,7 +12,7 @@ module BuildsHelper
build_url: namespace_project_build_url(@project.namespace, @project, @build, :json),
build_status: @build.status,
build_stage: @build.stage,
state1: @build.trace_with_state[:state]
log_state: @build.trace_with_state[:state].to_s
}
end
end
......@@ -5,7 +5,7 @@ module CiStatusHelper
end
def ci_status_with_icon(status, target = nil)
content = ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
content = ci_icon_for_status(status) + ci_label_for_status(status)
klass = "ci-status ci-#{status}"
if target
link_to content, target, class: klass
......
......@@ -64,6 +64,8 @@ module IssuesHelper
'status-box-merged'
elsif item.closed?
'status-box-closed'
elsif item.respond_to?(:upcoming?) && item.upcoming?
'status-box-upcoming'
else
'status-box-open'
end
......
......@@ -86,6 +86,30 @@ module MilestonesHelper
days = milestone.remaining_days
content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} remaining"
elsif milestone.upcoming?
content_tag(:strong, 'Upcoming')
elsif milestone.start_date && milestone.start_date.past?
days = milestone.elapsed_days
content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} elapsed"
end
end
def milestone_date_range(milestone)
if milestone.start_date && milestone.due_date
"#{milestone.start_date.to_s(:medium)} - #{milestone.due_date.to_s(:medium)}"
elsif milestone.due_date
if milestone.due_date.past?
"expired on #{milestone.due_date.to_s(:medium)}"
else
"expires on #{milestone.due_date.to_s(:medium)}"
end
elsif milestone.start_date
if milestone.start_date.past?
"started on #{milestone.start_date.to_s(:medium)}"
else
"starts on #{milestone.start_date.to_s(:medium)}"
end
end
end
end
......@@ -5,15 +5,11 @@ module SidekiqHelper
(?<mem>[\d\.,]+)\s+
(?<state>[DRSTWXZNLsl\+<]+)\s+
(?<start>.+)\s+
(?<command>sidekiq.*\])\s*
(?<command>sidekiq.*\])
\z/x
def parse_sidekiq_ps(line)
match = line.match(SIDEKIQ_PS_REGEXP)
if match
match[1..6]
else
%w[? ? ? ? ? ?]
end
match = line.strip.match(SIDEKIQ_PS_REGEXP)
match ? match[1..6] : Array.new(6, '?')
end
end
......@@ -135,15 +135,19 @@ class CommitStatus < ActiveRecord::Base
allow_failure? && (failed? || canceled?)
end
def duration
calculate_duration
end
def playable?
false
end
def duration
calculate_duration
def stuck?
false
end
def stuck?
def has_trace?
false
end
end
......@@ -23,7 +23,31 @@ module Milestoneish
(due_date - Date.today).to_i
end
def elapsed_days
return 0 if !start_date || start_date.future?
(Date.today - start_date).to_i
end
def issues_visible_to_user(user = nil)
issues.visible_to_user(user)
end
def upcoming?
start_date && start_date.future?
end
def expires_at
if due_date
if due_date.past?
"expired on #{due_date.to_s(:medium)}"
else
"expires on #{due_date.to_s(:medium)}"
end
end
end
def expired?
due_date && due_date.past?
end
end
......@@ -28,26 +28,16 @@ class GlobalMilestone
@title.to_slug.normalize.to_s
end
def expired?
if due_date
due_date.past?
else
false
end
end
def projects
@projects ||= Project.for_milestones(milestones.select(:id))
end
def state
state = milestones.map { |milestone| milestone.state }
if state.count('closed') == state.size
'closed'
else
'active'
milestones.each do |milestone|
return 'active' if milestone.state != 'closed'
end
'closed'
end
def active?
......@@ -81,18 +71,15 @@ class GlobalMilestone
@due_date =
if @milestones.all? { |x| x.due_date == @milestones.first.due_date }
@milestones.first.due_date
else
nil
end
end
def expires_at
if due_date
if due_date.past?
"expired on #{due_date.to_s(:medium)}"
else
"expires on #{due_date.to_s(:medium)}"
def start_date
return @start_date if defined?(@start_date)
@start_date =
if @milestones.all? { |x| x.start_date == @milestones.first.start_date }
@milestones.first.start_date
end
end
end
end
......@@ -65,7 +65,9 @@ class Group < Namespace
def select_for_project_authorization
if current_scope.joins_values.include?(:shared_projects)
select("members.user_id, projects.id AS project_id, project_group_links.group_access")
joins('INNER JOIN namespaces project_namespace ON project_namespace.id = projects.namespace_id')
.where('project_namespace.share_with_group_lock = ?', false)
.select("members.user_id, projects.id AS project_id, LEAST(project_group_links.group_access, members.access_level) AS access_level")
else
super
end
......
......@@ -93,7 +93,7 @@ class Issue < ActiveRecord::Base
# Check if we are scoped to a specific project's issues
if owner_project
if owner_project.authorized_for_user?(user, Gitlab::Access::REPORTER)
if owner_project.team.member?(user, Gitlab::Access::REPORTER)
# If the project is authorized for the user, they can see all issues in the project
return all
else
......
......@@ -246,7 +246,7 @@ class Member < ActiveRecord::Base
end
def post_update_hook
# override in subclass
UserProjectAccessChangedService.new(user.id).execute if access_level_changed?
end
def post_destroy_hook
......
......@@ -494,10 +494,14 @@ class MergeRequest < ActiveRecord::Base
discussions_resolvable? && diff_discussions.none?(&:to_be_resolved?)
end
def discussions_to_be_resolved?
discussions_resolvable? && !discussions_resolved?
end
def mergeable_discussions_state?
return true unless project.only_allow_merge_if_all_discussions_are_resolved?
discussions_resolved?
!discussions_to_be_resolved?
end
def hook_attrs
......
......@@ -29,6 +29,7 @@ class Milestone < ActiveRecord::Base
validates :title, presence: true, uniqueness: { scope: :project_id }
validates :project, presence: true
validate :start_date_should_be_less_than_due_date, if: Proc.new { |m| m.start_date.present? && m.due_date.present? }
strip_attributes :title
......@@ -131,24 +132,6 @@ class Milestone < ActiveRecord::Base
self.title
end
def expired?
if due_date
due_date.past?
else
false
end
end
def expires_at
if due_date
if due_date.past?
"expired on #{due_date.to_s(:medium)}"
else
"expires on #{due_date.to_s(:medium)}"
end
end
end
def can_be_closed?
active? && issues.opened.count.zero?
end
......@@ -212,4 +195,10 @@ class Milestone < ActiveRecord::Base
def sanitize_title(value)
CGI.unescape_html(Sanitize.clean(value.to_s))
end
def start_date_should_be_less_than_due_date
if due_date <= start_date
errors.add(:start_date, "Can't be greater than due date")
end
end
end
......@@ -27,6 +27,7 @@ class Namespace < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
after_update :move_dir, if: :path_changed?
after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
# Save the storage paths before the projects are destroyed to use them on after destroy
before_destroy(prepend: true) { @old_repository_storage_paths = repository_storage_paths }
......@@ -103,6 +104,8 @@ class Namespace < ActiveRecord::Base
gitlab_shell.add_namespace(repository_storage_path, path_was)
unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
......@@ -175,4 +178,11 @@ class Namespace < ActiveRecord::Base
end
end
end
def refresh_access_of_projects_invited_groups
Group.
joins(project_group_links: :project).
where(projects: { namespace_id: id }).
find_each(&:refresh_members_authorized_projects)
end
end
......@@ -126,6 +126,8 @@ class Project < ActiveRecord::Base
has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
has_many :project_authorizations, dependent: :destroy
has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User'
has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source
alias_method :members, :project_members
has_many :users, through: :project_members
......@@ -163,6 +165,7 @@ class Project < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
delegate :add_user, to: :team
delegate :add_guest, :add_reporter, :add_developer, :add_master, to: :team
# Validations
validates :creator, presence: true, on: :create
......@@ -174,6 +177,7 @@ class Project < ActiveRecord::Base
message: Gitlab::Regex.project_name_regex_message }
validates :path,
presence: true,
project_path: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.project_path_regex,
message: Gitlab::Regex.project_path_regex_message }
......@@ -1292,14 +1296,6 @@ class Project < ActiveRecord::Base
end
end
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
def authorized_for_user?(user, min_access_level = nil)
return false unless user
user.authorized_project?(self, min_access_level)
end
def append_or_update_attribute(name, value)
old_values = public_send(name.to_s)
......
......@@ -57,9 +57,9 @@ class JiraService < IssueTrackerService
end
def help
'See the ' \
'[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\
'for details.'
'You need to configure JIRA before enabling this service. For more details
read the
[JIRA service documentation](https://docs.gitlab.com/ce/project_services/jira.html).'
end
def title
......
......@@ -19,13 +19,6 @@ class MattermostSlashCommandsService < ChatService
'mattermost_slash_commands'
end
def help
"This service allows you to use slash commands with your Mattermost installation.<br/>
To setup this Service you need to create a new <b>Slash commands</b> in your Mattermost integration panel.<br/>
<br/>
Create integration with URL #{service_trigger_url(self)} and enter the token below."
end
def fields
[
{ type: 'text', name: 'token', placeholder: '' }
......
......@@ -21,6 +21,22 @@ class ProjectTeam
end
end
def add_guest(user, current_user: nil)
self << [user, :guest, current_user]
end
def add_reporter(user, current_user: nil)
self << [user, :reporter, current_user]
end
def add_developer(user, current_user: nil)
self << [user, :developer, current_user]
end
def add_master(user, current_user: nil)
self << [user, :master, current_user]
end
def find_member(user_id)
member = project.members.find_by(user_id: user_id)
......@@ -64,19 +80,19 @@ class ProjectTeam
alias_method :users, :members
def guests
@guests ||= fetch_members(:guests)
@guests ||= fetch_members(Gitlab::Access::GUEST)
end
def reporters
@reporters ||= fetch_members(:reporters)
@reporters ||= fetch_members(Gitlab::Access::REPORTER)
end
def developers
@developers ||= fetch_members(:developers)
@developers ||= fetch_members(Gitlab::Access::DEVELOPER)
end
def masters
@masters ||= fetch_members(:masters)
@masters ||= fetch_members(Gitlab::Access::MASTER)
end
def import(source_project, current_user = nil)
......@@ -125,8 +141,12 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER
end
def member?(user, min_member_access = Gitlab::Access::GUEST)
max_member_access(user.id) >= min_member_access
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
def member?(user, min_access_level = Gitlab::Access::GUEST)
return false unless user
user.authorized_project?(project, min_access_level)
end
def human_max_access(user_id)
......@@ -149,112 +169,29 @@ class ProjectTeam
# Lookup only the IDs we need
user_ids = user_ids - access.keys
users_access = project.project_authorizations.
where(user: user_ids).
group(:user_id).
maximum(:access_level)
if user_ids.present?
user_ids.each { |id| access[id] = Gitlab::Access::NO_ACCESS }
member_access = project.members.access_for_user_ids(user_ids)
merge_max!(access, member_access)
if group
group_access = group.members.access_for_user_ids(user_ids)
merge_max!(access, group_access)
end
# Each group produces a list of maximum access level per user. We take the
# max of the values produced by each group.
if project_shared_with_group?
project.project_group_links.each do |group_link|
invited_access = max_invited_level_for_users(group_link, user_ids)
merge_max!(access, invited_access)
end
end
end
access.merge!(users_access)
access
end
def max_member_access(user_id)
max_member_access_for_user_ids([user_id])[user_id]
max_member_access_for_user_ids([user_id])[user_id] || Gitlab::Access::NO_ACCESS
end
private
# For a given group, return the maximum access level for the user. This is the min of
# the invited access level of the group and the access level of the user within the group.
# For example, if the group has been given DEVELOPER access but the member has MASTER access,
# the user should receive only DEVELOPER access.
def max_invited_level_for_users(group_link, user_ids)
invited_group = group_link.group
capped_access_level = group_link.group_access
access = invited_group.group_members.access_for_user_ids(user_ids)
# If the user is not in the list, assume he/she does not have access
missing_users = user_ids - access.keys
missing_users.each { |id| access[id] = Gitlab::Access::NO_ACCESS }
# Cap the maximum access by the invited level access
access.each { |key, value| access[key] = [value, capped_access_level].min }
end
def fetch_members(level = nil)
project_members = project.members
group_members = group ? group.members : []
if level
project_members = project_members.public_send(level)
group_members = group_members.public_send(level) if group
end
user_ids = project_members.pluck(:user_id)
invited_members = fetch_invited_members(level)
user_ids.push(*invited_members.map(&:user_id)) if invited_members.any?
members = project.authorized_users
members = members.where(project_authorizations: { access_level: level }) if level
user_ids.push(*group_members.pluck(:user_id)) if group
User.where(id: user_ids)
members
end
def group
project.group
end
def merge_max!(first_hash, second_hash)
first_hash.merge!(second_hash) { |_key, old, new| old > new ? old : new }
end
def project_shared_with_group?
project.invited_groups.any? && project.allowed_to_share_with_group?
end
def fetch_invited_members(level = nil)
invited_members = []
return invited_members unless project_shared_with_group?
project.project_group_links.includes(group: [:group_members]).each do |link|
invited_group_members = link.group.members
if level
numeric_level = GroupMember.access_level_roles[level.to_s.singularize.titleize]
# If we're asked for a level that's higher than the group's access,
# there's nothing left to do
next if numeric_level > link.group_access
# Make sure we include everyone _above_ the requested level as well
invited_group_members =
if numeric_level == link.group_access
invited_group_members.where("access_level >= ?", link.group_access)
else
invited_group_members.public_send(level)
end
end
invited_members << invited_group_members
end
invited_members.flatten.compact
end
end
......@@ -291,8 +291,12 @@ class User < ActiveRecord::Base
end
end
def find_by_username(username)
iwhere(username: username).take
end
def find_by_username!(username)
find_by!('lower(username) = ?', username.downcase)
iwhere(username: username).take!
end
def find_by_personal_access_token(token_string)
......@@ -926,7 +930,7 @@ class User < ActiveRecord::Base
# Returns a union query of projects that the user is authorized to access
def project_authorizations_union
relations = [
personal_projects.select("#{id} AS user_id, projects.id AS project_id, #{Gitlab::Access::OWNER} AS access_level"),
personal_projects.select("#{id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"),
groups_projects.select_for_project_authorization,
projects.select_for_project_authorization,
groups.joins(:shared_projects).select_for_project_authorization
......
......@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end
expose :duration, as: :total_time do |build|
distance_of_time_as_hash(build[:duration].to_f)
distance_of_time_as_hash(build.duration.to_f)
end
expose :branch do
......
......@@ -2,7 +2,7 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper
def interval_in_words(diff)
"#{distance_of_time_in_words(diff.to_f)} ago"
"#{distance_of_time_in_words(Time.now, diff)} ago"
end
# Converts seconds into a hash such as:
......
require_relative 'base_service'
##
# Branch can be deleted either by DeleteBranchService
# or by GitPushService.
......
require_relative 'base_service'
class CreateBranchService < BaseService
def execute(branch_name, ref)
failure = validate_new_branch(branch_name)
......
require_relative 'base_service'
class CreateDeploymentService < BaseService
def execute(deployable = nil)
return unless executable?
......
require_relative 'base_service'
class CreateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
......
require_relative 'base_service'
class CreateTagService < BaseService
def execute(tag_name, target, message, release_description = nil)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
......
require_relative 'base_service'
class DeleteBranchService < BaseService
def execute(branch_name)
repository = project.repository
......
require_relative 'base_service'
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
......
require_relative 'base_service'
class DeleteTagService < BaseService
def execute(tag_name)
repository = project.repository
......
require_relative "base_service"
module Files
class CreateDirService < Files::BaseService
def commit
......
require_relative "base_service"
module Files
class CreateService < Files::BaseService
def commit
......
require_relative "base_service"
module Files
class DeleteService < Files::BaseService
def commit
......
require_relative "base_service"
module Files
class MultiService < Files::BaseService
class FileChangedError < StandardError; end
......
require_relative "base_service"
module Files
class UpdateService < Files::BaseService
class FileChangedError < StandardError; end
......
require_relative 'base_service'
require_relative 'reopen_service'
require_relative 'close_service'
module MergeRequests
class UpdateService < MergeRequests::BaseService
def execute(merge_request)
......
require_relative 'base_service'
class UpdateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
......
......@@ -35,8 +35,22 @@ class NamespaceValidator < ActiveModel::EachValidator
users
].freeze
def self.valid?(value)
!reserved?(value) && follow_format?(value)
end
def self.reserved?(value)
RESERVED.include?(value)
end
def self.follow_format?(value)
value =~ Gitlab::Regex.namespace_regex
end
delegate :reserved?, :follow_format?, to: :class
def validate_each(record, attribute, value)
unless value =~ Gitlab::Regex.namespace_regex
unless follow_format?(value)
record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
end
......@@ -44,10 +58,4 @@ class NamespaceValidator < ActiveModel::EachValidator
record.errors.add(attribute, "#{value} is a reserved name")
end
end
private
def reserved?(value)
RESERVED.include?(value)
end
end
# ProjectPathValidator
#
# Custom validator for GitLab project path values.
#
# Values are checked for formatting and exclusion from a list of reserved path
# names.
class ProjectPathValidator < ActiveModel::EachValidator
# All project routes with wildcard argument must be listed here.
# Otherwise it can lead to routing issues when route considered as project name.
#
# Example:
# /group/project/tree/deploy_keys
#
# without tree as reserved name routing can match 'group/project' as group name,
# 'tree' as project name and 'deploy_keys' as route.
#
RESERVED = (NamespaceValidator::RESERVED +
%w[tree commits wikis new edit create update logs_tree
preview blob blame raw files create_dir find_file]).freeze
def self.valid?(value)
!reserved?(value)
end
def self.reserved?(value)
RESERVED.include?(value)
end
delegate :reserved?, to: :class
def validate_each(record, attribute, value)
if reserved?(value)
record.errors.add(attribute, "#{value} is a reserved name")
end
end
end
......@@ -31,10 +31,8 @@
.form-group
= f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10
- data_attrs = { toggle: 'buttons' }
.btn-group{ data: data_attrs }
- import_sources_checkboxes('import-sources-help').each do |source|
= source
- import_sources_checkboxes('import-sources-help').each do |source|
.checkbox= source
%span.help-block#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
= link_to "(?)", help_page_path("integration/github")
......
......@@ -4,7 +4,8 @@
%p #{@service.description} template
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'form-horizontal fieldset-form' } do |form|
= render 'shared/service_settings', form: form
= render 'shared/service_settings', form: form, subject: @service
.form-actions
= form.submit 'Save', class: 'btn btn-save'
.footer-block.row-content-block
.form-actions
= form.submit 'Save', class: 'btn btn-save'
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
- awards_sort(grouped_emojis).each do |emoji, awards|
%button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)), data: { placement: "bottom", title: award_user_list(awards, current_user) } }
%button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button",
disabled: !current_user,
class: (award_active_class(awards, current_user)),
data: { placement: "bottom", title: award_user_list(awards, current_user) } }
= emoji_icon(emoji, sprite: false)
%span.award-control-text.js-counter
= awards.count
......
......@@ -36,19 +36,8 @@
= f.collection_select :project_ids, @group.projects.non_archived, :id, :name,
{ selected: @group.projects.non_archived.pluck(:id) }, required: true, multiple: true, class: 'select2'
.col-md-6
.form-group
= f.label :due_date, "Due Date", class: "control-label"
.col-sm-10
= f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
= render "shared/milestones/form_dates", f: f
.form-actions
= f.submit 'Create Milestone', class: "btn-create btn"
= link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
:javascript
$(".datepicker").datepicker({
dateFormat: "yy-mm-dd",
onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
}).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
%a{href: pipeline_build_url(pipeline, build), style: "color:#3777b0;text-decoration:none;"}
= build.name
Build #<%= build.id %> ( <%= pipeline_build_url(pipeline, build) %> )
......@@ -158,12 +158,14 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;"}
= build.stage
%td{align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"}
%a{href: pipeline_build_url(@pipeline, build), style: "color:#3777b0;text-decoration:none;"}
= build.name
= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build
%tr.build-log
%td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"}
%pre{style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;"}
= build.trace_html(last_lines: 10).html_safe
- if build.has_trace?
%td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"}
%pre{style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;"}
= build.trace_html(last_lines: 10).html_safe
- else
%td{colspan: "2"}
%tr.footer
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
%img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/
......
......@@ -19,10 +19,12 @@ Commit Author: <%= commit.author_name %>
Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) had <%= failed.size %> failed <%= 'build'.pluralize(failed.size) %>.
<% failed.each do |build| -%>
Build #<%= build.id %> ( <%= pipeline_build_url(@pipeline, build) %> )
<%= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %>
Stage: <%= build.stage %>
Name: <%= build.name %>
<% if build.has_trace? -%>
Trace: <%= build.trace_with_state(last_lines: 10)[:text] %>
<% end -%>
<% end -%>
......
......@@ -35,7 +35,7 @@
":issue" => "issue",
":issue-link-base" => "issueLinkBase",
":disabled" => "disabled",
"key" => "id" }
":key" => "issue.id" }
%li.board-list-count.text-center{ "v-if" => "showCount" }
= icon("spinner spin", "v-show" => "list.loadingMore" )
%span{ "v-if" => "list.issues.length === list.issuesSize" }
......
%li.card{ ":class" => '{ "user-can-drag": !disabled && issue.id, "is-disabled": disabled || !issue.id, "is-active": issueDetailVisible }',
":index" => "index",
"@mousedown" => "mouseDown",
"@mousemove" => "mouseMove",
"@mouseup" => "showIssue($event)" }
%h4.card-title
= icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential")
......
......@@ -128,7 +128,8 @@
.build-job{class: sidebar_build_class(build, @build), data: {stage: build.stage}}
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= icon('arrow-right')
= ci_icon_for_status(build.status)
%span{class: "ci-status-icon-#{build.status}"}
= ci_icon_for_status(build.status)
%span
- if build.name
= build.name
......
......@@ -5,9 +5,9 @@
.ci-status-text= subject.name
- elsif can?(current_user, :read_build, @project)
= link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do
%span.ci-status-icon
%span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
= ci_icon_for_status(subject.status)
.ci-status-text= subject.name
- else
%span.ci-status-icon
%span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
= ci_icon_for_status(subject.status)
......@@ -4,8 +4,9 @@
%tr.commit
%td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
= ci_status_with_icon(status)
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status)
= ci_label_for_status(status)
%td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment