Merge branch 'issue-edit-inline' into issue-edit-inline-confidential
This commit is contained in:
commit
468afcaaff
74 changed files with 855 additions and 265 deletions
|
@ -16,10 +16,14 @@ class FilteredSearchManager {
|
|||
this.recentSearchesStore = new RecentSearchesStore({
|
||||
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
|
||||
});
|
||||
let recentSearchesKey = 'issue-recent-searches';
|
||||
const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
|
||||
const projectPath = searchHistoryDropdownElement ?
|
||||
searchHistoryDropdownElement.dataset.projectFullPath : 'project';
|
||||
let recentSearchesPagePrefix = 'issue-recent-searches';
|
||||
if (page === 'merge_requests') {
|
||||
recentSearchesKey = 'merge-request-recent-searches';
|
||||
recentSearchesPagePrefix = 'merge-request-recent-searches';
|
||||
}
|
||||
const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`;
|
||||
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
|
||||
|
||||
// Fetch recent searches from localStorage
|
||||
|
@ -47,7 +51,7 @@ class FilteredSearchManager {
|
|||
this.recentSearchesRoot = new RecentSearchesRoot(
|
||||
this.recentSearchesStore,
|
||||
this.recentSearchesService,
|
||||
document.querySelector('.js-filtered-search-history-dropdown'),
|
||||
searchHistoryDropdownElement,
|
||||
);
|
||||
this.recentSearchesRoot.init();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import _ from 'underscore';
|
|||
class RecentSearchesStore {
|
||||
constructor(initialState = {}) {
|
||||
this.state = Object.assign({
|
||||
isLocalStorageAvailable: true,
|
||||
recentSearches: [],
|
||||
}, initialState);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import Service from '../services/index';
|
|||
import Store from '../stores';
|
||||
import titleComponent from './title.vue';
|
||||
import descriptionComponent from './description.vue';
|
||||
import confidentialCheckbox from './fields/confidential_checkbox.vue';
|
||||
import editActions from './edit_actions.vue';
|
||||
import formComponent from './form.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -61,9 +60,6 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
elementType() {
|
||||
return this.showForm ? 'form' : 'div';
|
||||
},
|
||||
formState() {
|
||||
return this.store.formState;
|
||||
},
|
||||
|
@ -71,8 +67,7 @@ export default {
|
|||
components: {
|
||||
descriptionComponent,
|
||||
titleComponent,
|
||||
editActions,
|
||||
confidentialCheckbox,
|
||||
formComponent,
|
||||
},
|
||||
methods: {
|
||||
openForm() {
|
||||
|
@ -156,10 +151,13 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :is="elementType">
|
||||
<title-component
|
||||
<div>
|
||||
<form-component
|
||||
v-if="canUpdate && showForm"
|
||||
:form-state="formState"
|
||||
:show-form="showForm"
|
||||
:can-destroy="canDestroy" />
|
||||
<div v-else>
|
||||
<title-component
|
||||
:issuable-ref="issuableRef"
|
||||
:title-html="state.titleHtml"
|
||||
:title-text="state.titleText" />
|
||||
|
@ -170,11 +168,6 @@ export default {
|
|||
:description-text="state.descriptionText"
|
||||
:updated-at="state.updatedAt"
|
||||
:task-status="state.taskStatus" />
|
||||
<confidential-checkbox
|
||||
v-if="showForm"
|
||||
:form-state="formState" />
|
||||
<edit-actions
|
||||
v-if="canUpdate && showForm"
|
||||
:can-destroy="canDestroy" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
34
app/assets/javascripts/issue_show/components/form.vue
Normal file
34
app/assets/javascripts/issue_show/components/form.vue
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import titleField from './fields/title.vue';
|
||||
import editActions from './edit_actions.vue';
|
||||
import confidentialCheckbox from './fields/confidential_checkbox.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
canDestroy: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
formState: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
titleField,
|
||||
editActions,
|
||||
confidentialCheckbox,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form>
|
||||
<title-field
|
||||
:form-state="formState" />
|
||||
<confidential-checkbox
|
||||
:form-state="formState" />
|
||||
<edit-actions
|
||||
:can-destroy="canDestroy" />
|
||||
</form>
|
||||
</template>
|
|
@ -1,12 +1,8 @@
|
|||
<script>
|
||||
import animateMixin from '../mixins/animate';
|
||||
import titleField from './fields/title.vue';
|
||||
|
||||
export default {
|
||||
mixins: [animateMixin],
|
||||
components: {
|
||||
titleField,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
preAnimation: false,
|
||||
|
@ -27,14 +23,6 @@
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
formState: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
showForm: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
titleHtml() {
|
||||
|
@ -53,12 +41,7 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<title-field
|
||||
v-if="showForm"
|
||||
:form-state="formState" />
|
||||
<h2
|
||||
v-else
|
||||
class="title"
|
||||
:class="{
|
||||
'issue-realtime-pre-pulse': preAnimation,
|
||||
|
@ -67,5 +50,4 @@
|
|||
v-html="titleHtml"
|
||||
>
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -8,15 +8,15 @@ export default class Service {
|
|||
this.endpoint = endpoint;
|
||||
|
||||
this.resource = Vue.resource(this.endpoint, {}, {
|
||||
rendered_title: {
|
||||
realtimeChanges: {
|
||||
method: 'GET',
|
||||
url: `${this.endpoint}/rendered_title`,
|
||||
url: `${this.endpoint}/realtime_changes`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.resource.rendered_title();
|
||||
return this.resource.realtimeChanges();
|
||||
}
|
||||
|
||||
deleteIssuable() {
|
||||
|
|
|
@ -24,7 +24,7 @@ const normalizeNewlines = function(str) {
|
|||
(function() {
|
||||
this.Notes = (function() {
|
||||
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
|
||||
const REGEX_SLASH_COMMANDS = /^\/\w+/gm;
|
||||
const REGEX_SLASH_COMMANDS = /^\/\w+.*$/gm;
|
||||
|
||||
Notes.interval = null;
|
||||
|
||||
|
@ -1170,6 +1170,7 @@ const normalizeNewlines = function(str) {
|
|||
*/
|
||||
Notes.prototype.createPlaceholderNote = function({ formContent, uniqueId, isDiscussionNote, currentUsername, currentUserFullname }) {
|
||||
const discussionClass = isDiscussionNote ? 'discussion' : '';
|
||||
const escapedFormContent = _.escape(formContent);
|
||||
const $tempNote = $(
|
||||
`<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
|
||||
<div class="timeline-entry-inner">
|
||||
|
@ -1183,14 +1184,11 @@ const normalizeNewlines = function(str) {
|
|||
<span class="hidden-xs">${currentUserFullname}</span>
|
||||
<span class="note-headline-light">@${currentUsername}</span>
|
||||
</a>
|
||||
<span class="note-headline-light">
|
||||
<i class="fa fa-spinner fa-spin" aria-label="Comment is being posted" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-body">
|
||||
<div class="note-text">
|
||||
<p>${formContent}</p>
|
||||
<p>${escapedFormContent}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
}
|
||||
|
||||
@mixin scrolling-links() {
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
display: flex;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
|
@ -35,6 +35,7 @@
|
|||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
|
@ -42,17 +43,16 @@
|
|||
border-bottom: 1px solid $border-color;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: $gl-btn-padding;
|
||||
padding-bottom: 11px;
|
||||
margin-bottom: -1px;
|
||||
font-size: 14px;
|
||||
line-height: 28px;
|
||||
color: $gl-text-color-secondary;
|
||||
border-bottom: 2px solid transparent;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
|
@ -85,10 +85,10 @@
|
|||
.container-fluid {
|
||||
background-color: $gray-normal;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li {
|
||||
|
||||
&.active a {
|
||||
border-bottom: none;
|
||||
color: $link-underline-blue;
|
||||
|
@ -137,9 +137,9 @@
|
|||
}
|
||||
|
||||
.nav-links {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
float: left;
|
||||
|
||||
&.wide {
|
||||
width: 100%;
|
||||
|
@ -337,6 +337,10 @@
|
|||
border-bottom: none;
|
||||
height: 51px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
li {
|
||||
a {
|
||||
padding-top: 10px;
|
||||
|
@ -347,6 +351,7 @@
|
|||
|
||||
.scrolling-tabs-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.nav-links {
|
||||
@include scrolling-links();
|
||||
|
@ -484,10 +489,7 @@
|
|||
|
||||
.inner-page-scroll-tabs {
|
||||
position: relative;
|
||||
|
||||
.nav-links {
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
overflow: hidden;
|
||||
|
||||
.fade-right {
|
||||
@include fade(left, $white-light);
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
.right-sidebar-expanded {
|
||||
padding-right: 0;
|
||||
z-index: 300;
|
||||
|
||||
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
|
||||
&:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper {
|
||||
|
|
|
@ -378,7 +378,7 @@
|
|||
background-color: $row-hover;
|
||||
}
|
||||
|
||||
.fa-spinner {
|
||||
.fa-refresh {
|
||||
font-size: 13px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
|
|
@ -23,16 +23,6 @@
|
|||
.merge-manually {
|
||||
@extend .fixed-width-container;
|
||||
}
|
||||
|
||||
.merge-request-tabs-holder {
|
||||
&.affix {
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
.nav-links {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.merge-request-details {
|
||||
|
@ -206,7 +196,7 @@
|
|||
transition: width .3s;
|
||||
background: $gray-light;
|
||||
padding: 10px 20px;
|
||||
z-index: 2;
|
||||
z-index: 200;
|
||||
|
||||
&.right-sidebar-expanded {
|
||||
width: $gutter_width;
|
||||
|
|
|
@ -121,6 +121,7 @@
|
|||
|
||||
.dropdown-menu {
|
||||
margin-top: 11px;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.ci-action-icon-wrapper {
|
||||
|
@ -690,8 +691,9 @@
|
|||
|
||||
.merge-request-tabs-holder {
|
||||
top: $header-height;
|
||||
z-index: 10;
|
||||
z-index: 100;
|
||||
background-color: $white-light;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
@media(min-width: $screen-sm-min) {
|
||||
position: sticky;
|
||||
|
@ -711,6 +713,16 @@
|
|||
padding-right: $gl-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.merge-request-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.limit-container-width {
|
||||
|
@ -721,6 +733,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
.merge-request-tabs-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.limit-container-width:not(.container-limited) {
|
||||
.merge-request-tabs-holder:not(.affix) {
|
||||
.merge-request-tabs-container {
|
||||
|
|
|
@ -609,6 +609,15 @@ ul.notes {
|
|||
}
|
||||
|
||||
.line-resolve-all-container {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
margin-right: 0;
|
||||
padding-left: $gl-padding;
|
||||
}
|
||||
|
||||
> div {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
before_action :redirect_to_external_issue_tracker, only: [:index, :new]
|
||||
before_action :module_enabled
|
||||
before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
|
||||
:related_branches, :can_create_branch, :rendered_title, :create_merge_request]
|
||||
:related_branches, :can_create_branch, :realtime_changes, :create_merge_request]
|
||||
|
||||
# Allow read any issue
|
||||
before_action :authorize_read_issue!, only: [:show, :rendered_title]
|
||||
before_action :authorize_read_issue!, only: [:show, :realtime_changes]
|
||||
|
||||
# Allow write(create) issue
|
||||
before_action :authorize_create_issue!, only: [:new, :create]
|
||||
|
@ -199,7 +199,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def rendered_title
|
||||
def realtime_changes
|
||||
Gitlab::PollingInterval.set_header(response, interval: 3_000)
|
||||
|
||||
render json: {
|
||||
|
|
0
app/controllers/projects/merge_requests_controller.rb
Executable file → Normal file
0
app/controllers/projects/merge_requests_controller.rb
Executable file → Normal file
74
app/finders/users_finder.rb
Normal file
74
app/finders/users_finder.rb
Normal file
|
@ -0,0 +1,74 @@
|
|||
# UsersFinder
|
||||
#
|
||||
# Used to filter users by set of params
|
||||
#
|
||||
# Arguments:
|
||||
# current_user - which user use
|
||||
# params:
|
||||
# username: string
|
||||
# extern_uid: string
|
||||
# provider: string
|
||||
# search: string
|
||||
# active: boolean
|
||||
# blocked: boolean
|
||||
# external: boolean
|
||||
#
|
||||
class UsersFinder
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def initialize(current_user, params = {})
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
end
|
||||
|
||||
def execute
|
||||
users = User.all
|
||||
users = by_username(users)
|
||||
users = by_search(users)
|
||||
users = by_blocked(users)
|
||||
users = by_active(users)
|
||||
users = by_external_identity(users)
|
||||
users = by_external(users)
|
||||
|
||||
users
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def by_username(users)
|
||||
return users unless params[:username]
|
||||
|
||||
users.where(username: params[:username])
|
||||
end
|
||||
|
||||
def by_search(users)
|
||||
return users unless params[:search].present?
|
||||
|
||||
users.search(params[:search])
|
||||
end
|
||||
|
||||
def by_blocked(users)
|
||||
return users unless params[:blocked]
|
||||
|
||||
users.blocked
|
||||
end
|
||||
|
||||
def by_active(users)
|
||||
return users unless params[:active]
|
||||
|
||||
users.active
|
||||
end
|
||||
|
||||
def by_external_identity(users)
|
||||
return users unless current_user.admin? && params[:extern_uid] && params[:provider]
|
||||
|
||||
users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid]))
|
||||
end
|
||||
|
||||
def by_external(users)
|
||||
return users = users.where.not(external: true) unless current_user.admin?
|
||||
return users unless params[:external]
|
||||
|
||||
users.external
|
||||
end
|
||||
end
|
|
@ -7,6 +7,10 @@ module SubmoduleHelper
|
|||
def submodule_links(submodule_item, ref = nil, repository = @repository)
|
||||
url = repository.submodule_url_for(ref, submodule_item.path)
|
||||
|
||||
if url == '.' || url == './'
|
||||
url = File.join(Gitlab.config.gitlab.url, @project.full_path)
|
||||
end
|
||||
|
||||
if url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/
|
||||
namespace, project = $1, $2
|
||||
project.sub!(/\.git\z/, '')
|
||||
|
|
|
@ -292,7 +292,7 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def expire_etag_cache
|
||||
key = Gitlab::Routing.url_helpers.rendered_title_namespace_project_issue_path(
|
||||
key = Gitlab::Routing.url_helpers.realtime_changes_namespace_project_issue_path(
|
||||
project.namespace,
|
||||
project,
|
||||
self
|
||||
|
|
|
@ -1163,8 +1163,6 @@ class Repository
|
|||
@project.repository_storage_path
|
||||
end
|
||||
|
||||
delegate :gitaly_channel, :gitaly_repository, to: :raw_repository
|
||||
|
||||
def initialize_raw_repository
|
||||
Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git')
|
||||
end
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
- else
|
||||
= build.id
|
||||
- if build.retried?
|
||||
%i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
|
||||
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
|
||||
|
||||
:javascript
|
||||
new Sidebar();
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true
|
||||
|
||||
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
|
||||
.merge-request-tabs-container.scrolling-tabs-container.inner-page-scroll-tabs
|
||||
.merge-request-tabs-container
|
||||
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
|
||||
.fade-left= icon('angle-left')
|
||||
.fade-right= icon('angle-right')
|
||||
%ul.merge-request-tabs.nav-links.scrolling-tabs
|
||||
.nav-links.scrolling-tabs
|
||||
%ul.merge-request-tabs
|
||||
%li.notes-tab
|
||||
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
|
||||
Discussion
|
||||
|
@ -49,7 +51,7 @@
|
|||
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
|
||||
Changes
|
||||
%span.badge= @merge_request.diff_size
|
||||
%li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
|
||||
#resolve-count-app.line-resolve-all-container.prepend-top-10{ "v-cloak" => true }
|
||||
%resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
|
||||
%div
|
||||
.line-resolve-all{ "v-show" => "discussionCount > 0",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
dropdown_class: "filtered-search-history-dropdown",
|
||||
content_class: "filtered-search-history-dropdown-content",
|
||||
title: "Recent searches" }) do
|
||||
.js-filtered-search-history-dropdown
|
||||
.js-filtered-search-history-dropdown{ data: { project_full_path: @project.full_path } }
|
||||
.filtered-search-box-input-container
|
||||
.scroll-container
|
||||
%ul.tokens-container.list-unstyled
|
||||
|
|
4
changelogs/unreleased/31106-tabs-alignment.yml
Normal file
4
changelogs/unreleased/31106-tabs-alignment.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: prevent nav tabs from wrapping to new line
|
||||
merge_request:
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Remove spinner from loading comment
|
||||
merge_request:
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Scope issue/merge request recent searches to project
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/adam-influxdb-hostname.yml
Normal file
4
changelogs/unreleased/adam-influxdb-hostname.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Allow GitLab instance to start when InfluxDB hostname cannot be resolved
|
||||
merge_request: 11356
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add indices for auto_canceled_by_id for ci_pipelines and ci_builds on PostgreSQL
|
||||
merge_request: 11034
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Enable cancelling non-HEAD pending pipelines by default for all projects
|
||||
merge_request: 11023
|
||||
author:
|
4
changelogs/unreleased/fix-github-import.yml
Normal file
4
changelogs/unreleased/fix-github-import.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Fix token interpolation when setting the Github remote
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/omega-submodules.yml
Normal file
4
changelogs/unreleased/omega-submodules.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 'Repository browser: handle in-repository submodule urls'
|
||||
merge_request:
|
||||
author: David Turner
|
|
@ -16,7 +16,7 @@ Rails.application.configure do
|
|||
# config.assets.css_compressor = :sass
|
||||
|
||||
# Don't fallback to assets pipeline if a precompiled asset is missed
|
||||
config.assets.compile = true
|
||||
config.assets.compile = false
|
||||
|
||||
# Generate digests for assets URLs
|
||||
config.assets.digest = true
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
require 'uri'
|
||||
|
||||
# Make sure we initialize our Gitaly channels before Sidekiq starts multi-threaded execution.
|
||||
if Gitlab.config.gitaly.enabled || Rails.env.test?
|
||||
Gitlab::GitalyClient.configure_channels
|
||||
Gitlab.config.repositories.storages.keys.each do |storage|
|
||||
# Force validation of each address
|
||||
Gitlab::GitalyClient.address(storage)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -244,7 +244,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
get :referenced_merge_requests
|
||||
get :related_branches
|
||||
get :can_create_branch
|
||||
get :rendered_title
|
||||
get :realtime_changes
|
||||
post :create_merge_request
|
||||
end
|
||||
collection do
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class MakeAutoCancelPendingPipelinesOnByDefault < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
change_column_default(:projects, :auto_cancel_pending_pipelines, 1)
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_default(:projects, :auto_cancel_pending_pipelines, 0)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
class CreateIndexCiPipelinesAutoCanceledById < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# MySQL would already have the index
|
||||
unless index_exists?(:ci_pipelines, :auto_canceled_by_id)
|
||||
add_concurrent_index(:ci_pipelines, :auto_canceled_by_id)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# We cannot remove index for MySQL because it's needed for foreign key
|
||||
if Gitlab::Database.postgresql?
|
||||
remove_concurrent_index(:ci_pipelines, :auto_canceled_by_id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
class CreateIndexCiBuildsAutoCanceledById < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# MySQL would already have the index
|
||||
unless index_exists?(:ci_builds, :auto_canceled_by_id)
|
||||
add_concurrent_index(:ci_builds, :auto_canceled_by_id)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# We cannot remove index for MySQL because it's needed for foreign key
|
||||
if Gitlab::Database.postgresql?
|
||||
remove_concurrent_index(:ci_builds, :auto_canceled_by_id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class EnableAutoCancelPendingPipelinesForAll < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
|
||||
end
|
||||
|
||||
def down
|
||||
# Nothing we can do!
|
||||
end
|
||||
end
|
|
@ -235,6 +235,7 @@ ActiveRecord::Schema.define(version: 20170508190732) do
|
|||
t.boolean "retried"
|
||||
end
|
||||
|
||||
add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
|
||||
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
|
||||
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
|
||||
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
|
||||
|
@ -284,6 +285,7 @@ ActiveRecord::Schema.define(version: 20170508190732) do
|
|||
t.integer "pipeline_schedule_id"
|
||||
end
|
||||
|
||||
add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
|
||||
add_index "ci_pipelines", ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
|
||||
add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree
|
||||
add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
|
||||
|
@ -986,7 +988,7 @@ ActiveRecord::Schema.define(version: 20170508190732) do
|
|||
t.text "description_html"
|
||||
t.boolean "only_allow_merge_if_all_discussions_are_resolved"
|
||||
t.boolean "printing_merge_request_link_enabled", default: true, null: false
|
||||
t.integer "auto_cancel_pending_pipelines", default: 0, null: false
|
||||
t.integer "auto_cancel_pending_pipelines", default: 1, null: false
|
||||
t.string "import_jid"
|
||||
t.integer "cached_markdown_version"
|
||||
t.datetime "last_repository_updated_at"
|
||||
|
|
66
doc/articles/how_to_install_git/index.md
Normal file
66
doc/articles/how_to_install_git/index.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Installing Git
|
||||
|
||||
> **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
|
||||
|
||||
To begin contributing to GitLab projects
|
||||
you will need to install the Git client on your computer.
|
||||
This article will show you how to install Git on macOS, Ubuntu Linux and Windows.
|
||||
|
||||
## Install Git on macOS using the Homebrew package manager
|
||||
|
||||
Although it is easy to use the version of Git shipped with macOS
|
||||
or install the latest version of Git on macOS by downloading it from the project website,
|
||||
we recommend installing it via Homebrew to get access to
|
||||
an extensive selection of dependancy managed libraries and applications.
|
||||
|
||||
If you are sure you don't need access to any additional development libraries
|
||||
or don't have approximately 15gb of available disk space for Xcode and Homebrew
|
||||
use one of the the aforementioned methods.
|
||||
|
||||
### Installing Xcode
|
||||
|
||||
Xcode is needed by Homebrew to build dependencies.
|
||||
You can install [XCode](https://developer.apple.com/xcode/)
|
||||
through the macOS App Store.
|
||||
|
||||
### Installing Homebrew
|
||||
|
||||
Once Xcode is installed browse to the [Homebrew website](http://brew.sh/index.html)
|
||||
for the official Homebrew installation instructions.
|
||||
|
||||
### Installing Git via Homebrew
|
||||
|
||||
With Homebrew installed you are now ready to install Git.
|
||||
Open a Terminal and enter in the following command:
|
||||
|
||||
```bash
|
||||
brew install git
|
||||
```
|
||||
|
||||
Congratulations you should now have Git installed via Homebrew.
|
||||
Next read our article on [adding an SSH key to GitLab](../../ssh/README.md).
|
||||
|
||||
## Install Git on Ubuntu Linux
|
||||
|
||||
On Ubuntu and other Linux operating systems
|
||||
it is recommended to use the built in package manager to install Git.
|
||||
|
||||
Open a Terminal and enter in the following commands
|
||||
to install the latest Git from the official Git maintained package archives:
|
||||
|
||||
```bash
|
||||
sudo apt-add-repository ppa:git-core/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install git
|
||||
```
|
||||
|
||||
Congratulations you should now have Git installed via the Ubuntu package manager.
|
||||
Next read our article on [adding an SSH key to GitLab](../../ssh/README.md).
|
||||
|
||||
## Installing Git on Windows from the Git website
|
||||
|
||||
Browse to the [Git website](https://git-scm.com/) and download and install Git for Windows.
|
||||
Next read our article on [adding an SSH key to GitLab](../../ssh/README.md).
|
|
@ -12,6 +12,10 @@ They are written by members of the GitLab Team and by
|
|||
- **LDAP**
|
||||
- [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md)
|
||||
|
||||
## Git
|
||||
|
||||
- [How to install Git](how_to_install_git/index.md)
|
||||
|
||||
## GitLab Pages
|
||||
|
||||
- **GitLab Pages from A to Z**
|
||||
|
|
|
@ -155,7 +155,7 @@ Find more information about different Runners in the
|
|||
[Runners](../runners/README.md) documentation.
|
||||
|
||||
You can find whether any Runners are assigned to your project by going to
|
||||
**Settings ➔ Runners**. Setting up a Runner is easy and straightforward. The
|
||||
**Settings ➔ CI/CD Pipelines**. Setting up a Runner is easy and straightforward. The
|
||||
official Runner supported by GitLab is written in Go and its documentation
|
||||
can be found at <https://docs.gitlab.com/runner/>.
|
||||
|
||||
|
@ -168,7 +168,7 @@ Follow the links above to set up your own Runner or use a Shared Runner as
|
|||
described in the next section.
|
||||
|
||||
Once the Runner has been set up, you should see it on the Runners page of your
|
||||
project, following **Settings ➔ Runners**.
|
||||
project, following **Settings ➔ CI/CD Pipelines**.
|
||||
|
||||
![Activated runners](img/runners_activated.png)
|
||||
|
||||
|
@ -181,7 +181,7 @@ These are special virtual machines that run on GitLab's infrastructure and can
|
|||
build any project.
|
||||
|
||||
To enable the **Shared Runners** you have to go to your project's
|
||||
**Settings ➔ Runners** and click **Enable shared runners**.
|
||||
**Settings ➔ CI/CD Pipelines** and click **Enable shared runners**.
|
||||
|
||||
[Read more on Shared Runners](../runners/README.md).
|
||||
|
||||
|
|
|
@ -392,7 +392,7 @@ Once you [have configured](#configuration) GitLab in your `values.yml` file,
|
|||
run the following:
|
||||
|
||||
```bash
|
||||
helm install --namepace <NAMEPACE> --name gitlab -f <CONFIG_VALUES_FILE> gitlab/gitlab
|
||||
helm install --namespace <NAMESPACE> --name gitlab -f <CONFIG_VALUES_FILE> gitlab/gitlab
|
||||
```
|
||||
|
||||
where:
|
||||
|
@ -407,7 +407,7 @@ Once your GitLab Chart is installed, configuration changes and chart updates
|
|||
should we done using `helm upgrade`
|
||||
|
||||
```bash
|
||||
helm upgrade --namepace <NAMEPACE> -f <CONFIG_VALUES_FILE> <RELEASE-NAME> gitlab/gitlab
|
||||
helm upgrade --namespace <NAMESPACE> -f <CONFIG_VALUES_FILE> <RELEASE-NAME> gitlab/gitlab
|
||||
```
|
||||
|
||||
where:
|
||||
|
|
|
@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
|
|||
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
|
||||
- [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase)
|
||||
- **Articles:**
|
||||
- [How to install Git](../../articles/how_to_install_git/index.md)
|
||||
- [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
|
||||
- [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
|
||||
- **Presentations:**
|
||||
|
|
|
@ -56,16 +56,7 @@ module API
|
|||
|
||||
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
|
||||
|
||||
users = User.all
|
||||
users = User.where(username: params[:username]) if params[:username]
|
||||
users = users.active if params[:active]
|
||||
users = users.search(params[:search]) if params[:search].present?
|
||||
users = users.blocked if params[:blocked]
|
||||
|
||||
if current_user.admin?
|
||||
users = users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid])) if params[:extern_uid] && params[:provider]
|
||||
users = users.external if params[:external]
|
||||
end
|
||||
users = UsersFinder.new(current_user, params).execute
|
||||
|
||||
entity = current_user.admin? ? Entities::UserPublic : Entities::UserBasic
|
||||
present paginate(users), with: entity
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require_relative 'error'
|
||||
|
||||
module Github
|
||||
class Import
|
||||
include Gitlab::ShellAdapter
|
||||
|
@ -6,6 +7,7 @@ module Github
|
|||
class MergeRequest < ::MergeRequest
|
||||
self.table_name = 'merge_requests'
|
||||
|
||||
self.reset_callbacks :create
|
||||
self.reset_callbacks :save
|
||||
self.reset_callbacks :commit
|
||||
self.reset_callbacks :update
|
||||
|
@ -16,6 +18,7 @@ module Github
|
|||
self.table_name = 'issues'
|
||||
|
||||
self.reset_callbacks :save
|
||||
self.reset_callbacks :create
|
||||
self.reset_callbacks :commit
|
||||
self.reset_callbacks :update
|
||||
self.reset_callbacks :validate
|
||||
|
@ -79,7 +82,7 @@ module Github
|
|||
def fetch_repository
|
||||
begin
|
||||
project.create_repository unless project.repository.exists?
|
||||
project.repository.add_remote('github', "https://{options.fetch(:token)}@github.com/#{repo}.git")
|
||||
project.repository.add_remote('github', "https://#{options.fetch(:token)}@github.com/#{repo}.git")
|
||||
project.repository.set_remote_as_mirror('github')
|
||||
project.repository.fetch_remote('github', forced: true)
|
||||
rescue Gitlab::Shell::Error => e
|
||||
|
|
|
@ -283,7 +283,6 @@ module Gitlab
|
|||
|
||||
add_column(table, new, new_type,
|
||||
limit: old_col.limit,
|
||||
null: old_col.null,
|
||||
precision: old_col.precision,
|
||||
scale: old_col.scale)
|
||||
|
||||
|
@ -307,6 +306,8 @@ module Gitlab
|
|||
|
||||
update_column_in_batches(table, new, Arel::Table.new(table)[old])
|
||||
|
||||
change_column_null(table, new, false) unless old_col.null
|
||||
|
||||
copy_indexes(table, old, new)
|
||||
copy_foreign_keys(table, old, new)
|
||||
end
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class BaseLinker
|
||||
def self.link(plain_text, highlighted_text)
|
||||
new(plain_text, highlighted_text).link
|
||||
class_attribute :file_type
|
||||
|
||||
def self.support?(blob_name)
|
||||
Gitlab::FileDetector.type_of(blob_name) == file_type
|
||||
end
|
||||
|
||||
def self.link(*args)
|
||||
new(*args).link
|
||||
end
|
||||
|
||||
attr_accessor :plain_text, :highlighted_text
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class GemfileLinker < BaseLinker
|
||||
def self.support?(blob_name)
|
||||
blob_name == 'Gemfile' || blob_name == 'gems.rb'
|
||||
end
|
||||
self.file_type = :gemfile
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ module Gitlab
|
|||
# - Don't contain a reserved word (expect for the words used in the
|
||||
# regex itself)
|
||||
# - Ending in `noteable/issue/<id>/notes` for the `issue_notes` route
|
||||
# - Ending in `issues/id`/rendered_title` for the `issue_title` route
|
||||
USED_IN_ROUTES = %w[noteable issue notes issues rendered_title
|
||||
# - Ending in `issues/id`/realtime_changes` for the `issue_title` route
|
||||
USED_IN_ROUTES = %w[noteable issue notes issues realtime_changes
|
||||
commit pipelines merge_requests new].freeze
|
||||
RESERVED_WORDS = DynamicPathValidator::WILDCARD_ROUTES - USED_IN_ROUTES
|
||||
RESERVED_WORDS_REGEX = Regexp.union(*RESERVED_WORDS)
|
||||
|
@ -18,7 +18,7 @@ module Gitlab
|
|||
'issue_notes'
|
||||
),
|
||||
Gitlab::EtagCaching::Router::Route.new(
|
||||
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/issues/\d+/rendered_title\z),
|
||||
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/issues/\d+/realtime_changes\z),
|
||||
'issue_title'
|
||||
),
|
||||
Gitlab::EtagCaching::Router::Route.new(
|
||||
|
|
|
@ -12,6 +12,7 @@ module Gitlab
|
|||
version: 'version',
|
||||
gitignore: '.gitignore',
|
||||
koding: '.koding.yml',
|
||||
gemfile: /\A(Gemfile|gems\.rb)\z/,
|
||||
gitlab_ci: '.gitlab-ci.yml',
|
||||
avatar: /\Alogo\.(png|jpg|gif)\z/,
|
||||
route_map: 'route-map.yml'
|
||||
|
|
|
@ -27,13 +27,15 @@ module Gitlab
|
|||
# Rugged repo object
|
||||
attr_reader :rugged
|
||||
|
||||
attr_reader :storage
|
||||
|
||||
# 'path' must be the path to a _bare_ git repository, e.g.
|
||||
# /path/to/my-repo.git
|
||||
def initialize(repository_storage, relative_path)
|
||||
@repository_storage = repository_storage
|
||||
def initialize(storage, relative_path)
|
||||
@storage = storage
|
||||
@relative_path = relative_path
|
||||
|
||||
storage_path = Gitlab.config.repositories.storages[@repository_storage]['path']
|
||||
storage_path = Gitlab.config.repositories.storages[@storage]['path']
|
||||
@path = File.join(storage_path, @relative_path)
|
||||
@name = @relative_path.split("/").last
|
||||
@attributes = Gitlab::Git::Attributes.new(path)
|
||||
|
@ -114,7 +116,7 @@ module Gitlab
|
|||
|
||||
# Returns the number of valid branches
|
||||
def branch_count
|
||||
Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled|
|
||||
gitaly_migrate(:branch_names) do |is_enabled|
|
||||
if is_enabled
|
||||
gitaly_ref_client.count_branch_names
|
||||
else
|
||||
|
@ -133,7 +135,7 @@ module Gitlab
|
|||
|
||||
# Returns the number of valid tags
|
||||
def tag_count
|
||||
Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled|
|
||||
gitaly_migrate(:tag_names) do |is_enabled|
|
||||
if is_enabled
|
||||
gitaly_ref_client.count_tag_names
|
||||
else
|
||||
|
@ -471,7 +473,7 @@ module Gitlab
|
|||
def ref_name_for_sha(ref_path, sha)
|
||||
# NOTE: This feature is intentionally disabled until
|
||||
# https://gitlab.com/gitlab-org/gitaly/issues/180 is resolved
|
||||
# Gitlab::GitalyClient.migrate(:find_ref_name) do |is_enabled|
|
||||
# gitaly_migrate(:find_ref_name) do |is_enabled|
|
||||
# if is_enabled
|
||||
# gitaly_ref_client.find_ref_name(sha, ref_path)
|
||||
# else
|
||||
|
@ -965,11 +967,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def gitaly_repository
|
||||
Gitlab::GitalyClient::Util.repository(@repository_storage, @relative_path)
|
||||
end
|
||||
|
||||
def gitaly_channel
|
||||
Gitlab::GitalyClient.get_channel(@repository_storage)
|
||||
Gitlab::GitalyClient::Util.repository(@storage, @relative_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -4,56 +4,42 @@ module Gitlab
|
|||
module GitalyClient
|
||||
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
|
||||
|
||||
# This function is not thread-safe because it updates Hashes in instance variables.
|
||||
def self.configure_channels
|
||||
@addresses = {}
|
||||
@channels = {}
|
||||
Gitlab.config.repositories.storages.each do |name, params|
|
||||
MUTEX = Mutex.new
|
||||
private_constant :MUTEX
|
||||
|
||||
def self.stub(name, storage)
|
||||
MUTEX.synchronize do
|
||||
@stubs ||= {}
|
||||
@stubs[storage] ||= {}
|
||||
@stubs[storage][name] ||= begin
|
||||
klass = Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
|
||||
addr = address(storage)
|
||||
addr = addr.sub(%r{^tcp://}, '') if URI(addr).scheme == 'tcp'
|
||||
klass.new(addr, :this_channel_is_insecure)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.clear_stubs!
|
||||
MUTEX.synchronize do
|
||||
@stubs = nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.address(storage)
|
||||
params = Gitlab.config.repositories.storages[storage]
|
||||
raise "storage not found: #{storage.inspect}" if params.nil?
|
||||
|
||||
address = params['gitaly_address']
|
||||
unless address.present?
|
||||
raise "storage #{name.inspect} is missing a gitaly_address"
|
||||
raise "storage #{storage.inspect} is missing a gitaly_address"
|
||||
end
|
||||
|
||||
unless URI(address).scheme.in?(%w(tcp unix))
|
||||
raise "Unsupported Gitaly address: #{address.inspect} does not use URL scheme 'tcp' or 'unix'"
|
||||
end
|
||||
|
||||
@addresses[name] = address
|
||||
@channels[name] = new_channel(address)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new_channel(address)
|
||||
address = address.sub(%r{^tcp://}, '') if URI(address).scheme == 'tcp'
|
||||
# NOTE: When Gitaly runs on a Unix socket, permissions are
|
||||
# handled using the file system and no additional authentication is
|
||||
# required (therefore the :this_channel_is_insecure flag)
|
||||
# TODO: Add authentication support when Gitaly is running on a TCP socket.
|
||||
GRPC::Core::Channel.new(address, {}, :this_channel_is_insecure)
|
||||
end
|
||||
|
||||
def self.get_channel(storage)
|
||||
if !Rails.env.production? && @channels.nil?
|
||||
# In development mode the Rails auto-loader may reset the instance
|
||||
# variables of this class. What we do here is not thread-safe. In normal
|
||||
# circumstances (including production) these instance variables have
|
||||
# been initialized from config/initializers.
|
||||
configure_channels
|
||||
end
|
||||
|
||||
@channels[storage]
|
||||
end
|
||||
|
||||
def self.get_address(storage)
|
||||
if !Rails.env.production? && @addresses.nil?
|
||||
# In development mode the Rails auto-loader may reset the instance
|
||||
# variables of this class. What we do here is not thread-safe. In normal
|
||||
# circumstances (including development) these instance variables have
|
||||
# been initialized from config/initializers.
|
||||
configure_channels
|
||||
end
|
||||
|
||||
@addresses[storage]
|
||||
address
|
||||
end
|
||||
|
||||
def self.enabled?
|
||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def is_ancestor(ancestor_id, child_id)
|
||||
stub = Gitaly::Commit::Stub.new(nil, nil, channel_override: @repository.gitaly_channel)
|
||||
stub = GitalyClient.stub(:commit, @repository.storage)
|
||||
request = Gitaly::CommitIsAncestorRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
ancestor_id: ancestor_id,
|
||||
|
@ -52,7 +52,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def diff_service_stub
|
||||
Gitaly::Diff::Stub.new(nil, nil, channel_override: @repository.gitaly_channel)
|
||||
GitalyClient.stub(:diff, @repository.storage)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
# 'repository' is a Gitlab::Git::Repository
|
||||
def initialize(repository)
|
||||
@gitaly_repo = repository.gitaly_repository
|
||||
@stub = Gitaly::Notifications::Stub.new(nil, nil, channel_override: repository.gitaly_channel)
|
||||
@stub = GitalyClient.stub(:notifications, repository.storage)
|
||||
end
|
||||
|
||||
def post_receive
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
# 'repository' is a Gitlab::Git::Repository
|
||||
def initialize(repository)
|
||||
@gitaly_repo = repository.gitaly_repository
|
||||
@stub = Gitaly::Ref::Stub.new(nil, nil, channel_override: repository.gitaly_channel)
|
||||
@stub = GitalyClient.stub(:ref, repository.storage)
|
||||
end
|
||||
|
||||
def default_branch_name
|
||||
|
|
|
@ -49,6 +49,9 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::EADDRNOTAVAIL, SocketError => ex
|
||||
Gitlab::EnvironmentLogger.error('Cannot resolve InfluxDB address. GitLab Performance Monitoring will not work.')
|
||||
Gitlab::EnvironmentLogger.error(ex)
|
||||
end
|
||||
|
||||
def self.prepare_metrics(metrics)
|
||||
|
|
|
@ -26,7 +26,7 @@ module Gitlab
|
|||
}
|
||||
|
||||
if Gitlab.config.gitaly.enabled
|
||||
address = Gitlab::GitalyClient.get_address(project.repository_storage)
|
||||
address = Gitlab::GitalyClient.address(project.repository_storage)
|
||||
params[:Repository] = repository.gitaly_repository.to_h
|
||||
|
||||
feature_enabled = case action.to_s
|
||||
|
|
|
@ -5,14 +5,7 @@ describe 'Auto deploy' do
|
|||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
project.create_kubernetes_service(
|
||||
active: true,
|
||||
properties: {
|
||||
namespace: project.path,
|
||||
api_url: 'https://kubernetes.example.com',
|
||||
token: 'a' * 40
|
||||
}
|
||||
)
|
||||
create :kubernetes_service, project: project
|
||||
project.team << [user, :master]
|
||||
login_as user
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
|
|||
end
|
||||
|
||||
it 'shows a button to resolve all discussions by creating a new issue' do
|
||||
within('li#resolve-count-app') do
|
||||
within('#resolve-count-app') do
|
||||
expect(page).to have_link "Resolve all discussions in new issue", href: new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,17 +3,17 @@ require 'spec_helper'
|
|||
describe 'Recent searches', js: true, feature: true do
|
||||
include FilteredSearchHelpers
|
||||
|
||||
let!(:group) { create(:group) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:user) { create(:user) }
|
||||
let(:project_1) { create(:empty_project, :public) }
|
||||
let(:project_2) { create(:empty_project, :public) }
|
||||
let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" }
|
||||
|
||||
before do
|
||||
Capybara.ignore_hidden_elements = false
|
||||
project.add_master(user)
|
||||
group.add_developer(user)
|
||||
create(:issue, project: project)
|
||||
login_as(user)
|
||||
create(:issue, project: project_1)
|
||||
create(:issue, project: project_2)
|
||||
|
||||
# Visit any fast-loading page so we can clear local storage without a DOM exception
|
||||
visit '/404'
|
||||
remove_recent_searches
|
||||
end
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe 'Recent searches', js: true, feature: true do
|
|||
end
|
||||
|
||||
it 'searching adds to recent searches' do
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1)
|
||||
|
||||
input_filtered_search('foo', submit: true)
|
||||
input_filtered_search('bar', submit: true)
|
||||
|
@ -35,8 +35,8 @@ describe 'Recent searches', js: true, feature: true do
|
|||
end
|
||||
|
||||
it 'visiting URL with search params adds to recent searches' do
|
||||
visit namespace_project_issues_path(project.namespace, project, label_name: 'foo', search: 'bar')
|
||||
visit namespace_project_issues_path(project.namespace, project, label_name: 'qux', search: 'garply')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1, label_name: 'foo', search: 'bar')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1, label_name: 'qux', search: 'garply')
|
||||
|
||||
items = all('.filtered-search-history-dropdown-item', visible: false)
|
||||
|
||||
|
@ -46,9 +46,9 @@ describe 'Recent searches', js: true, feature: true do
|
|||
end
|
||||
|
||||
it 'saved recent searches are restored last on the list' do
|
||||
set_recent_searches('["saved1", "saved2"]')
|
||||
set_recent_searches(project_1_local_storage_key, '["saved1", "saved2"]')
|
||||
|
||||
visit namespace_project_issues_path(project.namespace, project, search: 'foo')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1, search: 'foo')
|
||||
|
||||
items = all('.filtered-search-history-dropdown-item', visible: false)
|
||||
|
||||
|
@ -58,9 +58,27 @@ describe 'Recent searches', js: true, feature: true do
|
|||
expect(items[2].text).to eq('saved2')
|
||||
end
|
||||
|
||||
it 'searches are scoped to projects' do
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1)
|
||||
|
||||
input_filtered_search('foo', submit: true)
|
||||
input_filtered_search('bar', submit: true)
|
||||
|
||||
visit namespace_project_issues_path(project_2.namespace, project_2)
|
||||
|
||||
input_filtered_search('more', submit: true)
|
||||
input_filtered_search('things', submit: true)
|
||||
|
||||
items = all('.filtered-search-history-dropdown-item', visible: false)
|
||||
|
||||
expect(items.count).to eq(2)
|
||||
expect(items[0].text).to eq('things')
|
||||
expect(items[1].text).to eq('more')
|
||||
end
|
||||
|
||||
it 'clicking item fills search input' do
|
||||
set_recent_searches('["foo", "bar"]')
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1)
|
||||
|
||||
all('.filtered-search-history-dropdown-item', visible: false)[0].trigger('click')
|
||||
wait_for_filtered_search('foo')
|
||||
|
@ -69,8 +87,8 @@ describe 'Recent searches', js: true, feature: true do
|
|||
end
|
||||
|
||||
it 'clear recent searches button, clears recent searches' do
|
||||
set_recent_searches('["foo"]')
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
set_recent_searches(project_1_local_storage_key, '["foo"]')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1)
|
||||
|
||||
items_before = all('.filtered-search-history-dropdown-item', visible: false)
|
||||
|
||||
|
@ -83,8 +101,8 @@ describe 'Recent searches', js: true, feature: true do
|
|||
end
|
||||
|
||||
it 'shows flash error when failed to parse saved history' do
|
||||
set_recent_searches('fail')
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
set_recent_searches(project_1_local_storage_key, 'fail')
|
||||
visit namespace_project_issues_path(project_1.namespace, project_1)
|
||||
|
||||
expect(find('.flash-alert')).to have_text('An error occured while parsing recent searches')
|
||||
end
|
||||
|
|
66
spec/finders/users_finder_spec.rb
Normal file
66
spec/finders/users_finder_spec.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe UsersFinder do
|
||||
describe '#execute' do
|
||||
let!(:user1) { create(:user, username: 'johndoe') }
|
||||
let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
|
||||
let!(:external_user) { create(:user, :external) }
|
||||
let!(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
|
||||
|
||||
context 'with a normal user' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it 'returns all users' do
|
||||
users = described_class.new(user).execute
|
||||
|
||||
expect(users).to contain_exactly(user, user1, user2, omniauth_user)
|
||||
end
|
||||
|
||||
it 'filters by username' do
|
||||
users = described_class.new(user, username: 'johndoe').execute
|
||||
|
||||
expect(users).to contain_exactly(user1)
|
||||
end
|
||||
|
||||
it 'filters by search' do
|
||||
users = described_class.new(user, search: 'orando').execute
|
||||
|
||||
expect(users).to contain_exactly(user2)
|
||||
end
|
||||
|
||||
it 'filters by blocked users' do
|
||||
users = described_class.new(user, blocked: true).execute
|
||||
|
||||
expect(users).to contain_exactly(user2)
|
||||
end
|
||||
|
||||
it 'filters by active users' do
|
||||
users = described_class.new(user, active: true).execute
|
||||
|
||||
expect(users).to contain_exactly(user, user1, omniauth_user)
|
||||
end
|
||||
|
||||
it 'returns no external users' do
|
||||
users = described_class.new(user, external: true).execute
|
||||
|
||||
expect(users).to contain_exactly(user, user1, user2, omniauth_user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an admin user' do
|
||||
let(:admin) { create(:admin) }
|
||||
|
||||
it 'filters by external users' do
|
||||
users = described_class.new(admin, external: true).execute
|
||||
|
||||
expect(users).to contain_exactly(external_user)
|
||||
end
|
||||
|
||||
it 'returns all users' do
|
||||
users = described_class.new(admin).execute
|
||||
|
||||
expect(users).to contain_exactly(admin, user1, user2, external_user, omniauth_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -81,6 +81,19 @@ describe SubmoduleHelper do
|
|||
end
|
||||
end
|
||||
|
||||
context 'in-repository submodule' do
|
||||
let(:group) { create(:group, name: "Master Project", path: "master-project") }
|
||||
let(:project) { create(:empty_project, group: group) }
|
||||
before do
|
||||
self.instance_variable_set(:@project, project)
|
||||
end
|
||||
|
||||
it 'in-repository' do
|
||||
stub_url('./')
|
||||
expect(submodule_links(submodule_item)).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'submodule on gitlab.com' do
|
||||
it 'detects ssh' do
|
||||
stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git')
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('Issuable output', () => {
|
|||
propsData: {
|
||||
canUpdate: true,
|
||||
canDestroy: true,
|
||||
endpoint: '/gitlab-org/gitlab-shell/issues/9/rendered_title',
|
||||
endpoint: '/gitlab-org/gitlab-shell/issues/9/realtime_changes',
|
||||
issuableRef: '#1',
|
||||
initialTitle: '',
|
||||
initialDescriptionHtml: '',
|
||||
|
|
|
@ -377,7 +377,7 @@ import '~/notes';
|
|||
});
|
||||
|
||||
it('should return true when comment begins with a slash command', () => {
|
||||
const sampleComment = '/wip \n/milestone %1.0 \n/merge \n/unassign Merging this';
|
||||
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
|
||||
const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
|
||||
|
||||
expect(hasSlashCommands).toBeTruthy();
|
||||
|
@ -401,10 +401,18 @@ import '~/notes';
|
|||
describe('stripSlashCommands', () => {
|
||||
it('should strip slash commands from the comment which begins with a slash command', () => {
|
||||
this.notes = new Notes();
|
||||
const sampleComment = '/wip \n/milestone %1.0 \n/merge \n/unassign Merging this';
|
||||
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
|
||||
const stripedComment = this.notes.stripSlashCommands(sampleComment);
|
||||
|
||||
expect(stripedComment).not.toBe(sampleComment);
|
||||
expect(stripedComment).toBe('');
|
||||
});
|
||||
|
||||
it('should strip slash commands from the comment but leaves plain comment if it is present', () => {
|
||||
this.notes = new Notes();
|
||||
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
|
||||
const stripedComment = this.notes.stripSlashCommands(sampleComment);
|
||||
|
||||
expect(stripedComment).toBe('Merging this');
|
||||
});
|
||||
|
||||
it('should NOT strip string that has slashes within', () => {
|
||||
|
@ -424,6 +432,22 @@ import '~/notes';
|
|||
|
||||
beforeEach(() => {
|
||||
this.notes = new Notes('', []);
|
||||
spyOn(_, 'escape').and.callFake((comment) => {
|
||||
const escapedString = comment.replace(/["&'<>]/g, (a) => {
|
||||
const escapedToken = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'`': '`'
|
||||
}[a];
|
||||
|
||||
return escapedToken;
|
||||
});
|
||||
|
||||
return escapedString;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return constructed placeholder element for regular note based on form contents', () => {
|
||||
|
@ -444,7 +468,21 @@ import '~/notes';
|
|||
expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy();
|
||||
expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual(currentUserFullname);
|
||||
expect($tempNoteHeader.find('.note-headline-light').text().trim()).toEqual(`@${currentUsername}`);
|
||||
expect($tempNote.find('.note-body .note-text').text().trim()).toEqual(sampleComment);
|
||||
expect($tempNote.find('.note-body .note-text p').text().trim()).toEqual(sampleComment);
|
||||
});
|
||||
|
||||
it('should escape HTML characters from note based on form contents', () => {
|
||||
const commentWithHtml = '<script>alert("Boom!");</script>';
|
||||
const $tempNote = this.notes.createPlaceholderNote({
|
||||
formContent: commentWithHtml,
|
||||
uniqueId,
|
||||
isDiscussionNote: false,
|
||||
currentUsername,
|
||||
currentUserFullname
|
||||
});
|
||||
|
||||
expect(_.escape).toHaveBeenCalledWith(commentWithHtml);
|
||||
expect($tempNote.find('.note-body .note-text p').html()).toEqual('<script>alert("Boom!");</script>');
|
||||
});
|
||||
|
||||
it('should return constructed placeholder element for discussion note based on form contents', () => {
|
||||
|
|
|
@ -382,7 +382,6 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
|
|||
expect(model).to receive(:add_column).
|
||||
with(:users, :new, :integer,
|
||||
limit: old_column.limit,
|
||||
null: old_column.null,
|
||||
precision: old_column.precision,
|
||||
scale: old_column.scale)
|
||||
|
||||
|
@ -391,6 +390,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
|
|||
|
||||
expect(model).to receive(:update_column_in_batches)
|
||||
|
||||
expect(model).to receive(:change_column_null).with(:users, :new, false)
|
||||
|
||||
expect(model).to receive(:copy_indexes).with(:users, :old, :new)
|
||||
expect(model).to receive(:copy_foreign_keys).with(:users, :old, :new)
|
||||
|
||||
|
@ -408,7 +409,6 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
|
|||
expect(model).to receive(:add_column).
|
||||
with(:users, :new, :integer,
|
||||
limit: old_column.limit,
|
||||
null: old_column.null,
|
||||
precision: old_column.precision,
|
||||
scale: old_column.scale)
|
||||
|
||||
|
@ -417,6 +417,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
|
|||
|
||||
expect(model).to receive(:update_column_in_batches)
|
||||
|
||||
expect(model).to receive(:change_column_null).with(:users, :new, false)
|
||||
|
||||
expect(model).to receive(:copy_indexes).with(:users, :old, :new)
|
||||
expect(model).to receive(:copy_foreign_keys).with(:users, :old, :new)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ describe Gitlab::EtagCaching::Router do
|
|||
|
||||
it 'matches issue title endpoint' do
|
||||
env = build_env(
|
||||
'/my-group/my-project/issues/123/rendered_title'
|
||||
'/my-group/my-project/issues/123/realtime_changes'
|
||||
)
|
||||
|
||||
result = described_class.match(env)
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::GitalyClient, lib: true do
|
||||
describe '.new_channel' do
|
||||
describe '.stub' do
|
||||
before { described_class.clear_stubs! }
|
||||
|
||||
context 'when passed a UNIX socket address' do
|
||||
it 'passes the address as-is to GRPC::Core::Channel initializer' do
|
||||
it 'passes the address as-is to GRPC' do
|
||||
address = 'unix:/tmp/gitaly.sock'
|
||||
allow(Gitlab.config.repositories).to receive(:storages).and_return({
|
||||
'default' => { 'gitaly_address' => address }
|
||||
})
|
||||
|
||||
expect(GRPC::Core::Channel).to receive(:new).with(address, any_args)
|
||||
expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args)
|
||||
|
||||
described_class.new_channel(address)
|
||||
described_class.stub(:commit, 'default')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -17,9 +22,13 @@ describe Gitlab::GitalyClient, lib: true do
|
|||
address = 'localhost:9876'
|
||||
prefixed_address = "tcp://#{address}"
|
||||
|
||||
expect(GRPC::Core::Channel).to receive(:new).with(address, any_args)
|
||||
allow(Gitlab.config.repositories).to receive(:storages).and_return({
|
||||
'default' => { 'gitaly_address' => prefixed_address }
|
||||
})
|
||||
|
||||
described_class.new_channel(prefixed_address)
|
||||
expect(Gitaly::Commit::Stub).to receive(:new).with(address, any_args)
|
||||
|
||||
described_class.stub(:commit, 'default')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -202,7 +202,7 @@ describe Gitlab::Workhorse, lib: true do
|
|||
context 'when Gitaly is enabled' do
|
||||
let(:gitaly_params) do
|
||||
{
|
||||
GitalyAddress: Gitlab::GitalyClient.get_address('default')
|
||||
GitalyAddress: Gitlab::GitalyClient.address('default')
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -73,11 +73,11 @@ module FilteredSearchHelpers
|
|||
end
|
||||
|
||||
def remove_recent_searches
|
||||
execute_script('window.localStorage.removeItem(\'issue-recent-searches\');')
|
||||
execute_script('window.localStorage.clear();')
|
||||
end
|
||||
|
||||
def set_recent_searches(input)
|
||||
execute_script("window.localStorage.setItem('issue-recent-searches', '#{input}');")
|
||||
def set_recent_searches(key, input)
|
||||
execute_script("window.localStorage.setItem('#{key}', '#{input}');")
|
||||
end
|
||||
|
||||
def wait_for_filtered_search(text)
|
||||
|
|
162
spec/support/generate-seed-repo-rb
Executable file
162
spec/support/generate-seed-repo-rb
Executable file
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# # generate-seed-repo-rb
|
||||
#
|
||||
# This script generates the seed_repo.rb file used by lib/gitlab/git
|
||||
# tests. The seed_repo.rb file needs to be updated anytime there is a
|
||||
# Git push to https://gitlab.com/gitlab-org/gitlab-git-test.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# ./spec/support/generate-seed-repo-rb > spec/support/seed_repo.rb
|
||||
#
|
||||
#
|
||||
|
||||
require 'erb'
|
||||
require 'tempfile'
|
||||
|
||||
SOURCE = 'https://gitlab.com/gitlab-org/gitlab-git-test.git'.freeze
|
||||
SCRIPT_NAME = 'generate-seed-repo-rb'.freeze
|
||||
REPO_NAME = 'gitlab-git-test.git'.freeze
|
||||
|
||||
def main
|
||||
Dir.mktmpdir do |dir|
|
||||
unless system(*%W[git clone --bare #{SOURCE} #{REPO_NAME}], chdir: dir)
|
||||
abort "git clone failed"
|
||||
end
|
||||
repo = File.join(dir, REPO_NAME)
|
||||
erb = ERB.new(DATA.read)
|
||||
erb.run(binding)
|
||||
end
|
||||
end
|
||||
|
||||
def capture!(cmd, dir)
|
||||
output = IO.popen(cmd, 'r', chdir: dir) { |io| io.read }
|
||||
raise "command failed with #{$?}: #{cmd.join(' ')}" unless $?.success?
|
||||
output.chomp
|
||||
end
|
||||
|
||||
main
|
||||
|
||||
__END__
|
||||
# This file is generated by <%= SCRIPT_NAME %>. Do not edit this file manually.
|
||||
#
|
||||
# Seed repo:
|
||||
<%= capture!(%w{git log --format=#\ %H\ %s}, repo) %>
|
||||
|
||||
module SeedRepo
|
||||
module BigCommit
|
||||
ID = "913c66a37b4a45b9769037c55c2d238bd0942d2e".freeze
|
||||
PARENT_ID = "cfe32cf61b73a0d5e9f13e774abde7ff789b1660".freeze
|
||||
MESSAGE = "Files, encoding and much more".freeze
|
||||
AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze
|
||||
FILES_COUNT = 2
|
||||
end
|
||||
|
||||
module Commit
|
||||
ID = "570e7b2abdd848b95f2f578043fc23bd6f6fd24d".freeze
|
||||
PARENT_ID = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9".freeze
|
||||
MESSAGE = "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n".freeze
|
||||
AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze
|
||||
FILES = ["files/ruby/popen.rb", "files/ruby/regex.rb"].freeze
|
||||
FILES_COUNT = 2
|
||||
C_FILE_PATH = "files/ruby".freeze
|
||||
C_FILES = ["popen.rb", "regex.rb", "version_info.rb"].freeze
|
||||
BLOB_FILE = %{%h3= @key.title\n%hr\n%pre= @key.key\n.actions\n = link_to 'Remove', @key, :confirm => 'Are you sure?', :method => :delete, :class => \"btn danger delete-key\"\n\n\n}.freeze
|
||||
BLOB_FILE_PATH = "app/views/keys/show.html.haml".freeze
|
||||
end
|
||||
|
||||
module EmptyCommit
|
||||
ID = "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9".freeze
|
||||
PARENT_ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze
|
||||
MESSAGE = "Empty commit".freeze
|
||||
AUTHOR_FULL_NAME = "Rémy Coutable".freeze
|
||||
FILES = [].freeze
|
||||
FILES_COUNT = FILES.count
|
||||
end
|
||||
|
||||
module EncodingCommit
|
||||
ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze
|
||||
PARENT_ID = "66028349a123e695b589e09a36634d976edcc5e8".freeze
|
||||
MESSAGE = "Add ISO-8859-encoded file".freeze
|
||||
AUTHOR_FULL_NAME = "Stan Hu".freeze
|
||||
FILES = ["encoding/iso8859.txt"].freeze
|
||||
FILES_COUNT = FILES.count
|
||||
end
|
||||
|
||||
module FirstCommit
|
||||
ID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863".freeze
|
||||
PARENT_ID = nil
|
||||
MESSAGE = "Initial commit".freeze
|
||||
AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze
|
||||
FILES = ["LICENSE", ".gitignore", "README.md"].freeze
|
||||
FILES_COUNT = 3
|
||||
end
|
||||
|
||||
module LastCommit
|
||||
ID = <%= capture!(%w[git show -s --format=%H HEAD], repo).inspect %>.freeze
|
||||
PARENT_ID = <%= capture!(%w[git show -s --format=%P HEAD], repo).split.last.inspect %>.freeze
|
||||
MESSAGE = <%= capture!(%w[git show -s --format=%s HEAD], repo).inspect %>.freeze
|
||||
AUTHOR_FULL_NAME = <%= capture!(%w[git show -s --format=%an HEAD], repo).inspect %>.freeze
|
||||
FILES = <%=
|
||||
parents = capture!(%w[git show -s --format=%P HEAD], repo).split
|
||||
merge_base = parents.size > 1 ? capture!(%w[git merge-base] + parents, repo) : parents.first
|
||||
capture!( %W[git diff --name-only #{merge_base}..HEAD --], repo).split("\n").inspect
|
||||
%>.freeze
|
||||
FILES_COUNT = FILES.count
|
||||
end
|
||||
|
||||
module Repo
|
||||
HEAD = "master".freeze
|
||||
BRANCHES = %w[
|
||||
<%= capture!(%W[git for-each-ref --format=#{' ' * 3}%(refname:strip=2) refs/heads/], repo) %>
|
||||
].freeze
|
||||
TAGS = %w[
|
||||
<%= capture!(%W[git for-each-ref --format=#{' ' * 3}%(refname:strip=2) refs/tags/], repo) %>
|
||||
].freeze
|
||||
end
|
||||
|
||||
module RubyBlob
|
||||
ID = "7e3e39ebb9b2bf433b4ad17313770fbe4051649c".freeze
|
||||
NAME = "popen.rb".freeze
|
||||
CONTENT = <<-eos.freeze
|
||||
require 'fileutils'
|
||||
require 'open3'
|
||||
|
||||
module Popen
|
||||
extend self
|
||||
|
||||
def popen(cmd, path=nil)
|
||||
unless cmd.is_a?(Array)
|
||||
raise RuntimeError, "System commands must be given as an array of strings"
|
||||
end
|
||||
|
||||
path ||= Dir.pwd
|
||||
|
||||
vars = {
|
||||
"PWD" => path
|
||||
}
|
||||
|
||||
options = {
|
||||
chdir: path
|
||||
}
|
||||
|
||||
unless File.directory?(path)
|
||||
FileUtils.mkdir_p(path)
|
||||
end
|
||||
|
||||
@cmd_output = ""
|
||||
@cmd_status = 0
|
||||
|
||||
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
|
||||
@cmd_output << stdout.read
|
||||
@cmd_output << stderr.read
|
||||
@cmd_status = wait_thr.value.exitstatus
|
||||
end
|
||||
|
||||
return @cmd_output, @cmd_status
|
||||
end
|
||||
end
|
||||
eos
|
||||
end
|
||||
end
|
|
@ -1,4 +1,8 @@
|
|||
# This file is generated by generate-seed-repo-rb. Do not edit this file manually.
|
||||
#
|
||||
# Seed repo:
|
||||
# 4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6 Merge branch 'master' into 'master'
|
||||
# 0e1b353b348f8477bdbec1ef47087171c5032cd9 adds an executable with different permissions
|
||||
# 0e50ec4d3c7ce42ab74dda1d422cb2cbffe1e326 Merge branch 'lfs_pointers' into 'master'
|
||||
# 33bcff41c232a11727ac6d660bd4b0c2ba86d63d Add valid and invalid lfs pointers
|
||||
# 732401c65e924df81435deb12891ef570167d2e2 Update year in license file
|
||||
|
@ -94,7 +98,12 @@ module SeedRepo
|
|||
master
|
||||
merge-test
|
||||
].freeze
|
||||
TAGS = %w[v1.0.0 v1.1.0 v1.2.0 v1.2.1].freeze
|
||||
TAGS = %w[
|
||||
v1.0.0
|
||||
v1.1.0
|
||||
v1.2.0
|
||||
v1.2.1
|
||||
].freeze
|
||||
end
|
||||
|
||||
module RubyBlob
|
||||
|
|
|
@ -120,7 +120,7 @@ module TestEnv
|
|||
end
|
||||
|
||||
def setup_gitaly
|
||||
socket_path = Gitlab::GitalyClient.get_address('default').sub(/\Aunix:/, '')
|
||||
socket_path = Gitlab::GitalyClient.address('default').sub(/\Aunix:/, '')
|
||||
gitaly_dir = File.dirname(socket_path)
|
||||
|
||||
unless File.directory?(gitaly_dir) || system('rake', "gitlab:gitaly:install[#{gitaly_dir}]")
|
||||
|
@ -133,7 +133,8 @@ module TestEnv
|
|||
def start_gitaly(gitaly_dir)
|
||||
gitaly_exec = File.join(gitaly_dir, 'gitaly')
|
||||
gitaly_config = File.join(gitaly_dir, 'config.toml')
|
||||
@gitaly_pid = spawn(gitaly_exec, gitaly_config, [:out, :err] => '/dev/null')
|
||||
log_file = Rails.root.join('log/gitaly-test.log').to_s
|
||||
@gitaly_pid = spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file)
|
||||
end
|
||||
|
||||
def stop_gitaly
|
||||
|
|
|
@ -236,7 +236,6 @@ describe 'gitlab:app namespace rake task' do
|
|||
'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address }
|
||||
}
|
||||
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
|
||||
Gitlab::GitalyClient.configure_channels
|
||||
|
||||
# Create the projects now, after mocking the settings but before doing the backup
|
||||
project_a
|
||||
|
|
|
@ -4,13 +4,16 @@ describe PostReceive do
|
|||
let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
|
||||
let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
|
||||
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:project_identifier) { "project-#{project.id}" }
|
||||
let(:key) { create(:key, user: project.owner) }
|
||||
let(:key_id) { key.shell_id }
|
||||
|
||||
let(:project) do
|
||||
create(:project, :repository, auto_cancel_pending_pipelines: 'disabled')
|
||||
end
|
||||
|
||||
context "as a sidekiq worker" do
|
||||
it "reponds to #perform" do
|
||||
it "responds to #perform" do
|
||||
expect(described_class.new).to respond_to(:perform)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue