Commit 5aa4d7bf authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 42643-persist-external-ip-of-ingress-controller-gke

* master: (30 commits)
  Docs: Pages - clean up articles
  Only use features for events
  Put all event metrics exposed to prometheus behind a feature flag
  Add version which LFS lock was introduced
  Remove unecessary validate: true from belongs_to :project
  fix broken specs
  remove common_d3 bundle
  remove graphs_show webpack bundle
  Chart.html.haml refactor
  Remove extraneous tests from Issues API spec
  [GH Import] Create an empty wiki if wiki import failed
  Resolve "group request membership mail with too long list of "To:""
  Add changelog entry
  Fix single digit value clipping
  specify date format
  Increase feature flag cache TTL to one hour
  Convert Gitaly commit parent IDs to array as early as possible
  Clarify changelog for squash encoding fix
  Avoid slow File Lock checks when not used
  Fix squash rebase not working when diff contained encoded data
  ...
parents 3619c917 b876a52b
import Chart from 'vendor/Chart';
// export to global scope
window.Chart = Chart;
import Chart from 'vendor/Chart'; import Chart from 'chart.js';
import _ from 'underscore'; import _ from 'underscore';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
......
import flash from '../flash'; import flash from '~/flash';
import { __ } from '../locale'; import { __ } from '~/locale';
import axios from '../lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import ContributorsStatGraph from './stat_graph_contributors'; import ContributorsStatGraph from './stat_graph_contributors';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
import _ from 'underscore'; import _ from 'underscore';
import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util'; import ContributorsStatGraphUtil from './stat_graph_contributors_util';
import { n__, s__, createDateTimeFormat, sprintf } from '../locale';
export default (function() { export default (function() {
function ContributorsStatGraph() { function ContributorsStatGraph() {
......
...@@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis'; ...@@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis';
import { area } from 'd3-shape'; import { area } from 'd3-shape';
import { brushX } from 'd3-brush'; import { brushX } from 'd3-brush';
import { timeParse } from 'd3-time-format'; import { timeParse } from 'd3-time-format';
import { dateTickFormat } from '../lib/utils/tick_formats'; import { dateTickFormat } from '~/lib/utils/tick_formats';
const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse }; const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse };
......
import Chart from 'vendor/Chart'; import Chart from 'chart.js';
const options = { const options = {
scaleOverlay: true, scaleOverlay: true,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.status-neutral, .status-neutral,
.status-red, { .status-red, {
height: 100%; height: 100%;
min-width: 25px; min-width: 30px;
padding: 0 5px; padding: 0 5px;
font-size: $tooltip-font-size; font-size: $tooltip-font-size;
font-weight: normal; font-weight: normal;
......
...@@ -7,18 +7,11 @@ module Emails ...@@ -7,18 +7,11 @@ module Emails
helper_method :member_source, :member helper_method :member_source, :member
end end
def member_access_requested_email(member_source_type, member_id) def member_access_requested_email(member_source_type, member_id, recipient_notification_email)
@member_source_type = member_source_type @member_source_type = member_source_type
@member_id = member_id @member_id = member_id
admins = member_source.members.owners_and_masters.pluck(:notification_email) mail(to: recipient_notification_email,
# A project in a group can have no explicit owners/masters, in that case
# we fallbacks to the group's owners/masters.
if admins.empty? && member_source.respond_to?(:group) && member_source.group
admins = member_source.group.members.owners_and_masters.pluck(:notification_email)
end
mail(to: admins,
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end end
......
...@@ -417,6 +417,10 @@ class Commit ...@@ -417,6 +417,10 @@ class Commit
!!(title =~ WIP_REGEX) !!(title =~ WIP_REGEX)
end end
def merged_merge_request?(user)
!!merged_merge_request(user)
end
private private
def commit_reference(from, referable_commit_id, full: false) def commit_reference(from, referable_commit_id, full: false)
...@@ -445,10 +449,6 @@ class Commit ...@@ -445,10 +449,6 @@ class Commit
changes changes
end end
def merged_merge_request?(user)
!!merged_merge_request(user)
end
def merged_merge_request_no_cache(user) def merged_merge_request_no_cache(user)
MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit?
end end
......
class Deployment < ActiveRecord::Base class Deployment < ActiveRecord::Base
include InternalId include InternalId
belongs_to :project, required: true, validate: true belongs_to :project, required: true
belongs_to :environment, required: true, validate: true belongs_to :environment, required: true
belongs_to :user belongs_to :user
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
......
...@@ -4,7 +4,7 @@ class Environment < ActiveRecord::Base ...@@ -4,7 +4,7 @@ class Environment < ActiveRecord::Base
NUMBERS = '0'..'9' NUMBERS = '0'..'9'
SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a
belongs_to :project, required: true, validate: true belongs_to :project, required: true
has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......
...@@ -59,6 +59,8 @@ class User < ActiveRecord::Base ...@@ -59,6 +59,8 @@ class User < ActiveRecord::Base
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour # to limit database writes to at most once every hour
def update_tracked_fields!(request) def update_tracked_fields!(request)
return if Gitlab::Database.read_only?
update_tracked_fields(request) update_tracked_fields(request)
lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i) lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i)
......
...@@ -208,7 +208,12 @@ class NotificationService ...@@ -208,7 +208,12 @@ class NotificationService
def new_access_request(member) def new_access_request(member)
return true unless member.notifiable?(:subscription) return true unless member.notifiable?(:subscription)
mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later recipients = member.source.members.owners_and_masters
if fallback_to_group_owners_masters?(recipients, member)
recipients = member.source.group.members.owners_and_masters
end
recipients.each { |recipient| deliver_access_request_email(recipient, member) }
end end
def decline_access_request(member) def decline_access_request(member)
...@@ -435,4 +440,14 @@ class NotificationService ...@@ -435,4 +440,14 @@ class NotificationService
def notifiable_users(*args) def notifiable_users(*args)
NotificationRecipientService.notifiable_users(*args) NotificationRecipientService.notifiable_users(*args)
end end
def deliver_access_request_email(recipient, member)
mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
end
def fallback_to_group_owners_masters?(recipients, member)
return false if recipients.present?
member.source.respond_to?(:group) && member.source.group
end
end end
- if inject_u2f_api? - if inject_u2f_api?
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('u2f') = webpack_bundle_tag('u2f')
%div %div
= render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication' = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication'
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('profile') = webpack_bundle_tag('profile')
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
- if inject_u2f_api? - if inject_u2f_api?
= page_specific_javascript_bundle_tag('u2f') = webpack_bundle_tag('u2f')
= page_specific_javascript_bundle_tag('two_factor_auth') = webpack_bundle_tag('two_factor_auth')
.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } .js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
.row.prepend-top-default .row.prepend-top-default
......
...@@ -29,4 +29,4 @@ ...@@ -29,4 +29,4 @@
= commit_in_fork_help = commit_in_fork_help
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('blob') = webpack_bundle_tag('blob')
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- page_title "Edit", @blob.path, @ref - page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('blob') = webpack_bundle_tag('blob')
%div{ class: container_class } %div{ class: container_class }
- if @conflict - if @conflict
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- page_title "New File", @path.presence, @ref - page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('blob') = webpack_bundle_tag('blob')
.editor-title-row .editor-title-row
%h3.page-title.blob-new-page-title %h3.page-title.blob-new-page-title
New file New file
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('balsamiq_viewer') = webpack_bundle_tag('balsamiq_viewer')
.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } } .file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('notebook_viewer') = webpack_bundle_tag('notebook_viewer')
.file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } } .file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('pdf_viewer') = webpack_bundle_tag('pdf_viewer')
.file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } } .file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('sketch_viewer') = webpack_bundle_tag('sketch_viewer')
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } } .file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' } .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('stl_viewer') = webpack_bundle_tag('stl_viewer')
.file-content.is-stl-loading .file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } } .text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
......
...@@ -8,5 +8,5 @@ ...@@ -8,5 +8,5 @@
} } } }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('commit_pipelines') = webpack_bundle_tag('commit_pipelines')
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description - page_description @commit.description
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('diff_notes') = webpack_bundle_tag('diff_notes')
.container-fluid{ class: [limited_container_width, container_class] } .container-fluid{ class: [limited_container_width, container_class] }
= render "commit_box" = render "commit_box"
......
- @no_container = true - @no_container = true
- page_title "Cycle Analytics" - page_title "Cycle Analytics"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('cycle_analytics') = webpack_bundle_tag('cycle_analytics')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data - if @cycle_analytics_no_data
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
- page_title "Environments" - page_title "Environments"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag("environments_folder") = webpack_bundle_tag("environments_folder")
#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), #environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json),
"folder-name" => @folder, "folder-name" => @folder,
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag("common_vue") = webpack_bundle_tag("common_vue")
= page_specific_javascript_bundle_tag("environments") = webpack_bundle_tag("environments")
#environments-list-view{ data: { environments_data: environments_list_data, #environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
- page_title "Metrics for environment", @environment.name - page_title "Metrics for environment", @environment.name
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'common_d3'
.prometheus-container{ class: container_class } .prometheus-container{ class: container_class }
.top-area .top-area
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm/xterm" = stylesheet_link_tag "xterm/xterm"
= page_specific_javascript_bundle_tag("terminal") = webpack_bundle_tag("terminal")
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area
......
- @no_container = true - @no_container = true
- page_title "Charts" - page_title "Charts"
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_d3')
= webpack_bundle_tag('graphs')
= webpack_bundle_tag('graphs_charts')
.repo-charts{ class: container_class } .repo-charts{ class: container_class }
%h4.sub-header %h4.sub-header
......
- @no_container = true - @no_container = true
- page_title _('Contributors') - page_title _('Contributors')
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_d3')
= webpack_bundle_tag('graphs')
= webpack_bundle_tag('graphs_show')
.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) }
.sub-header-block .sub-header-block
......
...@@ -87,5 +87,5 @@ ...@@ -87,5 +87,5 @@
= render 'shared/issuable/sidebar', issuable: @issue = render 'shared/issuable/sidebar', issuable: @issue
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('issue_show') = webpack_bundle_tag('issue_show')
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('merge_conflicts') = webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title" = render "projects/merge_requests/mr_title"
......
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('merge_conflicts') = webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title" = render "projects/merge_requests/mr_title"
......
- breadcrumb_title "Graph" - breadcrumb_title "Graph"
- page_title "Graph", @ref - page_title "Graph", @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('network') = webpack_bundle_tag('network')
= render "head" = render "head"
%div{ class: container_class } %div{ class: container_class }
.project-network .project-network
......
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
"ci-lint-path" => ci_lint_path, "ci-lint-path" => ci_lint_path,
"reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } } "reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } }
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('pipelines') = webpack_bundle_tag('pipelines')
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('protected_branches') = webpack_bundle_tag('protected_branches')
- content_for :create_protected_branch do - content_for :create_protected_branch do
= render 'projects/protected_branches/create_protected_branch' = render 'projects/protected_branches/create_protected_branch'
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('protected_tags') = webpack_bundle_tag('protected_tags')
- content_for :create_protected_tag do - content_for :create_protected_tag do
= render 'projects/protected_tags/create_protected_tag' = render 'projects/protected_tags/create_protected_tag'
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
.col-lg-12 .col-lg-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } } #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } }
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('registry_list') = webpack_bundle_tag('registry_list')
.row.prepend-top-10 .row.prepend-top-10
.col-lg-12 .col-lg-12
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('deploy_keys') = webpack_bundle_tag('deploy_keys')
-# Protected branches & tags use a lot of nested partials. -# Protected branches & tags use a lot of nested partials.
-# The shared parts of the views can be found in the `shared` directory. -# The shared parts of the views can be found in the `shared` directory.
......
- todo = issuable_todo(issuable) - todo = issuable_todo(issuable)
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('sidebar') = webpack_bundle_tag('sidebar')
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } } .issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } }
......
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('snippet') = webpack_bundle_tag('snippet')
.snippet-form-holder .snippet-form-holder
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f| = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
......
...@@ -23,27 +23,25 @@ class ProcessCommitWorker ...@@ -23,27 +23,25 @@ class ProcessCommitWorker
return unless user return unless user
commit = build_commit(project, commit_hash) commit = build_commit(project, commit_hash)
author = commit.author || user author = commit.author || user
process_commit_message(project, commit, user, author, default) process_commit_message(project, commit, user, author, default)
update_issue_metrics(commit, author) update_issue_metrics(commit, author)
end end
def process_commit_message(project, commit, user, author, default = false) def process_commit_message(project, commit, user, author, default = false)
closed_issues = default ? commit.closes_issues(user) : [] # this is a GitLab generated commit message, ignore it.
return if commit.merged_merge_request?(user)
unless closed_issues.empty? closed_issues = default ? commit.closes_issues(user) : []
close_issues(project, user, author, commit, closed_issues)
end
close_issues(project, user, author, commit, closed_issues) if closed_issues.any?
commit.create_cross_references!(author, closed_issues) commit.create_cross_references!(author, closed_issues)
end end
def close_issues(project, user, author, commit, issues) def close_issues(project, user, author, commit, issues)
# We don't want to run permission related queries for every single issue, # We don't want to run permission related queries for every single issue,
# therefor we use IssueCollection here and skip the authorization check in # therefore we use IssueCollection here and skip the authorization check in
# Issues::CloseService#execute. # Issues::CloseService#execute.
IssueCollection.new(issues).updatable_by_user(user).each do |issue| IssueCollection.new(issues).updatable_by_user(user).each do |issue|
Issues::CloseService.new(project, author) Issues::CloseService.new(project, author)
......
---
title: Fix duplicate system notes when merging a merge request.
merge_request: 17035
author:
type: fixed
---
title: Fix long list of recipients on group request membership email
merge_request: 17121
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: "[GitHub Import] Create an empty wiki if wiki import failed"
merge_request:
author:
type: fixed
---
title: Increase feature flag cache TTL to one hour
merge_request:
author:
type: performance
---
title: Fix single digit value clipping for stacked progress bar
merge_request: 17217
author:
type: fixed
---
title: 'Remove unecessary validate: true from belongs_to :project'
merge_request:
author:
type: fixed
---
title: Fix squash not working when diff contained non-ASCII data
merge_request:
author:
type: fixed
---
title: Don't attempt to update user tracked fields if database is in read-only
merge_request:
author:
type: fixed
...@@ -8,7 +8,7 @@ Flipper.configure do |config| ...@@ -8,7 +8,7 @@ Flipper.configure do |config|
cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new( cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new(
adapter, adapter,
Rails.cache, Rails.cache,
expires_in: 10.seconds) expires_in: 1.hour)
Flipper.new(cached_adapter) Flipper.new(cached_adapter)
end end
......
...@@ -62,9 +62,6 @@ var config = { ...@@ -62,9 +62,6 @@ var config = {
environments: './environments/environments_bundle.js', environments: './environments/environments_bundle.js',
environments_folder: './environments/folder/environments_folder_bundle.js', environments_folder: './environments/folder/environments_folder_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js',
graphs: './graphs/graphs_bundle.js',
graphs_charts: './graphs/graphs_charts.js',
graphs_show: './graphs/graphs_show.js',
help: './help/help.js', help: './help/help.js',
how_to_merge: './how_to_merge.js', how_to_merge: './how_to_merge.js',
issue_show: './issue_show/index.js', issue_show: './issue_show/index.js',
...@@ -279,20 +276,6 @@ var config = { ...@@ -279,20 +276,6 @@ var config = {
}, },
}), }),
// create cacheable common library bundle for all d3 chunks
new webpack.optimize.CommonsChunkPlugin({
name: 'common_d3',
chunks: [
'graphs',
'graphs_show',
'monitoring',
'users',
],
minChunks: function (module, count) {
return module.resource && /d3-/.test(module.resource);
},
}),
// create cacheable common library bundles // create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
names: ['main', 'common', 'webpack_runtime'], names: ['main', 'common', 'webpack_runtime'],
......
# How to configure LDAP with GitLab CE ---
author: Chris Wilson
author_gitlab: MrChrisW
level: intermediary
article_type: admin guide
date: 2017-05-03
---
> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** admin guide || # How to configure LDAP with GitLab CE
> **Level:** intermediary ||
> **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) ||
> **Publication date:** 2017-05-03
## Introduction ## Introduction
......
--- ---
redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html' redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
author: Fabio Busatto
author_gitlab: bikebilly
level: intermediary
article_type: tutorial
date: 2017-08-15
--- ---
# How to deploy Maven projects to Artifactory with GitLab CI/CD # How to deploy Maven projects to Artifactory with GitLab CI/CD
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) ||
> **Publication date:** 2017-08-15
## Introduction ## Introduction
In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/)
......
--- ---
redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html' redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
author: Mehran Rasulian
author_gitlab: mehranrasulian
level: intermediary
article_type: tutorial
date: 2017-08-31
--- ---
# Test and deploy Laravel applications with GitLab CI/CD and Envoy # Test and deploy Laravel applications with GitLab CI/CD and Envoy
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Mehran Rasulian](https://gitlab.com/mehranrasulian) ||
> **Publication date:** 2017-08-31
## Introduction ## Introduction
GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want. GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want.
......
...@@ -279,8 +279,8 @@ After much discussion we settled on the current solution of one file per entry, ...@@ -279,8 +279,8 @@ After much discussion we settled on the current solution of one file per entry,
and then compiling the entries into the overall `CHANGELOG.md` file during the and then compiling the entries into the overall `CHANGELOG.md` file during the
[release process]. [release process].
[boring solution]: https://about.gitlab.com/handbook/#boring-solutions [boring solution]: https://about.gitlab.com/handbook/values/#boring-solutions
[release managers]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/release-manager.md [release managers]: https://gitlab.com/gitlab-org/release/docs/blob/master/quickstart/release-manager.md
[started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826 [started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826
[release process]: https://gitlab.com/gitlab-org/release-tools [release process]: https://gitlab.com/gitlab-org/release-tools
......
...@@ -240,7 +240,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 > ...@@ -240,7 +240,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 >
A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them. A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
- Live example: "GitLab Pages from A to Z - [Part 1](../user/project/pages/getting_started_part_one.md) to [Part 4](../user/project/pages/getting_started_part_four.md)" - Live example: "[Static sites and GitLab Pages domains (Part 1)](../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../user/project/pages/getting_started_part_four.md)"
A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B. A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
It does not only describes steps 2 and 3, but also shows you how to accomplish them. It does not only describes steps 2 and 3, but also shows you how to accomplish them.
...@@ -254,18 +254,25 @@ through the process of how to use it systematically. ...@@ -254,18 +254,25 @@ through the process of how to use it systematically.
#### Special format #### Special format
Every **Technical Article** contains, in the very beginning, a blockquote with the following information: Every **Technical Article** contains a frontmatter at the beginning of the doc
with the following information:
- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial) - **Type of article** (user guide, admin guide, technical overview, tutorial)
- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced) - **Knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
- A reference to the **author's name** and **GitLab.com handle** - **Author's name** and **GitLab.com handle**
- A reference of the **publication date** - **Publication date** (ISO format YYYY-MM-DD)
```md For example:
> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Name Surname](https://gitlab.com/username) || ```yaml
> **Publication date:** AAAA-MM-DD ---
author: John Doe
author_gitlab: johnDoe
level: beginner
article_type: user guide
date: 2017-02-01
---
``` ```
#### Technical Articles - Writing Method #### Technical Articles - Writing Method
......
# Getting started with OpenShift Origin 3 and GitLab ---
author: Achilleas Pipinellis
author_gitlab: axil
level: intermediary
article_type: tutorial
date: 2016-06-28
---
> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial || # Getting started with OpenShift Origin 3 and GitLab
> **Level:** intermediary ||
> **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) ||
> **Publication date:** 2016-06-28
## Introduction ## Introduction
......
# Installing Git ---
author: Sean Packham
author_gitlab: SeanPackham
level: beginner
article_type: user guide
date: 2017-05-15
---
> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** user guide || # Installing Git
> **Level:** beginner ||
> **Author:** [Sean Packham](https://gitlab.com/SeanPackham) ||
> **Publication date:** 2017-05-15
To begin contributing to GitLab projects To begin contributing to GitLab projects
you will need to install the Git client on your computer. you will need to install the Git client on your computer.
......
# Numerous undo possibilities in Git ---
author: Crt Mori
author_gitlab: Letme
level: intermediary
article_type: tutorial
date: 2017-05-15
---
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || # Numerous undo possibilities in Git
> **Level:** intermediary ||
> **Author:** [Crt Mori](https://gitlab.com/Letme) ||
> **Publication date:** 2017-08-17
## Introduction ## Introduction
......
# GitLab Pages from A to Z: Part 4 ---
last_updated: 2018-02-16
author: Marcia Ramos
author_gitlab: marcia
level: intermediate
article_type: user guide
date: 2017-02-22
---
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || # Creating and Tweaking GitLab CI/CD for GitLab Pages
> **Level**: intermediate ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017/02/22
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
- **Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages**
## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages
[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves [GitLab CI](https://about.gitlab.com/gitlab-ci/) serves
numerous purposes, to build, test, and deploy your app numerous purposes, to build, test, and deploy your app
...@@ -19,10 +16,13 @@ from GitLab through ...@@ -19,10 +16,13 @@ from GitLab through
methods. You will need it to build your website with GitLab Pages, methods. You will need it to build your website with GitLab Pages,
and deploy it to the Pages server. and deploy it to the Pages server.
To implement GitLab CI/CD, the first thing we need is a configuration
file called `.gitlab-ci.yml` placed at your website's root directory.
What this file actually does is telling the What this file actually does is telling the
[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts [GitLab Runner](https://docs.gitlab.com/runner/) to run scripts
as you would do from the command line. The Runner acts as your as you would do from the command line. The Runner acts as your
terminal. GitLab CI tells the Runner which commands to run. terminal. GitLab CI/CD tells the Runner which commands to run.
Both are built-in in GitLab, and you don't need to set up Both are built-in in GitLab, and you don't need to set up
anything for them to work. anything for them to work.
...@@ -34,7 +34,7 @@ need to understand just a few things to be able to write our own ...@@ -34,7 +34,7 @@ need to understand just a few things to be able to write our own
with its own syntax. You can always check your CI syntax with with its own syntax. You can always check your CI syntax with
the [GitLab CI Lint Tool](https://gitlab.com/ci/lint). the [GitLab CI Lint Tool](https://gitlab.com/ci/lint).
**Practical Example:** ## Practical example
Let's consider you have a [Jekyll](https://jekyllrb.com/) site. Let's consider you have a [Jekyll](https://jekyllrb.com/) site.
To build it locally, you would open your terminal, and run `jekyll build`. To build it locally, you would open your terminal, and run `jekyll build`.
...@@ -384,7 +384,3 @@ in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the ...@@ -384,7 +384,3 @@ in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the
[pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
to deploy this website you're looking at, docs.gitlab.com. to deploy this website you're looking at, docs.gitlab.com.
- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/). - On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).
|||
|:--|--:|
|[**← Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**](getting_started_part_three.md)||
# GitLab Pages from A to Z: Part 1 ---
last_updated: 2018-02-16
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || author: Marcia Ramos
> **Level**: beginner || author_gitlab: marcia
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) || level: beginner
> **Publication date:** 2017/02/22 article_type: user guide
date: 2017-02-22
- **Part 1: Static sites and GitLab Pages domains** ---
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md) # Static sites and GitLab Pages domains
- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
This document is the beginning of a comprehensive guide, made for those who want to
## GitLab Pages from A to Z
This is a comprehensive guide, made for those who want to
publish a website with GitLab Pages but aren't familiar with publish a website with GitLab Pages but aren't familiar with
the entire process involved. the entire process involved.
This [first part](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of This [first document](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of
static sites, and go over how the default Pages domains work. static sites, and go over how the default Pages domains work.
The [second part](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy The [second document](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy
a website from a forked project or create a new one from scratch. a website from a forked project or create a new one from scratch.
The [third part](getting_started_part_three.md) will show you how to set up a custom domain or subdomain The [third document](getting_started_part_three.md) will show you how to set up a custom domain or subdomain
to your site already deployed. to your site already deployed.
The [fourth part](getting_started_part_four.md) will show you how to create and tweak GitLab CI for The [fourth document](getting_started_part_four.md) will show you how to create and tweak GitLab CI for
GitLab Pages. GitLab Pages.
To **enable** GitLab Pages for GitLab CE (Community Edition) To **enable** GitLab Pages for GitLab CE (Community Edition)
...@@ -113,6 +110,4 @@ You can only create the highest level group website. ...@@ -113,6 +110,4 @@ You can only create the highest level group website.
- On your GitLab instance, replace `gitlab.io` above with your - On your GitLab instance, replace `gitlab.io` above with your
Pages server domain. Ask your sysadmin for this information. Pages server domain. Ask your sysadmin for this information.
||| _Read on about [Projects for GitLab Pages and URL structure](getting_started_part_two.md)._
|:--|--:|
||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)|
--- ---
last_updated: 2017-09-28 last_updated: 2018-02-16
author: Marcia Ramos
author_gitlab: marcia
level: beginner
article_type: user guide
date: 2017-02-22
--- ---
# GitLab Pages from A to Z: Part 3 # GitLab Pages custom domains and SSL/TLS Certificates
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles)**: user guide || Setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
> **Level**: beginner ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017-02-22 ||
> **Last updated**: 2017-09-28
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
- **Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**
- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
## Setting Up Custom Domains - DNS Records and SSL/TLS Certificates
As described in the previous part of this series, setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`. These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`.
### Adding your custom domain to GitLab Pages ## Adding your custom domain to GitLab Pages
To use one or more custom domain with your Pages site, there are two things To use one or more custom domain with your Pages site, there are two things
you should consider first, which we'll cover in this guide: you should consider first, which we'll cover in this guide:
...@@ -36,7 +28,7 @@ Let's start from the beginning with [DNS records](#dns-records). ...@@ -36,7 +28,7 @@ Let's start from the beginning with [DNS records](#dns-records).
If you already know how they work and want to skip the introduction to DNS, If you already know how they work and want to skip the introduction to DNS,
you may be interested in skipping it until the [TL;DR](#tl-dr) section below. you may be interested in skipping it until the [TL;DR](#tl-dr) section below.
### DNS Records ## DNS Records
A Domain Name System (DNS) web service routes visitors to websites A Domain Name System (DNS) web service routes visitors to websites
by translating domain names (such as `www.example.com`) into the by translating domain names (such as `www.example.com`) into the
...@@ -72,7 +64,7 @@ for the most popular hosting services: ...@@ -72,7 +64,7 @@ for the most popular hosting services:
If your hosting service is not listed above, you can just try to If your hosting service is not listed above, you can just try to
search the web for "how to add dns record on <my hosting service>". search the web for "how to add dns record on <my hosting service>".
#### DNS A record ### DNS A record
In case you want to point a root domain (`example.com`) to your In case you want to point a root domain (`example.com`) to your
GitLab Pages site, deployed to `namespace.gitlab.io`, you need to GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
...@@ -87,7 +79,7 @@ running on your instance). ...@@ -87,7 +79,7 @@ running on your instance).
![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png) ![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png)
#### DNS CNAME record ### DNS CNAME record
In case you want to point a subdomain (`hello-world.example.com`) In case you want to point a subdomain (`hello-world.example.com`)
to your GitLab Pages site initially deployed to `namespace.gitlab.io`, to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
...@@ -103,7 +95,7 @@ without any `/project-name`. ...@@ -103,7 +95,7 @@ without any `/project-name`.
![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png) ![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png)
#### TL;DR ### TL;DR
| From | DNS Record | To | | From | DNS Record | To |
| ---- | ---------- | -- | | ---- | ---------- | -- |
...@@ -119,7 +111,7 @@ domain. E.g., **do not** point your `subdomain.domain.com` to ...@@ -119,7 +111,7 @@ domain. E.g., **do not** point your `subdomain.domain.com` to
`namespace.gitlab.io.` or `namespace.gitlab.io/`. `namespace.gitlab.io.` or `namespace.gitlab.io/`.
> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`. > - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`.
### Add your custom domain to GitLab Pages settings ## Add your custom domain to GitLab Pages settings
Once you've set the DNS record, you'll need navigate to your project's Once you've set the DNS record, you'll need navigate to your project's
**Setting > Pages** and click **+ New domain** to add your custom domain to **Setting > Pages** and click **+ New domain** to add your custom domain to
...@@ -142,7 +134,7 @@ to your domain will respond with a 404. ...@@ -142,7 +134,7 @@ to your domain will respond with a 404.
Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding
custom domains to GitLab Pages sites. custom domains to GitLab Pages sites.
### SSL/TLS Certificates ## SSL/TLS Certificates
Every GitLab Pages project on GitLab.com will be available under Every GitLab Pages project on GitLab.com will be available under
HTTPS for the default Pages domain (`*.gitlab.io`). Once you set HTTPS for the default Pages domain (`*.gitlab.io`). Once you set
...@@ -158,7 +150,7 @@ highly recommendable. ...@@ -158,7 +150,7 @@ highly recommendable.
Let's start with an introduction to the importance of HTTPS. Let's start with an introduction to the importance of HTTPS.
Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-your-project). Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-your-project).
#### Why should I care about HTTPS? ### Why should I care about HTTPS?
This might be your first question. If our sites are hosted by GitLab Pages, This might be your first question. If our sites are hosted by GitLab Pages,
they are static, hence we are not dealing with server-side scripts they are static, hence we are not dealing with server-side scripts
...@@ -179,7 +171,7 @@ authentications and validations. ...@@ -179,7 +171,7 @@ authentications and validations.
How about taking Josh's advice and protecting our sites too? We will be How about taking Josh's advice and protecting our sites too? We will be
well supported, and we'll contribute to a safer internet. well supported, and we'll contribute to a safer internet.
#### Organizations supporting HTTPS ### Organizations supporting HTTPS
There is a huge movement in favor of securing all the web. W3C fully There is a huge movement in favor of securing all the web. W3C fully
[supports the cause](https://w3ctag.github.io/web-https/) and explains very well [supports the cause](https://w3ctag.github.io/web-https/) and explains very well
...@@ -189,7 +181,7 @@ and would no longer accept unsecured connections. Recently, Mozilla published a ...@@ -189,7 +181,7 @@ and would no longer accept unsecured connections. Recently, Mozilla published a
[communication](https://blog.mozilla.org/security/2016/03/29/march-2016-ca-communication/) [communication](https://blog.mozilla.org/security/2016/03/29/march-2016-ca-communication/)
reiterating the importance of HTTPS. reiterating the importance of HTTPS.
### Issuing Certificates ## Issuing Certificates
GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by
[Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) [Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority)
...@@ -218,7 +210,7 @@ Their certs are valid up to 15 years. Read through the tutorial on ...@@ -218,7 +210,7 @@ Their certs are valid up to 15 years. Read through the tutorial on
Regardless the CA you choose, the steps to add your certificate to Regardless the CA you choose, the steps to add your certificate to
your Pages project are the same. your Pages project are the same.
#### What do you need ### What do you need
1. A PEM certificate 1. A PEM certificate
1. An intermediate certificate 1. An intermediate certificate
...@@ -228,7 +220,7 @@ your Pages project are the same. ...@@ -228,7 +220,7 @@ your Pages project are the same.
These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**. These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**.
#### What's what? ### What's what?
- A PEM certificate is the certificate generated by the CA, - A PEM certificate is the certificate generated by the CA,
which needs to be added to the field **Certificate (PEM)**. which needs to be added to the field **Certificate (PEM)**.
...@@ -241,7 +233,7 @@ are one of these cases. ...@@ -241,7 +233,7 @@ are one of these cases.
- A public key is an encrypted key which validates - A public key is an encrypted key which validates
your PEM against your domain. your PEM against your domain.
#### Now what? ### Now what?
Now that you hopefully understand why you need all Now that you hopefully understand why you need all
of this, it's simple: of this, it's simple:
...@@ -258,6 +250,4 @@ just jumping a line between them. ...@@ -258,6 +250,4 @@ just jumping a line between them.
regular text editors. Always use code editors (such as regular text editors. Always use code editors (such as
Sublime Text, Atom, Dreamweaver, Brackets, etc). Sublime Text, Atom, Dreamweaver, Brackets, etc).
||| _Read on about [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md)_
|:--|--:|
|[**← Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)|[**Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_four.md)|
# GitLab Pages from A to Z: Part 2 ---
last_updated: 2018-02-16
author: Marcia Ramos
author_gitlab: marcia
level: beginner
article_type: user guide
date: 2017-02-22
---
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || # Projects for GitLab Pages and URL structure
> **Level**: beginner ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017/02/22
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- **Part 2: Quick start guide - Setting up GitLab Pages**
- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
## Setting up GitLab Pages
For a complete step-by-step tutorial, please read the
blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain
what do you need and why do you need them.
## What you need to get started ## What you need to get started
To get started with GitLab Pages, you need:
1. A project 1. A project
1. A configuration file (`.gitlab-ci.yml`) to deploy your site 1. A configuration file (`.gitlab-ci.yml`) to deploy your site
1. A specific `job` called `pages` in the configuration file 1. A specific `job` called `pages` in the configuration file
that will make GitLab aware that you are deploying a GitLab Pages website that will make GitLab aware that you are deploying a GitLab Pages website
1. A `public` directory with the content of the website
Optional Features: Optional Features:
...@@ -51,35 +47,26 @@ containing the most popular SSGs templates. ...@@ -51,35 +47,26 @@ containing the most popular SSGs templates.
Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've
created for the steps below. created for the steps below.
1. Choose your SSG template 1. [Fork a sample project](../../../gitlab-basics/fork-project.md) from the [Pages group](https://gitlab.com/pages)
1. Fork a project from the [Pages group](https://gitlab.com/pages) 1. Trigger a build (push a change to any file)
1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project** 1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your project's **Settings** > **Pages**
1. Optionally, remove the fork relationship by navigating to your project's **Settings** > expanding **Advanced settings** and scrolling down to **Remove fork relashionship**:
![remove fork relashionship](img/remove_fork_relashionship.png) ![remove fork relashionship](img/remove_fork_relashionship.png)
1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **Pipelines**
1. Trigger a build (push a change to any file)
1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your **Project**'s **Settings** > **Pages**
To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to: To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to:
- Rename it to `namespace.gitlab.io`: navigate to **Project**'s **Settings** > **Edit Project** > **Rename repository** - Rename it to `namespace.gitlab.io`: navigate to project's **Settings** > expand **Advanced settings** > and scroll down to **Rename repository**
- Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file. - Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file.
> **Notes:** > **Notes:**
> >
>1. Why do I need to remove the fork relationship? > Why do I need to remove the fork relationship?
> >
> Unless you want to contribute to the original project, > Unless you want to contribute to the original project,
you won't need it connected to the upstream. A you won't need it connected to the upstream. A
[fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork) [fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork)
is useful for submitting merge requests to the upstream. is useful for submitting merge requests to the upstream.
>
> 2. Why do I need to enable Shared Runners?
>
> Shared Runners will run the script set by your GitLab CI/CD
configuration file. They're enabled by default to new projects,
but not to forks.
### Create a project from scratch ### Create a project from scratch
...@@ -108,7 +95,7 @@ where you'll find its default URL. ...@@ -108,7 +95,7 @@ where you'll find its default URL.
> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, > - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
if you don't find yours among the templates, you'll need if you don't find yours among the templates, you'll need
to configure your own `.gitlab-ci.yml`. Do do that, please to configure your own `.gitlab-ci.yml`. Do do that, please
read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
the [example projects](https://gitlab.com/pages). If you set the [example projects](https://gitlab.com/pages). If you set
up a new one, please up a new one, please
[contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
...@@ -121,7 +108,7 @@ you can run `git init` in your local website directory, add the ...@@ -121,7 +108,7 @@ you can run `git init` in your local website directory, add the
remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
then add, commit, and push. then add, commit, and push.
### URLs and Baseurls ## URLs and Baseurls
Every Static Site Generator (SSG) default configuration expects Every Static Site Generator (SSG) default configuration expects
to find your website under a (sub)domain (`example.com`), not to find your website under a (sub)domain (`example.com`), not
...@@ -149,11 +136,7 @@ example we've just mentioned, you'd have to change Jekyll's `_config.yml` to: ...@@ -149,11 +136,7 @@ example we've just mentioned, you'd have to change Jekyll's `_config.yml` to:
baseurl: "" baseurl: ""
``` ```
### Custom Domains ## Custom Domains
GitLab Pages supports custom domains and subdomains, served under HTTPS or HTTPS. GitLab Pages supports custom domains and subdomains, served under HTTP or HTTPS.
Please check the [next part](getting_started_part_three.md) of this series for an overview. Please check the [next part](getting_started_part_three.md) of this series for an overview.
|||
|:--|--:|
|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Setting Up Custom Domains - DNS Records and SSL/TLS Certificates →**](getting_started_part_three.md)|
...@@ -37,10 +37,10 @@ to secure them. ...@@ -37,10 +37,10 @@ to secure them.
Read the following tutorials to know more about: Read the following tutorials to know more about:
- [Static websites and GitLab Pages domains](getting_started_part_one.md) - [Static websites and GitLab Pages domains](getting_started_part_one.md): Understand what is a static website, and how GitLab Pages default domains work
- [Forking projects and creating new ones from scratch, URLs and baseurls](getting_started_part_two.md) - [Projects for GitLab Pages and URL structure](getting_started_part_two.md): Forking projects and creating new ones from scratch, understanding URLs structure and baseurls
- [Custom domains and subdomains, DNS records, SSL/TLS certificates](getting_started_part_three.md) - [GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md): How to add custom domains and subdomains to your website, configure DNS records, and SSL/TLS certificates
- [How to create your own `.gitlab-ci.yml` for your site](getting_started_part_four.md) - [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md): Understand how to create your own `.gitlab-ci.yml` for your site
- [Technical aspects, custom 404 pages, limitations](introduction.md) - [Technical aspects, custom 404 pages, limitations](introduction.md)
- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) (outdated) - [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) (outdated)
......
...@@ -34,7 +34,7 @@ Documentation for GitLab instance administrators is under [LFS administration do ...@@ -34,7 +34,7 @@ Documentation for GitLab instance administrators is under [LFS administration do
credentials store is recommended credentials store is recommended
* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have * Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have
to add the URL to Git config manually (see [troubleshooting](#troubleshooting)) to add the URL to Git config manually (see [troubleshooting](#troubleshooting))
>**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication >**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication
still goes over HTTP, but now the SSH client passes the correct credentials still goes over HTTP, but now the SSH client passes the correct credentials
to the Git LFS client, so no action is required by the user. to the Git LFS client, so no action is required by the user.
...@@ -85,6 +85,8 @@ git lfs fetch master ...@@ -85,6 +85,8 @@ git lfs fetch master
## File Locking ## File Locking
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35856) in GitLab 10.5.
The first thing to do before using File Locking is to tell Git LFS which The first thing to do before using File Locking is to tell Git LFS which
kind of files are lockable. The following command will store PNG files kind of files are lockable. The following command will store PNG files
in LFS and flag them as lockable: in LFS and flag them as lockable:
......
Feature: Project Network Graph
Background:
Given I sign in as a user
And I own project "Shop"
And I visit project "Shop" network page
@javascript
Scenario: I should see project network
Then page should have network graph
And page should select "master" in select box
And page should have "master" on graph
@javascript
Scenario: I should see project network with 'test' branch
When I visit project network page on branch 'test'
Then page should have 'test' on graph
@javascript
Scenario: I should switch "branch" and "tag"
When I switch ref to "feature"
Then page should select "feature" in select box
And page should have "feature" on graph
When I switch ref to "v1.0.0"
Then page should select "v1.0.0" in select box
And page should have "v1.0.0" on graph
@javascript
Scenario: I should looking for a commit by SHA
When I looking for a commit by SHA of "v1.0.0"
Then page should have network graph
And page should select "master" in select box
And page should have "v1.0.0" on graph
@javascript
Scenario: I should filter selected tag
When I switch ref to "v1.0.0"
Then page should have "v1.0.0" in title
Then page should have content not containing "v1.0.0"
When click "Show only selected branch" checkbox
Then page should only have content from "v1.0.0"
When click "Show only selected branch" checkbox
Then page should have content not containing "v1.0.0"
Scenario: I should fail to look for a commit
When I look for a commit by ";"
Then I should see non-existent git revision error message
class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
step 'page should have network graph' do
expect(page).to have_selector ".network-graph"
end
When 'I visit project "Shop" network page' do
# Stub Graph max_size to speed up test (10 commits vs. 650)
Network::Graph.stub(max_count: 10)
@project = Project.find_by(name: "Shop")
visit project_network_path(@project, "master")
end
step "I visit project network page on branch 'test'" do
visit project_network_path(@project, "'test'")
end
step 'page should select "master" in select box' do
expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
end
step 'page should select "v1.0.0" in select box' do
expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
end
step 'page should have "master" on graph' do
page.within '.network-graph' do
expect(page).to have_content 'master'
end
end
step "page should have 'test' on graph" do
page.within '.network-graph' do
expect(page).to have_content "'test'"
end
end
When 'I switch ref to "feature"' do
first('.js-project-refs-dropdown').click
page.within '.project-refs-form' do
click_link 'feature'
end
end
When 'I switch ref to "v1.0.0"' do
first('.js-project-refs-dropdown').click
page.within '.project-refs-form' do
click_link 'v1.0.0'
end
end
When 'click "Show only selected branch" checkbox' do
find('#filter_ref').click
end
step 'page should have content not containing "v1.0.0"' do
page.within '.network-graph' do
expect(page).to have_content 'Change some files'
end
end
step 'page should have "v1.0.0" in title' do
expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
end
step 'page should only have content from "v1.0.0"' do
page.within '.network-graph' do
expect(page).not_to have_content 'Change some files'
end
end
step 'page should select "feature" in select box' do
expect(page).to have_selector '.dropdown-menu-toggle', text: "feature"
end
step 'page should select "v1.0.0" in select box' do
expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
end
step 'page should have "feature" on graph' do
page.within '.network-graph' do
expect(page).to have_content 'feature'
end
end
When 'I looking for a commit by SHA of "v1.0.0"' do
page.within ".network-form" do
fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
find('button').click
end
sleep 2
end
step 'page should have "v1.0.0" on graph' do
page.within '.network-graph' do
expect(page).to have_content 'v1.0.0'
end
end
When 'I look for a commit by ";"' do
page.within ".network-form" do
fill_in 'extended_sha1', with: ';'
find('button').click
end
end
step 'I should see non-existent git revision error message' do
expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist."
end
end
...@@ -121,6 +121,7 @@ module Gitlab ...@@ -121,6 +121,7 @@ module Gitlab
def commits_check def commits_check
return if deletion? || newrev.nil? return if deletion? || newrev.nil?
return unless should_run_commit_validations?
# n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593 # n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
::Gitlab::GitalyClient.allow_n_plus_1_calls do ::Gitlab::GitalyClient.allow_n_plus_1_calls do
...@@ -139,6 +140,10 @@ module Gitlab ...@@ -139,6 +140,10 @@ module Gitlab
private private
def should_run_commit_validations?
commit_check.validate_lfs_file_locks?
end
def updated_from_web? def updated_from_web?
protocol == 'web' protocol == 'web'
end end
...@@ -176,7 +181,7 @@ module Gitlab ...@@ -176,7 +181,7 @@ module Gitlab
end end
def commits def commits
project.repository.new_commits(newrev) @commits ||= project.repository.new_commits(newrev)
end end
end end
end end
......
...@@ -35,14 +35,14 @@ module Gitlab ...@@ -35,14 +35,14 @@ module Gitlab
end end
end end
private
def validate_lfs_file_locks? def validate_lfs_file_locks?
strong_memoize(:validate_lfs_file_locks) do strong_memoize(:validate_lfs_file_locks) do
project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev
end end
end end
private
def lfs_file_locks_validation def lfs_file_locks_validation
lambda do |paths| lambda do |paths|
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first
......
...@@ -508,7 +508,7 @@ module Gitlab ...@@ -508,7 +508,7 @@ module Gitlab
@committed_date = Time.at(commit.committer.date.seconds).utc @committed_date = Time.at(commit.committer.date.seconds).utc
@committer_name = commit.committer.name.dup @committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup @committer_email = commit.committer.email.dup
@parent_ids = commit.parent_ids @parent_ids = Array(commit.parent_ids)
end end
def serialize_keys def serialize_keys
......
...@@ -2195,6 +2195,7 @@ module Gitlab ...@@ -2195,6 +2195,7 @@ module Gitlab
# Apply diff of the `diff_range` to the worktree # Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range})) diff = run_git!(%W(diff --binary #{diff_range}))
run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin| run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin|
stdin.binmode
stdin.write(diff) stdin.write(diff)
end end
......
...@@ -63,6 +63,7 @@ module Gitlab ...@@ -63,6 +63,7 @@ module Gitlab
true true
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
if e.message !~ /repository not exported/ if e.message !~ /repository not exported/
project.create_wiki
fail_import("Failed to import the wiki: #{e.message}") fail_import("Failed to import the wiki: #{e.message}")
else else
true true
......
...@@ -73,7 +73,7 @@ module Gitlab ...@@ -73,7 +73,7 @@ module Gitlab
# event_name - The name of the event (e.g. "git_push"). # event_name - The name of the event (e.g. "git_push").
# tags - A set of tags to attach to the event. # tags - A set of tags to attach to the event.
def add_event(event_name, tags = {}) def add_event(event_name, tags = {})
self.class.transaction_metric(event_name, :counter, prefix: 'event_', tags: tags).increment(tags.merge(labels)) self.class.transaction_metric(event_name, :counter, prefix: 'event_', use_feature_flag: true, tags: tags).increment(tags.merge(labels))
@metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event)
end end
...@@ -150,11 +150,12 @@ module Gitlab ...@@ -150,11 +150,12 @@ module Gitlab
with_feature :prometheus_metrics_transaction_allocated_memory with_feature :prometheus_metrics_transaction_allocated_memory
end end
def self.transaction_metric(name, type, prefix: nil, tags: {}) def self.transaction_metric(name, type, prefix: nil, use_feature_flag: false, tags: {})
metric_name = "gitlab_transaction_#{prefix}#{name}_total".to_sym metric_name = "gitlab_transaction_#{prefix}#{name}_total".to_sym
fetch_metric(type, metric_name) do fetch_metric(type, metric_name) do
docstring "Transaction #{prefix}#{name} #{type}" docstring "Transaction #{prefix}#{name} #{type}"
base_labels tags.merge(BASE_LABELS) base_labels tags.merge(BASE_LABELS)
with_feature "prometheus_transaction_#{prefix}#{name}_total".to_sym if use_feature_flag
if type == :gauge if type == :gauge
multiprocess_mode :livesum multiprocess_mode :livesum
......
...@@ -4,18 +4,6 @@ module QA ...@@ -4,18 +4,6 @@ module QA
class SecretVariable < Factory::Base class SecretVariable < Factory::Base
attr_accessor :key, :value attr_accessor :key, :value
product :key do
Page::Project::Settings::CICD.act do
expand_secret_variables(&:variable_key)
end
end
product :value do
Page::Project::Settings::CICD.act do
expand_secret_variables(&:variable_value)
end
end
dependency Factory::Resource::Project, as: :project do |project| dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-secret-variables' project.name = 'project-with-secret-variables'
project.description = 'project for adding secret variable test' project.description = 'project for adding secret variable test'
......
...@@ -98,6 +98,10 @@ module QA ...@@ -98,6 +98,10 @@ module QA
views.map(&:errors).flatten views.map(&:errors).flatten
end end
def self.elements
views.map(&:elements).flatten
end
class DSL class DSL
attr_reader :views attr_reader :views
......
...@@ -6,39 +6,37 @@ module QA ...@@ -6,39 +6,37 @@ module QA
include Common include Common
view 'app/views/ci/variables/_variable_row.html.haml' do view 'app/views/ci/variables/_variable_row.html.haml' do
element :variable_row, '.ci-variable-row-body'
element :variable_key, '.js-ci-variable-input-key' element :variable_key, '.js-ci-variable-input-key'
element :variable_value, '.js-ci-variable-input-value' element :variable_value, '.js-ci-variable-input-value'
element :key_placeholder, 'Input variable key'
element :value_placeholder, 'Input variable value'
end end
view 'app/views/ci/variables/_index.html.haml' do view 'app/views/ci/variables/_index.html.haml' do
element :save_variables, '.js-secret-variables-save-button' element :save_variables, '.js-secret-variables-save-button'
element :reveal_values, '.js-secret-value-reveal-button'
end end
def fill_variable_key(key) def fill_variable_key(key)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do fill_in('Input variable key', with: key, match: :first)
page.find('.js-ci-variable-input-key').set(key)
end
end end
def fill_variable_value(value) def fill_variable_value(value)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do fill_in('Input variable value', with: value, match: :first)
page.find('.js-ci-variable-input-value').set(value)
end
end end
def save_variables def save_variables
click_button('Save variables') find('.js-secret-variables-save-button').click
end end
def variable_key def reveal_variables
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do find('.js-secret-value-reveal-button').click
page.find('.js-ci-variable-input-key').value
end
end end
def variable_value def variable_value(key)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do within('.ci-variable-row-body', text: key) do
page.find('.js-ci-variable-input-value').value find('.js-ci-variable-input-value').value
end end
end end
end end
......
...@@ -4,16 +4,21 @@ module QA ...@@ -4,16 +4,21 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
variable_key = 'VARIABLE_KEY' Factory::Resource::SecretVariable.fabricate! do |resource|
variable_value = 'variable value' resource.key = 'VARIABLE_KEY'
resource.value = 'some secret variable'
variable = Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = variable_key
resource.value = variable_value
end end
expect(variable.key).to eq(variable_key) Page::Project::Settings::CICD.perform do |settings|
expect(variable.value).to eq(variable_value) settings.expand_secret_variables do |page|
expect(page).to have_field(with: 'VARIABLE_KEY')
expect(page).not_to have_field(with: 'some secret variable')
page.reveal_variables
expect(page).to have_field(with: 'some secret variable')
end
end
end end
end end
end end
...@@ -14,7 +14,7 @@ describe QA::Page::Base do ...@@ -14,7 +14,7 @@ describe QA::Page::Base do
end end
view 'path/to/some/_partial.html.haml' do view 'path/to/some/_partial.html.haml' do
element :something, 'string pattern' element :another_element, 'string pattern'
end end
end end
end end
...@@ -25,11 +25,10 @@ describe QA::Page::Base do ...@@ -25,11 +25,10 @@ describe QA::Page::Base do
end end
it 'populates views objects with data about elements' do it 'populates views objects with data about elements' do
subject.views.first.elements.tap do |elements| expect(subject.elements.size).to eq 3
expect(elements.size).to eq 2 expect(subject.elements).to all(be_an_instance_of QA::Page::Element)
expect(elements).to all(be_an_instance_of QA::Page::Element) expect(subject.elements.map(&:name))
expect(elements.map(&:name)).to eq [:something, :something_else] .to eq [:something, :something_else, :another_element]
end
end end
end end
......
require 'spec_helper'
describe 'Project Network Graph', :js do
let(:user) { create :user }
let(:project) { create :project, :repository, namespace: user.namespace }
before do
sign_in(user)
# Stub Graph max_size to speed up test (10 commits vs. 650)
allow(Network::Graph).to receive(:max_count).and_return(10)
end
context 'when branch is master' do
def switch_ref_to(ref_name)
first('.js-project-refs-dropdown').click
page.within '.project-refs-form' do
click_link ref_name
end
end
def click_show_only_selected_branch_checkbox
find('#filter_ref').click
end
before do
visit project_network_path(project, 'master')
end
it 'renders project network' do
expect(page).to have_selector ".network-graph"
expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
page.within '.network-graph' do
expect(page).to have_content 'master'
end
end
it 'switches ref to branch' do
switch_ref_to('feature')
expect(page).to have_selector '.dropdown-menu-toggle', text: 'feature'
page.within '.network-graph' do
expect(page).to have_content 'feature'
end
end
it 'switches ref to tag' do
switch_ref_to('v1.0.0')
expect(page).to have_selector '.dropdown-menu-toggle', text: 'v1.0.0'
page.within '.network-graph' do
expect(page).to have_content 'v1.0.0'
end
end
it 'renders by commit sha of "v1.0.0"' do
page.within ".network-form" do
fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
find('button').click
end
expect(page).to have_selector ".network-graph"
expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
page.within '.network-graph' do
expect(page).to have_content 'v1.0.0'
end
end
it 'filters select tag' do
switch_ref_to('v1.0.0')
expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
page.within '.network-graph' do
expect(page).to have_content 'Change some files'
end
click_show_only_selected_branch_checkbox
page.within '.network-graph' do
expect(page).not_to have_content 'Change some files'
end
click_show_only_selected_branch_checkbox
page.within '.network-graph' do
expect(page).to have_content 'Change some files'
end
end
it 'renders error message when sha commit not exists' do
page.within ".network-form" do
fill_in 'extended_sha1', with: ';'
find('button').click
end
expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist."
end
end
it 'renders project network with test branch' do
visit project_network_path(project, "'test'")
page.within '.network-graph' do
expect(page).to have_content "'test'"
end
end
end
/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */ /* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
import { scaleLinear, scaleTime } from 'd3-scale'; import { scaleLinear, scaleTime } from 'd3-scale';
import { timeParse } from 'd3-time-format'; import { timeParse } from 'd3-time-format';
import { ContributorsGraph, ContributorsMasterGraph } from '~/graphs/stat_graph_contributors_graph'; import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
const d3 = { scaleLinear, scaleTime, timeParse }; const d3 = { scaleLinear, scaleTime, timeParse };
......
import ContributorsStatGraph from '~/graphs/stat_graph_contributors'; import ContributorsStatGraph from '~/pages/projects/graphs/show/stat_graph_contributors';
import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph'; import { ContributorsGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
import { setLanguage } from '../helpers/locale_helper'; import { setLanguage } from '../helpers/locale_helper';
......
/* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */ /* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */
import ContributorsStatGraphUtil from '~/graphs/stat_graph_contributors_util'; import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util';
describe("ContributorsStatGraphUtil", function () { describe("ContributorsStatGraphUtil", function () {
describe("#parse_log", function () { describe("#parse_log", function () {
......
...@@ -190,7 +190,7 @@ describe Gitlab::Checks::ChangeAccess do ...@@ -190,7 +190,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'with LFS not enabled' do context 'with LFS not enabled' do
it 'skips the validation' do it 'skips the validation' do
expect_any_instance_of(described_class).not_to receive(:lfs_file_locks_validation) expect_any_instance_of(Gitlab::Checks::CommitCheck).not_to receive(:validate)
subject.exec subject.exec
end end
...@@ -207,7 +207,7 @@ describe Gitlab::Checks::ChangeAccess do ...@@ -207,7 +207,7 @@ describe Gitlab::Checks::ChangeAccess do
end end
end end
context 'when change is sent by the author od the lock' do context 'when change is sent by the author of the lock' do
let(:user) { owner } let(:user) { owner }
it "doesn't raise any error" do it "doesn't raise any error" do
......
# coding: utf-8
require "spec_helper" require "spec_helper"
describe Gitlab::Git::Repository, seed_helper: true do describe Gitlab::Git::Repository, seed_helper: true do
...@@ -2221,6 +2222,17 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2221,6 +2222,17 @@ describe Gitlab::Git::Repository, seed_helper: true do
subject subject
end end
end end
context 'with an ASCII-8BIT diff', :skip_gitaly_mock do
let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
it 'applies a ASCII-8BIT diff' do
allow(repository).to receive(:run_git!).and_call_original
allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
expect(subject.length).to eq(40)
end
end
end end
end end
......
...@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
import_source: 'foo/bar', import_source: 'foo/bar',
repository_storage_path: 'foo', repository_storage_path: 'foo',
disk_path: 'foo', disk_path: 'foo',
repository: repository repository: repository,
create_wiki: true
) )
end end
...@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
expect(importer.import_wiki_repository).to eq(true) expect(importer.import_wiki_repository).to eq(true)
end end
it 'marks the import as failed if an error was raised' do it 'marks the import as failed and creates an empty repo if an error was raised' do
expect(importer.gitlab_shell) expect(importer.gitlab_shell)
.to receive(:import_repository) .to receive(:import_repository)
.and_raise(Gitlab::Shell::Error) .and_raise(Gitlab::Shell::Error)
...@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
.to receive(:fail_import) .to receive(:fail_import)
.and_return(false) .and_return(false)
expect(project)
.to receive(:create_wiki)
expect(importer.import_wiki_repository).to eq(false) expect(importer.import_wiki_repository).to eq(false)
end end
end end
......
...@@ -463,59 +463,30 @@ describe Notify do ...@@ -463,59 +463,30 @@ describe Notify do
end end
describe 'project access requested' do describe 'project access requested' do
context 'for a project in a user namespace' do let(:project) do
let(:project) do create(:project, :public, :access_requestable) do |project|
create(:project, :public, :access_requestable) do |project| project.add_master(project.owner)
project.add_master(project.owner, current_user: project.owner)
end
end
let(:project_member) do
project.request_access(user)
project.requesters.find_by(user_id: user.id)
end
subject { described_class.member_access_requested_email('project', project_member.id) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do
to_emails = subject.header[:to].addrs
expect(to_emails.size).to eq(1)
expect(to_emails[0].address).to eq(project.members.owners_and_masters.first.user.notification_email)
is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
is_expected.to have_html_escaped_body_text project.name_with_namespace
is_expected.to have_body_text project_project_members_url(project)
is_expected.to have_body_text project_member.human_access
end end
end end
context 'for a project in a group' do let(:project_member) do
let(:group_owner) { create(:user) } project.request_access(user)
let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } } project.requesters.find_by(user_id: user.id)
let(:project) { create(:project, :public, :access_requestable, namespace: group) } end
let(:project_member) do subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) }
project.request_access(user)
project.requesters.find_by(user_id: user.id)
end
subject { described_class.member_access_requested_email('project', project_member.id) }
it_behaves_like 'an email sent from GitLab' it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do it 'contains all the useful information' do
to_emails = subject.header[:to].addrs to_emails = subject.header[:to].addrs.map(&:address)
expect(to_emails.size).to eq(1) expect(to_emails).to eq([recipient.notification_email])
expect(to_emails[0].address).to eq(group.members.owners_and_masters.first.user.notification_email)
is_expected.to have_subject "Request to join the #{project.name_with_namespace} project" is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
is_expected.to have_html_escaped_body_text project.name_with_namespace is_expected.to have_html_escaped_body_text project.name_with_namespace
is_expected.to have_body_text project_project_members_url(project) is_expected.to have_body_text project_project_members_url(project)
is_expected.to have_body_text project_member.human_access is_expected.to have_body_text project_member.human_access
end
end end
end end
...@@ -959,13 +930,16 @@ describe Notify do ...@@ -959,13 +930,16 @@ describe Notify do
group.request_access(user) group.request_access(user)
group.requesters.find_by(user_id: user.id) group.requesters.find_by(user_id: user.id)
end end
subject { described_class.member_access_requested_email('group', group_member.id) } subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) }
it_behaves_like 'an email sent from GitLab' it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do it 'contains all the useful information' do
to_emails = subject.header[:to].addrs.map(&:address)
expect(to_emails).to eq([recipient.notification_email])
is_expected.to have_subject "Request to join the #{group.name} group" is_expected.to have_subject "Request to join the #{group.name} group"
is_expected.to have_html_escaped_body_text group.name is_expected.to have_html_escaped_body_text group.name
is_expected.to have_body_text group_group_members_url(group) is_expected.to have_body_text group_group_members_url(group)
......
...@@ -496,6 +496,14 @@ describe User do ...@@ -496,6 +496,14 @@ describe User do
user2.update_tracked_fields!(request) user2.update_tracked_fields!(request)
end.to change { user2.reload.current_sign_in_at } end.to change { user2.reload.current_sign_in_at }
end end
it 'does not write if the DB is in read-only mode' do
expect(Gitlab::Database).to receive(:read_only?).and_return(true)
expect do
user.update_tracked_fields!(request)
end.not_to change { user.reload.current_sign_in_at }
end
end end
shared_context 'user keys' do shared_context 'user keys' do
......
require 'spec_helper' require 'spec_helper'
describe API::Issues, :mailer do describe API::Issues do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:project) do set(:project) do
create(:project, :public, creator_id: user.id, namespace: user.namespace) create(:project, :public, creator_id: user.id, namespace: user.namespace)
...@@ -932,18 +932,6 @@ describe API::Issues, :mailer do ...@@ -932,18 +932,6 @@ describe API::Issues, :mailer do
expect(json_response['error']).to eq('confidential is invalid') expect(json_response['error']).to eq('confidential is invalid')
end end
it "sends notifications for subscribers of newly added labels" do
label = project.labels.first
label.toggle_subscription(user2, project)
perform_enqueued_jobs do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: label.title
end
should_email(user2)
end
it "returns a 400 bad request if title not given" do it "returns a 400 bad request if title not given" do
post api("/projects/#{project.id}/issues", user), labels: 'label, label2' post api("/projects/#{project.id}/issues", user), labels: 'label, label2'
expect(response).to have_gitlab_http_status(400) expect(response).to have_gitlab_http_status(400)
...@@ -1246,18 +1234,6 @@ describe API::Issues, :mailer do ...@@ -1246,18 +1234,6 @@ describe API::Issues, :mailer do
expect(json_response['labels']).to eq([label.title]) expect(json_response['labels']).to eq([label.title])
end end
it "sends notifications for subscribers of newly added labels when issue is updated" do
label = create(:label, title: 'foo', color: '#FFAABB', project: project)
label.toggle_subscription(user2, project)
perform_enqueued_jobs do
put api("/projects/#{project.id}/issues/#{issue.iid}", user),
title: 'updated title', labels: label.title
end
should_email(user2)
end
it 'removes all labels' do it 'removes all labels' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user), labels: '' put api("/projects/#{project.id}/issues/#{issue.iid}", user), labels: ''
......
require 'spec_helper' require 'spec_helper'
describe API::V3::Issues, :mailer do describe API::V3::Issues do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:user2) { create(:user) } set(:user2) { create(:user) }
set(:non_member) { create(:user) } set(:non_member) { create(:user) }
...@@ -780,18 +780,6 @@ describe API::V3::Issues, :mailer do ...@@ -780,18 +780,6 @@ describe API::V3::Issues, :mailer do
expect(json_response['error']).to eq('confidential is invalid') expect(json_response['error']).to eq('confidential is invalid')
end end
it "sends notifications for subscribers of newly added labels" do
label = project.labels.first
label.toggle_subscription(user2, project)
perform_enqueued_jobs do
post v3_api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: label.title
end
should_email(user2)
end
it "returns a 400 bad request if title not given" do it "returns a 400 bad request if title not given" do
post v3_api("/projects/#{project.id}/issues", user), labels: 'label, label2' post v3_api("/projects/#{project.id}/issues", user), labels: 'label, label2'
...@@ -1045,18 +1033,6 @@ describe API::V3::Issues, :mailer do ...@@ -1045,18 +1033,6 @@ describe API::V3::Issues, :mailer do
expect(json_response['labels']).to eq([label.title]) expect(json_response['labels']).to eq([label.title])
end end
it "sends notifications for subscribers of newly added labels when issue is updated" do
label = create(:label, title: 'foo', color: '#FFAABB', project: project)
label.toggle_subscription(user2, project)
perform_enqueued_jobs do
put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
title: 'updated title', labels: label.title
end
should_email(user2)
end
it 'removes all labels' do it 'removes all labels' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), labels: '' put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), labels: ''
......
...@@ -1307,6 +1307,33 @@ describe NotificationService, :mailer do ...@@ -1307,6 +1307,33 @@ describe NotificationService, :mailer do
end end
describe 'GroupMember' do describe 'GroupMember' do
let(:added_user) { create(:user) }
describe '#new_access_request' do
let(:master) { create(:user) }
let(:owner) { create(:user) }
let(:developer) { create(:user) }
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_owner(owner)
group.add_master(master)
group.add_developer(developer)
end
end
before do
reset_delivered_emails!
end
it 'sends notification to group owners_and_masters' do
group.request_access(added_user)
should_email(owner)
should_email(master)
should_not_email(developer)
end
end
describe '#decline_group_invite' do describe '#decline_group_invite' do
let(:creator) { create(:user) } let(:creator) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
...@@ -1328,18 +1355,9 @@ describe NotificationService, :mailer do ...@@ -1328,18 +1355,9 @@ describe NotificationService, :mailer do
describe '#new_group_member' do describe '#new_group_member' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:added_user) { create(:user) }
def create_member!
GroupMember.create(
group: group,
user: added_user,
access_level: Gitlab::Access::GUEST
)
end
it 'sends a notification' do it 'sends a notification' do
create_member! group.add_guest(added_user)
should_only_email(added_user) should_only_email(added_user)
end end
...@@ -1349,7 +1367,7 @@ describe NotificationService, :mailer do ...@@ -1349,7 +1367,7 @@ describe NotificationService, :mailer do
end end
it 'does not send a notification' do it 'does not send a notification' do
create_member! group.add_guest(added_user)
should_not_email_anyone should_not_email_anyone
end end
end end
...@@ -1357,8 +1375,42 @@ describe NotificationService, :mailer do ...@@ -1357,8 +1375,42 @@ describe NotificationService, :mailer do
end end
describe 'ProjectMember' do describe 'ProjectMember' do
let(:project) { create(:project) }
set(:added_user) { create(:user) }
describe '#new_access_request' do
context 'for a project in a user namespace' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
project.add_master(project.owner)
end
end
it 'sends notification to project owners_and_masters' do
project.request_access(added_user)
should_only_email(project.owner)
end
end
context 'for a project in a group' do
let(:group_owner) { create(:user) }
let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
let!(:project) { create(:project, :public, :access_requestable, namespace: group) }
before do
reset_delivered_emails!
end
it 'sends notification to group owners_and_masters' do
project.request_access(added_user)
should_only_email(group_owner)
end
end
end
describe '#decline_group_invite' do describe '#decline_group_invite' do
let(:project) { create(:project) }
let(:member) { create(:user) } let(:member) { create(:user) }
before do before do
...@@ -1375,19 +1427,12 @@ describe NotificationService, :mailer do ...@@ -1375,19 +1427,12 @@ describe NotificationService, :mailer do
end end
describe '#new_project_member' do describe '#new_project_member' do
let(:project) { create(:project) }
let(:added_user) { create(:user) }
def create_member!
create(:project_member, user: added_user, project: project)
end
it do it do
create_member! create_member!
should_only_email(added_user) should_only_email(added_user)
end end
describe 'when notifications are disabled' do context 'when notifications are disabled' do
before do before do
create_global_setting_for(added_user, :disabled) create_global_setting_for(added_user, :disabled)
end end
...@@ -1398,6 +1443,10 @@ describe NotificationService, :mailer do ...@@ -1398,6 +1443,10 @@ describe NotificationService, :mailer do
end end
end end
end end
def create_member!
create(:project_member, user: added_user, project: project)
end
end end
context 'guest user in private project' do context 'guest user in private project' do
......
...@@ -20,6 +20,32 @@ describe ProcessCommitWorker do ...@@ -20,6 +20,32 @@ describe ProcessCommitWorker do
worker.perform(project.id, -1, commit.to_hash) worker.perform(project.id, -1, commit.to_hash)
end end
context 'when commit is a merge request merge commit' do
let(:merge_request) do
create(:merge_request,
description: "Closes #{issue.to_reference}",
source_branch: 'feature-merged',
target_branch: 'master',
source_project: project)
end
let(:commit) do
project.repository.create_branch('feature-merged', 'feature')
sha = project.repository.merge(user,
merge_request.diff_head_sha,
merge_request,
"Closes #{issue.to_reference}")
project.repository.commit(sha)
end
it 'it does not close any issues from the commit message' do
expect(worker).not_to receive(:close_issues)
worker.perform(project.id, user.id, commit.to_hash)
end
end
it 'processes the commit message' do it 'processes the commit message' do
expect(worker).to receive(:process_commit_message).and_call_original expect(worker).to receive(:process_commit_message).and_call_original
...@@ -48,11 +74,9 @@ describe ProcessCommitWorker do ...@@ -48,11 +74,9 @@ describe ProcessCommitWorker do
describe '#process_commit_message' do describe '#process_commit_message' do
context 'when pushing to the default branch' do context 'when pushing to the default branch' do
it 'closes issues that should be closed per the commit message' do it 'closes issues that should be closed per the commit message' do
allow(commit).to receive(:safe_message) allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
.and_return("Closes #{issue.to_reference}")
expect(worker).to receive(:close_issues) expect(worker).to receive(:close_issues).with(project, user, user, commit, [issue])
.with(project, user, user, commit, [issue])
worker.process_commit_message(project, commit, user, user, true) worker.process_commit_message(project, commit, user, user, true)
end end
...@@ -60,8 +84,7 @@ describe ProcessCommitWorker do ...@@ -60,8 +84,7 @@ describe ProcessCommitWorker do
context 'when pushing to a non-default branch' do context 'when pushing to a non-default branch' do
it 'does not close any issues' do it 'does not close any issues' do
allow(commit).to receive(:safe_message) allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
.and_return("Closes #{issue.to_reference}")
expect(worker).not_to receive(:close_issues) expect(worker).not_to receive(:close_issues)
...@@ -102,8 +125,7 @@ describe ProcessCommitWorker do ...@@ -102,8 +125,7 @@ describe ProcessCommitWorker do
describe '#update_issue_metrics' do describe '#update_issue_metrics' do
it 'updates any existing issue metrics' do it 'updates any existing issue metrics' do
allow(commit).to receive(:safe_message) allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
.and_return("Closes #{issue.to_reference}")
worker.update_issue_metrics(commit, user) worker.update_issue_metrics(commit, user)
...@@ -113,10 +135,10 @@ describe ProcessCommitWorker do ...@@ -113,10 +135,10 @@ describe ProcessCommitWorker do
end end
it "doesn't execute any queries with false conditions" do it "doesn't execute any queries with false conditions" do
allow(commit).to receive(:safe_message) allow(commit).to receive(:safe_message).and_return("Lorem Ipsum")
.and_return("Lorem Ipsum")
expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/) expect { worker.update_issue_metrics(commit, user) }
.not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
end end
end end
...@@ -128,8 +150,9 @@ describe ProcessCommitWorker do ...@@ -128,8 +150,9 @@ describe ProcessCommitWorker do
end end
it 'parses date strings into Time instances' do it 'parses date strings into Time instances' do
commit = worker commit = worker.build_commit(project,
.build_commit(project, id: '123', authored_date: Time.now.to_s) id: '123',
authored_date: Time.now.to_s)
expect(commit.authored_date).to be_an_instance_of(Time) expect(commit.authored_date).to be_an_instance_of(Time)
end end
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -1443,6 +1443,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0: ...@@ -1443,6 +1443,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
escape-string-regexp "^1.0.5" escape-string-regexp "^1.0.5"
supports-color "^4.0.0" supports-color "^4.0.0"
chart.js@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-1.0.2.tgz#ad57d2229cfd8ccf5955147e8121b4911e69dfe7"
chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0: chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
......
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