Commit 1a2baf98 authored by Filipa Lacerda's avatar Filipa Lacerda

[ci skip] Merge branch 'master' into 4310-security-reports

* master: (73 commits)
  Add a lint check to restrict use of Rugged, EE version
  Fix EE offenses to the RSpec/SingleLineHook cop
  Update qa.rb
  Revert "Merge branch 'osw-updates-merge-status-on-api-actions' into 'master'"
  Use a fixed remote name for Geo mirrors
  Use flash message instead of regular text block
  Update common.rb
  Update gitlab-styles and re-enable the RSpec/SingleLineHook cop again
  Move EE-specific migrations under ee/db/
  Fix the EE assets paths and precompile config
  Add modal for deleting a milestone
  Make Gitlab::Git::Repository#run_git private
  Resolve conflicts in app/assets/javascripts/dispatcher.js
  Resolve conflicts in app/assets/javascripts/dispatcher.js
  Resolve conflicts in lib/gitlab/git/repository.rb
  Resolve conflict in spec/services/issues/move_service_spec.rb
  Resolve conflict in features/support/db_cleaner.rb
  Resolve conflict in spec/support/db_cleaner.rb
  Resolve conflict with .gitlab-ci.yml
  Update secret_values to support dynamic elements within parent
  ...
parents f7fc4d04 595a616c
...@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git ...@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git
- gitlab-org - gitlab-org
.default-cache: &default-cache .default-cache: &default-cache
key: "ruby-235-with-yarn" key: "ruby-2.3.6-with-yarn"
paths: paths:
- vendor/ruby - vendor/ruby
- .yarn-cache/ - .yarn-cache/
......
...@@ -13,11 +13,24 @@ AllCops: ...@@ -13,11 +13,24 @@ AllCops:
- 'db/*' - 'db/*'
- 'db/fixtures/**/*' - 'db/fixtures/**/*'
- 'db/geo/*' - 'db/geo/*'
- 'ee/db/geo/*'
- 'tmp/**/*' - 'tmp/**/*'
- 'bin/**/*' - 'bin/**/*'
- 'generator_templates/**/*' - 'generator_templates/**/*'
- 'builds/**/*' - 'builds/**/*'
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:
Enabled: true
Exclude:
- 'db/migrate/**/*'
- 'db/post_migrate/**/*'
- 'db/geo/migrate/**/*'
- 'ee/db/migrate/**/*'
- 'ee/db/post_migrate/**/*'
- 'ee/db/geo/migrate/**/*'
# Gitlab ################################################################### # Gitlab ###################################################################
Gitlab/ModuleWithInstanceVariables: Gitlab/ModuleWithInstanceVariables:
......
...@@ -342,10 +342,6 @@ RSpec/SharedContext: ...@@ -342,10 +342,6 @@ RSpec/SharedContext:
Exclude: Exclude:
- 'spec/features/admin/admin_groups_spec.rb' - 'spec/features/admin/admin_groups_spec.rb'
# Offense count: 90
RSpec/SingleLineHook:
Enabled: false
# Offense count: 5 # Offense count: 5
RSpec/VoidExpect: RSpec/VoidExpect:
Exclude: Exclude:
......
...@@ -422,7 +422,7 @@ group :ed25519 do ...@@ -422,7 +422,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.74.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.76.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -309,7 +309,7 @@ GEM ...@@ -309,7 +309,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.74.0) gitaly-proto (0.76.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -329,7 +329,7 @@ GEM ...@@ -329,7 +329,7 @@ GEM
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab-markup (1.6.3) gitlab-markup (1.6.3)
gitlab-styles (2.3.0) gitlab-styles (2.3.1)
rubocop (~> 0.51) rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0) rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19) rubocop-rspec (~> 1.19)
...@@ -1091,7 +1091,7 @@ DEPENDENCIES ...@@ -1091,7 +1091,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.74.0) gitaly-proto (~> 0.76.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
......
...@@ -2,18 +2,19 @@ import { n__ } from '../locale'; ...@@ -2,18 +2,19 @@ import { n__ } from '../locale';
import { convertPermissionToBoolean } from '../lib/utils/common_utils'; import { convertPermissionToBoolean } from '../lib/utils/common_utils';
export default class SecretValues { export default class SecretValues {
constructor(container) { constructor({
container,
valueSelector = '.js-secret-value',
placeholderSelector = '.js-secret-value-placeholder',
}) {
this.container = container; this.container = container;
this.valueSelector = valueSelector;
this.placeholderSelector = placeholderSelector;
} }
init() { init() {
this.values = this.container.querySelectorAll('.js-secret-value');
this.placeholders = this.container.querySelectorAll('.js-secret-value-placeholder');
this.revealButton = this.container.querySelector('.js-secret-value-reveal-button'); this.revealButton = this.container.querySelector('.js-secret-value-reveal-button');
this.revealText = n__('Reveal value', 'Reveal values', this.values.length);
this.hideText = n__('Hide value', 'Hide values', this.values.length);
const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus); const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus);
this.updateDom(isRevealed); this.updateDom(isRevealed);
...@@ -28,15 +29,17 @@ export default class SecretValues { ...@@ -28,15 +29,17 @@ export default class SecretValues {
} }
updateDom(isRevealed) { updateDom(isRevealed) {
this.values.forEach((value) => { const values = this.container.querySelectorAll(this.valueSelector);
values.forEach((value) => {
value.classList.toggle('hide', !isRevealed); value.classList.toggle('hide', !isRevealed);
}); });
this.placeholders.forEach((placeholder) => { const placeholders = this.container.querySelectorAll(this.placeholderSelector);
placeholders.forEach((placeholder) => {
placeholder.classList.toggle('hide', isRevealed); placeholder.classList.toggle('hide', isRevealed);
}); });
this.revealButton.textContent = isRevealed ? this.hideText : this.revealText; this.revealButton.textContent = isRevealed ? n__('Hide value', 'Hide values', values.length) : n__('Reveal value', 'Reveal values', values.length);
this.revealButton.dataset.secretRevealStatus = isRevealed; this.revealButton.dataset.secretRevealStatus = isRevealed;
} }
} }
...@@ -132,14 +132,17 @@ $(() => { ...@@ -132,14 +132,17 @@ $(() => {
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true); newIssue.setFetchingState('subscriptions', true);
newIssue.setFetchingState('weight', true); newIssue.setFetchingState('weight', true);
newIssue.setFetchingState('epic', true);
BoardService.getIssueInfo(sidebarInfoEndpoint) BoardService.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data) .then(res => res.data)
.then((data) => { .then((data) => {
newIssue.setFetchingState('subscriptions', false); newIssue.setFetchingState('subscriptions', false);
newIssue.setFetchingState('weight', false); newIssue.setFetchingState('weight', false);
newIssue.setFetchingState('epic', false);
newIssue.updateData({ newIssue.updateData({
subscribed: data.subscribed, subscribed: data.subscribed,
weight: data.weight, weight: data.weight,
epic: data.epic,
}); });
}) })
.catch(() => { .catch(() => {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
import notificationsDropdown from './notifications_dropdown';
import LineHighlighter from './line_highlighter';
import MergeRequest from './merge_request'; import MergeRequest from './merge_request';
import Flash from './flash'; import Flash from './flash';
import BlobViewer from './blob/viewer/index';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import Star from './star';
import ZenMode from './zen_mode'; import ZenMode from './zen_mode';
import PerformanceBar from './performance_bar';
import initNotes from './init_notes'; import initNotes from './init_notes';
import initIssuableSidebar from './init_issuable_sidebar'; import initIssuableSidebar from './init_issuable_sidebar';
import { convertPermissionToBoolean } from './lib/utils/common_utils'; import { convertPermissionToBoolean } from './lib/utils/common_utils';
...@@ -117,6 +112,11 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -117,6 +112,11 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'projects:milestones:index':
import('./pages/projects/milestones/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:show': case 'projects:milestones:show':
import('./pages/projects/milestones/show') import('./pages/projects/milestones/show')
.then(callDefault) .then(callDefault)
...@@ -700,23 +700,12 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -700,23 +700,12 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'show':
new Star();
notificationsDropdown();
break;
case 'wikis': case 'wikis':
import('./pages/projects/wikis') import('./pages/projects/wikis')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'snippets':
if (path[2] === 'show') {
new ZenMode();
new LineHighlighter();
new BlobViewer();
}
break;
} }
break; break;
} }
...@@ -726,7 +715,9 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -726,7 +715,9 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
} }
if (document.querySelector('#peek')) { if (document.querySelector('#peek')) {
new PerformanceBar({ container: '#peek' }); import('./performance_bar')
.then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
.catch(fail);
} }
}; };
......
import initMilestonesShow from '~/pages/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
export default initMilestonesShow; export default initMilestonesShow;
...@@ -3,7 +3,9 @@ import SecretValues from '~/behaviors/secret_values'; ...@@ -3,7 +3,9 @@ import SecretValues from '~/behaviors/secret_values';
export default () => { export default () => {
const secretVariableTable = document.querySelector('.js-secret-variable-table'); const secretVariableTable = document.querySelector('.js-secret-variable-table');
if (secretVariableTable) { if (secretVariableTable) {
const secretVariableTableValues = new SecretValues(secretVariableTable); const secretVariableTableValues = new SecretValues({
container: secretVariableTable,
});
secretVariableTableValues.init(); secretVariableTableValues.init();
} }
}; };
<script>
import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import modal from '~/vue_shared/components/modal.vue';
import { n__, s__, sprintf } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
modal,
},
props: {
issueCount: {
type: Number,
required: true,
},
mergeRequestCount: {
type: Number,
required: true,
},
milestoneId: {
type: Number,
required: true,
},
milestoneTitle: {
type: String,
required: true,
},
milestoneUrl: {
type: String,
required: true,
},
},
computed: {
text() {
const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { milestoneTitle: this.milestoneTitle });
if (this.issueCount === 0 && this.mergeRequestCount === 0) {
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} from this project.
%{milestoneTitle} is not currently used in any issues or merge requests.`),
{
milestoneTitle,
},
false,
);
}
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
Once deleted, it cannot be undone or recovered.`),
{
milestoneTitle,
issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
mergeRequestsWithCount: n__('%d merge request', '%d merge requests', this.mergeRequestCount),
},
false,
);
},
title() {
return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { milestoneTitle: this.milestoneTitle });
},
},
methods: {
onSubmit() {
eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
return axios.delete(this.milestoneUrl)
.then((response) => {
eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: true });
// follow the rediect to milestones overview page
redirectTo(response.request.responseURL);
})
.catch((error) => {
eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: false });
if (error.response && error.response.status === 404) {
Flash(sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { milestoneTitle: this.milestoneTitle }));
} else {
Flash(sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { milestoneTitle: this.milestoneTitle }));
}
throw error;
});
},
},
};
</script>
<template>
<modal
id="delete-milestone-modal"
:title="title"
:text="text"
kind="danger"
:primary-button-label="s__('Milestones|Delete milestone')"
@submit="onSubmit">
<template
slot="body"
slot-scope="props">
<p v-html="props.text"></p>
</template>
</modal>
</template>
import Vue from 'vue';
export default new Vue();
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import deleteMilestoneModal from './components/delete_milestone_modal.vue';
import eventHub from './event_hub';
export default () => {
Vue.use(Translate);
const onRequestFinished = ({ milestoneUrl, successful }) => {
const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
if (!successful) {
button.removeAttribute('disabled');
}
button.querySelector('.js-loading-icon').classList.add('hidden');
};
const onRequestStarted = (milestoneUrl) => {
const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
button.setAttribute('disabled', '');
button.querySelector('.js-loading-icon').classList.remove('hidden');
eventHub.$once('deleteMilestoneModal.requestFinished', onRequestFinished);
};
const onDeleteButtonClick = (event) => {
const button = event.currentTarget;
const modalProps = {
milestoneId: parseInt(button.dataset.milestoneId, 10),
milestoneTitle: button.dataset.milestoneTitle,
milestoneUrl: button.dataset.milestoneUrl,
issueCount: parseInt(button.dataset.milestoneIssueCount, 10),
mergeRequestCount: parseInt(button.dataset.milestoneMergeRequestCount, 10),
};
eventHub.$once('deleteMilestoneModal.requestStarted', onRequestStarted);
eventHub.$emit('deleteMilestoneModal.props', modalProps);
};
const deleteMilestoneButtons = document.querySelectorAll('.js-delete-milestone-button');
for (let i = 0; i < deleteMilestoneButtons.length; i += 1) {
const button = deleteMilestoneButtons[i];
button.addEventListener('click', onDeleteButtonClick);
}
eventHub.$once('deleteMilestoneModal.mounted', () => {
for (let i = 0; i < deleteMilestoneButtons.length; i += 1) {
const button = deleteMilestoneButtons[i];
button.removeAttribute('disabled');
}
});
return new Vue({
el: '#delete-milestone-modal',
components: {
deleteMilestoneModal,
},
data() {
return {
modalProps: {
milestoneId: -1,
milestoneTitle: '',
milestoneUrl: '',
issueCount: -1,
mergeRequestCount: -1,
},
};
},
mounted() {
eventHub.$on('deleteMilestoneModal.props', this.setModalProps);
eventHub.$emit('deleteMilestoneModal.mounted');
},
beforeDestroy() {
eventHub.$off('deleteMilestoneModal.props', this.setModalProps);
},
methods: {
setModalProps(modalProps) {
this.modalProps = modalProps;
},
},
render(createElement) {
return createElement(deleteMilestoneModal, {
props: this.modalProps,
});
},
});
};
import milestones from '~/pages/milestones/shared';
export default milestones;
import initMilestonesShow from '~/pages/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import milestones from '~/pages/milestones/shared';
export default initMilestonesShow; export default () => {
initMilestonesShow();
milestones();
};
...@@ -6,13 +6,17 @@ export default function () { ...@@ -6,13 +6,17 @@ export default function () {
initSettingsPanels(); initSettingsPanels();
const runnerToken = document.querySelector('.js-secret-runner-token'); const runnerToken = document.querySelector('.js-secret-runner-token');
if (runnerToken) { if (runnerToken) {
const runnerTokenSecretValue = new SecretValues(runnerToken); const runnerTokenSecretValue = new SecretValues({
container: runnerToken,
});
runnerTokenSecretValue.init(); runnerTokenSecretValue.init();
} }
const secretVariableTable = document.querySelector('.js-secret-variable-table'); const secretVariableTable = document.querySelector('.js-secret-variable-table');
if (secretVariableTable) { if (secretVariableTable) {
const secretVariableTableValues = new SecretValues(secretVariableTable); const secretVariableTableValues = new SecretValues({
container: secretVariableTable,
});
secretVariableTableValues.init(); secretVariableTableValues.init();
} }
} }
...@@ -5,8 +5,12 @@ import TreeView from '~/tree'; ...@@ -5,8 +5,12 @@ import TreeView from '~/tree';
import BlobViewer from '~/blob/viewer/index'; import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities'; import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils'; import { ajaxGet } from '~/lib/utils/common_utils';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
export default () => { export default () => {
new Star(); // eslint-disable-line no-new
notificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new new NotificationsForm(); // eslint-disable-line no-new
new UserCallout({ // eslint-disable-line no-new new UserCallout({ // eslint-disable-line no-new
......
import initNotes from '~/init_notes'; import initNotes from '~/init_notes';
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
import LineHighlighter from '../../../../line_highlighter';
import BlobViewer from '../../../../blob/viewer';
export default function () { export default function () {
new LineHighlighter(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
initNotes(); initNotes();
new ZenMode(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new
} }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
.modal-body { .modal-body {
background-color: $modal-body-bg; background-color: $modal-body-bg;
line-height: $line-height-base;
min-height: $modal-body-height; min-height: $modal-body-height;
position: relative; position: relative;
padding: #{3 * $grid-size} #{2 * $grid-size}; padding: #{3 * $grid-size} #{2 * $grid-size};
......
...@@ -155,6 +155,8 @@ class ApplicationController < ActionController::Base ...@@ -155,6 +155,8 @@ class ApplicationController < ActionController::Base
format.html do format.html do
render file: Rails.root.join("public", "404"), layout: false, status: "404" render file: Rails.root.join("public", "404"), layout: false, status: "404"
end end
# Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file
format.js { render json: '', status: :not_found, content_type: 'application/json' }
format.any { head :not_found } format.any { head :not_found }
end end
end end
......
...@@ -88,7 +88,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -88,7 +88,7 @@ class Projects::MilestonesController < Projects::ApplicationController
Milestones::DestroyService.new(project, current_user).execute(milestone) Milestones::DestroyService.new(project, current_user).execute(milestone)
respond_to do |format| respond_to do |format|
format.html { redirect_to namespace_project_milestones_path, status: 302 } format.html { redirect_to namespace_project_milestones_path, status: 303 }
format.js { head :ok } format.js { head :ok }
end end
end end
......
...@@ -537,7 +537,7 @@ module Ci ...@@ -537,7 +537,7 @@ module Ci
return unless sha return unless sha
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path) project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
rescue GRPC::NotFound, Rugged::ReferenceError, GRPC::Internal rescue GRPC::NotFound, GRPC::Internal
nil nil
end end
......
...@@ -45,14 +45,7 @@ class Deployment < ActiveRecord::Base ...@@ -45,14 +45,7 @@ class Deployment < ActiveRecord::Base
def includes_commit?(commit) def includes_commit?(commit)
return false unless commit return false unless commit
# Before 8.10, deployments didn't have keep-around refs. Any deployment
# created before then could have a `sha` referring to a commit that no
# longer exists in the repository, so just ignore those.
begin
project.repository.ancestor?(commit.id, sha) project.repository.ancestor?(commit.id, sha)
rescue Rugged::OdbError
false
end
end end
def update_merge_request_metrics! def update_merge_request_metrics!
......
...@@ -981,10 +981,10 @@ class Project < ActiveRecord::Base ...@@ -981,10 +981,10 @@ class Project < ActiveRecord::Base
hooks.hooks_for(hooks_scope).each do |hook| hooks.hooks_for(hooks_scope).each do |hook|
hook.async_execute(data, hooks_scope.to_s) hook.async_execute(data, hooks_scope.to_s)
end end
end
SystemHooksService.new.execute_hooks(data, hooks_scope) SystemHooksService.new.execute_hooks(data, hooks_scope)
end end
end
def execute_services(data, hooks_scope = :push_hooks) def execute_services(data, hooks_scope = :push_hooks)
# Call only service hooks that are active for this scope # Call only service hooks that are active for this scope
......
...@@ -25,6 +25,7 @@ class Repository ...@@ -25,6 +25,7 @@ class Repository
attr_accessor :full_path, :disk_path, :project, :is_wiki attr_accessor :full_path, :disk_path, :project, :is_wiki
delegate :ref_name_for_sha, to: :raw_repository delegate :ref_name_for_sha, to: :raw_repository
delegate :bundle_to_disk, to: :raw_repository
CreateTreeError = Class.new(StandardError) CreateTreeError = Class.new(StandardError)
...@@ -172,17 +173,11 @@ class Repository ...@@ -172,17 +173,11 @@ class Repository
return [] return []
end end
raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled| commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c|
commits = commit(c)
if is_enabled
find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
else
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end end
CommitCollection.new(project, commits, ref) CommitCollection.new(project, commits, ref)
end end
end
def find_branch(name, fresh_repo: true) def find_branch(name, fresh_repo: true)
# Since the Repository object may have in-memory index changes, invalidating the memoized Repository object may # Since the Repository object may have in-memory index changes, invalidating the memoized Repository object may
...@@ -746,23 +741,6 @@ class Repository ...@@ -746,23 +741,6 @@ class Repository
Commit.order_by(collection: commits, order_by: order_by, sort: sort) Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end end
def refs_contains_sha(ref_type, sha)
args = %W(#{ref_type} --contains #{sha})
names = run_git(args).first
if names.respond_to?(:split)
names = names.split("\n").map(&:strip)
names.each do |name|
name.slice! '* '
end
names
else
[]
end
end
def branch_names_contains(sha) def branch_names_contains(sha)
refs_contains_sha('branch', sha) refs_contains_sha('branch', sha)
end end
...@@ -969,25 +947,6 @@ class Repository ...@@ -969,25 +947,6 @@ class Repository
end end
end end
def search_files_by_content(query, ref)
return [] if empty? || query.blank?
offset = 2
args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
run_git(args).first.scrub.split(/^--$/)
end
def search_files_by_name(query, ref)
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
return [] if empty? || safe_query.blank?
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
run_git(args).first.lines.map(&:strip)
end
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil) def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil)
unless remote_name unless remote_name
remote_name = "tmp-#{SecureRandom.hex}" remote_name = "tmp-#{SecureRandom.hex}"
...@@ -1035,6 +994,18 @@ class Repository ...@@ -1035,6 +994,18 @@ class Repository
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
end end
def search_files_by_content(query, ref)
return [] if empty? || query.blank?
raw_repository.search_files_by_content(query, ref)
end
def search_files_by_name(query, ref)
return [] if empty?
raw_repository.search_files_by_name(query, ref)
end
def copy_gitattributes(ref) def copy_gitattributes(ref)
actual_ref = ref || root_ref actual_ref = ref || root_ref
begin begin
...@@ -1201,25 +1172,4 @@ class Repository ...@@ -1201,25 +1172,4 @@ class Repository
def rugged_can_be_merged?(their_commit, our_commit) def rugged_can_be_merged?(their_commit, our_commit)
!rugged.merge_commits(our_commit, their_commit).conflicts? !rugged.merge_commits(our_commit, their_commit).conflicts?
end end
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
ref ||= root_ref
args = %W(
log #{ref} --pretty=%H --skip #{offset}
--max-count #{limit} --grep=#{query} --regexp-ignore-case
)
args = args.concat(%W(-- #{path})) if path.present?
git_log_results = run_git(args).first.lines
git_log_results.map { |c| commit(c.chomp) }.compact
end
def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
raw_repository
.gitaly_commit_client
.commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
.map { |c| commit(c) }
end
end end
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
= webpack_bundle_tag "main" = webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test? = webpack_bundle_tag "test" if Rails.env.test?
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts) - if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts = yield :page_specific_javascripts
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
- branch_label = s_('ChangeTypeActionLabel|Revert in branch') - branch_label = s_('ChangeTypeActionLabel|Revert in branch')
- revert_merge_request = _('Revert this merge request') - revert_merge_request = _('Revert this merge request')
- revert_commit = _('Revert this commit') - revert_commit = _('Revert this commit')
- description = s_('ChangeTypeAction|This will create a new commit in order to revert the existing changes.')
- title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit - title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit
- when 'cherry-pick' - when 'cherry-pick'
- label = s_('ChangeTypeAction|Cherry-pick') - label = s_('ChangeTypeAction|Cherry-pick')
...@@ -17,6 +18,8 @@ ...@@ -17,6 +18,8 @@
%a.close{ href: "#", "data-dismiss" => "modal" } × %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title= title %h3.page-title= title
.modal-body .modal-body
- if description
%p.append-bottom-20= description
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do = form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch .form-group.branch
= label_tag 'start_branch', branch_label, class: 'control-label' = label_tag 'start_branch', branch_label, class: 'control-label'
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.settings-header .settings-header
%h4 %h4
Deploy Keys Deploy Keys
%button.btn.js-settings-toggle.qa-expand-deploy-keys %button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand' = expanded ? 'Collapse' : 'Expand'
%p %p
Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one. Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
New milestone New milestone
.milestones .milestones
#delete-milestone-modal
%ul.content-list %ul.content-list
= render @milestones = render @milestones
......
...@@ -35,8 +35,18 @@ ...@@ -35,8 +35,18 @@
- else - else
= link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped" = link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
= link_to project_milestone_path(@project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do %button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal',
Delete target: '#delete-milestone-modal',
milestone_id: @milestone.id,
milestone_title: markdown_field(@milestone, :title),
milestone_url: project_milestone_path(@project, @milestone),
milestone_issue_count: @milestone.issues.count,
milestone_merge_request_count: @milestone.merge_requests.count },
disabled: true }
= _('Delete')
= icon('spin spinner', class: 'js-loading-icon hidden' )
#delete-milestone-modal
%a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left') = icon('angle-double-left')
......
...@@ -57,7 +57,6 @@ ...@@ -57,7 +57,6 @@
Titles and Filenames Titles and Filenames
%span.badge %span.badge
= @search_results.snippet_titles_count = @search_results.snippet_titles_count
- else - else
%li{ class: active_when(@scope == 'projects') } %li{ class: active_when(@scope == 'projects') }
= link_to search_filter_path(scope: 'projects') do = link_to search_filter_path(scope: 'projects') do
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
= custom_icon("icon_close", size: 15) = custom_icon("icon_close", size: 15)
.js-issuable-update .js-issuable-update
= render "shared/boards/components/sidebar/assignee" = render "shared/boards/components/sidebar/assignee"
= render "shared/boards/components/sidebar/epic"
= render "shared/boards/components/sidebar/milestone" = render "shared/boards/components/sidebar/milestone"
= render "shared/boards/components/sidebar/due_date" = render "shared/boards/components/sidebar/due_date"
= render "shared/boards/components/sidebar/labels" = render "shared/boards/components/sidebar/labels"
......
...@@ -56,6 +56,13 @@ ...@@ -56,6 +56,13 @@
= link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped" = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped"
= link_to project_milestone_path(milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove btn-grouped" do %button.js-delete-milestone-button.btn.btn-xs.btn-grouped.btn-danger{ data: { toggle: 'modal',
Delete target: '#delete-milestone-modal',
milestone_id: milestone.id,
milestone_title: markdown_field(milestone, :title),
milestone_url: project_milestone_path(milestone.project, milestone),
milestone_issue_count: milestone.issues.count,
milestone_merge_request_count: milestone.merge_requests.count },
disabled: true }
= _('Delete')
= icon('spin spinner', class: 'js-loading-icon hidden' )
...@@ -20,10 +20,7 @@ module RepositoryCheck ...@@ -20,10 +20,7 @@ module RepositoryCheck
# Historically some projects never had their wiki repos initialized; # Historically some projects never had their wiki repos initialized;
# this happens on project creation now. Let's initialize an empty repo # this happens on project creation now. Let's initialize an empty repo
# if it is not already there. # if it is not already there.
begin
project.create_wiki project.create_wiki
rescue Rugged::RepositoryError
end
git_fsck(project.wiki.repository) git_fsck(project.wiki.repository)
else else
......
---
title: 'Geo: Improve replication status. Using pg_stat_wal_receiver'
merge_request:
author:
type: other
---
title: Add Epic information for selected issue in Issue boards sidebar
merge_request: 4104
author:
type: added
---
title: Use a fixed remote name for Geo mirrors
merge_request: 4249
author:
type: fixed
---
title: Add application create API
merge_request: 8160
author: Nicolas Merelli @PNSalocin
---
title: Changes Revert this merge request text
merge_request: 16611
author: Jacopo Beschi @jacopo-beschi
type: changed
---
title: Execute system hooks after-commit when executing project hooks
merge_request:
author:
type: fixed
---
title: Add modal for deleting a milestone
merge_request: 16229
author:
type: other
...@@ -111,17 +111,11 @@ module Gitlab ...@@ -111,17 +111,11 @@ module Gitlab
# Enable the asset pipeline # Enable the asset pipeline
config.assets.enabled = true config.assets.enabled = true
# Support legacy unicode file named img emojis, `1F939.png` # Support legacy unicode file named img emojis, `1F939.png`
config.assets.paths << Gemojione.images_path config.assets.paths << Gemojione.images_path
config.assets.paths << "vendor/assets/fonts" config.assets.paths << "#{config.root}/vendor/assets/fonts"
# EE specific paths.
config.assets.paths << "ee/app/assets/images"
config.assets.paths << "ee/app/assets/javascripts"
config.assets.paths << "ee/app/assets/stylesheets"
config.assets.precompile << "*.png"
config.assets.precompile << "*.ico"
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
...@@ -130,10 +124,23 @@ module Gitlab ...@@ -130,10 +124,23 @@ module Gitlab
config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "performance_bar.css" config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js" config.assets.precompile << "lib/ace.js"
config.assets.precompile << "vendor/assets/fonts/*"
config.assets.precompile << "test.css" config.assets.precompile << "test.css"
config.assets.precompile << "locale/**/app.js" config.assets.precompile << "locale/**/app.js"
## EE-specific assets config START
%w[images javascripts stylesheets].each do |path|
config.assets.paths << "#{config.root}/ee/app/assets/#{path}"
end
# Compile non-JS/CSS assets in the ee/app/assets folder by default
# Mimic sprockets-rails default: https://github.com/rails/sprockets-rails/blob/v3.2.1/lib/sprockets/railtie.rb#L84-L87
LOOSE_EE_APP_ASSETS = lambda do |logical_path, filename|
filename.start_with?(config.root.join("ee/app/assets").to_s) &&
!['.js', '.css', ''].include?(File.extname(logical_path))
end
config.assets.precompile << LOOSE_EE_APP_ASSETS
## EE-specific assets config END
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
......
...@@ -12,3 +12,12 @@ unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS'] ...@@ -12,3 +12,12 @@ unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
ActiveRecord::Migrator.migrations_paths << path ActiveRecord::Migrator.migrations_paths << path
end end
end end
migrate_paths = Rails.application.config.paths['db/migrate'].to_a
migrate_paths.each do |migrate_path|
absolute_migrate_path = Pathname.new(migrate_path).realpath(Rails.root)
ee_migrate_path = Rails.root.join('ee/', absolute_migrate_path.relative_path_from(Rails.root))
Rails.application.config.paths['db/migrate'] << ee_migrate_path.to_s
ActiveRecord::Migrator.migrations_paths << ee_migrate_path.to_s
end
# We don't want to ever call Rugged::Repository#fetch_attributes, because it has
# a lot of I/O overhead:
# <https://gitlab.com/gitlab-org/gitlab_git/commit/340e111e040ae847b614d35b4d3173ec48329015>
#
# While we don't do this from within the GitLab source itself, the Linguist gem
# has a dependency on Rugged and uses the gitattributes file when calculating
# repository-wide language statistics:
# <https://github.com/github/linguist/blob/v4.7.0/lib/linguist/lazy_blob.rb#L33-L36>
#
# The options passed by Linguist are those assumed by Gitlab::Git::Attributes
# anyway, and there is no great efficiency gain from just fetching the listed
# attributes with our implementation, so we ignore the additional arguments.
#
module Rugged
class Repository
module UseGitlabGitAttributes
def fetch_attributes(name, *)
attributes.attributes(name)
end
def attributes
@attributes ||= Gitlab::Git::Attributes.new(path)
end
end
prepend UseGitlabGitAttributes
end
end
...@@ -96,7 +96,6 @@ var config = { ...@@ -96,7 +96,6 @@ var config = {
test: './test.js', test: './test.js',
two_factor_auth: './two_factor_auth.js', two_factor_auth: './two_factor_auth.js',
users: './users/index.js', users: './users/index.js',
performance_bar: './performance_bar.js',
webpack_runtime: './webpack.js', webpack_runtime: './webpack.js',
}, },
......
# Applications API
> [Introduced][ce-8160] in GitLab 10.5
[ce-8160]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8160
## Create a application
Create a application by posting a JSON payload.
User must be admin to do that.
Returns `200` if the request succeeds.
```
POST /applications
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | yes | The name of the application |
| `redirect_uri` | string | yes | The redirect URI of the application |
| `scopes` | string | yes | The scopes of the application |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=" https://gitlab.example.com/api/v3/applications
```
Example response:
```json
{
"application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
"secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34",
"callback_url": "http://redirect.uri"
}
```
...@@ -111,7 +111,7 @@ future GitLab releases.** ...@@ -111,7 +111,7 @@ future GitLab releases.**
| `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` | | `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` |
| `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` | | `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` |
## `.gitlab-ci.yaml` defined variables ## `.gitlab-ci.yml` defined variables
>**Note:** >**Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher. This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
......
...@@ -167,7 +167,7 @@ otherwise it may fail with an encryption error. ...@@ -167,7 +167,7 @@ otherwise it may fail with an encryption error.
Secondary Geo nodes track data about what has been downloaded in a second Secondary Geo nodes track data about what has been downloaded in a second
PostgreSQL database that is distinct from the production GitLab database. PostgreSQL database that is distinct from the production GitLab database.
The database configuration is set in `config/database_geo.yml`. The database configuration is set in `config/database_geo.yml`.
`db/geo` contains the schema and migrations for this database. `ee/db/geo` contains the schema and migrations for this database.
To write a migration for the database, use the `GeoMigrationGenerator`: To write a migration for the database, use the `GeoMigrationGenerator`:
......
...@@ -13,6 +13,7 @@ module Geo ...@@ -13,6 +13,7 @@ module Geo
attr_reader :project attr_reader :project
GEO_REMOTE_NAME = 'geo'.freeze
LEASE_TIMEOUT = 8.hours.freeze LEASE_TIMEOUT = 8.hours.freeze
LEASE_KEY_PREFIX = 'geo_sync_service'.freeze LEASE_KEY_PREFIX = 'geo_sync_service'.freeze
RETRY_BEFORE_REDOWNLOAD = 5 RETRY_BEFORE_REDOWNLOAD = 5
...@@ -82,7 +83,9 @@ module Geo ...@@ -82,7 +83,9 @@ module Geo
authorization = ::Gitlab::Geo::BaseRequest.new.authorization authorization = ::Gitlab::Geo::BaseRequest.new.authorization
header = { "http.#{url}.extraHeader" => "Authorization: #{authorization}" } header = { "http.#{url}.extraHeader" => "Authorization: #{authorization}" }
repository.with_config(header) { repository.fetch_as_mirror(url, forced: true) } repository.with_config(header) do
repository.fetch_as_mirror(url, remote_name: GEO_REMOTE_NAME, forced: true)
end
end end
def registry def registry
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
%strong exact order %strong exact order
they appear. they appear.
- unless Gitlab::Database.pg_stat_wal_receiver_supported?
= content_for :flash_message do
.alert.alert-warning WARNING: Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version.
- if @nodes.any? - if @nodes.any?
#js-geo-nodes{ data: { primary_version: "#{Gitlab::VERSION}", primary_revision: "#{Gitlab::REVISION}", node_details_path: "#{admin_geo_nodes_path}", node_actions_allowed: "#{Gitlab::Database.read_write?}", node_edit_allowed: "#{Gitlab::Geo.license_allows?}" } } #js-geo-nodes{ data: { primary_version: "#{Gitlab::VERSION}", primary_revision: "#{Gitlab::REVISION}", node_details_path: "#{admin_geo_nodes_path}", node_actions_allowed: "#{Gitlab::Database.read_write?}", node_edit_allowed: "#{Gitlab::Geo.license_allows?}" } }
- else - else
......
- return unless @group&.feature_available?(:epics) || @project&.group&.feature_available?(:epics)
.block.epic
.title
Epic
%span.js-epic-label-loading{ "v-if" => "issue.isFetching && issue.isFetching.epic" }
= icon('spinner spin', class: 'loading-icon')
.value.js-epic-label{ "v-if" => "issue.isFetching && !issue.isFetching.epic" }
%a.bold{ "v-if" => "issue.epic", ":href" => "issue.epic.url" }
{{ issue.epic.title }}
.no-value{ "v-if" => "!issue.epic" }
None
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment