Commit 3b17bba1 authored by Clement Ho's avatar Clement Ho

Merge branch 'master' into dispatcher-mr-haml

parents da521875 fb2a76ac
......@@ -401,6 +401,7 @@ gem 'sys-filesystem', '~> 1.1.6'
# SSH host key support
gem 'net-ssh', '~> 4.1.0'
gem 'sshkey', '~> 1.9.0'
# Required for ED25519 SSH host key support
group :ed25519 do
......
......@@ -895,6 +895,7 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
sshkey (1.9.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
......@@ -1192,6 +1193,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0)
sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
......
......@@ -43,169 +43,14 @@ var Dispatcher;
});
switch (page) {
case 'projects:environments:metrics':
import('./pages/projects/environments/metrics')
.then(callDefault)
.catch(fail);
break;
case 'projects:merge_requests:index':
case 'projects:issues:index':
case 'projects:issues:show':
shortcut_handler = true;
break;
case 'projects:milestones:index':
import('./pages/projects/milestones/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:show':
import('./pages/projects/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:show':
import('./pages/groups/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:milestones:show':
import('./pages/dashboard/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:issues':
import('./pages/dashboard/issues')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:merge_requests':
import('./pages/dashboard/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'groups:issues':
import('./pages/groups/issues')
.then(callDefault)
.catch(fail);
break;
case 'groups:merge_requests':
import('./pages/groups/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:todos:index':
import('./pages/dashboard/todos/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:jobs:index':
import('./pages/admin/jobs/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:projects:index':
import('./pages/admin/projects/index/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:index':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:show':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:projects:index':
case 'dashboard:projects:starred':
import('./pages/dashboard/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:projects:index':
case 'explore:projects:trending':
case 'explore:projects:starred':
import('./pages/explore/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:groups:index':
import('./pages/explore/groups')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:new':
case 'projects:milestones:create':
import('./pages/projects/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:edit':
case 'projects:milestones:update':
import('./pages/projects/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:new':
case 'groups:milestones:create':
import('./pages/groups/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:edit':
case 'groups:milestones:update':
import('./pages/groups/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:compare:show':
import('./pages/projects/compare/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:new':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:create':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:index':
import('./pages/projects/branches/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:issues:new':
import('./pages/projects/issues/new')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:issues:edit':
import('./pages/projects/issues/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:merge_requests:creations:new':
import('./pages/projects/merge_requests/creations/new')
.then(callDefault)
.catch(fail);
case 'projects:merge_requests:creations:diffs':
import('./pages/projects/merge_requests/creations/diffs')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:merge_requests:edit':
import('./pages/projects/merge_requests/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:tags:new':
......
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import stopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate);
export default () => {
document.addEventListener('DOMContentLoaded', () => {
const stopJobsButton = document.getElementById('stop-jobs-button');
if (stopJobsButton) {
// eslint-disable-next-line no-new
......@@ -27,4 +25,4 @@ export default () => {
},
});
}
};
});
......@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteProjectModal from './components/delete_project_modal.vue';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate);
const deleteProjectModalEl = document.getElementById('delete-project-modal');
......@@ -34,4 +34,4 @@ export default () => {
deleteModal.projectName = buttonProps.projectName;
}
});
};
});
......@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteUserModal from './components/delete_user_modal.vue';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate);
const deleteUserModalEl = document.getElementById('delete-user-modal');
......@@ -40,4 +40,4 @@ export default () => {
deleteModal.username = buttonProps.username;
}
});
};
});
import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
projectSelect();
initLegacyFilters();
};
});
import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
projectSelect();
initLegacyFilters();
};
});
import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
new Milestone(); // eslint-disable-line no-new
new Sidebar(); // eslint-disable-line no-new
};
});
import ProjectsList from '~/projects_list';
export default () => new ProjectsList();
document.addEventListener('DOMContentLoaded', () => new ProjectsList());
import Todos from './todos';
export default () => new Todos();
document.addEventListener('DOMContentLoaded', () => new Todos());
......@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list';
import Landing from '~/landing';
import initGroupsList from '../../../groups';
export default function () {
document.addEventListener('DOMContentLoaded', () => {
new GroupsList(); // eslint-disable-line no-new
initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing');
......@@ -13,4 +13,4 @@ export default function () {
'explore_groups_landing_dismissed',
);
exploreGroupsLanding.toggle();
}
});
import ProjectsList from '~/projects_list';
export default () => new ProjectsList();
document.addEventListener('DOMContentLoaded', () => new ProjectsList());
......@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
});
projectSelect();
};
});
......@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
});
projectSelect();
};
});
import initForm from '../../../../shared/milestones/form';
export default () => initForm(false);
document.addEventListener('DOMContentLoaded', () => initForm(false));
import initForm from '../../../../shared/milestones/form';
export default () => initForm(false);
document.addEventListener('DOMContentLoaded', () => initForm(false));
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
export default initMilestonesShow;
document.addEventListener('DOMContentLoaded', initMilestonesShow);
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new
};
});
import NewBranchForm from '~/new_branch_form';
export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML));
document.addEventListener('DOMContentLoaded', () => (
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
));
import Diff from '~/diff';
import initChangesDropdown from '~/init_changes_dropdown';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
new Diff(); // eslint-disable-line no-new
const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
};
});
import monitoringBundle from '~/monitoring/monitoring_bundle';
export default monitoringBundle;
document.addEventListener('DOMContentLoaded', monitoringBundle);
import initForm from '../form';
export default () => {
initForm();
};
document.addEventListener('DOMContentLoaded', initForm);
import initForm from '../form';
export default () => {
initForm();
};
document.addEventListener('DOMContentLoaded', initForm);
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest;
document.addEventListener('DOMContentLoaded', initMergeRequest);
import Compare from '~/compare';
import MergeRequest from '~/merge_request';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) {
new Compare({ // eslint-disable-line no-new
......@@ -15,4 +15,4 @@ export default () => {
action: mrNewSubmitNode.dataset.mrSubmitAction,
});
}
};
});
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest;
document.addEventListener('DOMContentLoaded', initMergeRequest);
import initForm from '../../../../shared/milestones/form';
export default () => initForm();
document.addEventListener('DOMContentLoaded', () => initForm());
import milestones from '~/pages/milestones/shared';
export default milestones;
document.addEventListener('DOMContentLoaded', milestones);
import initForm from '../../../../shared/milestones/form';
export default () => initForm();
document.addEventListener('DOMContentLoaded', () => initForm());
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import milestones from '~/pages/milestones/shared';
export default () => {
document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow();
milestones();
};
});
......@@ -7,17 +7,24 @@ module WebpackHelper
def webpack_controller_bundle_tags
bundles = []
segments = [*controller.controller_path.split('/'), controller.action_name].compact
until segments.empty?
action = case controller.action_name
when 'create' then 'new'
when 'update' then 'edit'
else controller.action_name
end
route = [*controller.controller_path.split('/'), action].compact
until route.empty?
begin
asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js')
asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js')
bundles.unshift(*asset_paths)
rescue Webpack::Rails::Manifest::EntryPointMissingError
# no bundle exists for this path
end
segments.pop
route.pop
end
javascript_include_tag(*bundles)
......
......@@ -41,41 +41,12 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
# This convoluted mess is because we need to handle two cases of
# artifact files during the migration. And a simple OR clause
# makes it impossible to optimize.
# Instead we want to use UNION ALL and do two carefully
# constructed disjoint queries. But Rails cannot handle UNION or
# UNION ALL queries so we do the query in a subquery and wrap it
# in an otherwise redundant WHERE IN query (IN is fine for
# non-null columns).
# This should all be ripped out when the migration is finished and
# replaced with just the new storage to avoid the extra work.
scope :with_artifacts, ->() do
old = Ci::Build.select(:id).where(%q[artifacts_file <> ''])
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)],
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id'))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id'))
end
scope :with_artifacts_not_expired, ->() do
old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND (artifacts_expire_at IS NULL OR artifacts_expire_at > ?)], Time.now)
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)],
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND (expire_at IS NULL OR expire_at > ?)', Time.now))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
end
scope :with_expired_artifacts, ->() do
old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND artifacts_expire_at < ?], Time.now)
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)],
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND expire_at < ?', Time.now))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
end
scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
scope :ref_protected, -> { where(protected: true) }
......
......@@ -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
......
......@@ -33,9 +33,8 @@ class Key < ActiveRecord::Base
after_destroy :refresh_user_cache
def key=(value)
value&.delete!("\n\r")
value.strip! unless value.blank?
write_attribute(:key, value)
write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil)
@public_key = nil
end
......@@ -97,7 +96,7 @@ class Key < ActiveRecord::Base
def generate_fingerprint
self.fingerprint = nil
return unless self.key.present?
return unless public_key.valid?
self.fingerprint = public_key.fingerprint
end
......
......@@ -492,12 +492,8 @@ class Repository
end
def root_ref
if raw_repository
raw_repository.root_ref
else
# When the repo does not exist we raise this error so no data is cached.
raise Gitlab::Git::Repository::NoRepository
end
# When the repo does not exist, or there is no root ref, we raise this error so no data is cached.
raw_repository&.root_ref or raise Gitlab::Git::Repository::NoRepository # rubocop:disable Style/AndOr
end
cache_method :root_ref
......
......@@ -59,6 +59,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)
......
......@@ -48,7 +48,8 @@ module Issues
new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
milestone_id: cloneable_milestone_id,
project: @new_project, author: @old_issue.author,
description: rewrite_content(@old_issue.description) }
description: rewrite_content(@old_issue.description),
assignee_ids: @old_issue.assignee_ids }
new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
......
......@@ -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: Update issue closing pattern to allow variations in punctuation
merge_request: 17198
author: Vicky Chijwani
type: changed
---
title: Fix duplicate system notes when merging a merge request.
merge_request: 17035
author:
type: fixed
---
title: Sanitize extra blank spaces used when uploading a SSH key
merge_request: 40552
author:
type: fixed
---
title: Remember assignee when moving an issue
merge_request:
author:
type: fixed
---
title: Don't cache a nil repository root ref to prevent caching issues
merge_request:
author:
type: fixed
---
title: Change SQL for expired artifacts to use new ci_job_artifacts.expire_at
merge_request: 16578
author:
type: performance
---
title: Increase feature flag cache TTL to one hour
merge_request:
author:
type: performance
---
title: Fix Error 500 when viewing a commit with a GPG signature in Geo
merge_request:
author:
type: fixed
---
title: Fix squash not working when diff contained non-ASCII data
merge_request:
author:
type: fixed
---
title: Don't attempt to update user tracked fields if database is in read-only
merge_request:
author:
type: fixed
......@@ -262,7 +262,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
......
......@@ -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
......
......@@ -27,11 +27,11 @@ var pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'ap
// filter out entries currently imported dynamically in dispatcher.js
var dispatcher = fs.readFileSync(path.join(ROOT_PATH, 'app/assets/javascripts/dispatcher.js')).toString();
var dispatcherChunks = dispatcher.match(/(?!import\('.\/)pages\/[^']+/g);
var dispatcherChunks = dispatcher.match(/(?!import\(')\.\/pages\/[^']+/g);
pageEntries.forEach(( path ) => {
let chunkPath = path.replace(/\/index\.js$/, '');
if (!dispatcherChunks.includes(chunkPath)) {
if (!dispatcherChunks.includes('./' + chunkPath)) {
let chunkName = chunkPath.replace(/\//g, '.');
autoEntries[chunkName] = './' + path;
}
......
# How to configure LDAP with GitLab CE
---
author: Chris Wilson
author_gitlab: MrChrisW
level: intermediary
article_type: admin guide
date: 2017-05-03
---
> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** admin guide ||
> **Level:** intermediary ||
> **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) ||
> **Publication date:** 2017-05-03
# How to configure LDAP with GitLab CE
## Introduction
......
---
redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
author: Fabio Busatto
author_gitlab: bikebilly
level: intermediary
article_type: tutorial
date: 2017-08-15
---
# How to deploy Maven projects to Artifactory with GitLab CI/CD
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) ||
> **Publication date:** 2017-08-15
## Introduction
In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/)
......
---
redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
author: Mehran Rasulian
author_gitlab: mehranrasulian
level: intermediary
article_type: tutorial
date: 2017-08-31
---
# Test and deploy Laravel applications with GitLab CI/CD and Envoy
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Mehran Rasulian](https://gitlab.com/mehranrasulian) ||
> **Publication date:** 2017-08-31
## Introduction
GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want.
......
......@@ -254,18 +254,25 @@ through the process of how to use it systematically.
#### Special format
Every **Technical Article** contains, in the very beginning, a blockquote with the following information:
- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial)
- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
- A reference to the **author's name** and **GitLab.com handle**
- A reference of the **publication date**
```md
> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Name Surname](https://gitlab.com/username) ||
> **Publication date:** AAAA-MM-DD
Every **Technical Article** contains a frontmatter at the beginning of the doc
with the following information:
- **Type of article** (user guide, admin guide, tech overview, tutorial)
- **Knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
- **Author's name** and **GitLab.com handle**
- **Publication date**
For example:
```yaml
---
author: John Doe
author_gitlab: johnDoe
level: beginner
article_type: user guide
date: 2017-02-01
---
```
#### Technical Articles - Writing Method
......
# Getting started with OpenShift Origin 3 and GitLab
---
author: Achilleas Pipinellis
author_gitlab: axil
level: intermediary
article_type: tutorial
date: 2016-06-28
---
> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) ||
> **Publication date:** 2016-06-28
# Getting started with OpenShift Origin 3 and GitLab
## Introduction
......
# Installing Git
---
author: Sean Packham
author_gitlab: SeanPackham
level: beginner
article_type: user guide
date: 2017-05-15
---
> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** user guide ||
> **Level:** beginner ||
> **Author:** [Sean Packham](https://gitlab.com/SeanPackham) ||
> **Publication date:** 2017-05-15
# Installing Git
To begin contributing to GitLab projects
you will need to install the Git client on your computer.
......
# Numerous undo possibilities in Git
---
author: Crt Mori
author_gitlab: Letme
level: intermediary
article_type: tutorial
date: 2017-05-15
---
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
> **Level:** intermediary ||
> **Author:** [Crt Mori](https://gitlab.com/Letme) ||
> **Publication date:** 2017-08-17
# Numerous undo possibilities in Git
## Introduction
......
# GitLab Pages from A to Z: Part 4
---
author: Marcia Ramos
author_gitlab: marcia
level: intermediate
article_type: user guide
date: 2017-02-22
---
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
> **Level**: intermediate ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017/02/22
# GitLab Pages from A to Z: Part 4
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
......
# GitLab Pages from A to Z: Part 1
---
author: Marcia Ramos
author_gitlab: marcia
level: beginner
article_type: user guide
date: 2017-02-22
---
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
> **Level**: beginner ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017/02/22
# GitLab Pages from A to Z: Part 1
- **Part 1: Static sites and GitLab Pages domains**
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
......
---
last_updated: 2017-09-28
author: Marcia Ramos
author_gitlab: marcia
level: beginner
article_type: user guide
date: 2017-02-22
---
# GitLab Pages from A to Z: Part 3
> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles)**: user guide ||
> **Level**: beginner ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017-02-22 ||
> **Last updated**: 2017-09-28
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
- **Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**
......
# GitLab Pages from A to Z: Part 2
---
author: Marcia Ramos
author_gitlab: marcia
level: beginner
article_type: user guide
date: 2017-02-22
---
> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
> **Level**: beginner ||
> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
> **Publication date:** 2017/02/22
# GitLab Pages from A to Z: Part 2
- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
- **Part 2: Quick start guide - Setting up GitLab Pages**
......
......@@ -121,6 +121,7 @@ module Gitlab
def commits_check
return if deletion? || newrev.nil?
return unless should_run_commit_validations?
# n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
::Gitlab::GitalyClient.allow_n_plus_1_calls do
......@@ -139,6 +140,10 @@ module Gitlab
private
def should_run_commit_validations?
commit_check.validate_lfs_file_locks?
end
def updated_from_web?
protocol == 'web'
end
......@@ -176,7 +181,7 @@ module Gitlab
end
def commits
project.repository.new_commits(newrev)
@commits ||= project.repository.new_commits(newrev)
end
end
end
......
......@@ -35,14 +35,14 @@ module Gitlab
end
end
private
def validate_lfs_file_locks?
strong_memoize(:validate_lfs_file_locks) do
project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev
end
end
private
def lfs_file_locks_validation
lambda do |paths|
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first
......
......@@ -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
......
......@@ -60,7 +60,9 @@ module Gitlab
def create_cached_signature!
using_keychain do |gpg_key|
GpgSignature.create!(attributes(gpg_key))
signature = GpgSignature.new(attributes(gpg_key))
signature.save! unless Gitlab::Database.read_only?
signature
end
end
......
......@@ -21,6 +21,22 @@ module Gitlab
technology(name)&.supported_sizes
end
def self.sanitize(key_content)
ssh_type, *parts = key_content.strip.split
return key_content if parts.empty?
parts.each_with_object("#{ssh_type} ").with_index do |(part, content), index|
content << part
if Gitlab::SSHPublicKey.new(content).valid?
break [content, parts[index + 1]].compact.join(' ') # Add the comment part if present
elsif parts.size == index + 1 # return original content if we've reached the last element
break key_content
end
end
end
attr_reader :key_text, :key
# Unqualified MD5 fingerprint for compatibility
......@@ -37,23 +53,23 @@ module Gitlab
end
def valid?
key.present?
SSHKey.valid_ssh_public_key?(key_text)
end
def type
technology.name if valid?
technology.name if key.present?
end
def bits
return unless valid?
return if key.blank?
case type
when :rsa
key.n.num_bits
key.n&.num_bits
when :dsa
key.p.num_bits
key.p&.num_bits
when :ecdsa
key.group.order.num_bits
key.group.order&.num_bits
when :ed25519
256
else
......
......@@ -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
......
......@@ -5,6 +5,10 @@ FactoryBot.define do
title
key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com' }
factory :key_without_comment do
key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate }
end
factory :deploy_key, class: 'DeployKey'
factory :personal_key do
......@@ -18,38 +22,104 @@ FactoryBot.define do
factory :rsa_key_2048 do
key do
<<~KEY.delete("\n")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
/jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC98dbu7gxcbmAvwMqz/6AALhSr1jiX
G0UC8FQMvoDt+ciB+uSJhg7KlxinKjYJnPGfhX+q2K+mmCGAmI/D6q7rFxE+bn09O+75
qgkTHi+suDVE6KG7L3n0alGd/qSevfomR77Snh6fQPdG6sEAZz3kehcpfVnq5/IuLFq9
FBrgmu52Jd4XZLQZKkDq6zYOJ69FUkGf93LZIV/OOaS+f+qkOGPCUkdKl7oEcgpVNY9S
RjBCduXnvi2CyQnnJVkBguGL5VlXwFXH+17Whs7oFWmdiG+4jzBRLIMz4EuIW09b8Su5
PW6+bBuXOifHA8KG5TMmjs5LYdCMPFnhTyDyO3a1 dummy@gitlab.com
KEY
end
factory :rsa_deploy_key_2048, class: 'DeployKey'
end
factory :rsa_key_4096 do
key do
<<~KEY.delete("\n")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGSD77lLtjmzewiBs6nu2R5nu6oNkrA
kH/0co1fHHosKfRr+sWkSTKXOVcL7bhRu+tniGBmB5pn+i1qX7BXtrcnv//bCXWIp+me0
27L4RJa5/Ep077iiTJlzTpcV664xNUXC8mzBr601HR/Z2TzX5DWJvnyqqFkN7qHTYo/+I
oKECnKqNzI5SQrAxgi6sbWA5DFQ/nwcqsUSBo5gCCJ/0QPrR19yVV5lJA19EY2LawOb1S
JNOFo4mQupSlBZwvERZJ7IqhBTPtQIfrqqz5VJbI13jK3ViZTugIZqydWAhosUyejP3Sd
Cj1KMexrvV95tjUtmhVFlph4tKThQO0p9pXKZNCzYsbQTye6O6Hk2rojOJLyFWqNBVKtI
8Ymfu7OQWppRnuUFuhuuS515H1s888bZFMPsC74mPyo0Y7Q9wAoTnQ9Hw6b0J6OfY3PIR
VphaCmxh6b7dgSPFdD7TA6j0xk6PCTOIEzBKuc85B3GQc8Nt4sTv6fW8lGeuYWqepW74i
geC4qB6U3/3+p3nPdq/bTM1txrhnQsl1r4dv6TLZ51EtHp6sXayp0qd0pRaiavebXFC0i
aETLraQpye4FWbBL/8xTjQ/0VPrYVuUCDvDSMIIS3/9g7Kp7ERUDC9jUqOVonm4pTXL9i
ItiUBlK7Mob9C4fQIRFnVR00DCmkmVgw== dummy@gitlab.com
KEY
end
end
factory :rsa_key_5120 do
key do
<<~KEY.delete("\n")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACgQDxnZP0TucLH3zcrvt75DPNq+xKqOmJk
CEzTytKq4S5MDH0nlx+xOZ9WykhwDHXU0iZBJF7yRdLkZweYDJVKnBzr4t7QP5Sw2/ZdL
elvUMWGJjuz28x8Z+8NZ+IxL/exDz7itrhCsLupQhGO1obiIwf8xVzzPoxrQ9dxaN4x96
5N+QdQcld8O6xfpSE0p5Y3sRn3kp57aHWoNa/bUGZy0OHLr/ig0uc6EKyWsTmEESOgDyV
94wOyHR0KNGEENyxQt4BwAbEBn3Y41HKqD358KKh+XjbECebrrBFigdDL/eYFIUlstJ07
SK/HtYjZbiUZCPs8bJA+SBaLK0pGGqguM2LXRoMeMUZFwKKKS2LpRqjKGj3Qt7qMnp1Sk
VhiMnxNqL4nJnDOOVo07xDIPKqIBYO67/cp4Icv3IjKxy6K3EIpLr+iRCxcllpDogxolz
FC+pEDVpmEvcrGEv1ON6HcCdk/6Q8Iekr8rYDHpKCU5FF2uBHkqq7yNJ1/+NFC4dgyOo0
xCVL4D3DvDKNxFYkrzW4ICt0f5XcMnU10yS/OFXz8JwA3jvuLvMRe5JdFiIjb/l86+TgY
yvK8Y8N/UWgSgyjXUCv8nxdvpsxdz5h7HBF8E2DIxCVMC23655e5rp5eJW9EU9X5YFZc3
u6uWJ1f1aO+1ViTtqkPrqxovNDD+gVel8Ny6MJ4MvmDKY+eM8beNMSSf1n1Oyh/SvCffh
ZpUqrXdTr9qwZEOaC75T74AJ7KBl9VvO3vPLZuJrt38R2OZG/4SlNEUA6bb5TWQLtdor/
qpPN5jAskkAUzOh5L/M+dmq2jNn03U9xwORCYPZj+fFM9bL99/0knsV0ypZDZyWH dummy@gitlab.com
KEY
end
end
factory :rsa_key_8192 do
key do
<<~KEY.delete("\n")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQC5jMyGtgMOVX4t2GuXkbirJA0Edr+ql
OH9grnRBPHPo0Npt6XE6ZN3J3hDULTQo03wmekGw42dxdNSgk+F0GjsUBrMLbqrk485MM
e0cUbP4lRXNu4ao87wPVM5fAsD4E3FQiZcI6Df011ZGIL7hGTHt6eafTfr9cJheRyYSu6
g06rlnFWbbtSh9oQ7Y6sfDLBcsC9ECcXwe3mwViuQXPIVomZ02EdnBbAhbGHDtA+ZbSvT
fraxOMjkxkVvvdjLxXEykpwVuZf8eZ+R/Js8jQ5RKvTZMbfxJNsGEqHD32s43ml4VF549
Qz2GJDXF7Cld/n3CT6wvw0mMPM0LnykL2v0CMr44bjIA3KsNEs5MhkcBO8sv5hGfcPhrp
m9WwI6gd9vdZVcxarVI+iQS947owvdn4VbEZXynCDqEEv3Zh+FA5p23mf2p7DkG/swiK/
IPrjr1wmsiWmwIUsENzJNyJtibKuRsBawC4ZdL797tFilSoTzSpriegSL13joPXz3eOHC
Vu4ATHMo3QyLfIFbxrf9PQ79nyOpHoX2YeFXvei3xFkGMundkOqeI+pnJKDyqbiLV7UVl
clua11QWNQZf1ZUd0n1wZ1g89de+wl3oJSRbSA5ZpveZEPstcMC/JhogY4JBYsvCT1yHO
oNWHo90NZQsUCjNnR+/FVaACtpt2zcPTjjbXvxwCDlT3gXTmTBp/kEZq6u8p+BOlqFgxc
P/sdAR8jWTin3Iw/YAcbqNgRHdjMUzJBrPQ5NcK6xFcmkOEQahdJDZs98xozCHkD4Urx6
+auTr/uqRYobKoNUNiYqN1n7/dfZjQJJVkHtKd06JTFx+7/SqyfrTKS+/EIf2Hypdy9r9
IFR+SWAOi11N/wflS/ZbH95Qt3STifXRecmHzyYGkMOZ+mg3Hi2YU0yn7k+P1jy627xud
pT9Ak3HWT5ji8tMyn9udL7m80dYpUiEAxoYZdbSSNCDaKP4ViABnGIeZreIujabI8IdtE
IjFQTaF2d5HTYjp28/qf576CFP5L7AGydypipYqZUmsYnay5YVjdm89He3TMD71SwspJl
POC4RnM0HS87OE+U0+mVaIe8YYbcjTekpVU9mkqsE/GQ34Egw79VMNNgWq5avOzpT8msC
lTJxgfJ1agGgigTvGxUM0FB07+sIdJxxNymAGpLKZ1op8xaJI3o8D86jWgI22za1zxUB5
il9U7+KOzaWo9mp3bmhvZWGDwzTXEZhUJYMRby7o6UxSHlA6fKE63JSDD2yhXk4CjsQRN
C7Ph9cYSB+Wa3i9Am4rRlJgrF79okmEOMpj1idliHkpIsy/k2CN9Lf2EIHOD4NMuLrSUH
4qJsPUq19ZbGIMdImD3vMS5b dummy@gitlab.com
KEY
end
end
factory :dsa_key_2048 do
key do
<<~KEY.delete("\n")
ssh-dss AAAAB3NzaC1kc3MAAAEBAO/3/NPLA/zSFkMOCaTtGo+uos1flfQ5f038Uk+G
Y9AeLGzX+Srhw59GdVXmOQLYBrOt5HdGwqYcmLnE2VurUGmhtfeO5H+3p5pGJbkS0Gxp
YH1HRO9lWsncF3Hh1w4lYsDjkclDiSTdfTuN8F4Kb3DXNnVSCieeonp+B25F/CXagyTQ
/pvNmHFeYgGCVdnBtFdi+xfxaZ8NKdPrGggzokbKHElDZQ4Xo5EpdcyLajgM7nB2r2Rz
OrmeaevKi5lV68ehRa9Yyrb7vxvwiwBwOgqR/mnN7Gnaq1jUdmJY+ct04Qwx37f5jvhv
5gA4U40SGMoiHM8RFIN7Ksz0jsyX73MAAAAVALRWOfjfzHpK7KLz4iqDvvTUAevJAAAB
AEa9NZ+6y9iQ5erGsdfLTXFrhSefTG0NhghoO/5IFkSGfd8V7kzTvCHaFrcfpEA5kP8t
poeOG0TASB6tgGOxm1Bq4Wncry5RORBPJlAVpDGRcvZ931ddH7IgltEInS6za2uH6F/1
M1QfKePSLr6xJ1ZLYfP0Og5KTp1x6yMQvfwV0a+XdA+EPgaJWLWp/pWwKWa0oLUgjsIH
MYzuOGh5c708uZrmkzqvgtW2NgXhcIroRgynT3IfI2lP2rqqb3uuuE/qH5UCUFO+Dc3H
nAFNeQDT/M25AERdPYBAY5a+iPjIgO+jT7BfmfByT+AZTqZySrCyc7nNZL3YgGLK0l6A
1GgAAAEBAN9FpFOdIXE+YEZhKl1vPmbcn+b1y5zOl6N4x1B7Q8pD/pLMziWROIS8uLzb
aZ0sMIWezHIkxuo1iROMeT+jtCubn7ragaN6AX7nMpxYUH9+mYZZs/fyElt6wCviVhTI
zM+u7VdQsnZttOOlQfogHdL+SpeAft0DsfJjlcgQnsLlHQKv6aPqCPYUST2nE7RyW/Ex
PrMxLtOWt0/j8RYHbwwqvyeZqBz3ESBgrS9c5tBdBfauwYUV/E7gPLOU3OZFw9ue7o+z
wzoTZqW6Xouy5wtWvSLQSLT5XwOslmQz8QMBxD0AQyDfEFGsBCWzmbTgKv9uqrBjubsS
Taja+Cf9kMo== dummy@gitlab.com
ssh-dss AAAAB3NzaC1kc3MAAAEBALEB3sM2kPy6LKLiyL+UlDx2vzuKrzSD2nsW2Kb7
0ivIqDNJu5CbqIQSkjdMzJiocs33ESFqXid6ezOtVdDwXHJQRxKGalW1kBbFAPjtMxlD
bf559+7qN2zfCfcQsgTmNAZ7O+wltqJmyLv5i4QqNwPDvyeBvJ4C+770DzlcQtpkflKJ
X+O7i8Ylq34h6UTCTnjry+dFVm1xz97LPf7XuzXGZcAG/eGUNQgxQ2bferKnrpYOXx6c
ocSRj9W54nrRFMWuDeOspWp4MoYK0FRMfDQYPksUayGUnm1KQTGuDbB0ahRNCOm8b3tf
P9Z+vjANAkqenzDuXCpz2PU/Oj6/N/UAAAAhAPOLyut12Mjcp3eUXLe1xSoI5IRXSLso
W9no93dcFNprAAABAQCLhpqKY+PNcwbhhPruL+f+uROghHzDwRNX+e231F4wHHeDDomf
WyLVFj31XrHdDXZnS9tTTj5D2XWLovSSxYb3H7earTctmktL0lQ3HapujzvOkn+VM0pG
s6B3j54+AM3mg50KZdYWxxv+v/lb6oEcsCjfKNyRIx/5pqX6XI3dxl9MMIxrfVWpkNX+
FI68v1LVV61DC9PkNyEHU0v9YBOfrTiS21TIlVIZcSFhuDjg52MekfZAnoKaP7YFJNF3
fdCrXaU3hYQrwB9XdskBUppwxKGhf7O6SWEZhAEfPA9kgxaWHoJvsDz8aca576UNe7BP
mjzo/SLUX+P4uvcaffd+AAABAEqzpmwjzTxB+DV8C+0LnmKf3L/UlQWyGdmhd65rnbkH
GgRMAAkoh4GBOEHL5bznNRmO7X/H6g2fR7SEabxfbvb903KI4nbfFF+3QtnwyIbTBAcH
0893D3bi5rsaJcz+c6lBob2En2nThRciefXUk2oPzCQuDyFIyHLJikqRQVcalHCdQ00c
/H/JkiJedHNqaeU4TeMk8SM53Brjplj/iiJq+ujc5MlEgACdCwWp0BviFACEoYyFaa3R
kc7Xdm9vFpclm9fzgUfPloASA0SkO945in3mIqMfODTb4yRvbjk8If9483fEPgQkczpd
ptBz1VAKg8AmRcz1GmBIxs+Stn0= dummy@gitlab.com
KEY
end
end
......
......@@ -190,7 +190,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
......@@ -207,7 +207,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
......
......@@ -365,6 +365,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
it 'allows oxford commas (comma before and) when referencing multiple issues' do
message = "Closes #{reference}, #{reference2}, and #{reference3}"
expect(subject.closed_by_message(message))
.to match_array([issue, other_issue, third_issue])
end
it 'allows spaces before commas when referencing multiple issues' do
message = "Closes #{reference} , #{reference2} , and #{reference3}"
expect(subject.closed_by_message(message))
.to match_array([issue, other_issue, third_issue])
end
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
......
# 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
......
......@@ -49,7 +49,9 @@ describe Gitlab::Gpg::Commit do
end
it 'returns a valid signature' do
expect(described_class.new(commit).signature).to have_attributes(
signature = described_class.new(commit).signature
expect(signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
......@@ -58,9 +60,31 @@ describe Gitlab::Gpg::Commit do
gpg_key_user_email: GpgHelpers::User1.emails.first,
verification_status: 'verified'
)
expect(signature.persisted?).to be_truthy
end
it_behaves_like 'returns the cached signature on second call'
context 'read-only mode' do
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
it 'does not create a cached signature' do
signature = described_class.new(commit).signature
expect(signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid,
gpg_key_user_name: GpgHelpers::User1.names.first,
gpg_key_user_email: GpgHelpers::User1.emails.first,
verification_status: 'verified'
)
expect(signature.persisted?).to be_falsey
end
end
end
context 'commit signed with a subkey' do
......
......@@ -37,12 +37,61 @@ describe Gitlab::SSHPublicKey, lib: true do
end
end
describe '.sanitize(key_content)' do
let(:content) { build(:key).key }
context 'when key has blank space characters' do
it 'removes the extra blank space characters' do
unsanitized = content.insert(100, "\n")
.insert(40, "\r\n")
.insert(30, ' ')
sanitized = described_class.sanitize(unsanitized)
_, body = sanitized.split
expect(sanitized).not_to eq(unsanitized)
expect(body).not_to match(/\s/)
end
end
context "when key doesn't have blank space characters" do
it "doesn't modify the content" do
sanitized = described_class.sanitize(content)
expect(sanitized).to eq(content)
end
end
context "when key is invalid" do
it 'returns the original content' do
unsanitized = "ssh-foo any content=="
sanitized = described_class.sanitize(unsanitized)
expect(sanitized).to eq(unsanitized)
end
end
end
describe '#valid?' do
subject { public_key }
context 'with a valid SSH key' do
where(:factory) do
%i(rsa_key_2048
rsa_key_4096
rsa_key_5120
rsa_key_8192
dsa_key_2048
ecdsa_key_256
ed25519_key_256)
end
with_them do
let(:key) { attributes_for(factory)[:key] }
it { is_expected.to be_valid }
end
end
context 'with an invalid SSH key' do
let(:key) { 'this is not a key' }
......@@ -82,6 +131,9 @@ describe Gitlab::SSHPublicKey, lib: true do
where(:factory, :bits) do
[
[:rsa_key_2048, 2048],
[:rsa_key_4096, 4096],
[:rsa_key_5120, 5120],
[:rsa_key_8192, 8192],
[:dsa_key_2048, 2048],
[:ecdsa_key_256, 256],
[:ed25519_key_256, 256]
......@@ -106,8 +158,11 @@ describe Gitlab::SSHPublicKey, lib: true do
where(:factory, :fingerprint) do
[
[:rsa_key_2048, '2e:ca:dc:e0:37:29:ed:fc:f0:1d:bf:66:d4:cd:51:b1'],
[:dsa_key_2048, 'bc:c1:a4:be:7e:8c:84:56:b3:58:93:53:c6:80:78:8c'],
[:rsa_key_2048, '58:a8:9d:cd:1f:70:f8:5a:d9:e4:24:8e:da:89:e4:fc'],
[:rsa_key_4096, 'df:73:db:29:3c:a5:32:cf:09:17:7e:8e:9d:de:d7:f7'],
[:rsa_key_5120, 'fe:fa:3a:4d:7d:51:ec:bf:c7:64:0c:96:d0:17:8a:d0'],
[:rsa_key_8192, 'fb:53:7f:e9:2f:f7:17:aa:c8:32:52:06:8e:05:e2:82'],
[:dsa_key_2048, 'c8:85:1e:df:44:0f:20:00:3c:66:57:2b:21:10:5a:27'],
[:ecdsa_key_256, '67:a3:a9:7d:b8:e1:15:d4:80:40:21:34:bb:ed:97:38'],
[:ed25519_key_256, 'e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73']
]
......
......@@ -12,6 +12,9 @@ describe Key, :mailer do
it { is_expected.to validate_presence_of(:key) }
it { is_expected.to validate_length_of(:key).is_at_most(5000) }
it { is_expected.to allow_value(attributes_for(:rsa_key_2048)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:rsa_key_4096)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:rsa_key_5120)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:rsa_key_8192)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:dsa_key_2048)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ecdsa_key_256)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ed25519_key_256)[:key]).for(:key) }
......@@ -72,16 +75,35 @@ describe Key, :mailer do
expect(build(:key)).to be_valid
end
it 'accepts a key with newline charecters after stripping them' do
key = build(:key)
key.key = key.key.insert(100, "\n")
key.key = key.key.insert(40, "\r\n")
expect(key).to be_valid
end
it 'rejects the unfingerprintable key (not a key)' do
expect(build(:key, key: 'ssh-rsa an-invalid-key==')).not_to be_valid
end
where(:factory, :chars, :expected_sections) do
[
[:key, ["\n", "\r\n"], 3],
[:key, [' ', ' '], 3],
[:key_without_comment, [' ', ' '], 2]
]
end
with_them do
let!(:key) { create(factory) }
let!(:original_fingerprint) { key.fingerprint }
it 'accepts a key with blank space characters after stripping them' do
modified_key = key.key.insert(100, chars.first).insert(40, chars.last)
_, content = modified_key.split
key.update!(key: modified_key)
expect(key).to be_valid
expect(key.key.split.size).to eq(expected_sections)
expect(content).not_to match(/\s/)
expect(original_fingerprint).to eq(key.fingerprint)
end
end
end
context 'validate it meets key restrictions' do
......
......@@ -496,6 +496,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
......
......@@ -1380,7 +1380,7 @@ describe API::Issues, :mailer do
end
describe '/projects/:id/issues/:issue_iid/move' do
let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
......
......@@ -7,7 +7,7 @@ describe API::Projects do
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
let(:project2) { create(:project, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
......@@ -315,7 +315,7 @@ describe API::Projects do
context 'and with all query parameters' do
let!(:project5) { create(:project, :public, path: 'gitlab5', namespace: create(:namespace)) }
let!(:project6) { create(:project, :public, path: 'project6', namespace: user.namespace) }
let!(:project6) { create(:project, :public, namespace: user.namespace) }
let!(:project7) { create(:project, :public, path: 'gitlab7', namespace: user.namespace) }
let!(:project8) { create(:project, path: 'gitlab8', namespace: user.namespace) }
let!(:project9) { create(:project, :public, path: 'gitlab9') }
......
......@@ -1191,7 +1191,7 @@ describe API::V3::Issues, :mailer do
end
describe '/projects/:id/issues/:issue_id/move' do
let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
......
......@@ -6,7 +6,7 @@ describe API::V3::Projects do
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
......
......@@ -232,6 +232,28 @@ describe Issues::MoveService do
end
end
context 'issue with assignee' do
let(:assignee) { create(:user) }
before do
old_issue.assignees = [assignee]
end
it 'preserves assignee with access to the new issue' do
new_project.add_reporter(assignee)
new_issue = move_service.execute(old_issue, new_project)
expect(new_issue.assignees).to eq([assignee])
end
it 'ignores assignee without access to the new issue' do
new_issue = move_service.execute(old_issue, new_project)
expect(new_issue.assignees).to be_empty
end
end
context 'notes with references' do
before do
create(:merge_request, source_project: old_project)
......
......@@ -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
......
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