Commit 468a2ed6 authored by Filipa Lacerda's avatar Filipa Lacerda

[ci skip] Merge branch 'master' into 3776-ci-view-for-sast

* master: (28 commits)
  Merge branch '4984-broken-master-gitlab-git_access_wiki_spec' into 'master'
  fix broken specs
  remove common_d3 bundle
  remove graphs_show webpack bundle
  Chart.html.haml refactor (EE Version)
  Refactor and simplify the docs on 'include' for CI/CD
  [GH Import] Create an empty wiki if wiki import failed
  Resolve "group request membership mail with too long list of "To:""
  Fix a test that was hardcoding a documentation URL
  Add changelog entry
  Fix single digit value clipping
  Fix the ee-files-location-check job
  Extend Epics API - searching and sorting
  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
  Workaround for epic spec failure
  Don't attempt to update user tracked fields if database is in read-only
  ...
parents 69fbe24f 9b3f93a2
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';
document.addEventListener('DOMContentLoaded', () => {
......
import flash from '../flash';
import { __ } from '../locale';
import axios from '../lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import ContributorsStatGraph from './stat_graph_contributors';
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 */
import _ from 'underscore';
import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
import { n__, s__, createDateTimeFormat, sprintf } from '../locale';
export default (function() {
function ContributorsStatGraph() {
......
......@@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis';
import { area } from 'd3-shape';
import { brushX } from 'd3-brush';
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 };
......
import Chart from 'vendor/Chart';
import Chart from 'chart.js';
const options = {
scaleOverlay: true,
......
......@@ -10,7 +10,7 @@
.status-neutral,
.status-red, {
height: 100%;
min-width: 25px;
min-width: 30px;
padding: 0 5px;
font-size: $tooltip-font-size;
font-weight: normal;
......
......@@ -7,18 +7,11 @@ module Emails
helper_method :member_source, :member
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_id = member_id
admins = member_source.members.owners_and_masters.pluck(: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,
mail(to: recipient_notification_email,
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end
......
......@@ -417,6 +417,10 @@ class Commit
!!(title =~ WIP_REGEX)
end
def merged_merge_request?(user)
!!merged_merge_request(user)
end
private
def commit_reference(from, referable_commit_id, full: false)
......@@ -445,10 +449,6 @@ class Commit
changes
end
def merged_merge_request?(user)
!!merged_merge_request(user)
end
def merged_merge_request_no_cache(user)
MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit?
end
......
......@@ -61,6 +61,8 @@ class User < ActiveRecord::Base
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
def update_tracked_fields!(request)
return if Gitlab::Database.read_only?
update_tracked_fields(request)
lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i)
......
......@@ -231,7 +231,12 @@ class NotificationService
def new_access_request(member)
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
def decline_access_request(member)
......@@ -482,4 +487,14 @@ class NotificationService
def notifiable_users(*args)
NotificationRecipientService.notifiable_users(*args)
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
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('u2f')
= webpack_bundle_tag('u2f')
%div
= render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication'
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('profile')
= webpack_bundle_tag('profile')
......@@ -6,8 +6,8 @@
- content_for :page_specific_javascripts do
- if inject_u2f_api?
= page_specific_javascript_bundle_tag('u2f')
= page_specific_javascript_bundle_tag('two_factor_auth')
= webpack_bundle_tag('u2f')
= 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 }
.row.prepend-top-default
......
......@@ -29,4 +29,4 @@
= commit_in_fork_help
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('blob')
= webpack_bundle_tag('blob')
......@@ -3,7 +3,7 @@
- page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('blob')
= webpack_bundle_tag('blob')
%div{ class: container_class }
- if @conflict
......
......@@ -2,7 +2,7 @@
- page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('blob')
= webpack_bundle_tag('blob')
.editor-title-row
%h3.page-title.blob-new-page-title
New file
......
- 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 } }
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('notebook_viewer')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('notebook_viewer')
.file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } }
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('pdf_viewer')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('pdf_viewer')
.file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } }
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('sketch_viewer')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('sketch_viewer')
.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' }
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('stl_viewer')
= webpack_bundle_tag('stl_viewer')
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
......
......@@ -8,5 +8,5 @@
} }
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('commit_pipelines')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('commit_pipelines')
......@@ -7,8 +7,8 @@
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('diff_notes')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('diff_notes')
.container-fluid{ class: [limited_container_width, container_class] }
= render "commit_box"
......
- @no_container = true
- page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('cycle_analytics')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('cycle_analytics')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
......
......@@ -2,8 +2,8 @@
- page_title "Environments"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag("environments_folder")
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag("environments_folder")
#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json),
"folder-name" => @folder,
......
......@@ -3,8 +3,8 @@
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag("common_vue")
= page_specific_javascript_bundle_tag("environments")
= webpack_bundle_tag("common_vue")
= webpack_bundle_tag("environments")
#environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
......
......@@ -2,7 +2,6 @@
- page_title "Metrics for environment", @environment.name
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'common_d3'
.prometheus-container{ class: container_class }
.top-area
......
......@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm/xterm"
= page_specific_javascript_bundle_tag("terminal")
= webpack_bundle_tag("terminal")
%div{ class: container_class }
.top-area
......
- @no_container = true
- 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 }
%h4.sub-header
......
- @no_container = true
- 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) }
.sub-header-block
......
......@@ -100,5 +100,5 @@
= render 'shared/issuable/sidebar', issuable: @issue
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('issue_show')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('issue_show')
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('merge_conflicts')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title"
......
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('merge_conflicts')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title"
......
- breadcrumb_title "Graph"
- page_title "Graph", @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('network')
= webpack_bundle_tag('network')
= render "head"
%div{ class: container_class }
.project-network
......
......@@ -16,5 +16,5 @@
"ci-lint-path" => ci_lint_path,
"reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } }
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('pipelines')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('pipelines')
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('protected_branches')
= webpack_bundle_tag('protected_branches')
- content_for :create_protected_branch do
= render 'projects/protected_branches/create_protected_branch'
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('protected_tags')
= webpack_bundle_tag('protected_tags')
- content_for :create_protected_tag do
= render 'projects/protected_tags/create_protected_tag'
......
......@@ -14,8 +14,8 @@
.col-lg-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } }
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('registry_list')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('registry_list')
.row.prepend-top-10
.col-lg-12
......
......@@ -3,8 +3,8 @@
- @content_class = "limit-container-width" unless fluid_layout
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('deploy_keys')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('deploy_keys')
= render "projects/push_rules/index"
= render "projects/mirrors/show"
......
- todo = issuable_todo(issuable)
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('ee_sidebar')
= webpack_bundle_tag('common_vue')
= webpack_bundle_tag('ee_sidebar')
%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)}" } }
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_bundle_tag('snippet')
= webpack_bundle_tag('snippet')
.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|
......
......@@ -23,27 +23,25 @@ class ProcessCommitWorker
return unless user
commit = build_commit(project, commit_hash)
author = commit.author || user
process_commit_message(project, commit, user, author, default)
update_issue_metrics(commit, author)
end
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?
close_issues(project, user, author, commit, closed_issues)
end
closed_issues = default ? commit.closes_issues(user) : []
close_issues(project, user, author, commit, closed_issues) if closed_issues.any?
commit.create_cross_references!(author, closed_issues)
end
def close_issues(project, user, author, commit, issues)
# 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.
IssueCollection.new(issues).updatable_by_user(user).each do |issue|
Issues::CloseService.new(project, author)
......
---
title: Add basic searching and sorting to Epics API
merge_request:
author:
type: added
---
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: 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|
cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new(
adapter,
Rails.cache,
expires_in: 10.seconds)
expires_in: 1.hour)
Flipper.new(cached_adapter)
end
......
......@@ -67,9 +67,6 @@ var config = {
new_epic: 'ee/epics/new_epic/new_epic_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js',
geo_nodes: 'ee/geo_nodes',
graphs: './graphs/graphs_bundle.js',
graphs_charts: './graphs/graphs_charts.js',
graphs_show: './graphs/graphs_show.js',
help: './help/help.js',
issuable: './issuable/issuable_bundle.js',
issues: './issues/issues_bundle.js',
......@@ -296,21 +293,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',
'burndown_chart', // EE
],
minChunks: function (module, count) {
return module.resource && /d3-/.test(module.resource);
},
}),
// create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({
names: ['main', 'common', 'webpack_runtime'],
......
......@@ -16,11 +16,16 @@ Gets all epics of the requested group and its subgroups.
```
GET /groups/:id/-/epics
GET /groups/:id/-/epics?author_id=5
```
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `author_id` | integer | no | Return epics created by the given user `id` |
| `order_by` | string | no | Return epics ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return epics sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search epics against their `title` and `description` |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/1/-/epics
......@@ -45,7 +50,9 @@ Example response:
"web_url": "http://localhost:3001/kam"
},
"start_date": null,
"end_date": null
"end_date": null,
"created_at": "2018-01-21T06:21:13.165Z",
"updated_at": "2018-01-22T12:41:41.166Z"
}
]
```
......@@ -85,7 +92,9 @@ Example response:
"web_url": "http://localhost:3001/arnita"
},
"start_date": null,
"end_date": null
"end_date": null,
"created_at": "2018-01-21T06:21:13.165Z",
"updated_at": "2018-01-22T12:41:41.166Z"
}
```
......@@ -127,7 +136,9 @@ Example response:
"username" : "eileen.lowe"
},
"start_date": null,
"end_date": null
"end_date": null,
"created_at": "2018-01-21T06:21:13.165Z",
"updated_at": "2018-01-22T12:41:41.166Z"
}
```
......@@ -170,7 +181,9 @@ Example response:
"username" : "eileen.lowe"
},
"start_date": null,
"end_date": null
"end_date": null,
"created_at": "2018-01-21T06:21:13.165Z",
"updated_at": "2018-01-22T12:41:41.166Z"
}
```
......
......@@ -361,72 +361,82 @@ Additionally, if you have a job that unconditionally recreates the cache without
reference to its previous contents, you can use `policy: push` in that job to
skip the download step.
### include (EEP)
### include
> Introduced in [GitLab Enterprise Edition Premium][ee] 10.5.
> Introduced in [GitLab Edition Premium][ee] 10.5.
From 10.5 we can use `include` keyword to allow the inclusion of external YAML files.
Using the `include` keyword, you can allow the inclusion of external YAML files.
In the following example, the content of`.before-script-template.yml` will be
automatically fetched and evaluated along with the content of `.gitlab-ci.yml`:
```yaml
# Content of https://gitlab.com/awesome-project/raw/master/.before-script-template.yml
before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
```
```yaml
# Content of .gitlab-ci.yml
include: 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml'
rspec:
script:
- bundle exec rspec
rubocop:
script:
- bundle exec rubocop
```
In the above example `.before-script-template.yml` content will be automatically fetched and evaluated along with the content of `.gitlab-ci.yml`.
You can define it either as a single string, or, in case you want to include
more than one files, an array of different values . The following examples
are both valid cases:
`include` supports two types of files:
```yaml
# Single string
- **local** to the same repository, referenced using the paths in the same the repository, i.e:
include: '/templates/.after-script-template.yml'
```
```yaml
# Within the repository
include: '/templates/.gitlab-ci-template.yml'
# Array
include:
- 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml'
- '/templates/.after-script-template.yml'
```
- **remote** in a different location, accessed using HTTP/HTTPS protocol, referenced using the full URL, i.e:
---
```yaml
include: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
```
`include` supports two types of files:
Also, `include` supports a single string or an array composed by different values, so
- **local** to the same repository, referenced by using full paths in the same
repository, with `/` being the root directory. For example:
```yaml
include: '/templates/.gitlab-ci-template.yml'
```
```yaml
# Within the repository
include: '/templates/.gitlab-ci-template.yml'
```
and
NOTE: **Note:**
You can only use files that are currently tracked by Git on the same branch
your configuration file is. In other words, when using a **local file**, make
sure that both `.gitlab-ci.yml` and the local file are on the same branch.
```yaml
include:
- 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
- '/templates/.gitlab-ci-template.yml'
```
- **remote** in a different location, accessed using HTTP/HTTPS, referenced
using the full URL. For example:
are both valid use cases.
```yaml
include: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
```
#### Restrictions
---
- We can only use files that are currently tracked by Git on the same branch your configuration file is. In other words, when using a **local file** make sure that both, `.gitlab-ci.yml` and the local file are on the same branch.
- Since external files defined on `include` are evaluated first, the content on `.gitlab-ci.yml` **will always take precedence over the content of the external files, no matters of the position of the `include` keyword, allowing to override values and functions with local definitions**, for example:
Since external files defined by `include` are evaluated first, the content of
`.gitlab-ci.yml` will always take precedence over the content of the external
files, no matter of the position of the `include` keyword. This allows you to
override values and functions with local definitions. For example:
```yaml
# Content of https://company.com/autodevops-template.yml
......@@ -434,32 +444,18 @@ are both valid use cases.
variables:
POSTGRES_USER: user
POSTGRES_PASSWORD: testing_password
POSTGRES_ENABLED: "true"
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
KUBERNETES_VERSION: 1.8.6
HELM_VERSION: 2.6.1
CODECLIMATE_VERSION: 0.69.0
production:
stage: production
script:
- check_kube_domain
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- create_secret
- deploy
- delete canary
- persist_environment_url
environment:
name: production
url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
url: https://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
only:
refs:
- master
kubernetes: active
- master
```
```yaml
......@@ -477,35 +473,24 @@ variables:
stages:
- build
- test
- review
- dast
- staging
- canary
- production
- performance
- cleanup
production:
stage: production
script:
- check_kube_domain
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- create_secret
- deploy
environment:
name: production
url: http://auto_devops_domain.com
url: https://domain.com
only:
refs:
- master
# ....
- master
```
In this case, the variables `POSTGRES_USER`, `POSTGRES_PASSWORD` and `POSTGRES_DB` along with the `production` job defined on `autodevops-template.yml` will be overridden by the ones defined on `.gitlab-ci.yml`.
In this case, the variables `POSTGRES_USER`, `POSTGRES_PASSWORD` and
`POSTGRES_DB` along with the `production` job defined in
`autodevops-template.yml` will be overridden by the ones defined in
`.gitlab-ci.yml`.
## Jobs
......
/* global Chart */
import Chart from 'chart.js';
export default () => {
const dataEl = document.getElementById('js-analytics-data');
if (dataEl) {
......
......@@ -2,10 +2,6 @@
- header_title group_title(@group, "Contribution Analytics", group_analytics_path(@group))
- if @group.feature_available?(:contribution_analytics)
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
.sub-header-block
.pull-right
.dropdown.inline
......
= content_for :sub_nav do
.scrolling-tabs-container.sub-nav-scroll
= render 'shared/nav_scroll'
.nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
= nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
= link_to 'Commits', commits_namespace_project_graph_path
= nav_link(action: :languages) do
= link_to 'Languages', languages_namespace_project_graph_path
- if @project.feature_available?(:builds, current_user)
= nav_link(action: :ci) do
= link_to ci_namespace_project_graph_path do
Continuous Integration
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('ee_protected_branches')
= webpack_bundle_tag 'ee_protected_branches'
- content_for :create_protected_branch do
= render 'projects/protected_branches/ee/create_protected_branch'
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('ee_protected_tags')
= webpack_bundle_tag 'ee_protected_tags'
- content_for :create_protected_tag do
= render 'projects/protected_tags/ee/create_protected_tag'
......
......@@ -4,8 +4,7 @@
- warning = data_warning_for(burndown)
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('burndown_chart')
= webpack_bundle_tag 'burndown_chart'
= warning
......
......@@ -29,6 +29,14 @@ module API
def epic
@epic ||= user_group.epics.find_by(iid: params[:epic_iid])
end
def find_epics(args = {})
args = declared_params.merge(args)
epics = EpicsFinder.new(current_user, args).execute
epics.reorder(args[:order_by] => args[:sort])
end
end
params do
......@@ -39,8 +47,16 @@ module API
desc 'Get epics for the group' do
success Entities::Epic
end
params do
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return epics ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return epics sorted in `asc` or `desc` order.'
optional :search, type: String, desc: 'Search epics for text present in the title or description'
optional :author_id, type: Integer, desc: 'Return epics which are authored by the user with the given ID'
end
get ':id/-/epics' do
present EpicsFinder.new(current_user, group_id: user_group.id).execute, with: Entities::Epic
present find_epics(group_id: user_group.id), with: Entities::Epic
end
desc 'Get details of an epic' do
......
......@@ -55,6 +55,10 @@ describe 'Epic Issues', :js do
def add_issues(references)
find('.related-issues-block h3.panel-title button').click
find('.js-add-issuable-form-input').set(references)
# When adding long references, for some reason the input gets stuck
# waiting for more text. Send a keystroke before clicking the button to
# get out of this mode.
find('.js-add-issuable-form-input').send_keys(:tab)
find('.js-add-issuable-form-add-button').click
wait_for_requests
......
......@@ -19,7 +19,9 @@
"additionalProperties": false
},
"start_date": { "type": ["string", "null"] },
"end_date": { "type": ["string", "null"] }
"end_date": { "type": ["string", "null"] },
"created_at": { "type": ["string", "null"] },
"updated_at": { "type": ["string", "null"] }
},
"required": [
"id", "iid", "group_id", "title"
......
......@@ -19,7 +19,9 @@
"description": { "type": ["string", "null"] },
"author": { "type": ["object", "null"] },
"start_date": { "type": ["string", "null"] },
"end_date": { "type": ["string", "null"] }
"end_date": { "type": ["string", "null"] },
"created_at": { "type": ["string", "null"] },
"updated_at": { "type": ["string", "null"] }
},
"additionalProperties": false
},
......
......@@ -26,7 +26,7 @@ describe Gitlab::GitAccess do
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance. "\
"Please use the Primary node URL: https://localhost:3000/gitlab/#{project.full_path}.git. Documentation: https://docs.gitlab.com/ee/gitlab-geo/using_a_geo_server.html"
"Please use the Primary node URL: https://localhost:3000/gitlab/#{project.full_path}.git. Documentation: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
......
......@@ -30,7 +30,7 @@ describe Gitlab::GitAccessWiki do
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance. Please use the Primary node URL: "\
"https://localhost:3000/gitlab/#{project.full_path}.wiki.git. Documentation: https://docs.gitlab.com/ee/gitlab-geo/using_a_geo_server.html"
"https://localhost:3000/gitlab/#{project.full_path}.wiki.git. Documentation: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
......
......@@ -60,6 +60,77 @@ describe API::Epics do
expect(response).to match_response_schema('public_api/v4/epics', dir: 'ee')
end
end
context 'with multiple epics' do
let(:user2) { create(:user) }
let!(:epic) do
create(:epic,
group: group,
created_at: 3.days.ago,
updated_at: 2.days.ago)
end
let!(:epic2) do
create(:epic,
author: user2,
group: group,
title: 'foo',
description: 'bar',
created_at: 2.days.ago,
updated_at: 3.days.ago)
end
before do
stub_licensed_features(epics: true)
end
def expect_array_response(expected)
items = json_response.map { |i| i['id'] }
expect(items).to eq(expected)
end
it 'returns epics authored by the given author id' do
get api(url, user), author_id: user2.id
expect_array_response([epic2.id])
end
it 'returns epics matching given search string for title' do
get api(url, user), search: epic2.title
expect_array_response([epic2.id])
end
it 'returns epics matching given search string for description' do
get api(url, user), search: epic2.description
expect_array_response([epic2.id])
end
it 'sorts by created_at descending by default' do
get api(url, user)
expect_array_response([epic2.id, epic.id])
end
it 'sorts ascending when requested' do
get api(url, user), sort: :asc
expect_array_response([epic.id, epic2.id])
end
it 'sorts by updated_at descending when requested' do
get api(url, user), order_by: :updated_at
expect_array_response([epic.id, epic2.id])
end
it 'sorts by updated_at ascending when requested' do
get api(url, user), order_by: :updated_at, sort: :asc
expect_array_response([epic2.id, epic.id])
end
end
end
describe 'GET /groups/:id/-/epics/:epic_iid' do
......
......@@ -506,6 +506,8 @@ module API
expose :author, using: Entities::UserBasic
expose :start_date
expose :end_date
expose :created_at
expose :updated_at
end
class EpicIssue < Issue
......
......@@ -508,7 +508,7 @@ module Gitlab
@committed_date = Time.at(commit.committer.date.seconds).utc
@committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup
@parent_ids = commit.parent_ids
@parent_ids = Array(commit.parent_ids)
end
def serialize_keys
......
......@@ -2195,6 +2195,7 @@ module Gitlab
# Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range}))
run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin|
stdin.binmode
stdin.write(diff)
end
......
......@@ -63,6 +63,7 @@ module Gitlab
true
rescue Gitlab::Shell::Error => e
if e.message !~ /repository not exported/
project.create_wiki
fail_import("Failed to import the wiki: #{e.message}")
else
true
......
......@@ -4,18 +4,6 @@ module QA
class SecretVariable < Factory::Base
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|
project.name = 'project-with-secret-variables'
project.description = 'project for adding secret variable test'
......
......@@ -98,6 +98,10 @@ module QA
views.map(&:errors).flatten
end
def self.elements
views.map(&:elements).flatten
end
class DSL
attr_reader :views
......
......@@ -6,39 +6,37 @@ module QA
include Common
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_value, '.js-ci-variable-input-value'
element :key_placeholder, 'Input variable key'
element :value_placeholder, 'Input variable value'
end
view 'app/views/ci/variables/_index.html.haml' do
element :save_variables, '.js-secret-variables-save-button'
element :reveal_values, '.js-secret-value-reveal-button'
end
def fill_variable_key(key)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
page.find('.js-ci-variable-input-key').set(key)
end
fill_in('Input variable key', with: key, match: :first)
end
def fill_variable_value(value)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
page.find('.js-ci-variable-input-value').set(value)
end
fill_in('Input variable value', with: value, match: :first)
end
def save_variables
click_button('Save variables')
find('.js-secret-variables-save-button').click
end
def variable_key
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
page.find('.js-ci-variable-input-key').value
end
def reveal_variables
find('.js-secret-value-reveal-button').click
end
def variable_value
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
page.find('.js-ci-variable-input-value').value
def variable_value(key)
within('.ci-variable-row-body', text: key) do
find('.js-ci-variable-input-value').value
end
end
end
......
......@@ -4,16 +4,21 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
variable_key = 'VARIABLE_KEY'
variable_value = 'variable value'
variable = Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = variable_key
resource.value = variable_value
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = 'VARIABLE_KEY'
resource.value = 'some secret variable'
end
expect(variable.key).to eq(variable_key)
expect(variable.value).to eq(variable_value)
Page::Project::Settings::CICD.perform do |settings|
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
......@@ -14,7 +14,7 @@ describe QA::Page::Base do
end
view 'path/to/some/_partial.html.haml' do
element :something, 'string pattern'
element :another_element, 'string pattern'
end
end
end
......@@ -25,11 +25,10 @@ describe QA::Page::Base do
end
it 'populates views objects with data about elements' do
subject.views.first.elements.tap do |elements|
expect(elements.size).to eq 2
expect(elements).to all(be_an_instance_of QA::Page::Element)
expect(elements.map(&:name)).to eq [:something, :something_else]
end
expect(subject.elements.size).to eq 3
expect(subject.elements).to all(be_an_instance_of QA::Page::Element)
expect(subject.elements.map(&:name))
.to eq [:something, :something_else, :another_element]
end
end
......
......@@ -13,11 +13,21 @@ WHITELIST = %w[
`git remote add canonical-ee https://gitlab.com/gitlab-org/gitlab-ee.git`
`git remote add canonical-ce https://gitlab.com/gitlab-org/gitlab-ce.git`
`git fetch canonical-ee master --quiet`
`git fetch canonical-ce master --quiet`
new_files_in_this_branch_not_at_the_ee_top_level =
`git diff canonical-ee/master...HEAD --name-status --diff-filter=A -- ./ ':!ee' | cut -f2`.lines.map(&:strip)
current_branch = ENV.fetch('CI_COMMIT_REF_NAME', `git rev-parse --abbrev-ref HEAD`).strip
ce_branch_name = current_branch.sub(/(\Aee\-|\-ee\z)/, '')
`git fetch canonical-ce #{ce_branch_name} --quiet`
unless $?.success?
`git fetch canonical-ce 'master' --quiet`
end
ee_specific_files_in_ce_master_not_at_the_ee_top_level =
`git diff canonical-ce/master...HEAD --name-status --diff-filter=A -- ./ ':!ee' | cut -f2`.lines.map(&:strip)
`git diff FETCH_HEAD..HEAD --name-status --diff-filter=A -- ./ ':!ee' | cut -f2`.lines.map(&:strip)
new_ee_specific_files_not_at_the_ee_top_level =
new_files_in_this_branch_not_at_the_ee_top_level & ee_specific_files_in_ce_master_not_at_the_ee_top_level
......
/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
import { scaleLinear, scaleTime } from 'd3-scale';
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 };
......
import ContributorsStatGraph from '~/graphs/stat_graph_contributors';
import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph';
import ContributorsStatGraph from '~/pages/projects/graphs/show/stat_graph_contributors';
import { ContributorsGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
import { setLanguage } from '../helpers/locale_helper';
......
/* 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("#parse_log", function () {
......
......@@ -221,7 +221,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'with LFS not enabled' 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
end
......@@ -238,7 +238,7 @@ describe Gitlab::Checks::ChangeAccess do
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 }
it "doesn't raise any error" do
......
# coding: utf-8
require "spec_helper"
describe Gitlab::Git::Repository, seed_helper: true do
......@@ -2221,6 +2222,17 @@ describe Gitlab::Git::Repository, seed_helper: true do
subject
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
......
......@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
import_source: 'foo/bar',
repository_storage_path: 'foo',
disk_path: 'foo',
repository: repository
repository: repository,
create_wiki: true
)
end
......@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
expect(importer.import_wiki_repository).to eq(true)
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)
.to receive(:import_repository)
.and_raise(Gitlab::Shell::Error)
......@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
.to receive(:fail_import)
.and_return(false)
expect(project)
.to receive(:create_wiki)
expect(importer.import_wiki_repository).to eq(false)
end
end
......
......@@ -578,59 +578,30 @@ describe Notify do
end
describe 'project access requested' 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, 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
let(:project) do
create(:project, :public, :access_requestable) do |project|
project.add_master(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) }
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) }
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, recipient.notification_email) }
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_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(group.members.owners_and_masters.first.user.notification_email)
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 #{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
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
......@@ -1074,13 +1045,16 @@ describe Notify do
group.request_access(user)
group.requesters.find_by(user_id: user.id)
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 '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.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_html_escaped_body_text group.name
is_expected.to have_body_text group_group_members_url(group)
......
......@@ -524,6 +524,14 @@ describe User do
user2.update_tracked_fields!(request)
end.to change { user2.reload.current_sign_in_at }
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
shared_context 'user keys' do
......
......@@ -1349,6 +1349,33 @@ describe NotificationService, :mailer do
end
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
let(:creator) { create(:user) }
let(:group) { create(:group) }
......@@ -1370,18 +1397,9 @@ describe NotificationService, :mailer do
describe '#new_group_member' do
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
create_member!
group.add_guest(added_user)
should_only_email(added_user)
end
......@@ -1391,7 +1409,7 @@ describe NotificationService, :mailer do
end
it 'does not send a notification' do
create_member!
group.add_guest(added_user)
should_not_email_anyone
end
end
......@@ -1399,8 +1417,42 @@ describe NotificationService, :mailer do
end
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
let(:project) { create(:project) }
let(:member) { create(:user) }
before do
......@@ -1417,19 +1469,12 @@ describe NotificationService, :mailer do
end
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
create_member!
should_only_email(added_user)
end
describe 'when notifications are disabled' do
context 'when notifications are disabled' do
before do
create_global_setting_for(added_user, :disabled)
end
......@@ -1440,6 +1485,10 @@ describe NotificationService, :mailer do
end
end
end
def create_member!
create(:project_member, user: added_user, project: project)
end
end
context 'guest user in private project' do
......
......@@ -20,6 +20,32 @@ describe ProcessCommitWorker do
worker.perform(project.id, -1, commit.to_hash)
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
expect(worker).to receive(:process_commit_message).and_call_original
......@@ -48,11 +74,9 @@ describe ProcessCommitWorker do
describe '#process_commit_message' do
context 'when pushing to the default branch' do
it 'closes issues that should be closed per the commit message' do
allow(commit).to receive(:safe_message)
.and_return("Closes #{issue.to_reference}")
allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
expect(worker).to receive(:close_issues)
.with(project, user, user, commit, [issue])
expect(worker).to receive(:close_issues).with(project, user, user, commit, [issue])
worker.process_commit_message(project, commit, user, user, true)
end
......@@ -60,8 +84,7 @@ describe ProcessCommitWorker do
context 'when pushing to a non-default branch' do
it 'does not close any issues' do
allow(commit).to receive(:safe_message)
.and_return("Closes #{issue.to_reference}")
allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
expect(worker).not_to receive(:close_issues)
......@@ -102,8 +125,7 @@ describe ProcessCommitWorker do
describe '#update_issue_metrics' do
it 'updates any existing issue metrics' do
allow(commit).to receive(:safe_message)
.and_return("Closes #{issue.to_reference}")
allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
worker.update_issue_metrics(commit, user)
......@@ -113,10 +135,10 @@ describe ProcessCommitWorker do
end
it "doesn't execute any queries with false conditions" do
allow(commit).to receive(:safe_message)
.and_return("Lorem Ipsum")
allow(commit).to receive(:safe_message).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
......@@ -128,8 +150,9 @@ describe ProcessCommitWorker do
end
it 'parses date strings into Time instances' do
commit = worker
.build_commit(project, id: '123', authored_date: Time.now.to_s)
commit = worker.build_commit(project,
id: '123',
authored_date: Time.now.to_s)
expect(commit.authored_date).to be_an_instance_of(Time)
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:
escape-string-regexp "^1.0.5"
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:
version "1.7.0"
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