Commit 727b6d16 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into 'bvl-nfs-circuitbreaker'

# Conflicts:
#   app/models/repository.rb
#   spec/models/repository_spec.rb
parents 4b34720c 440dc934
...@@ -66,16 +66,18 @@ stages: ...@@ -66,16 +66,18 @@ stages:
- mysql:latest - mysql:latest
- redis:alpine - redis:alpine
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql .only-if-want-mysql: &only-if-want-mysql
only: only:
- /mysql/ - /mysql/
- /-stable/ - /-stable/
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
- tags@gitlab-org/gitlab-ce - tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
- tags@gitlab/gitlabhq - tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee - tags@gitlab/gitlab-ee
- //@gitlab/gitlab-ee
# Skip all jobs except the ones that begin with 'docs/'. # Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes. # Used for commits including ONLY documentation changes.
...@@ -114,7 +116,7 @@ stages: ...@@ -114,7 +116,7 @@ stages:
.rspec-knapsack-mysql: &rspec-knapsack-mysql .rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
...@@ -146,7 +148,7 @@ stages: ...@@ -146,7 +148,7 @@ stages:
.spinach-knapsack-mysql: &spinach-knapsack-mysql .spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.only-canonical-masters: &only-canonical-masters .only-canonical-masters: &only-canonical-masters
......
...@@ -35,9 +35,18 @@ linters: ...@@ -35,9 +35,18 @@ linters:
HtmlAttributes: HtmlAttributes:
enabled: true enabled: true
IdNames:
enabled: false
ImplicitDiv: ImplicitDiv:
enabled: true enabled: true
InlineStyles:
enabled: false
InstanceVariables:
enabled: false
LeadingCommentSpace: LeadingCommentSpace:
enabled: false enabled: false
...@@ -54,6 +63,9 @@ linters: ...@@ -54,6 +63,9 @@ linters:
ObjectReferenceAttributes: ObjectReferenceAttributes:
enabled: true enabled: true
RepeatedId:
enabled: false
RuboCop: RuboCop:
enabled: false enabled: false
# These cops are incredibly noisy when it comes to HAML templates, so we # These cops are incredibly noisy when it comes to HAML templates, so we
...@@ -101,3 +113,6 @@ linters: ...@@ -101,3 +113,6 @@ linters:
UnnecessaryStringOutput: UnnecessaryStringOutput:
enabled: true enabled: true
ViewLength:
enabled: false
...@@ -1708,8 +1708,6 @@ entry. ...@@ -1708,8 +1708,6 @@ entry.
## 8.16.7 (2017-02-27) ## 8.16.7 (2017-02-27)
- No changes.
- No changes.
- Fix MR changes tab size count when there are over 100 files in the diff. - Fix MR changes tab size count when there are over 100 files in the diff.
## 8.16.6 (2017-02-17) ## 8.16.6 (2017-02-17)
...@@ -1923,6 +1921,14 @@ entry. ...@@ -1923,6 +1921,14 @@ entry.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support. - Patch XSS vulnerability in RDOC support.
## 8.15.5 (2017-01-20)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.15.4 (2017-01-09) ## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176 - Make successful pipeline emails off for watchers. !8176
...@@ -2205,6 +2211,14 @@ entry. ...@@ -2205,6 +2211,14 @@ entry.
- Speed up group milestone index by passing group_id to IssuesFinder. !8363 - Speed up group milestone index by passing group_id to IssuesFinder. !8363
- Ensure issuable state changes only fire webhooks once. - Ensure issuable state changes only fire webhooks once.
## 8.14.7 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.14.6 (2017-01-10) ## 8.14.6 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509 - Update the gitlab-markup gem to the version 1.5.1. !8509
...@@ -2487,6 +2501,14 @@ entry. ...@@ -2487,6 +2501,14 @@ entry.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page - Fix 404 when visit /projects page
## 8.13.12 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.13.11 (2017-01-10) ## 8.13.11 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509 - Update the gitlab-markup gem to the version 1.5.1. !8509
......
...@@ -342,7 +342,7 @@ group :development, :test do ...@@ -342,7 +342,7 @@ group :development, :test do
gem 'rubocop', '~> 0.49.1', require: false gem 'rubocop', '~> 0.49.1', require: false
gem 'rubocop-rspec', '~> 1.15.1', require: false gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'scss_lint', '~> 0.54.0', require: false gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false gem 'haml_lint', '~> 0.26.0', require: false
gem 'simplecov', '~> 0.14.0', require: false gem 'simplecov', '~> 0.14.0', require: false
gem 'flay', '~> 2.8.0', require: false gem 'flay', '~> 2.8.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
...@@ -391,7 +391,7 @@ gem 'vmstat', '~> 2.3.0' ...@@ -391,7 +391,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.23.0' gem 'gitaly', '~> 0.24.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -269,7 +269,7 @@ GEM ...@@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.23.0) gitaly (0.24.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -356,10 +356,11 @@ GEM ...@@ -356,10 +356,11 @@ GEM
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
haml (4.0.7) haml (4.0.7)
tilt tilt
haml_lint (0.21.0) haml_lint (0.26.0)
haml (~> 4.0) haml (>= 4.0, < 5.1)
rainbow
rake (>= 10, < 13) rake (>= 10, < 13)
rubocop (>= 0.47.0) rubocop (>= 0.49.0)
sysexits (~> 1.1) sysexits (~> 1.1)
hamlit (2.6.1) hamlit (2.6.1)
temple (~> 0.7.6) temple (~> 0.7.6)
...@@ -974,7 +975,7 @@ DEPENDENCIES ...@@ -974,7 +975,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.23.0) gitaly (~> 0.24.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -987,7 +988,7 @@ DEPENDENCIES ...@@ -987,7 +988,7 @@ DEPENDENCIES
grape (~> 0.19.2) grape (~> 0.19.2)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.0.0) grape-route-helpers (~> 2.0.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.26.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
hashie-forbidden_attributes hashie-forbidden_attributes
health_check (~> 2.6.0) health_check (~> 2.6.0)
......
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
import queryData from '../../utils/query_data'; import queryData from '~/boards/utils/query_data';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header'; import './header';
import './list'; import './list';
import './footer'; import './footer';
......
...@@ -348,6 +348,8 @@ import GpgBadges from './gpg_badges'; ...@@ -348,6 +348,8 @@ import GpgBadges from './gpg_badges';
break; break;
case 'projects:edit': case 'projects:edit':
setupProjectEdit(); setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
break; break;
case 'projects:imports:show': case 'projects:imports:show':
new ProjectImport(); new ProjectImport();
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
</template> </template>
<script> <script>
import pdfjsLib from 'pdfjs-dist'; import pdfjsLib from 'vendor/pdf';
import workerSrc from 'vendor/pdf.worker'; import workerSrc from 'vendor/pdf.worker.min';
import page from './page/index.vue'; import page from './page/index.vue';
......
<script> <script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import '~/flash';
import stageColumnComponent from './stage_column_component.vue'; import stageColumnComponent from './stage_column_component.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import '../../../flash';
export default { export default {
props: { props: {
......
...@@ -7,6 +7,14 @@ const LOADING_HTML = ` ...@@ -7,6 +7,14 @@ const LOADING_HTML = `
</div> </div>
`; `;
function getSystemDate(systemUtcOffsetSeconds) {
const date = new Date();
const localUtcOffsetMinutes = 0 - date.getTimezoneOffset();
const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60;
date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes);
return date;
}
function formatTooltipText({ date, count }) { function formatTooltipText({ date, count }) {
const dateObject = new Date(date); const dateObject = new Date(date);
const dateDayName = gl.utils.getDayName(dateObject); const dateDayName = gl.utils.getDayName(dateObject);
...@@ -22,7 +30,7 @@ function formatTooltipText({ date, count }) { ...@@ -22,7 +30,7 @@ function formatTooltipText({ date, count }) {
const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
export default class ActivityCalendar { export default class ActivityCalendar {
constructor(container, timestamps, calendarActivitiesPath) { constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) {
this.calendarActivitiesPath = calendarActivitiesPath; this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this); this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = ''; this.currentSelectedDate = '';
...@@ -37,7 +45,7 @@ export default class ActivityCalendar { ...@@ -37,7 +45,7 @@ export default class ActivityCalendar {
this.timestampsTmp = []; this.timestampsTmp = [];
let group = 0; let group = 0;
const today = new Date(); const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0); today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today); const oneYearAgo = new Date(today);
......
...@@ -150,15 +150,21 @@ export default class UserTabs { ...@@ -150,15 +150,21 @@ export default class UserTabs {
const $calendarWrap = this.$parentEl.find('.user-calendar'); const $calendarWrap = this.$parentEl.find('.user-calendar');
const calendarPath = $calendarWrap.data('calendarPath'); const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC';
if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`;
}
$.ajax({ $.ajax({
dataType: 'json', dataType: 'json',
url: calendarPath, url: calendarPath,
success: (activityData) => { success: (activityData) => {
$calendarWrap.html(CALENDAR_TEMPLATE); $calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath); new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath, utcOffset);
}, },
}); });
......
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
name: 'MRWidgetAuthor', name: 'MRWidgetAuthor',
props: { props: {
...@@ -5,11 +7,14 @@ export default { ...@@ -5,11 +7,14 @@ export default {
showAuthorName: { type: Boolean, required: false, default: true }, showAuthorName: { type: Boolean, required: false, default: true },
showAuthorTooltip: { type: Boolean, required: false, default: false }, showAuthorTooltip: { type: Boolean, required: false, default: false },
}, },
directives: {
tooltip,
},
template: ` template: `
<a <a
:href="author.webUrl || author.web_url" :href="author.webUrl || author.web_url"
class="author-link" class="author-link inline"
:class="{ 'has-tooltip': showAuthorTooltip }" :v-tooltip="showAuthorTooltip"
:title="author.name"> :title="author.name">
<img <img
:src="author.avatarUrl || author.avatar_url" :src="author.avatarUrl || author.avatar_url"
......
/* global Flash */ /* global Flash */
import '~/lib/utils/datetime_utility'; import '~/lib/utils/datetime_utility';
import { statusIconEntityMap } from '../../vue_shared/ci_status_icons';
import MemoryUsage from './mr_widget_memory_usage'; import MemoryUsage from './mr_widget_memory_usage';
import StatusIcon from './mr_widget_status_icon';
import MRWidgetService from '../services/mr_widget_service'; import MRWidgetService from '../services/mr_widget_service';
export default { export default {
...@@ -13,11 +13,7 @@ export default { ...@@ -13,11 +13,7 @@ export default {
}, },
components: { components: {
'mr-widget-memory-usage': MemoryUsage, 'mr-widget-memory-usage': MemoryUsage,
}, 'status-icon': StatusIcon,
computed: {
svg() {
return statusIconEntityMap.icon_status_success;
},
}, },
methods: { methods: {
formatDate(date) { formatDate(date) {
...@@ -51,51 +47,51 @@ export default { ...@@ -51,51 +47,51 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-heading"> <div class="mr-widget-heading deploy-heading">
<div v-for="deployment in mr.deployments"> <div v-for="deployment in mr.deployments">
<div class="ci-widget"> <div class="ci-widget media">
<div class="ci-status-icon ci-status-icon-success"> <div class="ci-status-icon ci-status-icon-success">
<span class="js-icon-link icon-link"> <span class="js-icon-link icon-link">
<span class="ci-status-icon" <status-icon status="success" />
v-html="svg"
aria-hidden="true"></span>
</span> </span>
</div> </div>
<span> <div class="media-body space-children">
<span <span>
v-if="hasDeploymentMeta(deployment)"> <span
Deployed to v-if="hasDeploymentMeta(deployment)">
</span> Deployed to
<a </span>
v-if="hasDeploymentMeta(deployment)" <a
:href="deployment.url" v-if="hasDeploymentMeta(deployment)"
target="_blank" :href="deployment.url"
rel="noopener noreferrer nofollow" target="_blank"
class="js-deploy-meta"> rel="noopener noreferrer nofollow"
{{deployment.name}} class="js-deploy-meta inline">
</a> {{deployment.name}}
<span </a>
v-if="hasExternalUrls(deployment)"> <span
on v-if="hasExternalUrls(deployment)">
</span> on
<a </span>
v-if="hasExternalUrls(deployment)" <a
:href="deployment.external_url" v-if="hasExternalUrls(deployment)"
target="_blank" :href="deployment.external_url"
rel="noopener noreferrer nofollow" target="_blank"
class="js-deploy-url"> rel="noopener noreferrer nofollow"
<i class="js-deploy-url inline">
class="fa fa-external-link" <i
aria-hidden="true" /> class="fa fa-external-link"
{{deployment.external_url_formatted}} aria-hidden="true" />
</a> {{deployment.external_url_formatted}}
<span </a>
v-if="hasDeploymentTime(deployment)" <span
:data-title="deployment.deployed_at_formatted" v-if="hasDeploymentTime(deployment)"
class="js-deploy-time" :data-title="deployment.deployed_at_formatted"
data-toggle="tooltip" class="js-deploy-time"
data-placement="top"> data-toggle="tooltip"
{{formatDate(deployment.deployed_at)}} data-placement="top">
{{formatDate(deployment.deployed_at)}}
</span>
</span> </span>
<button <button
type="button" type="button"
...@@ -104,13 +100,13 @@ export default { ...@@ -104,13 +100,13 @@ export default {
class="btn btn-default btn-xs"> class="btn btn-default btn-xs">
Stop environment Stop environment
</button> </button>
</span> <mr-widget-memory-usage
v-if="deployment.metrics_url"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
</div> </div>
<mr-widget-memory-usage
v-if="deployment.metrics_url"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div> </div>
</div> </div>
`, `,
......
import tooltip from '../../vue_shared/directives/tooltip';
import '../../lib/utils/text_utility'; import '../../lib/utils/text_utility';
export default { export default {
...@@ -5,6 +6,9 @@ export default { ...@@ -5,6 +6,9 @@ export default {
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
directives: {
tooltip,
},
computed: { computed: {
shouldShowCommitsBehindText() { shouldShowCommitsBehindText() {
return this.mr.divergedCommitsCount > 0; return this.mr.divergedCommitsCount > 0;
...@@ -29,18 +33,51 @@ export default { ...@@ -29,18 +33,51 @@ export default {
}, },
template: ` template: `
<div class="mr-source-target"> <div class="mr-source-target">
<div <div class="normal">
v-if="mr.isOpen" <strong>
class="pull-right"> Request to merge
<span
class="label-branch"
:class="{'label-truncated': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
v-html="mr.sourceBranchLink"></span>
<button
v-tooltip
class="btn btn-transparent btn-clipboard"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
:class="{'label-truncatedtooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
<div v-if="mr.isOpen">
<a <a
href="#modal_merge_info" href="#modal_merge_info"
data-toggle="modal" data-toggle="modal"
class="btn inline btn-grouped btn-sm"> class="btn btn-small inline">
Check out branch Check out branch
</a> </a>
<span class="dropdown inline prepend-left-5"> <span class="dropdown inline prepend-left-10">
<a <a
class="btn btn-sm dropdown-toggle" class="btn btn-xs dropdown-toggle"
data-toggle="dropdown" data-toggle="dropdown"
aria-label="Download as" aria-label="Download as"
role="button"> role="button">
...@@ -69,38 +106,6 @@ export default { ...@@ -69,38 +106,6 @@ export default {
</ul> </ul>
</span> </span>
</div> </div>
<div class="normal">
<strong>
Request to merge
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
v-html="mr.sourceBranchLink"></span>
<button
class="btn btn-transparent btn-clipboard has-tooltip"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
</div> </div>
`, `,
}; };
...@@ -120,13 +120,12 @@ export default { ...@@ -120,13 +120,12 @@ export default {
}, },
template: ` template: `
<div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage"> <div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage">
<div class="legend"></div>
<p <p
v-if="shouldShowLoading" v-if="shouldShowLoading"
class="usage-info js-usage-info usage-info-loading"> class="usage-info js-usage-info usage-info-loading">
<i <i
class="fa fa-spinner fa-spin usage-info-load-spinner" class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true" />Loading deployment statistics. aria-hidden="true" />Loading deployment statistics
</p> </p>
<p <p
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
...@@ -136,12 +135,12 @@ export default { ...@@ -136,12 +135,12 @@ export default {
<p <p
v-if="shouldShowLoadFailure" v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed"> class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics. Failed to load deployment statistics
</p> </p>
<p <p
v-if="shouldShowMetricsUnavailable" v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable"> class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently. Deployment statistics are not available currently
</p> </p>
<mr-memory-graph <mr-memory-graph
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
......
...@@ -16,7 +16,7 @@ export default { ...@@ -16,7 +16,7 @@ export default {
<a <a
data-toggle="modal" data-toggle="modal"
href="#modal_merge_info"> href="#modal_merge_info">
command line. command line
</a> </a>
</section> </section>
`, `,
......
...@@ -29,58 +29,55 @@ export default { ...@@ -29,58 +29,55 @@ export default {
}, },
template: ` template: `
<div class="mr-widget-heading"> <div class="mr-widget-heading">
<div class="ci-widget"> <div class="ci-widget media">
<template v-if="hasCIError"> <template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error"> <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
<span class="js-icon-link icon-link"> <span
<span v-html="svg"
v-html="svg" aria-hidden="true"></span>
aria-hidden="true"></span> </div>
</span> <div class="media-body">
Could not connect to the CI server. Please check your settings and try again
</div> </div>
<span>Could not connect to the CI server. Please check your settings and try again.</span>
</template> </template>
<template v-else> <template v-else>
<div> <div class="ci-status-icon append-right-10">
<a <a
class="icon-link" class="icon-link"
:href="this.status.details_path"> :href="this.status.details_path">
<ci-icon :status="status" /> <ci-icon :status="status" />
</a> </a>
</div> </div>
<span> <div class="media-body">
Pipeline <span>
<a Pipeline
:href="mr.pipeline.path" <a
class="pipeline-id">#{{mr.pipeline.id}}</a> :href="mr.pipeline.path"
{{mr.pipeline.details.status.label}} class="pipeline-id">#{{mr.pipeline.id}}</a>
</span> </span>
<span <span class="mr-widget-pipeline-graph">
v-if="mr.pipeline.details.stages.length > 0"> <span class="stage-cell">
with {{stageText}} <div
</span> v-if="mr.pipeline.details.stages.length > 0"
<div class="mr-widget-pipeline-graph"> v-for="stage in mr.pipeline.details.stages"
<div class="stage-cell"> class="stage-container dropdown js-mini-pipeline-graph">
<div <pipeline-stage :stage="stage" />
v-if="mr.pipeline.details.stages.length > 0" </div>
v-for="stage in mr.pipeline.details.stages" </span>
class="stage-container dropdown js-mini-pipeline-graph"> </span>
<pipeline-stage :stage="stage" /> <span>
</div> {{mr.pipeline.details.status.label}} for
</div> <a
:href="mr.pipeline.commit.commit_path"
class="commit-sha js-commit-link">
{{mr.pipeline.commit.short_id}}</a>.
</span>
<span
v-if="mr.pipeline.coverage"
class="js-mr-coverage">
Coverage {{mr.pipeline.coverage}}%
</span>
</div> </div>
<span>
for
<a
:href="mr.pipeline.commit.commit_path"
class="commit-sha js-commit-link">
{{mr.pipeline.commit.short_id}}</a>.
</span>
<span
v-if="mr.pipeline.coverage"
class="js-mr-coverage">
Coverage {{mr.pipeline.coverage}}%.
</span>
</template> </template>
</div> </div>
</div> </div>
......
...@@ -2,37 +2,32 @@ export default { ...@@ -2,37 +2,32 @@ export default {
name: 'MRWidgetRelatedLinks', name: 'MRWidgetRelatedLinks',
props: { props: {
relatedLinks: { type: Object, required: true }, relatedLinks: { type: Object, required: true },
state: { type: String, required: false },
}, },
computed: { computed: {
hasLinks() { hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks; const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe; return closing || mentioned || assignToMe;
}, },
}, closesText() {
methods: { if (this.state === 'merged') {
hasMultipleIssues(text) { return 'Closed';
return !text ? false : text.match(/<\/a> and <a/); }
}, if (this.state === 'closed') {
issueLabel(field) { return 'Did not close';
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue'; }
}, return 'Closes';
verbLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
}, },
}, },
template: ` template: `
<section <section
v-if="hasLinks" v-if="hasLinks"
class="mr-info-list mr-links"> class="mr-info-list mr-links">
<div class="legend"></div>
<p v-if="relatedLinks.closing"> <p v-if="relatedLinks.closing">
Closes {{issueLabel('closing')}} {{closesText}} <span v-html="relatedLinks.closing"></span>
<span v-html="relatedLinks.closing"></span>.
</p> </p>
<p v-if="relatedLinks.mentioned"> <p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span> Mentions <span v-html="relatedLinks.mentioned"></span>
<span v-html="relatedLinks.mentioned"></span>
{{verbLabel('mentioned')}} mentioned but will not be closed.
</p> </p>
<p v-if="relatedLinks.assignToMe"> <p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span> <span v-html="relatedLinks.assignToMe"></span>
......
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
status: { type: String, required: true },
showDisabledButton: { type: Boolean, required: false },
},
components: {
ciIcon,
loadingIcon,
},
computed: {
statusObj() {
return {
group: this.status,
icon: `icon_status_${this.status}`,
};
},
},
template: `
<div class="space-children flex-container-block append-right-10">
<div v-if="status === 'loading'" class="mr-widget-icon">
<loading-icon />
</div>
<ci-icon v-else :status="statusObj" />
<button
v-if="showDisabledButton"
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
</div>
`,
};
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetArchived', name: 'MRWidgetArchived',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <div class="space-children">
type="button" <status-icon status="failed" />
class="btn btn-success btn-small" <button
disabled="true"> type="button"
Merge class="btn btn-success btn-small"
</button> disabled="true">
<span class="bold"> Merge
This project is archived, write access has been disabled. </button>
</span> </div>
<div class="media-body">
<span class="bold">
This project is archived, write access has been disabled
</span>
</div>
</div> </div>
`, `,
}; };
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetAutoMergeFailed', name: 'MRWidgetAutoMergeFailed',
...@@ -10,6 +11,9 @@ export default { ...@@ -10,6 +11,9 @@ export default {
isRefreshing: false, isRefreshing: false,
}; };
}, },
components: {
statusIcon,
},
methods: { methods: {
refreshWidget() { refreshWidget() {
this.isRefreshing = true; this.isRefreshing = true;
...@@ -19,18 +23,16 @@ export default { ...@@ -19,18 +23,16 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" />
class="btn btn-success btn-small" <div class="media-body space-children">
disabled="true" <span class="bold">
type="button"> <template v-if="mr.mergeError">{{mr.mergeError}}.</template>
Merge This merge request failed to be merged automatically
</button> </span>
<span class="bold danger">
This merge request failed to be merged automatically.
<button <button
@click="refreshWidget" @click="refreshWidget"
:class="{ disabled: isRefreshing }" :disabled="isRefreshing"
type="button" type="button"
class="btn btn-xs btn-default"> class="btn btn-xs btn-default">
<i <i
...@@ -39,9 +41,6 @@ export default { ...@@ -39,9 +41,6 @@ export default {
aria-hidden="true" /> aria-hidden="true" />
Refresh Refresh
</button> </button>
</span>
<div class="merge-error-text danger bold">
{{mr.mergeError}}
</div> </div>
</div> </div>
`, `,
......
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetChecking', name: 'MRWidgetChecking',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="loading" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> Checking ability to merge automatically
Merge </span>
</button> </div>
<span class="bold">
Checking ability to merge automatically.
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</span>
</div> </div>
`, `,
}; };
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetClosed', name: 'MRWidgetClosed',
...@@ -7,24 +8,28 @@ export default { ...@@ -7,24 +8,28 @@ export default {
}, },
components: { components: {
'mr-widget-author-and-time': mrWidgetAuthorTime, 'mr-widget-author-and-time': mrWidgetAuthorTime,
statusIcon,
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<mr-widget-author-and-time <status-icon status="failed" />
actionText="Closed by" <div class="media-body">
:author="mr.closedBy" <mr-widget-author-and-time
:dateTitle="mr.updatedAt" actionText="Closed by"
:dateReadable="mr.closedAt" :author="mr.closedBy"
/> :dateTitle="mr.updatedAt"
<section> :dateReadable="mr.closedAt"
<p> />
The changes were not merged into <section class="mr-info-list">
<a <p>
:href="mr.targetBranchPath" The changes were not merged into
class="label-branch"> <a
{{mr.targetBranch}}</a>. :href="mr.targetBranchPath"
</p> class="label-branch">
</section> {{mr.targetBranch}}</a>
</p>
</section>
</div>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetConflicts', name: 'MRWidgetConflicts',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> There are merge conflicts<span v-if="!mr.canMerge">.</span>
Merge <span v-if="!mr.canMerge">
</button> Resolve these conflicts or ask someone with write access to this repository to merge it locally
<span class="bold"> </span>
There are merge conflicts.
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally.
</span> </span>
</span>
<div
v-if="mr.canMerge"
class="btn-group">
<a <a
v-if="mr.conflictResolutionPath" v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath" :href="mr.conflictResolutionPath"
class="btn btn-default btn-xs js-resolve-conflicts-button"> class="btn btn-default btn-xs js-resolve-conflicts-button">
Resolve conflicts Resolve conflicts
......
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -38,39 +39,40 @@ export default { ...@@ -38,39 +39,40 @@ export default {
} }
}, },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <template v-if="isRefreshing">
class="btn btn-success btn-small" <status-icon status="loading" />
disabled="true" <span class="media-body bold js-refresh-label">
type="button"> Refreshing now
Merge
</button>
<span
v-if="!isRefreshing"
class="bold danger">
<span
class="has-error-message"
v-if="mr.mergeError">
{{mr.mergeError}}
</span>
<span v-else>Merge failed.</span>
<span
:class="{ 'has-custom-error': mr.mergeError }">
Refreshing in {{timerText}} to show the updated status...
</span> </span>
<button </template>
@click="refresh" <template v-else>
class="btn btn-default btn-xs js-refresh-button" <status-icon status="failed" showDisabledButton />
type="button"> <div class="media-body space-children">
Refresh now <span class="bold">
</button> <span
</span> class="has-error-message"
<span v-if="mr.mergeError">
v-if="isRefreshing" {{mr.mergeError}}.
class="bold js-refresh-label"> </span>
Refreshing now... <span v-else>Merge failed.</span>
</span> <span
:class="{ 'has-custom-error': mr.mergeError }">
Refreshing in {{timerText}} to show the updated status...
</span>
</span>
<button
@click="refresh"
class="btn btn-default btn-xs js-refresh-button"
type="button">
Refresh now
</button>
</div>
</template>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetLocked', name: 'MRWidgetLocked',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body mr-state-locked"> <div class="mr-widget-body mr-state-locked media">
<span class="state-label">Locked</span> <status-icon status="loading" />
This merge request is in the process of being merged, during which time it is locked and cannot be closed. <div class="media-body">
<i <h4>
class="fa fa-spinner fa-spin" This merge request is in the process of being merged, during which time it is locked and cannot be closed
aria-hidden="true" /> </h4>
<section class="mr-info-list mr-links"> <section class="mr-info-list">
<div class="legend"></div> <p>
<p> The changes will be merged into
The changes will be merged into <span class="label-branch">
<span class="label-branch"> <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a> </span>
</span>. </p>
</p> </section>
</section> </div>
</div> </div>
`, `,
}; };
/* global Flash */ /* global Flash */
import statusIcon from '../mr_widget_status_icon';
import MRWidgetAuthor from '../../components/mr_widget_author'; import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
...@@ -11,6 +11,7 @@ export default { ...@@ -11,6 +11,7 @@ export default {
}, },
components: { components: {
'mr-widget-author': MRWidgetAuthor, 'mr-widget-author': MRWidgetAuthor,
statusIcon,
}, },
data() { data() {
return { return {
...@@ -61,56 +62,56 @@ export default { ...@@ -61,56 +62,56 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<h4> <status-icon status="success" />
Set by <div class="media-body">
<mr-widget-author :author="mr.setToMWPSBy" /> <h4>
to be merged automatically when the pipeline succeeds. Set by
<a <mr-widget-author :author="mr.setToMWPSBy" />
v-if="mr.canCancelAutomaticMerge" to be merged automatically when the pipeline succeeds
@click.prevent="cancelAutomaticMerge"
:disabled="isCancellingAutoMerge"
role="button"
href="#"
class="btn btn-xs btn-default js-cancel-auto-merge">
<i
v-if="isCancellingAutoMerge"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Cancel automatic merge
</a>
</h4>
<section class="mr-info-list">
<div class="legend"></div>
<p>The changes will be merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}
</a>.
</p>
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed.
</p>
<p
v-else
class="with-button">
The source branch will not be removed.
<a <a
v-if="canRemoveSourceBranch" v-if="mr.canCancelAutomaticMerge"
:disabled="isRemovingSourceBranch" @click.prevent="cancelAutomaticMerge"
@click.prevent="removeSourceBranch" :disabled="isCancellingAutoMerge"
role="button" role="button"
class="btn btn-xs btn-default js-remove-source-branch" href="#"
href="#"> class="btn btn-xs btn-default js-cancel-auto-merge">
<i <i
v-if="isRemovingSourceBranch" v-if="isCancellingAutoMerge"
class="fa fa-spinner fa-spin" class="fa fa-spinner fa-spin"
aria-hidden="true" /> aria-hidden="true" />
Remove source branch Cancel automatic merge
</a> </a>
</p> </h4>
</section> <section class="mr-info-list">
<p>The changes will be merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}
</a>
</p>
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed
</p>
<p v-else>
The source branch will not be removed
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
@click.prevent="removeSourceBranch"
role="button"
class="btn btn-xs btn-default js-remove-source-branch"
href="#">
<i
v-if="isRemovingSourceBranch"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Remove source branch
</a>
</p>
</section>
</div>
</div> </div>
`, `,
}; };
/* global Flash */ /* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -9,14 +12,19 @@ export default { ...@@ -9,14 +12,19 @@ export default {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
service: { type: Object, required: true }, service: { type: Object, required: true },
}, },
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
},
data() { data() {
return { return {
isMakingRequest: false, isMakingRequest: false,
}; };
}, },
directives: {
tooltip,
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
loadingIcon,
statusIcon,
},
computed: { computed: {
shouldShowRemoveSourceBranch() { shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr; const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
...@@ -55,75 +63,77 @@ export default { ...@@ -55,75 +63,77 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<mr-widget-author-and-time <status-icon status="success" />
actionText="Merged by" <div class="media-body">
:author="mr.mergedBy" <div class="space-children">
:dateTitle="mr.updatedAt" <mr-widget-author-and-time
:dateReadable="mr.mergedAt" /> actionText="Merged by"
<section class="mr-info-list"> :author="mr.mergedBy"
<div class="legend"></div> :dateTitle="mr.updatedAt"
<p> :dateReadable="mr.mergedAt" />
The changes were merged into <a
<span class="label-branch"> v-if="mr.canRevertInCurrentMR"
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a> v-tooltip
</span> class="btn btn-close btn-xs"
</p> href="#modal-revert-commit"
<p v-if="mr.sourceBranchRemoved">The source branch has been removed.</p> data-toggle="modal"
<p v-if="shouldShowRemoveSourceBranch"> data-container="body"
You can remove source branch now. title="Revert this merge request in a new merge request">
<button Revert
@click="removeSourceBranch" </a>
:class="{ disabled: isMakingRequest }" <a
type="button" v-else-if="mr.revertInForkPath"
class="btn btn-xs btn-default js-remove-branch-button"> v-tooltip
Remove Source Branch class="btn btn-close btn-xs"
</button> data-method="post"
</p> :href="mr.revertInForkPath"
<p v-if="shouldShowSourceBranchRemoving"> title="Revert this merge request in a new merge request">
<i Revert
class="fa fa-spinner fa-spin" </a>
aria-hidden="true" /> <a
The source branch is being removed. v-if="mr.canCherryPickInCurrentMR"
</p> v-tooltip
</section> class="btn btn-default btn-xs"
<div href="#modal-cherry-pick-commit"
v-if="shouldShowMergedButtons" data-toggle="modal"
class="merged-buttons clearfix"> data-container="body"
<a title="Cherry-pick this merge request in a new merge request">
v-if="mr.canRevertInCurrentMR" Cherry-pick
class="btn btn-close btn-sm has-tooltip" </a>
href="#modal-revert-commit" <a
data-toggle="modal" v-else-if="mr.cherryPickInForkPath"
data-container="body" v-tooltip
title="Revert this merge request in a new merge request"> class="btn btn-default btn-xs"
Revert data-method="post"
</a> :href="mr.cherryPickInForkPath"
<a title="Cherry-pick this merge request in a new merge request">
v-else-if="mr.revertInForkPath" Cherry-pick
class="btn btn-close btn-sm has-tooltip" </a>
data-method="post" </div>
:href="mr.revertInForkPath" <section class="mr-info-list">
title="Revert this merge request in a new merge request"> <p>
Revert The changes were merged into
</a> <span class="label-branch">
<a <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
v-if="mr.canCherryPickInCurrentMR" </span>
class="btn btn-default btn-sm has-tooltip" </p>
href="#modal-cherry-pick-commit" <p v-if="mr.sourceBranchRemoved">The source branch has been removed</p>
data-toggle="modal" <p v-if="shouldShowRemoveSourceBranch" class="space-children">
data-container="body" <span>You can remove source branch now</span>
title="Cherry-pick this merge request in a new merge request"> <button
Cherry-pick @click="removeSourceBranch"
</a> :disabled="isMakingRequest"
<a type="button"
v-else-if="mr.cherryPickInForkPath" class="btn btn-xs btn-default js-remove-branch-button">
class="btn btn-default btn-sm has-tooltip" Remove Source Branch
data-method="post" </button>
:href="mr.cherryPickInForkPath" </p>
title="Cherry-pick this merge request in a new merge request"> <p v-if="shouldShowSourceBranchRemoving">
Cherry-pick <loading-icon inline />
</a> <span>The source branch is being removed</span>
</p>
</section>
</div> </div>
</div> </div>
`, `,
......
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help'; import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
export default { export default {
...@@ -5,30 +7,37 @@ export default { ...@@ -5,30 +7,37 @@ export default {
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
directives: {
tooltip,
},
components: { components: {
'mr-widget-merge-help': mrWidgetMergeHelp, 'mr-widget-merge-help': mrWidgetMergeHelp,
statusIcon,
}, },
computed: { computed: {
missingBranchName() { missingBranchName() {
return this.mr.sourceBranchRemoved ? 'source' : 'target'; return this.mr.sourceBranchRemoved ? 'source' : 'target';
}, },
message() {
return `If the ${this.missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line`;
},
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold js-branch-text">
disabled="true"> <span class="capitalize">
Merge {{missingBranchName}}
</button> </span> branch does not exist.
<span class="bold js-branch-text"> Please restore it or use a different {{missingBranchName}} branch
<span class="capitalize"> <i
{{missingBranchName}} v-tooltip
</span> branch does not exist. class="fa fa-question-circle"
Please restore the {{missingBranchName}} branch or use a different {{missingBranchName}} branch. :title="message"
</span> :aria-label="message"></i>
<mr-widget-merge-help </span>
:missing-branch="missingBranchName" /> </div>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetNotAllowed', name: 'MRWidgetNotAllowed',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="success" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> Ready to be merged automatically.
Merge Ask someone with write access to this repository to merge this request
</button> </span>
<span class="bold"> </div>
Ready to be merged automatically.
Ask someone with write access to this repository to merge this request.
</span>
</div> </div>
`, `,
}; };
...@@ -12,7 +12,7 @@ export default { ...@@ -12,7 +12,7 @@ export default {
return { emptyStateSVG }; return { emptyStateSVG };
}, },
template: ` template: `
<div class="mr-widget-body empty-state"> <div class="mr-widget-body mr-widget-empty-state">
<div class="row"> <div class="row">
<div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center"> <div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
<span v-html="emptyStateSVG"></span> <span v-html="emptyStateSVG"></span>
...@@ -29,12 +29,14 @@ export default { ...@@ -29,12 +29,14 @@ export default {
Currently there are no changes in this merge request's source branch. Currently there are no changes in this merge request's source branch.
Please push new commits or use a different branch. Please push new commits or use a different branch.
</p> </p>
<a <div>
v-if="mr.newBlobPath" <a
:href="mr.newBlobPath" v-if="mr.newBlobPath"
class="btn btn-inverted btn-save"> :href="mr.newBlobPath"
Create file class="btn btn-inverted btn-save">
</a> Create file
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
......
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
Merge </span>
</button> </div>
<span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed.
</span>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
class="btn btn-success btn-small" <div class="media-body space-children">
disabled="true" <span class="bold">
type="button"> The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
Merge </span>
</button> </div>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure.
</span>
</div> </div>
`, `,
}; };
/* global Flash */ /* global Flash */
import successSvg from 'icons/_icon_status_success.svg'; import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg'; import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -25,6 +25,9 @@ export default { ...@@ -25,6 +25,9 @@ export default {
warningSvg, warningSvg,
}; };
}, },
components: {
statusIcon,
},
computed: { computed: {
commitMessageLinkTitle() { commitMessageLinkTitle() {
const withDesc = 'Include description in commit message'; const withDesc = 'Include description in commit message';
...@@ -196,84 +199,98 @@ export default { ...@@ -196,84 +199,98 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<span class="btn-group"> <status-icon status="success" />
<button <div class="media-body">
@click="handleMergeButtonClick()" <div class="media space-children">
:disabled="isMergeButtonDisabled" <span class="btn-group">
:class="mergeButtonClass" <button
type="button"> @click="handleMergeButtonClick()"
<i :disabled="isMergeButtonDisabled"
v-if="isMakingRequest" :class="mergeButtonClass"
class="fa fa-spinner fa-spin" type="button">
aria-hidden="true" /> <i
{{mergeButtonText}} v-if="isMakingRequest"
</button> class="fa fa-spinner fa-spin"
<button aria-hidden="true" />
v-if="shouldShowMergeOptionsDropdown" {{mergeButtonText}}
:disabled="isMergeButtonDisabled" </button>
type="button" <button
class="btn btn-small btn-info dropdown-toggle" v-if="shouldShowMergeOptionsDropdown"
data-toggle="dropdown"> :disabled="isMergeButtonDisabled"
<i type="button"
class="fa fa-caret-down" class="btn btn-small btn-info dropdown-toggle js-merge-moment"
aria-hidden="true" /> data-toggle="dropdown"
<span class="sr-only"> aria-label="Select merge moment">
Select merge moment <i
class="fa fa-chevron-down"
aria-hidden="true" />
</button>
<ul
v-if="shouldShowMergeOptionsDropdown"
class="dropdown-menu dropdown-menu-right"
role="menu">
<li>
<a
@click.prevent="handleMergeButtonClick(true)"
class="merge_when_pipeline_succeeds"
href="#">
<span class="media">
<span
v-html="successSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="media-body merge-opt-title">Merge when pipeline succeeds</span>
</span>
</a>
</li>
<li>
<a
@click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request"
href="#">
<span class="media">
<span
v-html="warningSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="media-body merge-opt-title">Merge immediately</span>
</span>
</a>
</li>
</ul>
</span> </span>
</button> <div class="media-body space-children">
<ul <template v-if="isMergeAllowed()">
v-if="shouldShowMergeOptionsDropdown" <label>
class="dropdown-menu dropdown-menu-right" <input
role="menu"> id="remove-source-branch-input"
<li> v-model="removeSourceBranch"
<a :disabled="isRemoveSourceBranchButtonDisabled"
@click.prevent="handleMergeButtonClick(true)" type="checkbox"/> Remove source branch
class="merge_when_pipeline_succeeds" </label>
href="#">
<span
v-html="successSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="merge-opt-title">Merge when pipeline succeeds</span>
</a>
</li>
<li>
<a
@click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request"
href="#">
<span
v-html="warningSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="merge-opt-title">Merge immediately</span>
</a>
</li>
</ul>
</span>
<template v-if="isMergeAllowed()">
<label class="spacing">
<input
id="remove-source-branch-input"
v-model="removeSourceBranch"
:disabled="isRemoveSourceBranchButtonDisabled"
type="checkbox"/> Remove source branch
</label>
<!-- Placeholder for EE extension of this component --> <!-- Placeholder for EE extension of this component -->
<squash-before-merge <squash-before-merge
v-if="shouldShowSquashBeforeMerge" v-if="shouldShowSquashBeforeMerge"
:mr="mr" :mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" /> :is-merge-button-disabled="isMergeButtonDisabled" />
<button <button
@click="toggleCommitMessageEditor" @click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled" :disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs" class="btn btn-default btn-xs"
type="button"> type="button">
Modify commit message Modify commit message
</button> </button>
</template>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span>
</template>
</div>
</div>
<div <div
v-if="showCommitMessageEditor" v-if="showCommitMessageEditor"
class="prepend-top-default commit-message-editor"> class="prepend-top-default commit-message-editor">
...@@ -293,7 +310,7 @@ export default { ...@@ -293,7 +310,7 @@ export default {
rows="14" rows="14"
name="Commit message"></textarea> name="Commit message"></textarea>
</div> </div>
<p class="hint">Try to keep the first line under 52 characters and the others under 72.</p> <p class="hint">Try to keep the first line under 52 characters and the others under 72</p>
<div class="hint"> <div class="hint">
<a <a
@click.prevent="updateCommitMessage" @click.prevent="updateCommitMessage"
...@@ -302,12 +319,7 @@ export default { ...@@ -302,12 +319,7 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</template> </div>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure.
</span>
</template>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetSHAMismatch', name: 'MRWidgetSHAMismatch',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> The source branch HEAD has recently changed. Please reload the page and review the changes before merging
Merge </span>
</button> </div>
<span class="bold">
The source branch HEAD has recently changed. Please reload the page and review the changes before merging.
</span>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetUnresolvedDiscussions', name: 'MRWidgetUnresolvedDiscussions',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> There are unresolved discussions. Please resolve these discussions
Merge </span>
</button> <a
<span class="bold"> v-if="mr.createIssueToResolveDiscussionsPath"
There are unresolved discussions. Please resolve these discussions :href="mr.createIssueToResolveDiscussionsPath"
<span v-if="mr.canCreateIssue">or</span> class="btn btn-default btn-xs js-create-issue">
<span v-else>.</span> Create an issue to resolve them later
</span> </a>
<a </div>
v-if="mr.createIssueToResolveDiscussionsPath"
:href="mr.createIssueToResolveDiscussionsPath"
class="btn btn-default btn-xs js-create-issue">
Create an issue to resolve them later
</a>
</div> </div>
`, `,
}; };
/* global Flash */ /* global Flash */
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -7,11 +9,17 @@ export default { ...@@ -7,11 +9,17 @@ export default {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
service: { type: Object, required: true }, service: { type: Object, required: true },
}, },
directives: {
tooltip,
},
data() { data() {
return { return {
isMakingRequest: false, isMakingRequest: false,
}; };
}, },
components: {
statusIcon,
},
methods: { methods: {
removeWIP() { removeWIP() {
this.isMakingRequest = true; this.isMakingRequest = true;
...@@ -29,20 +37,20 @@ export default { ...@@ -29,20 +37,20 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" :showDisabledButton="Boolean(mr.removeWIPPath)" />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small" <span class="bold">
disabled="true"> This is a Work in Progress
Merge</button> <i
<span class="bold"> v-tooltip
This merge request is currently Work In Progress and therefore unable to merge class="fa fa-question-circle"
</span> title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
<template v-if="mr.removeWIPPath"> aria-label="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged">
<i </i>
class="fa fa-question-circle has-tooltip" </span>
title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged." />
<button <button
v-if="mr.removeWIPPath"
@click="removeWIP" @click="removeWIP"
:disabled="isMakingRequest" :disabled="isMakingRequest"
type="button" type="button"
...@@ -53,7 +61,7 @@ export default { ...@@ -53,7 +61,7 @@ export default {
aria-hidden="true" /> aria-hidden="true" />
Resolve WIP status Resolve WIP status
</button> </button>
</template> </div>
</div> </div>
`, `,
}; };
/** /**
* This file is the centerpiece of an attempt to reduce potential conflicts * This file is the centerpiece of an attempt to reduce potential conflicts
* between the CE and EE versions of the MR widget. EE additions to the MR widget should * between the CE and EE versions of the MR widget. EE additions to the MR widget should
* be contained in the ./vue_merge_request_widget/ee directory, and should **extend** * be contained in the ee/vue_merge_request_widget directory, and should **extend**
* rather than mutate CE MR Widget code. * rather than mutate CE MR Widget code.
* *
* This file should be the only source of conflicts between EE and CE. EE-only components should * This file should be the only source of conflicts between EE and CE. EE-only components should
......
...@@ -35,8 +35,14 @@ import { ...@@ -35,8 +35,14 @@ import {
export default { export default {
el: '#js-vue-mr-widget', el: '#js-vue-mr-widget',
name: 'MRWidget', name: 'MRWidget',
props: {
mrData: {
type: Object,
required: false,
},
},
data() { data() {
const store = new MRWidgetStore(gl.mrWidgetData); const store = new MRWidgetStore(this.mrData || window.gl.mrWidgetData);
const service = this.createService(store); const service = this.createService(store);
return { return {
mr: store, mr: store,
...@@ -234,14 +240,21 @@ export default { ...@@ -234,14 +240,21 @@ export default {
v-if="shouldRenderDeployments" v-if="shouldRenderDeployments"
:mr="mr" :mr="mr"
:service="service" /> :service="service" />
<component <div class="mr-widget-section">
:is="componentName" <component
:mr="mr" :is="componentName"
:service="service" /> :mr="mr"
<mr-widget-related-links :service="service" />
v-if="shouldRenderRelatedLinks" <mr-widget-related-links
:related-links="mr.relatedLinks" /> v-if="shouldRenderRelatedLinks"
<mr-widget-merge-help v-if="shouldRenderMergeHelp" /> :state="mr.state"
:related-links="mr.relatedLinks" />
</div>
<div
class="mr-widget-footer"
v-if="shouldRenderMergeHelp">
<mr-widget-merge-help />
</div>
</div> </div>
`, `,
}; };
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
@import "framework/lists"; @import "framework/lists";
@import "framework/logo"; @import "framework/logo";
@import "framework/markdown_area"; @import "framework/markdown_area";
@import "framework/media_object";
@import "framework/mobile"; @import "framework/mobile";
@import "framework/modal"; @import "framework/modal";
@import "framework/nav"; @import "framework/nav";
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
margin-top: -23px; margin-top: -23px;
float: right; float: right;
font-size: 12px; font-size: 12px;
direction: ltr;
} }
.pika-single.gitlab-theme { .pika-single.gitlab-theme {
......
...@@ -728,6 +728,10 @@ ...@@ -728,6 +728,10 @@
@mixin new-style-dropdown { @mixin new-style-dropdown {
.dropdown-menu, .dropdown-menu,
.dropdown-menu-nav { .dropdown-menu-nav {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
......
...@@ -325,9 +325,9 @@ header { ...@@ -325,9 +325,9 @@ header {
li { li {
.badge { .badge {
position: inherit; position: inherit;
top: -3px; top: -8px;
font-weight: normal; font-weight: normal;
margin-left: -12px; margin-left: -11px;
font-size: 11px; font-size: 11px;
color: $white-light; color: $white-light;
padding: 1px 5px 2px; padding: 1px 5px 2px;
......
.media {
display: flex;
align-items: flex-start;
}
.media-body {
flex: 1;
}
...@@ -206,7 +206,6 @@ $general-hover-transition-curve: linear; ...@@ -206,7 +206,6 @@ $general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232); $highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px; $performance-bar-height: 35px;
/* /*
* Common component specific colors * Common component specific colors
*/ */
...@@ -315,6 +314,12 @@ $btn-white-active: #848484; ...@@ -315,6 +314,12 @@ $btn-white-active: #848484;
$badge-bg: rgba(0, 0, 0, 0.07); $badge-bg: rgba(0, 0, 0, 0.07);
$badge-color: $gl-text-color-secondary; $badge-color: $gl-text-color-secondary;
/*
* Status icons
*/
$status-icon-size: 22px;
$status-icon-margin: $gl-btn-padding;
/* /*
* Award emoji * Award emoji
*/ */
......
...@@ -312,6 +312,10 @@ header.navbar-gitlab-new { ...@@ -312,6 +312,10 @@ header.navbar-gitlab-new {
// TODO: fallback to global style // TODO: fallback to global style
.dropdown-menu { .dropdown-menu {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
......
...@@ -328,9 +328,17 @@ ...@@ -328,9 +328,17 @@
margin-bottom: 10px; margin-bottom: 10px;
color: $issuable-sidebar-color; color: $issuable-sidebar-color;
svg {
fill: $issuable-sidebar-color;
}
&:hover, &:hover,
&:hover .todo-undone { &:hover .todo-undone {
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
span { span {
......
...@@ -2,10 +2,35 @@ ...@@ -2,10 +2,35 @@
* MR -> show: Automerge widget * MR -> show: Automerge widget
* *
*/ */
.space-children {
@include clearfix;
> * {
float: left;
}
> *:not(:last-child) {
margin-right: 10px;
}
}
.mr-state-widget { .mr-state-widget {
color: $gl-text-color; color: $gl-text-color;
border: 1px solid $border-color; border: 1px solid $border-color;
border-radius: 2px; border-radius: 2px;
line-height: 28px;
.mr-widget-heading,
.mr-widget-section,
.mr-widget-footer {
padding: $gl-padding;
border-top: solid 1px $border-color;
}
.mr-widget-footer {
padding: 0;
}
form { form {
margin-bottom: 0; margin-bottom: 0;
...@@ -15,15 +40,35 @@ ...@@ -15,15 +40,35 @@
} }
} }
label {
margin-bottom: 0;
}
.btn {
font-size: $gl-font-size;
&[disabled] {
opacity: 0.3;
}
&.btn-xs {
line-height: 1;
padding: 5px 10px;
margin-top: 1px;
}
&.dropdown-toggle {
.fa {
color: inherit;
}
}
}
.accept-merge-holder { .accept-merge-holder {
.accept-action { .accept-action {
display: inline-block; display: inline-block;
float: left; float: left;
.btn-success.dropdown-toggle .fa {
color: inherit;
}
.accept-merge-request { .accept-merge-request {
&.ci-pending, &.ci-pending,
&.ci-running { &.ci-running {
...@@ -84,77 +129,64 @@ ...@@ -84,77 +129,64 @@
.ci-widget { .ci-widget {
color: $gl-text-color; color: $gl-text-color;
display: -webkit-flex;
display: flex; display: flex;
-webkit-align-items: center;
align-items: center;
padding: $gl-padding-top $gl-padding 0;
svg {
position: relative;
top: 1px;
overflow: visible;
}
> span {
padding-right: 4px;
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
flex-wrap: wrap; flex-wrap: wrap;
} }
}
.icon-link > .ci-status-icon > svg { .mr-widget-icon {
width: 22px; font-size: 22px;
height: 22px; margin-right: $status-icon-margin;
margin-right: 8px; }
}
.ci-error { .ci-status-icon svg {
margin-right: $btn-side-margin; width: $status-icon-size;
} height: $status-icon-size;
margin: 3px 0;
position: relative;
overflow: visible;
display: block;
} }
.mr-widget-body, .mr-widget-body {
.mr-widget-footer { @include clearfix;
margin: 16px;
&.media > *:first-child {
margin-right: 10px;
}
} }
.mr-widget-pipeline-graph { .mr-widget-pipeline-graph {
flex-shrink: 0; padding: 0 4px;
.dropdown-menu { .dropdown-menu {
margin-top: 11px;
z-index: 300; z-index: 300;
} }
.ci-action-icon-wrapper { .ci-action-icon-wrapper {
line-height: 16px; line-height: 16px;
} }
}
@media (max-width: $screen-xs-max) { .mini-pipeline-graph-dropdown-toggle {
order: 1; vertical-align: top;
margin-top: $gl-padding-top; }
border-radius: 3px;
background-color: $white-light;
border: 1px solid $gray-darker;
width: 100%;
text-align: center;
.dropdown-menu { .mini-pipeline-graph-dropdown-menu .mini-pipeline-graph-dropdown-item {
margin-left: -97.5px; display: flex;
} align-items: center;
.arrow-up::before, .ci-status-text,
.arrow-up::after, { .ci-status-icon {
margin-left: 97.5px; top: 0;
} margin-right: 10px;
} }
} }
.normal { .normal {
color: $gl-text-color; line-height: 28px;
font-size: 15px;
} }
.capitalize { .capitalize {
...@@ -165,9 +197,8 @@ ...@@ -165,9 +197,8 @@
@extend .ref-name; @extend .ref-name;
color: $gl-text-color; color: $gl-text-color;
font-weight: bold; font-weight: 600;
overflow: hidden; overflow: hidden;
margin: 0 3px;
word-break: break-all; word-break: break-all;
&.label-truncated { &.label-truncated {
...@@ -189,52 +220,19 @@ ...@@ -189,52 +220,19 @@
} }
} }
.js-deployment-link {
display: inline-block;
}
.mr-widget-help { .mr-widget-help {
margin: $gl-padding; padding: 10px 16px 10px 48px;
color: $ci-skipped-color; font-style: italic;
}
.mr-info-list {
&.mr-links {
margin-left: 28px;
}
&.mr-memory-usage {
margin: 5px 0 10px 25px;
}
}
.mr-widget-heading {
.btn-default.btn-xs {
margin-left: 5px;
}
}
.mr-widget-body {
.btn {
font-size: 15px;
}
.btn-group .btn {
padding: 5px 10px;
&.dropdown-toggle {
padding: 5px 7px;
}
}
} }
.mr-widget-body { .mr-widget-body {
h4 { h4 {
font-weight: bold; float: left;
font-size: 15px; font-weight: 600;
margin: 5px 0; font-size: 14px;
color: $gl-text-color; line-height: inherit;
margin-top: 0;
margin-bottom: 0;
&.has-conflicts .fa-exclamation-triangle { &.has-conflicts .fa-exclamation-triangle {
color: $gl-warning; color: $gl-warning;
...@@ -255,18 +253,16 @@ ...@@ -255,18 +253,16 @@
} }
.spacing { .spacing {
margin: 0 $gl-padding; margin: 0 0 0 10px;
} }
.bold { .bold {
font-weight: bold; font-weight: 600;
font-size: 15px;
color: $gl-gray-light; color: $gl-gray-light;
} }
.state-label { .state-label {
font-size: 16px; font-weight: 600;
font-weight: bold;
padding-right: 10px; padding-right: 10px;
} }
...@@ -274,16 +270,6 @@ ...@@ -274,16 +270,6 @@
color: $gl-danger; color: $gl-danger;
} }
.mr-widget-help {
margin: $gl-padding 0;
}
.with-button {
position: relative;
top: 6px;
margin-bottom: 24px;
}
.spacing, .spacing,
.bold { .bold {
vertical-align: middle; vertical-align: middle;
...@@ -294,15 +280,8 @@ ...@@ -294,15 +280,8 @@
padding: 5px; padding: 5px;
} }
.merge-opt-icon, .merge-opt-icon {
.merge-opt-title { line-height: 1.5;
display: inline-block;
float: left;
}
.merge-opt-icon svg {
height: 15px;
width: 15px;
} }
.merge-opt-title { .merge-opt-title {
...@@ -316,34 +295,15 @@ ...@@ -316,34 +295,15 @@
} }
} }
.has-error-message + .has-custom-error {
margin-left: 0;
}
.has-custom-error { .has-custom-error {
display: inline-block; display: inline-block;
margin-left: 70px;
}
.merge-error-text {
margin-left: 70px;
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
h4 {
font-size: 14px;
}
p { p {
font-size: 13px; font-size: 13px;
} }
.btn,
.btn-group,
.accept-action {
margin-bottom: 4px;
}
.btn-grouped { .btn-grouped {
float: none; float: none;
margin-right: 0; margin-right: 0;
...@@ -367,19 +327,16 @@ ...@@ -367,19 +327,16 @@
} }
} }
&.mr-state-locked .mr-info-list { &.mr-widget-empty-state {
margin-top: 10px; line-height: 20px;
margin-left: 12px;
}
&.empty-state {
.artwork { .artwork {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
} }
.text { .text {
span { span {
font-weight: bold; font-weight: 600;
} }
p { p {
...@@ -389,10 +346,6 @@ ...@@ -389,10 +346,6 @@
} }
} }
.mr-widget-footer {
border-top: 1px solid $gray-darker;
}
.ci-coverage { .ci-coverage {
float: right; float: right;
} }
...@@ -497,8 +450,6 @@ ...@@ -497,8 +450,6 @@
} }
.btn-clipboard { .btn-clipboard {
@extend .pull-right;
margin-right: 20px; margin-right: 20px;
margin-top: 5px; margin-top: 5px;
position: absolute; position: absolute;
...@@ -506,56 +457,29 @@ ...@@ -506,56 +457,29 @@
} }
} }
.mr-links {
padding-left: $status-icon-size + $status-icon-margin;
}
.mr-info-list { .mr-info-list {
clear: left;
position: relative; position: relative;
margin: 10px 0 $gl-padding 12px; padding-top: 4px;
p { p {
margin: 6px 0; margin: 0;
position: relative; position: relative;
padding-left: 15px; padding: 4px 0;
&::before {
content: '';
position: absolute;
border-top: 2px solid $border-color;
height: 1px;
top: 9px;
width: 8px;
left: 0;
}
&:last-child { &:last-child {
margin-bottom: 0; padding-bottom: 0;
} }
} }
.legend {
height: 100%;
width: 2px;
background: $border-color;
position: absolute;
top: -9px;
}
} }
.mr-info-list.mr-memory-usage { .mr-info-list.mr-memory-usage {
.legend {
height: 65%;
top: 0;
@media (max-width: $screen-xs-max) {
height: 20px;
}
}
p { p {
float: left; float: left;
padding-left: 21px;
&::before {
top: 13px;
}
} }
.memory-graph-container { .memory-graph-container {
...@@ -565,12 +489,13 @@ ...@@ -565,12 +489,13 @@
} }
.mr-source-target { .mr-source-target {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
background-color: $gray-light; background-color: $gray-light;
border-radius: 3px 3px 0 0; border-radius: $border-radius-default $border-radius-default 0 0;
border-bottom: 1px solid $border-color; padding: $gl-padding / 2 $gl-padding;
padding: 0 $gl-padding;
margin-bottom: 6px;
line-height: 44px;
.dropdown-toggle .fa { .dropdown-toggle .fa {
color: $gl-text-color; color: $gl-text-color;
...@@ -679,14 +604,8 @@ ...@@ -679,14 +604,8 @@
} }
.merged-buttons { .merged-buttons {
margin-top: 20px;
.btn { .btn {
float: left; float: left;
&:not(:last-child) {
margin-right: 10px;
}
} }
} }
...@@ -803,20 +722,8 @@ ...@@ -803,20 +722,8 @@
} }
.mr-memory-usage { .mr-memory-usage {
p.usage-info-loading,
p.usage-info-unavailable,
p.usage-info-failed {
margin-bottom: 5px;
}
p.usage-info-loading .usage-info-load-spinner { p.usage-info-loading .usage-info-load-spinner {
margin-right: 10px; margin-right: 10px;
font-size: 16px; font-size: 16px;
} }
@media (max-width: $screen-md-min) {
.mr-info-list.mr-memory-usage .legend {
height: 80%;
}
}
} }
...@@ -220,7 +220,11 @@ ...@@ -220,7 +220,11 @@
position: relative; position: relative;
vertical-align: middle; vertical-align: middle;
height: 22px; height: 22px;
margin: 3px 6px 3px 0; margin: 3px 0;
+ .stage-container {
margin-left: 6px;
}
// Hack to show a button tooltip inline // Hack to show a button tooltip inline
button.has-tooltip + .tooltip { button.has-tooltip + .tooltip {
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
} }
select { select {
background: transparent;
transition: background 2s ease-out; transition: background 2s ease-out;
&.highlight-changes { &.highlight-changes {
......
...@@ -54,8 +54,7 @@ ...@@ -54,8 +54,7 @@
.settings-content { .settings-content {
max-height: 1px; max-height: 1px;
overflow-y: scroll; overflow-y: scroll;
margin-right: -20px; padding-right: 110px;
padding-right: 130px;
animation: collapseMaxHeight 300ms ease-out; animation: collapseMaxHeight 300ms ease-out;
&.expanded { &.expanded {
...@@ -87,6 +86,23 @@ ...@@ -87,6 +86,23 @@
overflow: hidden; overflow: hidden;
margin-top: 20px; margin-top: 20px;
} }
.sub-section {
margin-bottom: 32px;
padding: 16px;
border: 1px solid $border-color;
background-color: $gray-light;
}
.bs-callout,
.checkbox:first-child,
.help-block {
margin-top: 0;
}
.label-light {
margin-bottom: 0;
}
} }
.settings-list-icon { .settings-list-icon {
......
...@@ -941,7 +941,7 @@ class Project < ActiveRecord::Base ...@@ -941,7 +941,7 @@ class Project < ActiveRecord::Base
end end
def repo def repo
repository.raw repository.rugged
end end
def url_to_repo def url_to_repo
......
...@@ -614,17 +614,26 @@ class Repository ...@@ -614,17 +614,26 @@ class Repository
end end
def last_commit_for_path(sha, path) def last_commit_for_path(sha, path)
sha = last_commit_id_for_path(sha, path) raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled|
commit(sha) if is_enabled
last_commit_for_path_by_gitaly(sha, path)
else
last_commit_for_path_by_rugged(sha, path)
end
end
end end
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/383
def last_commit_id_for_path(sha, path) def last_commit_id_for_path(sha, path)
key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}" key = path.blank? ? "last_commit_id_for_path:#{sha}" : "last_commit_id_for_path:#{sha}:#{Digest::SHA1.hexdigest(path)}"
cache.fetch(key) do cache.fetch(key) do
args = %W(rev-list --max-count=1 #{sha} -- #{path}) raw_repository.gitaly_migrate(:last_commit_for_path) do |is_enabled|
run_git(args).first.strip if is_enabled
last_commit_for_path_by_gitaly(sha, path).id
else
last_commit_id_for_path_by_shelling_out(sha, path)
end
end
end end
end end
...@@ -1149,6 +1158,21 @@ class Repository ...@@ -1149,6 +1158,21 @@ class Repository
Rugged::Commit.create(rugged, params) Rugged::Commit.create(rugged, params)
end end
def last_commit_for_path_by_gitaly(sha, path)
c = raw_repository.gitaly_commit_client.last_commit_for_path(sha, path)
commit(c)
end
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path_by_shelling_out(sha, path)
commit(sha)
end
def last_commit_id_for_path_by_shelling_out(sha, path)
args = %W(rev-list --max-count=1 #{sha} -- #{path})
run_git(args).first.strip
end
def repository_storage_path def repository_storage_path
@project.repository_storage_path @project.repository_storage_path
end end
......
...@@ -12,7 +12,6 @@ module MergeRequests ...@@ -12,7 +12,6 @@ module MergeRequests
merge_request.source_project = source_project merge_request.source_project = source_project
merge_request.source_branch = params[:source_branch] merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
merge_request.head_pipeline = head_pipeline_for(merge_request)
create(merge_request) create(merge_request)
end end
...@@ -22,10 +21,16 @@ module MergeRequests ...@@ -22,10 +21,16 @@ module MergeRequests
notification_service.new_merge_request(issuable, current_user) notification_service.new_merge_request(issuable, current_user)
todo_service.new_merge_request(issuable, current_user) todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user) issuable.cache_merge_request_closes_issues!(current_user)
update_merge_requests_head_pipeline(issuable)
end end
private private
def update_merge_requests_head_pipeline(merge_request)
pipeline = head_pipeline_for(merge_request)
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
end
def head_pipeline_for(merge_request) def head_pipeline_for(merge_request)
return unless merge_request.source_project return unless merge_request.source_project
......
- page_title "CI Lint" - page_title "CI Lint"
- page_description "Validate your GitLab CI configuration file" - page_description "Validate your GitLab CI configuration file"
- content_for :page_specific_javascripts do - content_for :library_javascripts do
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
%h2 Check your .gitlab-ci.yml %h2 Check your .gitlab-ci.yml
......
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
= Gon::Base.render_data = Gon::Base.render_data
- if content_for?(:library_javascripts)
= yield :library_javascripts
= webpack_bundle_tag "webpack_runtime" = webpack_bundle_tag "webpack_runtime"
= webpack_bundle_tag "common" = webpack_bundle_tag "common"
= webpack_bundle_tag "locale" = webpack_bundle_tag "locale"
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
= icon('tachometer fw') = icon('tachometer fw')
%li %li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw') = custom_icon('issues')
- issues_count = assigned_issuables_count(:issues) - issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) } %span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count) = number_with_delimiter(issues_count)
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
= icon('tachometer fw') = icon('tachometer fw')
%li %li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw') = custom_icon('issues')
- issues_count = assigned_issuables_count(:issues) - issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) } %span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count) = number_with_delimiter(issues_count)
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%span %span
Members Members
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
= link_to edit_group_path(@group), title: 'Settings' do = link_to edit_group_path(@group), title: 'Settings' do
%span %span
Settings Settings
...@@ -110,7 +110,7 @@ ...@@ -110,7 +110,7 @@
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
.nav-icon-container .nav-icon-container
= custom_icon('mr_bold') = custom_icon('spam_logs')
%span.nav-item-name %span.nav-item-name
Spam Logs Spam Logs
......
...@@ -85,6 +85,6 @@ ...@@ -85,6 +85,6 @@
Projects Projects
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do = link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
%span %span
Pipelines CI / CD
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
= nav_link(controller: %w[projects/registry/repositories]) do = nav_link(controller: %w[projects/registry/repositories]) do
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do = link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
.nav-icon-container .nav-icon-container
= custom_icon('mr_bold') = custom_icon('container_registry')
%span.nav-item-name %span.nav-item-name
Registry Registry
...@@ -122,11 +122,11 @@ ...@@ -122,11 +122,11 @@
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'CI / CD', class: 'shortcuts-pipelines' do
.nav-icon-container .nav-icon-container
= custom_icon('pipeline') = custom_icon('pipeline')
%span.nav-item-name %span.nav-item-name
Pipelines CI / CD
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
...@@ -205,9 +205,9 @@ ...@@ -205,9 +205,9 @@
Repository Repository
- if @project.feature_available?(:builds, current_user) - if @project.feature_available?(:builds, current_user)
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
= link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
%span %span
Pipelines CI / CD
- if Gitlab.config.pages.enabled - if Gitlab.config.pages.enabled
= nav_link(controller: :pages) do = nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do = link_to project_pages_path(@project), title: 'Pages' do
......
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
%fieldset.features.merge-requests-feature.append-bottom-default = render 'projects/merge_request_merge_settings', form: form
%hr
%h5.prepend-top-0
Merge Requests
= render 'projects/merge_request_merge_settings', form: form
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
- else - else
= s_("PipelineSchedules|None") = s_("PipelineSchedules|None")
%td.next-run-cell %td.next-run-cell
- if pipeline_schedule.active? - if pipeline_schedule.active? && pipeline_schedule.next_run_at
= time_ago_with_tooltip(pipeline_schedule.real_next_run) = time_ago_with_tooltip(pipeline_schedule.real_next_run)
- else - else
= s_("PipelineSchedules|Inactive") = s_("PipelineSchedules|Inactive")
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
%h4 Runner ##{@runner.id} %h4 Runner ##{@runner.id}
%hr %hr
= render 'form', runner: @runner, runner_form_url: runner_path(@runner) = render 'form', runner: @runner, runner_form_url: runner_path(@runner)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m16 11.764v-8.764c0-1.657-1.343-3-3-3h-10c-1.657 0-3 1.343-3 3v8.764c.531-.475 1.232-.764 2-.764v-8c0-.552.448-1 1-1h10c.552 0 1 .448 1 1v8c.768 0 1.469.289 2 .764m-14 .236h12c1.105 0 2 .895 2 2 0 1.105-.895 2-2 2h-12c-1.105 0-2-.895-2-2 0-1.105.895-2 2-2m10 1c-.552 0-1 .448-1 1 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-.552-.448-1-1-1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
.block.issues .block.issues
.sidebar-collapsed-icon .sidebar-collapsed-icon
%strong %strong
= icon('hashtag', 'aria-hidden': 'true') = custom_icon('issues')
%span= milestone.issues_visible_to_user(current_user).count %span= milestone.issues_visible_to_user(current_user).count
.title.hide-collapsed .title.hide-collapsed
Issues Issues
......
%h4.prepend-top-20 %h4.prepend-top-20
Contributions for Contributions for
%strong= @calendar_date.to_s(:short) %strong= @calendar_date.to_s(:medium)
- if @events.any? - if @events.any?
%ul.bordered-list %ul.bordered-list
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
%li %li
%span.light %span.light
%i.fa.fa-clock-o %i.fa.fa-clock-o
= event.created_at.to_s(:time) = event.created_at.strftime('%-I:%M%P')
- if event.push? - if event.push?
#{event.action_name} #{event.ref_type} #{event.action_name} #{event.ref_type}
%strong %strong
...@@ -30,4 +30,4 @@ ...@@ -30,4 +30,4 @@
= event.project_name = event.project_name
- else - else
%p %p
No contributions found for #{@calendar_date.to_s(:short)} No contributions found for #{@calendar_date.to_s(:medium)}
...@@ -104,7 +104,7 @@ ...@@ -104,7 +104,7 @@
.tab-content .tab-content
#activity.tab-pane #activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs .row-content-block.calender-block.white.second-block.hidden-xs
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path } } .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light %h4.center.light
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
.user-calendar-activities .user-calendar-activities
......
---
title: Fix timezone inconsistencies in user contribution graph
merge_request: 13208
author:
---
title: clean up merge request widget UI
merge_request:
author:
---
title: Rename Pipelines tab to CI / CD in new navigation
merge_request:
author:
---
title: Fix an order of operations for CI connection error message in merge request
widget
merge_request: 13252
author:
---
title: Fix pipeline_schedules pages when active schedule has an abnormal state
merge_request: 13286
author:
---
title: Fix Mattermost integration
merge_request:
author:
...@@ -23,13 +23,13 @@ module Gitlab ...@@ -23,13 +23,13 @@ module Gitlab
# https://github.com/rails/rails/blob/v4.2.6/railties/lib/rails/engine.rb#L687 # https://github.com/rails/rails/blob/v4.2.6/railties/lib/rails/engine.rb#L687
# This is a nice reference article on autoloading/eager loading: # This is a nice reference article on autoloading/eager loading:
# http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload # http://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload
config.eager_load_paths.push(*%W(#{config.root}/lib config.eager_load_paths.push(*%W[#{config.root}/lib
#{config.root}/app/models/hooks #{config.root}/app/models/hooks
#{config.root}/app/models/members #{config.root}/app/models/members
#{config.root}/app/models/project_services #{config.root}/app/models/project_services
#{config.root}/app/workers/concerns #{config.root}/app/workers/concerns
#{config.root}/app/services/concerns #{config.root}/app/services/concerns
#{config.root}/app/finders/concerns)) #{config.root}/app/finders/concerns])
config.generators.templates.push("#{config.root}/generator_templates") config.generators.templates.push("#{config.root}/generator_templates")
......
...@@ -110,9 +110,12 @@ var config = { ...@@ -110,9 +110,12 @@ var config = {
options: { limit: 2048 }, options: { limit: 2048 },
}, },
{ {
test: /\.(worker\.js|pdf|bmpr)$/, test: /\.(worker(\.min)?\.js|pdf|bmpr)$/,
exclude: /node_modules/, exclude: /node_modules/,
loader: 'file-loader', loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
}
}, },
{ {
test: /locale\/\w+\/(.*)\.js$/, test: /locale\/\w+\/(.*)\.js$/,
......
...@@ -80,7 +80,7 @@ Install, upgrade, integrate, migrate to GitLab: ...@@ -80,7 +80,7 @@ Install, upgrade, integrate, migrate to GitLab:
| :------------ | :------: | --------------: | | :------------ | :------: | --------------: |
| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 | | [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017/01/23 |
| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 | | [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016/07/13 |
| [Get started with OpenShift Origin 3 and GitLab](https://about.gitlab.com/2016/06/28/get-started-with-openshift-origin-3-and-gitlab/) | Tutorial | 2016/06/28 | | [Get started with OpenShift Origin 3 and GitLab](openshift_and_gitlab/index.md) | Tutorial | 2016/06/28 |
| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 | | [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016/04/27 |
## Software development ## Software development
......
This diff is collapsed.
...@@ -7,6 +7,11 @@ storing data in a single JSON column the data is stored in a separate table. ...@@ -7,6 +7,11 @@ storing data in a single JSON column the data is stored in a separate table.
## When To Use Background Migrations ## When To Use Background Migrations
>**Note:**
When adding background migrations _you must_ make sure they are announced in the
monthly release post along with an estimate of how long it will take to complete
the migrations.
In the vast majority of cases you will want to use a regular Rails migration In the vast majority of cases you will want to use a regular Rails migration
instead. Background migrations should _only_ be used when migrating _data_ in instead. Background migrations should _only_ be used when migrating _data_ in
tables that have so many rows this process would take hours when performed in a tables that have so many rows this process would take hours when performed in a
...@@ -91,6 +96,10 @@ BackgroundMigrationWorker.perform_bulk_in(5.minutes, jobs) ...@@ -91,6 +96,10 @@ BackgroundMigrationWorker.perform_bulk_in(5.minutes, jobs)
## Cleaning Up ## Cleaning Up
>**Note:**
Cleaning up any remaining background migrations _must_ be done in either a major
or minor release, you _must not_ do this in a patch release.
Because background migrations can take a long time you can't immediately clean Because background migrations can take a long time you can't immediately clean
things up after scheduling them. For example, you can't drop a column that's things up after scheduling them. For example, you can't drop a column that's
used in the migration process as this would cause jobs to fail. This means that used in the migration process as this would cause jobs to fail. This means that
......
...@@ -11,7 +11,7 @@ See [our current .eslintrc][eslintrc] for specific rules and patterns. ...@@ -11,7 +11,7 @@ See [our current .eslintrc][eslintrc] for specific rules and patterns.
#### ESlint #### ESlint
1. **Never** disable eslint rules unless you have a good reason. 1. **Never** disable eslint rules unless you have a good reason.
You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */` You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */`
at the top, but legacy files are a special case. Any time you develop a new feature or at the top, but legacy files are a special case. Any time you develop a new feature or
refactor an existing one, you should abide by the eslint rules. refactor an existing one, you should abide by the eslint rules.
...@@ -100,26 +100,44 @@ followed by any global declarations, then a blank newline prior to any imports o ...@@ -100,26 +100,44 @@ followed by any global declarations, then a blank newline prior to any imports o
export default Foo; export default Foo;
``` ```
1. Relative paths: Unless you are writing a test, always reference other scripts using 1. Relative paths: when importing a module in the same directory, a child
relative paths instead of `~` directory, or an immediate parent directory prefer relative paths. When
* In **app/assets/javascripts**: importing a module which is two or more levels up, prefer either `~/` or `ee/`
.
```javascript In **app/assets/javascripts/my-feature/subdir**:
// bad
import Foo from '~/foo'
// good ``` javascript
import Foo from '../foo'; // bad
``` import Foo from '~/my-feature/foo';
* In **spec/javascripts**: import Bar from '~/my-feature/subdir/bar';
import Bin from '~/my-feature/subdir/lib/bin';
```javascript // good
// bad import Foo from '../foo';
import Foo from '../../app/assets/javascripts/foo' import Bar from './bar';
import Bin from './lib/bin';
```
// good In **spec/javascripts**:
import Foo from '~/foo';
``` ``` javascript
// bad
import Foo from '../../app/assets/javascripts/my-feature/foo';
// good
import Foo from '~/my-feature/foo';
```
When referencing an **EE component**:
``` javascript
// bad
import Foo from '../../../../../ee/app/assets/javascripts/my-feature/ee-foo';
// good
import Foo from 'ee/my-feature/foo';
```
1. Avoid using IIFE. Although we have a lot of examples of files which wrap their 1. Avoid using IIFE. Although we have a lot of examples of files which wrap their
contents in IIFEs (immediately-invoked function expressions), contents in IIFEs (immediately-invoked function expressions),
...@@ -465,7 +483,7 @@ A forEach will cause side effects, it will be mutating the array being iterated. ...@@ -465,7 +483,7 @@ A forEach will cause side effects, it will be mutating the array being iterated.
#### Vue and Boostrap #### Vue and Boostrap
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components 1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
```javascript ```javascript
// bad // bad
<span <span
class="has-tooltip" class="has-tooltip"
......
...@@ -35,12 +35,11 @@ Please don't depend on GitLab-specific code since it can change in future ...@@ -35,12 +35,11 @@ Please don't depend on GitLab-specific code since it can change in future
versions. If needed copy-paste GitLab code into the migration to make it forward versions. If needed copy-paste GitLab code into the migration to make it forward
compatible. compatible.
## Commit Guidelines ## Schema Changes
Each migration **must** be added in its own commit with a descriptive commit Migrations that make changes to the database schema (e.g. adding a column) can
message. If a commit adds a migration it _should only_ include the migration and only be added in the monthly release, patch releases may only contain data
any corresponding changes to `db/schema.rb`. This makes it easy to revert a migrations _unless_ schema changes are absolutely required to solve a problem.
database migration without accidentally reverting other changes.
## Downtime Tagging ## Downtime Tagging
...@@ -224,9 +223,9 @@ add_column(:projects, :foo, :integer, default: 10, limit: 8) ...@@ -224,9 +223,9 @@ add_column(:projects, :foo, :integer, default: 10, limit: 8)
## Timestamp column type ## Timestamp column type
By default, Rails uses the `timestamp` data type that stores timestamp data without timezone information. By default, Rails uses the `timestamp` data type that stores timestamp data without timezone information.
The `timestamp` data type is used by calling either the `add_timestamps` or the `timestamps` method. The `timestamp` data type is used by calling either the `add_timestamps` or the `timestamps` method.
Also Rails converts the `:datetime` data type to the `timestamp` one. Also Rails converts the `:datetime` data type to the `timestamp` one.
Example: Example:
......
...@@ -168,8 +168,10 @@ are out of date, so we'll need to install through the following commands: ...@@ -168,8 +168,10 @@ are out of date, so we'll need to install through the following commands:
curl --location https://deb.nodesource.com/setup_7.x | sudo bash - curl --location https://deb.nodesource.com/setup_7.x | sudo bash -
sudo apt-get install -y nodejs sudo apt-get install -y nodejs
# install yarn curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
curl --location https://yarnpkg.com/install.sh | bash - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
Visit the official websites for [node](https://nodejs.org/en/download/package-manager/) and [yarn](https://yarnpkg.com/en/docs/install/) if you have any trouble with these steps. Visit the official websites for [node](https://nodejs.org/en/download/package-manager/) and [yarn](https://yarnpkg.com/en/docs/install/) if you have any trouble with these steps.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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