Commit 57b19769 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into auto-pipelines-vue

* master: (73 commits)
  Refactor JiraService by moving code out of JiraService#execute method
  Rename a label to fix an intermittently-failing spec
  Refactor the Git submodules with CI docs
  Add CHANGELOG entry
  Improve Gitlab::GitAccessWiki spec with download access checks
  Improve ProjectPolicy spec to check permissions when wiki is disabled
  Allow access to the wiki with git when repository feature disabled
  Refactor branch chooser in issuable form
  Improve the `Gitlab::OAuth::User` error message
  Disable the ee_compat_check task on dev
  Make the downtime_check task happy
  Revert bump in rufus-scheduler
  Fix comma-dangle in function's arguments errors
  Improvements after review
  Use created date from last_deployment
  API: Expose branch status
  Grapify the files API
  Move task helpers to a module
  Fixed GFM autocomplete regex
  Add Human Readable Timestamp to backup tar file
  ...
parents 6eb37284 7c66ea94
/coverage/
/coverage-javascript/
/public/
/tmp/
......
{
"env": {
"jquery": true,
"browser": true,
"es6": true
},
"extends": "airbnb",
"extends": "airbnb-base",
"globals": {
"$": false,
"_": false,
"gl": false,
"gon": false,
"jQuery": false
"gon": false
},
"plugins": [
"filenames"
......
......@@ -229,7 +229,6 @@ rake ee_compat_check:
<<: *exec
only:
- branches@gitlab-org/gitlab-ce
- branches@gitlab/gitlabhq
except:
- master
- tags
......
......@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs
gem 'sidekiq', '~> 4.2'
gem 'sidekiq-cron', '~> 0.4.0'
gem 'sidekiq-cron', '~> 0.4.4'
gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4'
......@@ -309,6 +309,8 @@ group :development, :test do
gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2'
gem 'stackprof', '~> 0.2.10'
end
group :test do
......
......@@ -650,10 +650,10 @@ GEM
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (~> 1.5)
redis (~> 3.2, >= 3.2.1)
sidekiq-cron (0.4.0)
sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0)
sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4)
simplecov (0.12.0)
......@@ -691,6 +691,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1)
......@@ -925,7 +926,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2)
sidekiq-cron (~> 0.4.0)
sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0)
slack-notifier (~> 1.2.0)
......@@ -937,6 +938,7 @@ DEPENDENCIES
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0)
......
# GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](http://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
......
......@@ -10,10 +10,15 @@
},
template: `
<span class="total-time">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
<template v-if="Object.keys(time).length">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
</template>
<template v-else>
--
</template>
</span>
`,
});
......
......@@ -23,6 +23,7 @@
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};
window.gl.environmentsList.timeagoInstance = new timeago(); // eslint-disable-line
gl.environmentsList.EnvironmentItem = Vue.component('environment-item', {
......@@ -147,15 +148,26 @@
this.model.last_deployment.deployable;
},
/**
* Verifies if the date to be shown is present.
*
* @returns {Boolean|Undefined}
*/
canShowDate() {
return this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable !== undefined;
},
/**
* Human readable date.
*
* @returns {String}
*/
createdDate() {
const timeagoInstance = new timeago(); // eslint-disable-line
return timeagoInstance.format(this.model.created_at);
return window.gl.environmentsList.timeagoInstance.format(
this.model.last_deployment.deployable.created_at,
);
},
/**
......@@ -453,7 +465,7 @@
<td>
<span
v-if="!isFolder && model.last_deployment"
v-if="!isFolder && canShowDate"
class="environment-created-date-timeago">
{{createdDate}}
</span>
......
......@@ -59,12 +59,13 @@
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
var _a, _y, regexp, match;
subtext = subtext.split(' ').pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF");
regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?!\\W)([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
match = regexp.exec(subtext);
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
......
/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */
/*= require autosize */
......
......@@ -80,6 +80,7 @@
border-radius: 0;
border: none;
height: auto;
width: 100%;
margin: 0;
align-self: center;
}
......
......@@ -56,16 +56,9 @@
.md-header {
.nav-links {
.active {
a {
border-bottom-color: #000;
}
}
a {
padding-top: 0;
line-height: 19px;
border-bottom: 1px solid $border-color;
&.btn.btn-xs {
padding: 2px 5px;
......
......@@ -94,6 +94,7 @@
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
a {
......@@ -103,7 +104,6 @@
&:hover,
&:active,
&:focus {
color: $black;
border-bottom: none;
}
}
......
......@@ -132,7 +132,7 @@
display: none;
}
.btn-clipboard {
.btn-clipboard:hover {
color: $gl-gray;
}
}
......@@ -235,6 +235,10 @@
padding-bottom: 10px;
color: #999;
&:hover {
color: $gl-gray;
}
span {
display: block;
margin-top: 0;
......@@ -244,15 +248,17 @@
display: none;
}
.avatar:hover {
border-color: #999;
}
.btn-clipboard {
border: none;
color: #999;
&:hover {
background: transparent;
}
i {
color: #999;
color: $gl-gray;
}
}
}
......
......@@ -90,14 +90,14 @@ ul.notes {
}
&.system-note-commit-list {
max-height: 63px;
max-height: 70px;
overflow: hidden;
display: block;
ul {
margin: 3px 0 3px 15px !important;
margin: 3px 0 3px 16px !important;
li {
.gfm-commit {
font-family: $monospace_font;
font-size: 12px;
}
......@@ -172,6 +172,10 @@ ul.notes {
&.timeline-entry {
padding: 14px 10px;
}
.system-note {
padding: 0;
}
}
&.is-editting {
......
......@@ -11,7 +11,7 @@ class AutocompleteController < ApplicationController
@users = @users.reorder(:name)
@users = @users.page(params[:page])
if params[:todo_filter].present?
if params[:todo_filter].present? && current_user
@users = @users.todo_authors(current_user.id, params[:todo_state_filter])
end
......
......@@ -325,16 +325,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present?
unless @merge_request.pipeline
unless @merge_request.head_pipeline
@status = :failed
return
end
if @merge_request.pipeline.active?
if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request)
@status = :merge_when_build_succeeds
elsif @merge_request.pipeline.success?
elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
......@@ -398,7 +398,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def ci_status
pipeline = @merge_request.pipeline
pipeline = @merge_request.head_pipeline
if pipeline
status = pipeline.status
coverage = pipeline.try(:coverage)
......@@ -534,7 +535,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_widget_vars
@pipeline = @merge_request.pipeline
@pipeline = @merge_request.head_pipeline
end
def define_commit_vars
......@@ -563,7 +564,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars
@pipelines = @merge_request.all_pipelines
@pipeline = @merge_request.pipeline
@pipeline = @merge_request.head_pipeline
@statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end
......@@ -631,7 +632,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active?
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
def build_merge_request
......
......@@ -338,7 +338,7 @@ module Ci
def merge_requests
@merge_requests ||= project.merge_requests
.where(source_branch: self.ref)
.select { |merge_request| merge_request.pipeline.try(:id) == self.id }
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
private
......
......@@ -2,6 +2,9 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern
included do
belongs_to :protected_branch
delegate :project, to: :protected_branch
scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end
......@@ -9,4 +12,10 @@ module ProtectedBranchAccess
def humanize
self.class.human_access_levels[self.access_level]
end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end
......@@ -678,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
!pipeline || pipeline.success? || pipeline.skipped?
!head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end
def environments
......@@ -774,10 +774,10 @@ class MergeRequest < ActiveRecord::Base
commits.map(&:sha)
end
def pipeline
def head_pipeline
return unless diff_head_sha && source_project
@pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
@head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end
def all_pipelines
......
......@@ -9,6 +9,9 @@ class JiraService < IssueTrackerService
before_update :reset_password
# This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service
# configuration screen.
def supported_events
%w(commit merge_request)
end
......@@ -105,18 +108,29 @@ class JiraService < IssueTrackerService
"#{url}/secure/CreateIssue.jspa"
end
def execute(push, issue = nil)
if issue.nil?
# No specific issue, that means
# we just want to test settings
test_settings
else
jira_issue = jira_request { client.Issue.find(issue.iid) }
def execute(push)
# This method is a no-op, because currently JiraService does not
# support any events.
end
return false unless jira_issue.present?
def close_issue(entity, external_issue)
issue = jira_request { client.Issue.find(external_issue.iid) }
close_issue(push, jira_issue)
end
return if issue.nil? || issue.resolution.present? || !jira_issue_transition_id.present?
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Refresh the issue after transition and check
# if it is closed, so we don't have one comment for every commit.
issue = jira_request { client.Issue.find(issue.key) } if transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url) if issue.resolution
end
def create_cross_reference_note(mentioned, noteable, author)
......@@ -156,6 +170,11 @@ class JiraService < IssueTrackerService
"Please fill in Password and Username."
end
def test(_)
result = test_settings
{ success: result.present?, result: result }
end
def can_test?
username.present? && password.present?
end
......@@ -182,24 +201,6 @@ class JiraService < IssueTrackerService
end
end
def close_issue(entity, issue)
return if issue.nil? || issue.resolution.present? || !jira_issue_transition_id.present?
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Refresh the issue after transition and check
# if it is closed, so we don't have one comment for every commit.
issue = jira_request { client.Issue.find(issue.key) } if transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url) if issue.resolution
end
def transition_issue(issue)
issue.transitions.build.save(transition: { id: jira_issue_transition_id })
end
......
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER] }
......@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
Gitlab::Access::DEVELOPER => "Developers + Masters"
}.with_indifferent_access
end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS] }
......@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
def check_access(user)
return false if access_level == Gitlab::Access::NO_ACCESS
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
super
end
end
......@@ -50,6 +50,7 @@ class ProjectPolicy < BasePolicy
def reporter_access!
can! :download_code
can! :download_wiki_code
can! :fork_project
can! :create_project_snippet
can! :update_issue
......@@ -187,6 +188,7 @@ class ProjectPolicy < BasePolicy
unless project.feature_available?(:wiki, user) || project.has_external_wiki?
cannot!(*named_abilities(:wiki))
cannot!(:download_wiki_code)
end
unless project.feature_available?(:builds, user) && repository_enabled
......@@ -226,6 +228,7 @@ class ProjectPolicy < BasePolicy
can! :read_commit_status
can! :read_container_image
can! :download_code
can! :download_wiki_code
can! :read_cycle_analytics
# NOTE: may be overridden by IssuePolicy
......
......@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end
expose :duration, as: :total_time do |build|
distance_of_time_as_hash(build.duration.to_f)
build.duration ? distance_of_time_as_hash(build.duration.to_f) : {}
end
expose :branch do
......
......@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
path_to(:play_namespace_project_build, build)
end
expose :created_at
expose :updated_at
private
def path_to(route, build)
......
......@@ -2,6 +2,8 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper
def interval_in_words(diff)
return 'Not started' unless diff
"#{distance_of_time_in_words(Time.now, diff)} ago"
end
......
......@@ -45,9 +45,15 @@ module Ci
return error('No builds for this pipeline.')
end
pipeline.save
pipeline.process!
pipeline
Ci::Pipeline.transaction do
pipeline.save
Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
pipeline.tap(&:process!)
end
private
......
......@@ -5,10 +5,7 @@ module Ci
def execute(pipeline)
@pipeline = pipeline
# This method will ensure that our pipeline does have all builds for all stages created
if created_builds.empty?
create_builds!
end
ensure_created_builds! # TODO, remove me in 9.0
new_builds =
stage_indexes_of_created_builds.map do |index|
......@@ -22,10 +19,6 @@ module Ci
private
def create_builds!
Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
end
def process_stage(index)
current_status = status_for_prior_stages(index)
......@@ -76,5 +69,18 @@ module Ci
def created_builds
pipeline.builds.created
end
# This method is DEPRECATED and should be removed in 9.0.
#
# We need it to maintain backwards compatibility with previous versions
# when builds were not created within one transaction with the pipeline.
#
def ensure_created_builds!
return if created_builds.any?
Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
end
end
......@@ -17,7 +17,7 @@ module Issues
# allowed to close the given issue.
def close_issue(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue)
project.jira_service.close_issue(commit, issue)
todo_service.close_issue(issue, current_user)
return issue
end
......
......@@ -55,7 +55,7 @@ module MergeRequests
def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request|
next unless pipeline == merge_request.pipeline
next unless pipeline == merge_request.head_pipeline
yield merge_request
end
......@@ -63,7 +63,7 @@ module MergeRequests
def commit_status_merge_requests(commit_status)
merge_requests_for(commit_status.ref).each do |merge_request|
pipeline = merge_request.pipeline
pipeline = merge_request.head_pipeline
next unless pipeline
next unless pipeline.sha == commit_status.sha
......
......@@ -4,13 +4,13 @@
%ul.nav-links
= nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects
Your projects
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects
Starred projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects
Explore projects
.nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
......
......@@ -4,6 +4,18 @@
Welcome to GitLab
%p.blank-state-text
Code, test, and deploy together
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
.blank-state
.blank-state-icon
= custom_icon("project", size: 50)
......@@ -21,17 +33,6 @@
= link_to new_project_path, class: "btn btn-new" do
New project
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
-if publicish_project_count > 0
.blank-state
.blank-state-icon
......
- page_title "Access Denied"
%h1 403
%h3 Access Denied
%hr
%p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
- content_for(:title, 'Access Denied')
%img{:alt => "GitLab Logo",
:src => image_path('logo.svg')}
%h1
403
.container
%h3 Access Denied
%hr
%p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
- page_title "Encoding Error"
%h1 500
%h3 Encoding Error
%hr
%p Page can't be loaded because of an encoding error.
- content_for(:title, 'Encoding Error')
%img{:alt => "GitLab Logo",
:src => image_path('logo.svg')}
%h1
500
.container
%h3 Encoding Error
%hr
%p Page can't be loaded because of an encoding error.
- page_title "Git Resource Not Found"
%h1 404
%h3 Git Resource Not found
%hr
%p
Application can't get access to some branch or commit in your repository. It
may have been moved.
- content_for(:title, 'Git Resource Not Found')
%img{:alt => "GitLab Logo",
:src => image_path('logo.svg')}
%h1
404
.container
%h3 Git Resource Not found
%hr
%p Application can't get access to some branch or commit in your repository. It
may have been moved
- page_title "Not Found"
%h1 404
%h3 The resource you were looking for doesn't exist.
%hr
%p You may have mistyped the address or the page may have moved.
- content_for(:title, 'Not Found')
%img{:alt => "GitLab Logo",
:src => image_path('logo.svg')}
%h1
404
.container
%h3 The resource you were looking for doesn't exist.
%hr
%p You may have mistyped the address or the page may have moved.
- page_title "Auth Error"
%h1 422
%h3 Sign-in using #{@provider} auth failed
%hr
%p Sign-in failed because #{@error}.
%p There are couple of steps you can take:
- content_for(:title, 'Auth Error')
%img{:alt => "GitLab Logo",
:src => image_path('logo.svg')}
%h1
422
.container
%h3 Sign-in using #{@provider} auth failed
%hr
%p Sign-in failed because #{@error}.
%p There are couple of steps you can take:
%ul
%li Try logging in using your email
......
......@@ -6,7 +6,7 @@
- if inviter = @member.created_by
by
= link_to inviter.name, user_url(inviter)
to join
to join
- case @member.source
- when Project
- project = @member.source
......@@ -20,11 +20,18 @@
= link_to group.name, group_url(group)
as #{@member.human_access}.
- if @member.source.users.include?(current_user)
- is_member = @member.source.users.include?(current_user)
- if is_member
%p
However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}.
Sign in using a different account to accept the invitation.
- else
- if @member.invite_email != current_user.email
%p
Note that this invitation was sent to #{mail_to @member.invite_email}, but you are signed in as #{link_to current_user.to_reference, user_url(current_user)} with email #{mail_to current_user.email}.
- unless is_member
.actions
= link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success"
= link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10"
!!! 5
%html{ lang: "en"}
= render "layouts/head"
%body{class: "#{user_application_theme} application navless"}
= Gon::Base.render_data
= render "layouts/header/empty"
.container.navless-container
= render "layouts/flash"
.error-page
= yield
%head
%meta{:content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport"}
%title= yield(:title)
:css
body {
color: #666;
text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
font-size: 14px;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: normal;
color: #456;
}
h2 {
font-size: 24px;
color: #666;
line-height: 1.5em;
}
h3 {
color: #456;
font-size: 20px;
font-weight: normal;
line-height: 28px;
}
hr {
max-width: 800px;
margin: 18px auto;
border: 0;
border-top: 1px solid #EEE;
border-bottom: 1px solid white;
}
img {
max-width: 40vw;
display: block;
margin: 40px auto;
}
.container {
margin: auto 20px;
}
ul {
margin: auto;
text-align: left;
display:inline-block;
}
%body
= yield
......@@ -66,8 +66,6 @@
%td
- if build.project
= link_to build.project.name_with_namespace, admin_namespace_project_path(build.project.namespace, build.project)
- if admin
%td
- if build.try(:runner)
= runner_link(build.runner)
......@@ -93,9 +91,8 @@
%span #{time_ago_with_tooltip(build.finished_at)}
%td.coverage
- if coverage
- if build.try(:coverage)
#{build.coverage}%
- if coverage && build.try(:coverage)
#{build.coverage}%
%td
.pull-right
......
%tr.generic_commit_status
- admin = local_assigns.fetch(:admin, false)
- ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false)
- pipeline_link = local_assigns.fetch(:pipeline_link, false)
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
%tr.generic_commit_status{class: ('retried' if retried)}
%td.status
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url
= ci_status_with_icon(generic_commit_status.status, generic_commit_status.target_url)
......@@ -8,14 +16,35 @@
%td.generic_commit_status-link
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url
= link_to generic_commit_status.target_url do
%strong ##{generic_commit_status.id}
%span.build-link ##{generic_commit_status.id}
- else
%strong ##{generic_commit_status.id}
%span.build-link ##{generic_commit_status.id}
- if defined?(retried) && retried
- if ref
- if generic_commit_status.ref
.icon-container
= generic_commit_status.tags.any? ? icon('tag') : icon('code-fork')
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
- else
.light none
.icon-container.commit-icon
= custom_icon("icon_commit")
- if commit_sha
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "commit-id monospace"
- if retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.')
- if defined?(pipeline_link) && pipeline_link
.label-container
- if generic_commit_status.tags.any?
- generic_commit_status.tags.each do |tag|
%span.label.label-primary
= tag
- if retried
%span.label.label-warning retried
- if pipeline_link
%td
= link_to pipeline_path(generic_commit_status.pipeline) do
%span.pipeline-id ##{generic_commit_status.pipeline.id}
......@@ -25,25 +54,17 @@
- else
%span.monospace API
- if defined?(commit_sha) && commit_sha
%td
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
- if defined?(ref) && ref
- if admin
%td
- if generic_commit_status.ref
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
- else
.light none
- if defined?(runner) && runner
- if generic_commit_status.project
= link_to generic_commit_status.project.name_with_namespace, admin_namespace_project_path(generic_commit_status.project.namespace, generic_commit_status.project)
%td
- if generic_commit_status.try(:runner)
= runner_link(generic_commit_status.runner)
- else
.light none
- if defined?(stage) && stage
- if stage
%td
= generic_commit_status.stage
......@@ -51,24 +72,19 @@
= generic_commit_status.name
%td
- if generic_commit_status.tags.any?
- generic_commit_status.tags.each do |tag|
%span.label.label-primary
= tag
- if defined?(retried) && retried
%span.label.label-warning retried
%td.duration
- if generic_commit_status.duration
= icon("clock-o")
= time_interval_in_words(generic_commit_status.duration)
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(generic_commit_status.duration)
%td.timestamp
- if generic_commit_status.finished_at
= icon("calendar")
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
- if generic_commit_status.try(:coverage)
#{generic_commit_status.coverage}%
%td.coverage
- if coverage && generic_commit_status.try(:coverage)
#{generic_commit_status.coverage}%
%td
-# empty column to match number of columns in ci/builds/_build.html.haml
......@@ -2,12 +2,12 @@
%h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list.related-merge-requests
- has_any_ci = @merge_requests.any?(&:pipeline)
- has_any_ci = @merge_requests.any?(&:head_pipeline)
- @merge_requests.each do |merge_request|
%li
%span.merge-request-ci-status
- if merge_request.pipeline
= render_pipeline_status(merge_request.pipeline)
- if merge_request.head_pipeline
= render_pipeline_status(merge_request.head_pipeline)
- elsif has_any_ci
= icon('blank fw')
%span.merge-request-id
......
......@@ -15,9 +15,9 @@
= icon('ban')
CLOSED
- if merge_request.pipeline
- if merge_request.head_pipeline
%li
= render_pipeline_status(merge_request.pipeline)
= render_pipeline_status(merge_request.head_pipeline)
- if merge_request.open? && merge_request.broken?
%li
......
- if @pipeline
.mr-widget-heading
- %w[success success_with_warnings skipped canceled failed running pending].each do |status|
.ci_widget{ class: "ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
.ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
= ci_icon_for_status(status)
%span
Pipeline
......
......@@ -14,7 +14,7 @@
ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}",
ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: {
normal: "Build {{status}} for \"{{title}}\"",
preparing: "{{status}} build for \"{{title}}\""
......
......@@ -40,30 +40,7 @@
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
= icon('question-circle')
- if issuable.is_a?(MergeRequest) && !issuable.closed_without_fork?
%hr
- if @merge_request.new_record?
.form-group
= form.label :source_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
= form.label :target_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record?
&nbsp;
= link_to 'Change branches', mr_change_branches_path(@merge_request)
- if @merge_request.can_remove_source_branch?(current_user)
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
Remove source branch when merge request is accepted.
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
......
- issuable = local_assigns.fetch(:issuable)
- form = local_assigns.fetch(:form)
- return unless issuable.is_a?(MergeRequest)
- return if issuable.closed_without_fork?
%hr
- if issuable.new_record?
.form-group
= form.label :source_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:source_branch, [issuable.source_branch], {}, { class: 'source_branch select2 span2', disabled: true })
.form-group
= form.label :target_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:target_branch, issuable.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: issuable.new_record?, data: { placeholder: "Select branch" }})
- if issuable.new_record?
&nbsp;
= link_to 'Change branches', mr_change_branches_path(issuable)
- if issuable.can_remove_source_branch?(current_user)
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?
Remove source branch when merge request is accepted.
#!/usr/bin/env ruby
require 'stackprof'
$:.unshift 'spec'
require 'rails_helper'
filename = ARGV[0].split('/').last
interval = ENV.fetch('INTERVAL', 1000).to_i
limit = ENV.fetch('LIMIT', 20)
output_file = "tmp/#{filename}.dump"
StackProf.run(mode: :wall, out: output_file, interval: interval) do
RSpec::Core::Runner.run(ARGV, $stderr, $stdout)
end
system("stackprof #{output_file} --text --limit #{limit}")
---
title: New `gitlab:workhorse:install` rake task
merge_request: 6574
author:
---
title: Add Human Readable format for rake backup
merge_request: 7188
author: David Gerő
---
title: Moved new projects button below new group button on the welcome screen
merge_request: 7770
author:
---
title: Update generic/external build status to match normal build status template
merge_request: 7811
author:
---
title: Fixes Environments displaying incorrect date since 8.14 upgrade
merge_request:
author:
---
title: Fixes system note style in commit discussion
merge_request: 7721
author:
---
title: Adjust the width of project avatars to fix alignment within their container
merge_request:
author: Ryan Harris
---
title: Sentence cased the nav tab headers on the project dashboard page
merge_request:
author: Ryan Harris
---
title: Adds hoverstates for collapsed Issue/Merge Request sidebar
merge_request: !7777
author:
---
title: Do not raise error in AutocompleteController#users when not authorized
merge_request: 7817
author: Semyon Pupkov
---
title: Fix pipelines info being hidden in merge request widget
merge_request: 7808
author:
---
title: 'API: Expose merge status for branch API'
merge_request:
author: Robert Schilling
---
title: Refactor JiraService by moving code out of JiraService#execute method
merge_request: 7756
author:
---
title: Fix for error thrown in cycle analytics events if build has not started
merge_request:
author:
---
title: Create builds in transaction to avoid empty pipelines
merge_request: 7742
author:
---
title: Allow access to the wiki with git when repository feature disabled
merge_request:
author:
---
title: Add note to the invite page when the logged in user email is not the same as the invitation
merge_request:
author:
---
title: Fix appearance in error pages
merge_request:
author: Luis Alonso Chavez Armendariz
---
title: Changed eslint airbnb config to the base airbnb config and corrected eslintrc
plugins and envs
merge_request: 7470
author: Luke "Jared" Bennett
---
title: Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1
merge_request:
author:
# rubocop:disable all
class CreateForkedProjectLinks < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :forked_project_links do |t|
t.integer :forked_to_project_id, null: false
t.integer :forked_from_project_id, null: false
t.timestamps
t.timestamps null: true
end
add_index :forked_project_links, :forked_to_project_id, unique: true
end
......
# rubocop:disable all
class CreateDeployKeysProjects < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :deploy_keys_projects do |t|
t.integer :deploy_key_id, null: false
t.integer :project_id, null: false
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateUsersGroups < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :users_groups do |t|
t.integer :group_access, null: false
t.integer :group_id, null: false
t.integer :user_id, null: false
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateProjectGroupLinks < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :project_group_links do |t|
t.integer :project_id, null: false
t.integer :group_id, null: false
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateBroadcastMessages < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :broadcast_messages do |t|
t.text :message, null: false
......@@ -7,7 +9,7 @@ class CreateBroadcastMessages < ActiveRecord::Migration
t.datetime :ends_at
t.integer :alert_type
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateMergeRequestDiffs < ActiveRecord::Migration
DOWNTIME = false
def up
create_table :merge_request_diffs do |t|
t.string :state, null: false, default: 'collected'
......@@ -7,7 +9,7 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration
t.text :st_diffs, null: true
t.integer :merge_request_id, null: false
t.timestamps
t.timestamps null: true
end
if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
......
# rubocop:disable all
class CreateEmails < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :emails do |t|
t.integer :user_id, null: false
t.string :email, null: false
t.timestamps
t.timestamps null: true
end
add_index :emails, :user_id
......
# rubocop:disable all
class CreateUsersStarProjects < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :users_star_projects do |t|
t.integer :project_id, null: false
t.integer :user_id, null: false
t.timestamps
t.timestamps null: true
end
add_index :users_star_projects, :user_id
add_index :users_star_projects, :project_id
......
# rubocop:disable all
class CreateLabels < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :labels do |t|
t.string :title
t.string :color
t.integer :project_id
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateLabelLinks < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :label_links do |t|
t.integer :label_id
t.integer :target_id
t.string :target_type
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class AddMembersTable < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :members do |t|
t.integer :access_level, null: false
......@@ -9,7 +11,7 @@ class AddMembersTable < ActiveRecord::Migration
t.integer :notification_level, null: false
t.string :type
t.timestamps
t.timestamps null: true
end
add_index :members, :type
......
# rubocop:disable all
class RemoveOldMemberTables < ActiveRecord::Migration
DOWNTIME = false
def up
drop_table :users_groups
drop_table :users_projects
......@@ -12,7 +14,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3
t.timestamps
t.timestamps null: true
end
create_table :users_projects do |t|
......@@ -21,7 +23,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class AddAuditEvent < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :audit_events do |t|
t.integer :author_id, null: false
......@@ -13,7 +15,7 @@ class AddAuditEvent < ActiveRecord::Migration
# Details for the event
t.text :details
t.timestamps
t.timestamps null: true
end
add_index :audit_events, :author_id
......
# rubocop:disable all
class CreateDoorkeeperTables < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :oauth_applications do |t|
t.string :name, null: false
......@@ -7,7 +9,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
t.string :secret, null: false
t.text :redirect_uri, null: false
t.string :scopes, null: false, default: ''
t.timestamps
t.timestamps null: true
end
add_index :oauth_applications, :uid, unique: true
......
# rubocop:disable all
class CreateApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :application_settings do |t|
t.integer :default_projects_limit
......@@ -8,7 +10,7 @@ class CreateApplicationSettings < ActiveRecord::Migration
t.boolean :gravatar_enabled
t.text :sign_in_text
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateSubscriptionsTable < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :subscriptions do |t|
t.integer :user_id
t.references :subscribable, polymorphic: true
t.boolean :subscribed
t.timestamps
t.timestamps null: true
end
add_index :subscriptions,
add_index :subscriptions,
[:subscribable_id, :subscribable_type, :user_id],
unique: true,
name: 'subscriptions_user_id_and_ref_fields'
......
# rubocop:disable all
class CreateAbuseReports < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :abuse_reports do |t|
t.integer :reporter_id
t.integer :user_id
t.text :message
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateLfsObjects < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :lfs_objects do |t|
t.string :oid, null: false, unique: true
t.integer :size, null: false
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class CreateLfsObjectsProjects < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :lfs_objects_projects do |t|
t.integer :lfs_object_id, null: false
t.integer :project_id, null: false
t.timestamps
t.timestamps null: true
end
add_index :lfs_objects_projects, :project_id
......
# rubocop:disable all
class CreateReleases < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :releases do |t|
t.string :tag
t.text :description
t.integer :project_id
t.timestamps
t.timestamps null: true
end
add_index :releases, :project_id
......
# rubocop:disable all
class CreateTasks < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :tasks do |t|
t.references :user, null: false, index: true
......@@ -9,7 +11,7 @@ class CreateTasks < ActiveRecord::Migration
t.integer :action, null: false
t.string :state, null: false, index: true
t.timestamps
t.timestamps null: true
end
end
end
# rubocop:disable all
class AddAwardEmoji < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :award_emoji do |t|
t.string :name
t.references :user
t.references :awardable, polymorphic: true
t.timestamps
t.timestamps null: true
end
add_index :award_emoji, :user_id
......
......@@ -10,7 +10,7 @@ class CreateProjectFeatures < ActiveRecord::Migration
t.integer :snippets_access_level
t.integer :builds_access_level
t.timestamps
t.timestamps null: true
end
end
end
......@@ -22,6 +22,7 @@ Example response:
[
{
"name": "master",
"merged": false,
"protected": true,
"developers_can_push": false,
"developers_can_merge": false,
......@@ -65,6 +66,7 @@ Example response:
```json
{
"name": "master",
"merged": false,
"protected": true,
"developers_can_push": false,
"developers_can_merge": false,
......@@ -123,6 +125,7 @@ Example response:
]
},
"name": "master",
"merged": false,
"protected": true,
"developers_can_push": true,
"developers_can_merge": true
......@@ -166,6 +169,7 @@ Example response:
]
},
"name": "master",
"merged": false,
"protected": false,
"developers_can_push": false,
"developers_can_merge": false
......@@ -206,6 +210,7 @@ Example response:
]
},
"name": "newbranch",
"merged": false,
"protected": false,
"developers_can_push": false,
"developers_can_merge": false
......
......@@ -626,6 +626,7 @@ Parameters:
| `path` | string | no | Custom repository name for new project. By default generated based on name |
| `default_branch` | string | no | `master` by default |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
......
......@@ -60,7 +60,7 @@ Parameters:
- `file_path` (required) - Full path to new file. Ex. lib/class.rb
- `branch_name` (required) - The name of branch
- `encoding` (optional) - 'text' or 'base64'. Text is default.
- `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address
- `author_name` (optional) - Specify the commit author's name
- `content` (required) - File content
......@@ -89,7 +89,7 @@ Parameters:
- `file_path` (required) - Full path to file. Ex. lib/class.rb
- `branch_name` (required) - The name of branch
- `encoding` (optional) - 'text' or 'base64'. Text is default.
- `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address
- `author_name` (optional) - Specify the commit author's name
- `content` (required) - New file content
......
......@@ -21,6 +21,7 @@
- [CI services (linked docker containers)](services/README.md)
- [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [Review Apps](review_apps/index.md)
- [Git submodules](git_submodules.md) Using Git submodules in your CI jobs
## Breaking changes
......
# Using Git submodules with GitLab CI
> **Notes:**
- GitLab 8.12 introduced a new [CI build permissions model][newperms] and you
are encouraged to upgrade your GitLab instance if you haven't done already.
If you are **not** using GitLab 8.12 or higher, you would need to work your way
around submodules in order to access the sources of e.g., `gitlab.com/group/project`
with the use of [SSH keys](ssh_keys/README.md).
- With GitLab 8.12 onward, your permissions are used to evaluate what a CI build
can access. More information about how this system works can be found in the
[Build permissions model](../user/permissions.md#builds-permissions).
- The HTTP(S) Git protocol [must be enabled][gitpro] in your GitLab instance.
## Configuring the `.gitmodules` file
If dealing with [Git submodules][gitscm], your project will probably have a file
named `.gitmodules`.
Let's consider the following example:
1. Your project is located at `https://gitlab.com/secret-group/my-project`.
1. To checkout your sources you usually use an SSH address like
`git@gitlab.com:secret-group/my-project.git`.
1. Your project depends on `https://gitlab.com/group/project`, which you want
to include as a submodule.
If you are using GitLab 8.12+ and your submodule is on the same GitLab server,
you must update your `.gitmodules` file to use **relative URLs**.
Since Git allows the usage of relative URLs for your `.gitmodules` configuration,
this easily allows you to use HTTP(S) for cloning all your CI builds and SSH
for all your local checkouts. The `.gitmodules` would look like:
```ini
[submodule "project"]
path = project
url = ../../group/project.git
```
The above configuration will instruct Git to automatically deduce the URL that
should be used when cloning sources. Whether you use HTTP(S) or SSH, Git will use
that same channel and it will allow to make all your CI builds use HTTP(S)
(because GitLab CI only uses HTTP(S) for cloning your sources), and all your local
clones will continue using SSH.
For all other submodules not located on the same GitLab server, use the full
HTTP(S) protocol URL:
```ini
[submodule "project-x"]
path = project-x
url = https://gitserver.com/group/project-x.git
```
Once `.gitmodules` is correctly configured, you can move on to
[configuring your `.gitlab-ci.yml`](#using-git-submodules-in-your-ci-jobs).
## Using Git submodules in your CI jobs
There are a few steps you need to take in order to make submodules work
correctly with your CI builds:
1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file)
for the submodules located in the same GitLab server.
1. Then, use `git submodule sync/update` in `before_script`:
```yaml
before_script:
- git submodule sync --recursive
- git submodule update --init --recursive
```
`--recursive` should be used in either both or none (`sync/update`) depending on
whether you have recursive submodules.
The rationale to set the `sync` and `update` in `before_script` is because of
the way Git submodules work. On a fresh Runner workspace, Git will set the
submodule URL including the token in `.git/config`
(or `.git/modules/<submodule>/config`) based on `.gitmodules` and the current
remote URL. On subsequent builds on the same Runner, `.git/config` is cached
and already contains a full URL for the submodule, corresponding to the previous
build, and to **a token from a previous build**. `sync` allows to force updating
the full URL.
[gitpro]: ../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols
[gitscm]: https://git-scm.com/book/en/v2/Git-Tools-Submodules "Git submodules documentation"
[newperms]: ../user/project/new_ci_build_permissions_model.md
......@@ -101,6 +101,116 @@ In short:
5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
`Benchmark` module.
## Profiling
By collecting snapshots of process state at regular intervals, profiling allows
you to see where time is spent in a process. The [StackProf](https://github.com/tmm1/stackprof)
gem is included in GitLab's development environment, allowing you to investigate
the behaviour of suspect code in detail.
It's important to note that profiling an application *alters its performance*,
and will generally be done *in an unrepresentative environment*. In particular,
a method is not necessarily troublesome just because it is executed many times,
or takes a long time to execute. Profiles are tools you can use to better
understand what is happening in an application - using that information wisely
is up to you!
Keeping that in mind, to create a profile, identify (or create) a spec that
exercises the troublesome code path, then run it using the `bin/rspec-stackprof`
helper, e.g.:
```
$ LIMIT=10 bin/rspec-stackprof spec/policies/project_policy_spec.rb
8/8 |====== 100 ======>| Time: 00:00:18
Finished in 18.19 seconds (files took 4.8 seconds to load)
8 examples, 0 failures
==================================
Mode: wall(1000)
Samples: 17033 (5.59% miss rate)
GC: 1901 (11.16%)
==================================
TOTAL (pct) SAMPLES (pct) FRAME
6000 (35.2%) 2566 (15.1%) Sprockets::Cache::FileStore#get
2018 (11.8%) 888 (5.2%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
1338 (7.9%) 640 (3.8%) ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#execute
3125 (18.3%) 394 (2.3%) Sprockets::Cache::FileStore#safe_open
913 (5.4%) 301 (1.8%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_cache
288 (1.7%) 288 (1.7%) ActiveRecord::Attribute#initialize
246 (1.4%) 246 (1.4%) Sprockets::Cache::FileStore#safe_stat
295 (1.7%) 193 (1.1%) block (2 levels) in class_attribute
187 (1.1%) 187 (1.1%) block (4 levels) in class_attribute
```
You can limit the specs that are run by passing any arguments `rspec` would
normally take.
The output is sorted by the `Samples` column by default. This is the number of
samples taken where the method is the one currently being executed. The `Total`
column shows the number of samples taken where the method, or any of the methods
it calls, were being executed.
To create a graphical view of the call stack:
```shell
$ stackprof tmp/project_policy_spec.rb.dump --graphviz > project_policy_spec.dot
$ dot -Tsvg project_policy_spec.dot > project_policy_spec.svg
```
To load the profile in [kcachegrind](https://kcachegrind.github.io/):
```
$ stackprof tmp/project_policy_spec.dump --callgrind > project_policy_spec.callgrind
$ kcachegrind project_policy_spec.callgrind # Linux
$ qcachegrind project_policy_spec.callgrind # Mac
```
It may be useful to zoom in on a specific method, e.g.:
```
$ stackprof tmp/project_policy_spec.rb.dump --method warm_asset_cache
TestEnv#warm_asset_cache (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/spec/support/test_env.rb:164)
samples: 0 self (0.0%) / 6288 total (36.9%)
callers:
6288 ( 100.0%) block (2 levels) in <top (required)>
callees (6288 total):
6288 ( 100.0%) Capybara::RackTest::Driver#visit
code:
| 164 | def warm_asset_cache
| 165 | return if warm_asset_cache?
| 166 | return unless defined?(Capybara)
| 167 |
6288 (36.9%) | 168 | Capybara.current_session.driver.visit '/'
| 169 | end
$ stackprof tmp/project_policy_spec.rb.dump --method BasePolicy#abilities
BasePolicy#abilities (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/app/policies/base_policy.rb:79)
samples: 0 self (0.0%) / 50 total (0.3%)
callers:
25 ( 50.0%) BasePolicy.abilities
25 ( 50.0%) BasePolicy#collect_rules
callees (50 total):
25 ( 50.0%) ProjectPolicy#rules
25 ( 50.0%) BasePolicy#collect_rules
code:
| 79 | def abilities
| 80 | return RuleSet.empty if @user && @user.blocked?
| 81 | return anonymous_abilities if @user.nil?
50 (0.3%) | 82 | collect_rules { rules }
| 83 | end
```
Since the profile includes the work done by the test suite as well as the
application code, these profiles can be used to investigate slow tests as well.
However, for smaller runs (like this example), this means that the cost of
setting up the test suite will tend to dominate.
It's also possible to modify the application code in-place to output profiles
whenever a particular code path is triggered without going through the test
suite first. See the
[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
for details.
## Importance of Changes
When working on performance improvements, it's important to always ask yourself
......
......@@ -175,7 +175,7 @@ We recommend using a PostgreSQL database. For MySQL check the
```bash
sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;"
```
1. Create the `pg_trgm` extension (required for GitLab 8.6+):
```bash
......@@ -396,15 +396,25 @@ GitLab Shell is an SSH access and repository management software developed speci
### Install gitlab-workhorse
GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/).
If you are not using Linux you may have to run `gmake` instead of
`make` below.
GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). The
following command-line will install GitLab-Workhorse in `/home/git/gitlab-workhorse`
which is the recommended location.
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
sudo -u git -H git checkout v1.0.1
sudo -u git -H make
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
You can specify a different Git repository by providing `GITLAB_WORKHORSE_REPO`:
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" GITLAB_WORKHORSE_REPO=https://example.com/gitlab-workhorse.git RAILS_ENV=production
You can specify a different version to use by providing `GITLAB_WORKHORSE_VERSION`:
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" GITLAB_WORKHORSE_VERSION=0.8.1 RAILS_ENV=production
### Initialize Database and Activate Advanced Features
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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