Commit 77004fe8 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ce-to-ee' into 'master'

CE upstream: Thursday

Closes gitaly#689 et gitlab-ce#39513

See merge request gitlab-org/gitlab-ee!3250
parents 97cffea7 5477331b
## Contributor license agreement
## Developer Certificate of Origin + License
By submitting code as an individual you agree to the
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
By submitting code as an entity you agree to the
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
By contributing to GitLab B.V., You accept and agree to the following terms and
conditions for Your present and future Contributions submitted to GitLab B.V.
Except for the license granted herein to GitLab B.V. and recipients of software
distributed by GitLab B.V., You reserve all right, title, and interest in and to
Your Contributions. All Contributions are subject to the following DCO + License
terms.
[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
_This notice should stay as the first item in the CONTRIBUTING.md file._
......
......@@ -316,7 +316,7 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-license (1.0.0)
gitlab-markup (1.6.2)
gitlab-markup (1.6.3)
gitlab_omniauth-ldap (2.0.4)
net-ldap (~> 0.16)
omniauth (~> 1.3)
......
......@@ -46,9 +46,14 @@ import './board_new_form';
currentPage() {
return this.state.currentPage;
},
reload() {
reload: {
get() {
return this.state.reload;
},
set(newValue) {
this.state.reload = newValue;
},
},
board() {
return this.state.currentBoard;
},
......
......@@ -4,6 +4,7 @@ import _ from 'underscore';
import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
import { n__ } from '../locale';
export default (function() {
function ContributorsStatGraph() {}
......@@ -44,7 +45,7 @@ export default (function() {
commits = $('<span/>', {
"class": 'graph-author-commits-count'
});
commits.text(author.commits + " commits");
commits.text(n__('%d commit', '%d commits', author.commits));
return $('<span/>').append(commits);
};
......
/* eslint-disable one-export, one-var, one-var-declaration-per-line */
import _ from 'underscore';
export const placeholderImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
......@@ -21,8 +19,11 @@ export default class LazyLoader {
}
searchLazyImages() {
this.lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
if (this.lazyImages.length) {
this.checkElementsInView();
}
}
startContentObserver() {
const contentNode = document.querySelector(this.observerNode) || document.querySelector('body');
......@@ -45,15 +46,13 @@ export default class LazyLoader {
checkElementsInView() {
const scrollTop = pageYOffset;
const visHeight = scrollTop + innerHeight + SCROLL_THRESHOLD;
let imgBoundRect, imgTop, imgBound;
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
if (selectedImage.getAttribute('data-src')) {
imgBoundRect = selectedImage.getBoundingClientRect();
imgTop = scrollTop + imgBoundRect.top;
imgBound = imgTop + imgBoundRect.height;
const imgBoundRect = selectedImage.getBoundingClientRect();
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
if (scrollTop < imgBound && visHeight > imgTop) {
LazyLoader.loadImage(selectedImage);
......
......@@ -122,7 +122,9 @@
// we need to do this to prevent noteForm inconsistent content warning
// this is something we intentionally do so we need to recover the content
this.note.note = noteText;
if (this.$refs.noteBody) {
this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better
}
},
},
created() {
......
......@@ -98,7 +98,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
@toggle="toggleOpen"
@submit="onSubmit">
<template slot="body" scope="props">
<template slot="body" slot-scope="props">
<p v-html="props.text"></p>
<form
......
......@@ -23,11 +23,16 @@
@include webkit-prefix(animation-duration, 2s);
}
&.spin {
&.spin-cw {
transform-origin: center;
animation: spin 4s linear infinite;
}
&.spin-ccw {
transform-origin: center;
animation: spin 4s linear infinite reverse;
}
&.flipOutX,
&.flipOutY,
&.bounceIn,
......
......@@ -209,7 +209,6 @@
padding: 24px 0 0;
.nav-links {
justify-content: center;
width: 100%;
float: none;
......@@ -217,6 +216,14 @@
float: none;
}
}
li:first-child {
margin-left: auto;
}
li:last-child {
margin-right: auto;
}
}
.group-info {
......
......@@ -50,3 +50,10 @@
font-size: 11px;
}
}
@media (max-width: $screen-md-max) {
.runners-content {
width: 100%;
overflow: auto;
}
}
......@@ -12,12 +12,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
if group
return render_404 unless can?(current_user, :read_group, group)
project.project_group_links.create(
group: group,
group_access: params[:link_group_access],
expires_at: params[:expires_at]
)
Projects::GroupLinks::CreateService.new(project, current_user, group_link_create_params).execute(group)
else
flash[:alert] = 'Please select a group.'
end
......@@ -32,7 +27,9 @@ class Projects::GroupLinksController < Projects::ApplicationController
end
def destroy
project.project_group_links.find(params[:id]).destroy
group_link = project.project_group_links.find(params[:id])
::Projects::GroupLinks::DestroyService.new(project, current_user).execute(group_link)
respond_to do |format|
format.html do
......@@ -47,4 +44,8 @@ class Projects::GroupLinksController < Projects::ApplicationController
def group_link_params
params.require(:group_link).permit(:group_access, :expires_at)
end
def group_link_create_params
params.permit(:link_group_access, :expires_at)
end
end
module Projects
module GroupLinks
class CreateService < BaseService
def execute(group)
return false unless group
project.project_group_links.create(
group: group,
group_access: params[:link_group_access],
expires_at: params[:expires_at]
)
end
end
end
end
module Projects
module GroupLinks
class DestroyService < BaseService
def execute(group_link)
return false unless group_link
group_link.destroy
end
end
end
end
......@@ -52,6 +52,7 @@
%br
- if @runners.any?
.runners-content
.table-holder
%table.table
%thead
......@@ -63,7 +64,7 @@
%th Projects
%th Jobs
%th Tags
%th= link_to 'Last contact', admin_runners_path(params.slice(:search).merge(sort: 'contacted_asc'))
%th Last contact
%th
- @runners.each do |runner|
......
- @no_container = true
- page_title "Contributors"
- page_title _('Contributors')
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_d3')
= webpack_bundle_tag('graphs')
......@@ -7,23 +7,23 @@
.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) }
.sub-header-block
.tree-ref-holder
.tree-ref-holder.inline.vertical-align-middle
= render 'shared/ref_switcher', destination: 'graphs'
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn'
.loading-graph
.center
%h3.page-title
%i.fa.fa-spinner.fa-spin
Building repository graph.
%p.slead Please wait a moment, this page will automatically refresh when ready.
= s_('ContributorsPage|Building repository graph.')
%p.slead
= s_('ContributorsPage|Please wait a moment, this page will automatically refresh when ready.')
.stat-graph.hide
.header.clearfix
%h3#date_header.page-title
%p.light
Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
= s_('ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits.') % { branch_name: @ref }
%input#brush_change{ :type => "hidden" }
.graphs.row
#contributors-master
......
This diff is collapsed.
......@@ -2,6 +2,10 @@ class UpdateMergeRequestsWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def metrics_tags
@metrics_tags || {}
end
def perform(project_id, user_id, oldrev, newrev, ref)
project = Project.find_by(id: project_id)
return unless project
......@@ -9,6 +13,11 @@ class UpdateMergeRequestsWorker
user = User.find_by(id: user_id)
return unless user
@metrics_tags = {
project_id: project_id,
user_id: user_id
}
MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
end
end
---
title: Add metric tagging for sidekiq workers
merge_request: 15111
author:
type: added
---
title: Support show-all-refs for git over HTTP
merge_request: 14834
author:
type: added
---
title: 'Support uml:: and captions in reStructuredText'
merge_request: 15120
author: Markus Koller
type: changed
---
title: Fixed user profile activity tab being off-screen on mobile
merge_request:
author:
type: fixed
---
title: Mobile-friendly table on Admin Runners
merge_request:
author: Takuya Noguchi
type: fixed
---
title: Refactor GroupLinksController
merge_request:
author: 15121
type: other
---
title: Disable Unicorn sampling in Sidekiq since there are no Unicorn sockets to monitor
merge_request:
author:
type: performance
---
title: Make contributors page translatable
merge_request: 14915
author:
type: other
......@@ -151,7 +151,9 @@ def instrument_classes(instrumentation)
end
# rubocop:enable Metrics/AbcSize
Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
unless Sidekiq.server?
Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
end
Gitlab::Application.configure do |config|
# 0 should be Sentry to catch errors in this middleware
......
......@@ -56,29 +56,34 @@ that, login with an Admin account and do following:
With PlantUML integration enabled and configured, we can start adding diagrams to
our AsciiDoc snippets, wikis and repos using delimited blocks:
```
[plantuml, format="png", id="myDiagram", width="200px"]
--
Bob->Alice : hello
Alice -> Bob : Go Away
--
```
And in Markdown using fenced code blocks:
- **Markdown**
```plantuml
Bob -> Alice : hello
Alice -> Bob : Go Away
```
And in reStructuredText using a directive:
- **AsciiDoc**
```
.. plantuml::
```
[plantuml, format="png", id="myDiagram", width="200px"]
--
Bob->Alice : hello
Alice -> Bob : Go Away
--
```
- **reStructuredText**
```
.. plantuml::
:caption: Caption with **bold** and *italic*
Bob -> Alice: hello
Alice -> Bob: Go Away
```
```
You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.python.org/pypi/sphinxcontrib-plantuml), but please note that we currently only support the `caption` option.
The above blocks will be converted to an HTML img tag with source pointing to the
PlantUML instance. If the PlantUML server is correctly configured, this should
......
......@@ -76,7 +76,7 @@ To migrate your existing projects to the new storage type, check the specific [r
We are incrementally moving every storable object in GitLab to the Hashed Storage pattern. You can check the current
coverage status below.
Not that things stored in S3 compatible endpoint, will not have the downsides mentioned earlier, if they are not
Note that things stored in an S3 compatible endpoint will not have the downsides mentioned earlier, if they are not
prefixed with `#{namespace}/#{project_name}`, which is true for CI Cache and LFS Objects.
| Storable Object | Legacy Storage | Hashed Storage | S3 Compatible | GitLab Version |
......
......@@ -43,7 +43,7 @@ future GitLab releases.**
| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` |
| **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled |
| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. |
| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. |
| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job |
| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. |
| **CI_ENVIRONMENT_URL** | 9.3 | all | The URL of the environment for this job |
......@@ -75,7 +75,7 @@ future GitLab releases.**
| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs |
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Mark that job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
......
......@@ -123,7 +123,7 @@ Existing users using GitLab with MySQL/MariaDB are advised to
### PostgreSQL Requirements
As of GitLab 10.0, PostgreSQL 9.6 or newer is required, and earlier versions are
As of GitLab 10.0, PostgreSQL 9.6 or newer (but less than 10) is required, and earlier versions are
not supported. We highly recommend users to use PostgreSQL 9.6 as this
is the PostgreSQL version used for development and testing.
......
# Corporate contributor license agreement
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab B.V.. Except for the license granted herein to GitLab B.V. and recipients of software distributed by GitLab B.V., You reserve all right, title, and interest in and to Your Contributions.
1. Definitions.
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, that is submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Grant of Copyright License.
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
3. Grant of Patent License.
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent that You are legally entitled to grant the above license. You represent further that each of Your employees is authorized to submit Contributions on Your behalf, but excluding employees that are designated in writing by You as "Not authorized to submit Contributions on behalf of [name of Your corporation here]." Such designations of exclusion for unauthorized employees are to be submitted via email to legal@gitlab.com.
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab B.V. separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
8. It is Your responsibility to notify GitLab.com when any change is required to the list of designated employees excluded from submitting Contributions on Your behalf per Section 4. Such notification should be sent via email to legal@gitlab.com.
This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
This document has been replaced by a Developer Certificate of Origin and License,
as described in [Contributing.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md).
\ No newline at end of file
# Individual contributor license agreement
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab B.V.. Except for the license granted herein to GitLab B.V. and recipients of software distributed by GitLab B.V., You reserve all right, title, and interest in and to Your Contributions.
1. Definitions.
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab B.V., or that your employer has executed a separate Corporate CLA with GitLab B.V..
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab B.V. separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [insert_name_here]".
8. You agree to notify GitLab B.V. of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
This document has been replaced by a Developer Certificate of Origin and License,
as described in [Contributing.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md).
\ No newline at end of file
......@@ -1165,6 +1165,18 @@ From this page, you can repeat delivery with the same data by clicking `Resend R
>**Note:** If URL or secret token of the webhook were updated, data will be delivered to the new address.
### Receiving duplicate or multiple web hook requests triggered by one event
When GitLab sends a webhook it expects a response in 10 seconds (set default value). If it does not receive one, it'll retry the webhook.
If the endpoint doesn't send its HTTP response within those 10 seconds, GitLab may decide the hook failed and retry it.
If you are receiving multiple requests, you can try increasing the default value to wait for the HTTP response after sending the webhook
by uncommenting or adding the following setting to your `/etc/gitlab/gitlab.rb`:
```
gitlab_rails['webhook_timeout'] = 10
```
## Example webhook receiver
If you want to see GitLab's webhooks in action for testing purposes you can use
......
......@@ -2,8 +2,8 @@
module Gitlab
# Checks if a set of migrations requires downtime or not.
class EeCompatCheck
DEFAULT_CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.git'.freeze
EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
DEFAULT_CE_PROJECT_URL = 'https://gitlab.com/gitlab-org/gitlab-ce'.freeze
EE_REPO_URL = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
CHECK_DIR = Rails.root.join('ee_compat_check')
IGNORED_FILES_REGEX = /(VERSION|CHANGELOG\.md:\d+)/.freeze
PLEASE_READ_THIS_BANNER = %Q{
......@@ -17,14 +17,16 @@ module Gitlab
============================================================\n
}.freeze
attr_reader :ee_repo_dir, :patches_dir, :ce_repo, :ce_branch, :ee_branch_found
attr_reader :failed_files
attr_reader :ee_repo_dir, :patches_dir, :ce_project_url, :ce_repo_url, :ce_branch, :ee_branch_found
attr_reader :job_id, :failed_files
def initialize(branch:, ce_repo: DEFAULT_CE_REPO)
def initialize(branch:, ce_project_url: DEFAULT_CE_PROJECT_URL, job_id: nil)
@ee_repo_dir = CHECK_DIR.join('ee-repo')
@patches_dir = CHECK_DIR.join('patches')
@ce_branch = branch
@ce_repo = ce_repo
@ce_project_url = ce_project_url
@ce_repo_url = "#{ce_project_url}.git"
@job_id = job_id
end
def check
......@@ -59,8 +61,8 @@ module Gitlab
step("#{ee_repo_dir} already exists")
else
step(
"Cloning #{EE_REPO} into #{ee_repo_dir}",
%W[git clone --branch master --single-branch --depth=200 #{EE_REPO} #{ee_repo_dir}]
"Cloning #{EE_REPO_URL} into #{ee_repo_dir}",
%W[git clone --branch master --single-branch --depth=200 #{EE_REPO_URL} #{ee_repo_dir}]
)
end
end
......@@ -132,7 +134,7 @@ module Gitlab
def check_patch(patch_path)
step("Checking out master", %w[git checkout master])
step("Resetting to latest master", %w[git reset --hard origin/master])
step("Fetching CE/#{ce_branch}", %W[git fetch #{ce_repo} #{ce_branch}])
step("Fetching CE/#{ce_branch}", %W[git fetch #{ce_repo_url} #{ce_branch}])
step(
"Checking if #{patch_path} applies cleanly to EE/master",
# Don't use --check here because it can result in a 0-exit status even
......@@ -237,7 +239,7 @@ module Gitlab
end
def patch_url
"https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/#{ENV['CI_JOB_ID']}/artifacts/raw/ee_compat_check/patches/#{ce_patch_name}"
"#{ce_project_url}/-/jobs/#{job_id}/artifacts/raw/ee_compat_check/patches/#{ce_patch_name}"
end
def step(desc, cmd = nil)
......@@ -304,7 +306,7 @@ module Gitlab
# In the EE repo
$ git fetch origin
$ git checkout -b #{ee_branch_prefix} origin/master
$ git fetch #{ce_repo} #{ce_branch}
$ git fetch #{ce_repo_url} #{ce_branch}
$ git cherry-pick SHA # Repeat for all the commits you want to pick
You can squash the `#{ce_branch}` commits into a single "Port of #{ce_branch} to EE" commit.
......
......@@ -12,6 +12,12 @@ module Gitlab
# blob data should use load_all_data!.
MAX_DATA_DISPLAY_SIZE = 10.megabytes
# These limits are used as a heuristic to ignore files which can't be LFS
# pointers. The format of these is described in
# https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md#the-pointer
LFS_POINTER_MIN_SIZE = 120.bytes
LFS_POINTER_MAX_SIZE = 200.bytes
attr_accessor :name, :path, :size, :data, :mode, :id, :commit_id, :loaded_size, :binary
class << self
......@@ -30,16 +36,7 @@ module Gitlab
if is_enabled
Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
else
blob = repository.lookup(sha)
next unless blob.is_a?(Rugged::Blob)
new(
id: blob.oid,
size: blob.size,
data: blob.content(MAX_DATA_DISPLAY_SIZE),
binary: blob.binary?
)
rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE)
end
end
end
......@@ -59,10 +56,25 @@ module Gitlab
end
end
# Find LFS blobs given an array of sha ids
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
blob_ids.lazy
.select { |sha| possible_lfs_blob?(repository, sha) }
.map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
.select(&:lfs_pointer?)
.force
end
def binary?(data)
EncodingHelper.detect_libgit2_binary?(data)
end
def size_could_be_lfs?(size)
size.between?(LFS_POINTER_MIN_SIZE, LFS_POINTER_MAX_SIZE)
end
private
# Recursive search of blob id by path
......@@ -167,6 +179,29 @@ module Gitlab
end
end
end
def rugged_raw(repository, sha, limit:)
blob = repository.lookup(sha)
return unless blob.is_a?(Rugged::Blob)
new(
id: blob.oid,
size: blob.size,
data: blob.content(limit),
binary: blob.binary?
)
end
# Efficient lookup to determine if object size
# and type make it a possible LFS blob without loading
# blob content into memory with repository.lookup(sha)
def possible_lfs_blob?(repository, sha)
object_header = repository.rugged.read_header(sha)
object_header[:type] == :blob &&
size_could_be_lfs?(object_header[:len])
end
end
def initialize(options)
......@@ -226,7 +261,7 @@ module Gitlab
# size
# see https://github.com/github/git-lfs/blob/v1.1.0/docs/spec.md#the-pointer
def lfs_pointer?
has_lfs_version_key? && lfs_oid.present? && lfs_size.present?
self.class.size_could_be_lfs?(size) && has_lfs_version_key? && lfs_oid.present? && lfs_size.present?
end
def lfs_oid
......
module Gitlab
module Git
class LfsChanges
def initialize(repository, newrev)
@repository = repository
@newrev = newrev
end
def new_pointers(object_limit: nil, not_in: nil)
@new_pointers ||= begin
object_ids = new_objects(not_in: not_in)
object_ids = object_ids.take(object_limit) if object_limit
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
end
end
def all_pointers
object_ids = rev_list.all_objects(require_path: true)
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
end
private
def new_objects(not_in:)
rev_list.new_objects(require_path: true, lazy: true, not_in: not_in)
end
def rev_list
::Gitlab::Git::RevList.new(path_to_repo: @repository.path_to_repo,
newrev: @newrev)
end
end
end
end
......@@ -290,6 +290,14 @@ module Gitlab
end
end
def batch_existence(object_ids, existing: true)
filter_method = existing ? :select : :reject
object_ids.public_send(filter_method) do |oid| # rubocop:disable GitlabSecurity/PublicSend
rugged.exists?(oid)
end
end
# Returns an Array of branch and tag names
def ref_names
branch_names + tag_names
......
......@@ -13,11 +13,31 @@ module Gitlab
@path_to_repo = path_to_repo
end
# This method returns an array of new references
# This method returns an array of new commit references
def new_refs
execute([*base_args, newrev, '--not', '--all'])
end
# Finds newly added objects
# Returns an array of shas
#
# Can skip objects which do not have a path using required_path: true
# This skips commit objects and root trees, which might not be needed when
# looking for blobs
#
# Can return a lazy enumerator to limit work done on megabytes of data
def new_objects(require_path: nil, lazy: false, not_in: nil)
object_output = execute([*base_args, newrev, *not_in_refs(not_in), '--objects'])
objects_from_output(object_output, require_path: require_path, lazy: lazy)
end
def all_objects(require_path: nil)
object_output = execute([*base_args, '--all', '--objects'])
objects_from_output(object_output, require_path: require_path, lazy: true)
end
# This methods returns an array of missed references
#
# Should become obsolete after https://gitlab.com/gitlab-org/gitaly/issues/348.
......@@ -27,6 +47,13 @@ module Gitlab
private
def not_in_refs(references)
return ['--not', '--all'] unless references
return [] if references.empty?
references.prepend('--not')
end
def execute(args)
output, status = popen(args, nil, Gitlab::Git::Env.to_env_hash)
......@@ -44,6 +71,22 @@ module Gitlab
'rev-list'
]
end
def objects_from_output(object_output, require_path: nil, lazy: nil)
objects = object_output.lazy.map do |output_line|
sha, path = output_line.split(' ', 2)
next if require_path && path.blank?
sha
end.reject(&:nil?)
if lazy
objects
else
objects.force
end
end
end
end
end
......@@ -68,11 +68,13 @@ module Gitlab
end
def file(name, version)
version ||= self.class.default_ref
gollum_file = gollum_wiki.file(name, version)
return unless gollum_file
Gitlab::Git::WikiFile.new(gollum_file)
@repository.gitaly_migrate(:wiki_find_file) do |is_enabled|
if is_enabled
gitaly_find_file(name, version)
else
gollum_find_file(name, version)
end
end
end
def page_versions(page_path)
......@@ -156,6 +158,14 @@ module Gitlab
new_page(gollum_page)
end
def gollum_find_file(name, version)
version ||= self.class.default_ref
gollum_file = gollum_wiki.file(name, version)
return unless gollum_file
Gitlab::Git::WikiFile.new(gollum_file)
end
def gitaly_write_page(name, format, content, commit_details)
gitaly_wiki_client.write_page(name, format, content, commit_details)
end
......@@ -170,6 +180,13 @@ module Gitlab
Gitlab::Git::WikiPage.new(wiki_page, version)
end
def gitaly_find_file(name, version)
wiki_file = gitaly_wiki_client.find_file(name, version)
return unless wiki_file
Gitlab::Git::WikiFile.new(wiki_file)
end
end
end
end
......@@ -18,7 +18,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :commit_service, :list_files, request)
response.flat_map do |msg|
msg.paths.map { |d| d.dup.force_encoding(Encoding::UTF_8) }
msg.paths.map { |d| EncodingHelper.encode!(d.dup) }
end
end
......
module Gitlab
module GitalyClient
class WikiFile
FIELDS = %i(name mime_type path raw_data).freeze
attr_accessor(*FIELDS)
def initialize(params)
params = params.with_indifferent_access
FIELDS.each do |field|
instance_variable_set("@#{field}", params[field])
end
end
end
end
end
......@@ -80,6 +80,32 @@ module Gitlab
[wiki_page, version]
end
def find_file(name, revision)
request = Gitaly::WikiFindFileRequest.new(
repository: @gitaly_repo,
name: GitalyClient.encode(name),
revision: GitalyClient.encode(revision)
)
response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_file, request)
wiki_file = nil
response.each do |message|
next unless message.name.present?
if wiki_file
wiki_file.raw_data << message.raw_data
else
wiki_file = GitalyClient::WikiFile.new(message.to_h)
# All gRPC strings in a response are frozen, so we get
# an unfrozen version here so appending in the else clause below doesn't blow up.
wiki_file.raw_data = wiki_file.raw_data.dup
end
end
wiki_file
end
private
def gitaly_commit_details(commit_details)
......
......@@ -11,6 +11,8 @@ module Gitlab
# Old gitlad-shell messages don't provide enqueued_at/created_at attributes
trans.set(:sidekiq_queue_duration, Time.now.to_f - (message['enqueued_at'] || message['created_at'] || 0))
trans.run { yield }
worker.metrics_tags.each { |tag, value| trans.add_tag(tag, value) } if worker.respond_to?(:metrics_tags)
rescue Exception => error # rubocop: disable Lint/RescueException
trans.add_event(:sidekiq_exception)
......
......@@ -16,14 +16,15 @@ module Gitlab
SECRET_LENGTH = 32
class << self
def git_http_ok(repository, is_wiki, user, action)
def git_http_ok(repository, is_wiki, user, action, show_all_refs: false)
project = repository.project
repo_path = repository.path_to_repo
params = {
GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
GL_USERNAME: user&.username,
RepoPath: repo_path
RepoPath: repo_path,
ShowAllRefs: show_all_refs
}
server = {
address: Gitlab::GitalyClient.address(project.repository_storage),
......
......@@ -5,10 +5,9 @@ namespace :gitlab do
opts =
if ENV['CI']
{
# We don't use CI_REPOSITORY_URL since it includes `gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@`
# which is confusing in the steps suggested in the job's output.
ce_repo: "#{ENV['CI_PROJECT_URL']}.git",
branch: ENV['CI_COMMIT_REF_NAME']
ce_project_url: ENV['CI_PROJECT_URL'],
branch: ENV['CI_COMMIT_REF_NAME'],
job_id: ENV['CI_JOB_ID']
}
else
unless args[:branch]
......
......@@ -81,10 +81,15 @@ describe 'User views a wiki page' do
end
it 'shows a file stored in a page' do
file = Gollum::File.new(project.wiki)
allow_any_instance_of(Gollum::Wiki).to receive(:file).with('image.jpg', 'master').and_return(file)
allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return('image/jpeg')
gollum_file_double = double('Gollum::File',
mime_type: 'image/jpeg',
name: 'images/image.jpg',
path: 'images/image.jpg',
raw_data: '')
wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double)
allow(wiki_file).to receive(:mime_type).and_return('image/jpeg')
allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file)
expect(page).to have_xpath('//img[@data-src="image.jpg"]')
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
......
......@@ -431,9 +431,9 @@ describe('AppComponent', () => {
});
it('should render groups tree', (done) => {
vm.groups = [mockParentGroupItem];
vm.store.state.groups = [mockParentGroupItem];
vm.isLoading = false;
vm.pageInfo = mockPageInfo;
vm.store.state.pageInfo = mockPageInfo;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
done();
......
......@@ -106,8 +106,8 @@ describe('Merge Request Code Quality', () => {
Vue.nextTick(() => {
expect(
vm.$el.querySelector('.code-quality-container').geAttribute('style'),
).toEqual(null);
vm.$el.querySelector('.code-quality-container').getAttribute('style'),
).toEqual('');
expect(
vm.$el.querySelector('button').textContent.trim(),
).toEqual('Collapse');
......@@ -116,14 +116,15 @@ describe('Merge Request Code Quality', () => {
Vue.nextTick(() => {
expect(
vm.$el.querySelector('.code-quality-container').geAttribute('style'),
vm.$el.querySelector('.code-quality-container').getAttribute('style'),
).toEqual('display: none;');
expect(
vm.$el.querySelector('button').textContent.trim(),
).toEqual('Expand');
done();
});
});
done();
}, 0);
});
});
......
......@@ -42,6 +42,7 @@ describe('Markdown field component', () => {
beforeEach(() => {
spyOn(Vue.http, 'post').and.callFake(() => new Promise((resolve) => {
setTimeout(() => {
resolve({
json() {
return {
......@@ -49,6 +50,7 @@ describe('Markdown field component', () => {
};
},
});
});
}));
previewLink = vm.$el.querySelector('.nav-links li:nth-child(2) a');
......
......@@ -143,6 +143,16 @@ describe Gitlab::Git::Blob, seed_helper: true do
expect(blob.loaded_size).to eq(blob_size)
end
end
context 'when sha references a tree' do
it 'returns nil' do
tree = Gitlab::Git::Commit.find(repository, 'master').tree
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
expect(blob).to be_nil
end
end
end
describe '.raw' do
......@@ -226,6 +236,51 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
describe '.batch_lfs_pointers' do
let(:tree_object) { Gitlab::Git::Commit.find(repository, 'master').tree }
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
repository,
'master',
'README.md'
)
end
let(:lfs_blob) do
Gitlab::Git::Blob.find(
repository,
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
'files/lfs/image.jpg'
)
end
it 'returns a list of Gitlab::Git::Blob' do
blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
expect(blobs.count).to eq(1)
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
end
it 'silently ignores tree objects' do
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
expect(blobs).to eq([])
end
it 'silently ignores non lfs objects' do
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
expect(blobs).to eq([])
end
it 'avoids loading large blobs into memory' do
expect(repository).not_to receive(:lookup)
described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
end
end
describe 'encoding' do
context 'file with russian text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
......
require 'spec_helper'
describe Gitlab::Git::LfsChanges do
let(:project) { create(:project, :repository) }
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
let(:blob_object_id) { '0c304a93cb8430108629bbbcaa27db3343299bc0' }
subject { described_class.new(project.repository, newrev) }
describe 'new_pointers' do
before do
allow_any_instance_of(Gitlab::Git::RevList).to receive(:new_objects).and_return([blob_object_id])
end
it 'uses rev-list to find new objects' do
rev_list = double
allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
expect(rev_list).to receive(:new_objects).and_return([])
subject.new_pointers
end
it 'filters new objects to find lfs pointers' do
expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id])
subject.new_pointers(object_limit: 1)
end
it 'limits new_objects using object_limit' do
expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [])
subject.new_pointers(object_limit: 0)
end
end
describe 'all_pointers' do
it 'uses rev-list to find all objects' do
rev_list = double
allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
allow(rev_list).to receive(:all_objects).and_return([blob_object_id])
expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id])
subject.all_pointers
end
end
end
......@@ -1163,6 +1163,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
describe "#ls_files" do
let(:master_file_paths) { repository.ls_files("master") }
let(:utf8_file_paths) { repository.ls_files("ls-files-utf8") }
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
it "read every file paths of master branch" do
......@@ -1184,6 +1185,10 @@ describe Gitlab::Git::Repository, seed_helper: true do
it "returns empty array when not existed branch" do
expect(not_existed_branch.length).to equal(0)
end
it "returns valid utf-8 data" do
expect(utf8_file_paths.map { |file| file.force_encoding('utf-8') }).to all(be_valid_encoding)
end
end
describe "#copy_gitattributes" do
......@@ -1336,6 +1341,24 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
describe '#batch_existence' do
let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] }
it 'returns existing refs back' do
result = repository.batch_existence(refs)
expect(result).to eq([SeedRepo::RubyBlob::ID])
end
context 'existing: true' do
it 'inverts meaning and returns non-existing refs' do
result = repository.batch_existence(refs, existing: false)
expect(result).to eq(%w(deadbeef 909e6157199))
end
end
end
describe '#local_branches' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
......
......@@ -2,53 +2,82 @@ require 'spec_helper'
describe Gitlab::Git::RevList do
let(:project) { create(:project, :repository) }
let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
before do
expect(Gitlab::Git::Env).to receive(:all).and_return({
allow(Gitlab::Git::Env).to receive(:all).and_return({
GIT_OBJECT_DIRECTORY: 'foo',
GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar'
})
end
context "#new_refs" do
let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
it 'calls out to `popen`' do
def stub_popen_rev_list(*additional_args, output:)
expect(rev_list).to receive(:popen).with([
Gitlab.config.git.bin_path,
"--git-dir=#{project.repository.path_to_repo}",
'rev-list',
'newrev',
'--not',
'--all'
*additional_args
],
nil,
{
'GIT_OBJECT_DIRECTORY' => 'foo',
'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
}).and_return(["sha1\nsha2", 0])
}).and_return([output, 0])
end
context "#new_refs" do
it 'calls out to `popen`' do
stub_popen_rev_list('newrev', '--not', '--all', output: "sha1\nsha2")
expect(rev_list.new_refs).to eq(%w[sha1 sha2])
end
end
context '#new_objects' do
it 'fetches list of newly pushed objects using rev-list' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
expect(rev_list.new_objects).to eq(%w[sha1 sha2])
end
it 'can skip pathless objects' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
expect(rev_list.new_objects(require_path: true)).to eq(%w[sha2])
end
it 'can return a lazy enumerator' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
expect(rev_list.new_objects(lazy: true)).to be_a Enumerator::Lazy
end
it 'can accept list of references to exclude' do
stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
expect(rev_list.new_objects(not_in: ['master'])).to eq(%w[sha1 sha2])
end
it 'handles empty list of references to exclude as listing all known objects' do
stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
expect(rev_list.new_objects(not_in: [])).to eq(%w[sha1 sha2])
end
end
context '#all_objects' do
it 'fetches list of all pushed objects using rev-list' do
stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
expect(rev_list.all_objects.force).to eq(%w[sha1 sha2])
end
end
context "#missed_ref" do
let(:rev_list) { described_class.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
it 'calls out to `popen`' do
expect(rev_list).to receive(:popen).with([
Gitlab.config.git.bin_path,
"--git-dir=#{project.repository.path_to_repo}",
'rev-list',
'--max-count=1',
'oldrev',
'^newrev'
],
nil,
{
'GIT_OBJECT_DIRECTORY' => 'foo',
'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
}).and_return(["sha1\nsha2", 0])
stub_popen_rev_list('--max-count=1', 'oldrev', '^newrev', output: "sha1\nsha2")
expect(rev_list.missed_ref).to eq(%w[sha1 sha2])
end
......
......@@ -4,10 +4,7 @@ describe Gitlab::Metrics::SidekiqMiddleware do
let(:middleware) { described_class.new }
let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } }
describe '#call' do
it 'tracks the transaction' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
def run(worker, message)
expect(Gitlab::Metrics::Transaction).to receive(:new)
.with('TestWorker#perform')
.and_call_original
......@@ -20,19 +17,17 @@ describe Gitlab::Metrics::SidekiqMiddleware do
middleware.call(worker, message, :test) { nil }
end
it 'tracks the transaction (for messages without `enqueued_at`)' do
describe '#call' do
it 'tracks the transaction' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
expect(Gitlab::Metrics::Transaction).to receive(:new)
.with('TestWorker#perform')
.and_call_original
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
.with(:sidekiq_queue_duration, instance_of(Float))
run(worker, message)
end
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
it 'tracks the transaction (for messages without `enqueued_at`)' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
middleware.call(worker, {}, :test) { nil }
run(worker, {})
end
it 'tracks any raised exceptions' do
......@@ -50,5 +45,18 @@ describe Gitlab::Metrics::SidekiqMiddleware do
expect { middleware.call(worker, message, :test) }
.to raise_error(RuntimeError)
end
it 'tags the metrics accordingly' do
tags = { one: 1, two: 2 }
worker = double(:worker, class: double(:class, name: 'TestWorker'))
allow(worker).to receive(:metrics_tags).and_return(tags)
tags.each do |tag, value|
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:add_tag)
.with(tag, value)
end
run(worker, message)
end
end
end
......@@ -268,7 +268,8 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "project-#{project.id}",
RepoPath: repo_path
RepoPath: repo_path,
ShowAllRefs: false
}
end
......@@ -282,7 +283,8 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "wiki-#{project.id}",
RepoPath: repo_path
RepoPath: repo_path,
ShowAllRefs: false
}
end
......@@ -324,6 +326,12 @@ describe Gitlab::Workhorse do
expect(subject).to include(gitaly_params)
end
context 'show_all_refs enabled' do
subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
it { is_expected.to include(ShowAllRefs: true) }
end
end
context "when git_receive_pack action is passed" do
......@@ -336,6 +344,12 @@ describe Gitlab::Workhorse do
let(:action) { 'info_refs' }
it { expect(subject).to include(gitaly_params) }
context 'show_all_refs enabled' do
subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
it { is_expected.to include(ShowAllRefs: true) }
end
end
context 'when action passed is not supported by Gitaly' do
......
......@@ -164,27 +164,17 @@ describe ProjectWiki do
end
describe '#find_file' do
shared_examples 'finding a wiki file' do
before do
file = Gollum::File.new(subject.wiki)
allow_any_instance_of(Gollum::Wiki)
.to receive(:file).with('image.jpg', 'master')
.and_return(file)
allow_any_instance_of(Gollum::File)
.to receive(:mime_type)
.and_return('image/jpeg')
allow_any_instance_of(Gollum::Wiki)
.to receive(:file).with('non-existant', 'master')
.and_return(nil)
end
file = File.open(Rails.root.join('spec', 'fixtures', 'dk.png'))
subject.wiki # Make sure the wiki repo exists
after do
allow_any_instance_of(Gollum::Wiki).to receive(:file).and_call_original
allow_any_instance_of(Gollum::File).to receive(:mime_type).and_call_original
BareRepoOperations.new(subject.repository.path_to_repo).commit_file(file, 'image.png')
end
it 'returns the latest version of the file if it exists' do
file = subject.find_file('image.jpg')
expect(file.mime_type).to eq('image/jpeg')
file = subject.find_file('image.png')
expect(file.mime_type).to eq('image/png')
end
it 'returns nil if the page does not exist' do
......@@ -192,11 +182,20 @@ describe ProjectWiki do
end
it 'returns a Gitlab::Git::WikiFile instance' do
file = subject.find_file('image.jpg')
file = subject.find_file('image.png')
expect(file).to be_a Gitlab::Git::WikiFile
end
end
context 'when Gitaly wiki_find_file is enabled' do
it_behaves_like 'finding a wiki file'
end
context 'when Gitaly wiki_find_file is disabled', :skip_gitaly_mock do
it_behaves_like 'finding a wiki file'
end
end
describe "#create_page" do
shared_examples 'creating a wiki page' do
after do
......
require 'spec_helper'
describe Projects::GroupLinks::CreateService, '#execute' do
let(:user) { create :user }
let(:group) { create :group }
let(:project) { create :project }
let(:opts) do
{
link_group_access: '30',
expires_at: nil
}
end
let(:subject) { described_class.new(project, user, opts) }
it 'adds group to project' do
expect { subject.execute(group) }.to change { project.project_group_links.count }.from(0).to(1)
end
it 'returns false if group is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end
end
require 'spec_helper'
describe Projects::GroupLinks::DestroyService, '#execute' do
let(:group_link) { create :project_group_link }
let(:project) { group_link.project }
let(:user) { create :user }
let(:subject) { described_class.new(project, user) }
it 'removes group from project' do
expect { subject.execute(group_link) }.to change { project.project_group_links.count }.from(1).to(0)
end
it 'returns false if group_link is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end
end
require 'zlib'
class BareRepoOperations
# The ID of empty tree.
# See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
include Gitlab::Popen
def initialize(path_to_repo)
@path_to_repo = path_to_repo
end
# Based on https://stackoverflow.com/a/25556917/1856239
def commit_file(file, dst_path, branch = 'master')
head_id = execute(['show', '--format=format:%H', '--no-patch', branch], allow_failure: true)[0] || EMPTY_TREE_ID
execute(['read-tree', '--empty'])
execute(['read-tree', head_id])
blob_id = execute(['hash-object', '--stdin', '-w']) do |stdin|
stdin.write(file.read)
end
execute(['update-index', '--add', '--cacheinfo', '100644', blob_id[0], dst_path])
tree_id = execute(['write-tree'])
commit_tree_args = ['commit-tree', tree_id[0], '-m', "Add #{dst_path}"]
commit_tree_args += ['-p', head_id] unless head_id == EMPTY_TREE_ID
commit_id = execute(commit_tree_args)
execute(['update-ref', "refs/heads/#{branch}", commit_id[0]])
end
private
def execute(args, allow_failure: false)
output, status = popen(base_args + args, nil) do |stdin|
yield stdin if block_given?
end
unless status.zero?
if allow_failure
return []
else
raise "Got a non-zero exit code while calling out `#{args.join(' ')}`: #{output}"
end
end
output.split("\n")
end
def base_args
[
Gitlab.config.git.bin_path,
"--git-dir=#{@path_to_repo}"
]
end
end
xK
0EgNI|ADt*^ mZ qGčY8ZK7"Fc%oHD9rZLsMJ2=ACmeFgVxI9H2XJrp6;N8z??>+zWƏBÞ f}bN@K\SY iSC
\ No newline at end of file
......@@ -6398,9 +6398,9 @@ vue-style-loader@^2.0.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue-template-compiler@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.2.6.tgz#2e2928daf0cd0feca9dfc35a9729adeae173ec68"
vue-template-compiler@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.2.tgz#6f198ebc677b8f804315cd33b91e849315ae7177"
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
......@@ -6409,9 +6409,9 @@ vue-template-es2015-compiler@^1.2.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.5.1.tgz#0c36cc57aa3a9ec13e846342cb14a72fcac8bd93"
vue@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.6.tgz#451714b394dd6d4eae7b773c40c2034a59621aed"
vue@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.2.tgz#fd367a87bae7535e47f9dc5c9ec3b496e5feb5a4"
vuex@^3.0.0:
version "3.0.0"
......
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