Commit 9cb3abab authored by Jacob Schatz's avatar Jacob Schatz

Merge branch 'master' into 'dispatcher-cleanup'

# Conflicts:
#   config/webpack.config.js
parents 54432de3 ccbce7af
<!---
Please read this! Please read this!
Before opening a new issue, make sure to search for keywords in the issues Before opening a new issue, make sure to search for keywords in the issues
...@@ -14,10 +15,7 @@ For the Enterprise Edition issue tracker: ...@@ -14,10 +15,7 @@ For the Enterprise Edition issue tracker:
- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=bug - https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=bug
and verify the issue you're about to submit isn't a duplicate. and verify the issue you're about to submit isn't a duplicate.
--->
Please remove this notice if you're confident your issue isn't a duplicate.
------
### Summary ### Summary
......
...@@ -6,7 +6,7 @@ import { __ } from '../../locale'; ...@@ -6,7 +6,7 @@ import { __ } from '../../locale';
import Sidebar from '../../right_sidebar'; import Sidebar from '../../right_sidebar';
import eventHub from '../../sidebar/event_hub'; import eventHub from '../../sidebar/event_hub';
import assigneeTitle from '../../sidebar/components/assignees/assignee_title'; import assigneeTitle from '../../sidebar/components/assignees/assignee_title';
import assignees from '../../sidebar/components/assignees/assignees'; import assignees from '../../sidebar/components/assignees/assignees.vue';
import DueDateSelectors from '../../due_date_select'; import DueDateSelectors from '../../due_date_select';
import './sidebar/remove_issue'; import './sidebar/remove_issue';
import IssuableContext from '../../issuable_context'; import IssuableContext from '../../issuable_context';
......
...@@ -14,10 +14,10 @@ import CycleAnalyticsStore from './cycle_analytics_store'; ...@@ -14,10 +14,10 @@ import CycleAnalyticsStore from './cycle_analytics_store';
Vue.use(Translate); Vue.use(Translate);
$(() => { export default () => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
gl.cycleAnalyticsApp = new Vue({ new Vue({ // eslint-disable-line no-new
el: '#cycle-analytics', el: '#cycle-analytics',
name: 'CycleAnalytics', name: 'CycleAnalytics',
components: { components: {
...@@ -132,4 +132,4 @@ $(() => { ...@@ -132,4 +132,4 @@ $(() => {
}, },
}, },
}); });
}); };
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import repoCommitSection from './repo_commit_section.vue'; import repoCommitSection from './repo_commit_section.vue';
import icon from '../../vue_shared/components/icon.vue';
import panelResizer from '../../vue_shared/components/panel_resizer.vue';
export default { export default {
components: { components: {
......
<script> <script>
import icon from '~/vue_shared/components/icon.vue';
import repoTree from './ide_repo_tree.vue'; import repoTree from './ide_repo_tree.vue';
import icon from '../../vue_shared/components/icon.vue';
import newDropdown from './new_dropdown/index.vue'; import newDropdown from './new_dropdown/index.vue';
export default { export default {
......
<script> <script>
import projectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
import branchesTree from './ide_project_branches_tree.vue'; import branchesTree from './ide_project_branches_tree.vue';
import projectAvatarImage from '../../vue_shared/components/project_avatar/image.vue';
export default { export default {
components: { components: {
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import repoPreviousDirectory from './repo_prev_directory.vue'; import repoPreviousDirectory from './repo_prev_directory.vue';
import repoFile from './repo_file.vue'; import repoFile from './repo_file.vue';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
import { treeList } from '../stores/utils'; import { treeList } from '../stores/utils';
export default { export default {
......
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import projectTree from './ide_project_tree.vue'; import projectTree from './ide_project_tree.vue';
import icon from '../../vue_shared/components/icon.vue';
import panelResizer from '../../vue_shared/components/panel_resizer.vue';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
export default { export default {
components: { components: {
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import icon from '../../vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '../../vue_shared/mixins/timeago'; import timeAgoMixin from '~/vue_shared/mixins/timeago';
export default { export default {
components: { components: {
......
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import flash, { hideFlash } from '../../flash'; import flash, { hideFlash } from '~/flash';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
export default { export default {
components: { components: {
......
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import modal from '../../vue_shared/components/modal.vue'; import modal from '~/vue_shared/components/modal.vue';
import commitFilesList from './commit_sidebar/list.vue'; import commitFilesList from './commit_sidebar/list.vue';
export default { export default {
......
<script> <script>
import { mapGetters, mapActions, mapState } from 'vuex'; import { mapGetters, mapActions, mapState } from 'vuex';
import modal from '../../vue_shared/components/modal.vue'; import modal from '~/vue_shared/components/modal.vue';
export default { export default {
components: { components: {
......
<script> <script>
/* global monaco */ /* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '../../flash'; import flash from '~/flash';
import monacoLoader from '../monaco_loader'; import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor'; import Editor from '../lib/editor';
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue'; import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
export default { export default {
components: { components: {
......
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import LineHighlighter from '../../line_highlighter'; import LineHighlighter from '~/line_highlighter';
import syntaxHighlight from '../../syntax_highlight'; import syntaxHighlight from '~/syntax_highlight';
export default { export default {
computed: { computed: {
......
import Vue from 'vue'; import Vue from 'vue';
import { visitUrl } from '../../lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import flash from '../../flash'; import flash from '~/flash';
import service from '../services'; import service from '../services';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { stripHtml } from '../../lib/utils/text_utility'; import { stripHtml } from '../../lib/utils/text_utility';
......
...@@ -21,7 +21,7 @@ export default class LabelsSelect { ...@@ -21,7 +21,7 @@ export default class LabelsSelect {
} }
$els.each(function(i, dropdown) { $els.each(function(i, dropdown) {
var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer; var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer;
$dropdown = $(dropdown); $dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter'); $dropdownContainer = $dropdown.closest('.labels-filter');
$toggleText = $dropdown.find('.dropdown-toggle-text'); $toggleText = $dropdown.find('.dropdown-toggle-text');
...@@ -53,13 +53,6 @@ export default class LabelsSelect { ...@@ -53,13 +53,6 @@ export default class LabelsSelect {
.map(function () { .map(function () {
return this.value; return this.value;
}).get(); }).get();
if (issueUpdateURL != null) {
issueURLSplit = issueUpdateURL.split('/');
}
if (issueUpdateURL) {
labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>');
labelNoneHTMLTemplate = '<span class="no-value">None</span>';
}
const handleClick = options.handleClick; const handleClick = options.handleClick;
$sidebarLabelTooltip.tooltip(); $sidebarLabelTooltip.tooltip();
...@@ -91,14 +84,17 @@ export default class LabelsSelect { ...@@ -91,14 +84,17 @@ export default class LabelsSelect {
$loading.fadeOut(); $loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown'); $dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide(); $selectbox.hide();
data.issueURLSplit = issueURLSplit; data.issueUpdateURL = issueUpdateURL;
labelCount = 0; labelCount = 0;
if (data.labels.length) { if (data.labels.length && issueUpdateURL) {
template = labelHTMLTemplate(data); template = LabelsSelect.getLabelTemplate({
labels: data.labels,
issueUpdateURL,
});
labelCount = data.labels.length; labelCount = data.labels.length;
} }
else { else {
template = labelNoneHTMLTemplate; template = '<span class="no-value">None</span>';
} }
$value.removeAttr('style').html(template); $value.removeAttr('style').html(template);
$sidebarCollapsedValue.text(labelCount); $sidebarCollapsedValue.text(labelCount);
...@@ -418,6 +414,26 @@ export default class LabelsSelect { ...@@ -418,6 +414,26 @@ export default class LabelsSelect {
this.bindEvents(); this.bindEvents();
} }
static getLabelTemplate(tplData) {
// We could use ES6 template string here
// and properly indent markup for readability
// but that also introduces unintended white-space
// so best approach is to use traditional way of
// concatenation
// see: http://2ality.com/2016/05/template-literal-whitespace.html#joining-arrays
const tpl = _.template([
'<% _.each(labels, function(label){ %>',
'<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
'<span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
'<%- label.title %>',
'</span>',
'</a>',
'<% }); %>',
].join(''));
return tpl(tplData);
}
bindEvents() { bindEvents() {
return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue); return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue);
} }
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import { isMetaClick } from '~/lib/utils/common_utils'; import { isMetaClick } from '~/lib/utils/common_utils';
import { __ } from '../../../../locale'; import { __ } from '~/locale';
import flash from '../../../../flash'; import flash from '~/flash';
import axios from '../../../../lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
export default class Todos { export default class Todos {
constructor() { constructor() {
......
import initCycleAnalytics from '~/cycle_analytics/cycle_analytics_bundle';
document.addEventListener('DOMContentLoaded', initCycleAnalytics);
<script>
export default { export default {
name: 'Assignees', name: 'Assignees',
data() {
return {
defaultRenderCount: 5,
defaultMaxCounter: 99,
showLess: true,
};
},
props: { props: {
rootPath: { rootPath: {
type: String, type: String,
...@@ -21,6 +15,13 @@ export default { ...@@ -21,6 +15,13 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
defaultRenderCount: 5,
defaultMaxCounter: 99,
showLess: true,
};
},
computed: { computed: {
firstUser() { firstUser() {
return this.users[0]; return this.users[0];
...@@ -101,7 +102,10 @@ export default { ...@@ -101,7 +102,10 @@ export default {
return index === 0 || firstTwo; return index === 0 || firstTwo;
}, },
}, },
template: ` };
</script>
<template>
<div> <div>
<div <div
class="sidebar-collapsed-icon sidebar-collapsed-user" class="sidebar-collapsed-icon sidebar-collapsed-user"
...@@ -114,12 +118,14 @@ export default { ...@@ -114,12 +118,14 @@ export default {
v-if="hasNoUsers" v-if="hasNoUsers"
aria-label="No Assignee" aria-label="No Assignee"
class="fa fa-user" class="fa fa-user"
/> >
</i>
<button <button
type="button" type="button"
class="btn-link" class="btn-link"
v-for="(user, index) in users" v-for="(user, index) in users"
v-if="shouldRenderCollapsedAssignee(index)" v-if="shouldRenderCollapsedAssignee(index)"
:key="user.id"
> >
<img <img
width="24" width="24"
...@@ -184,9 +190,11 @@ export default { ...@@ -184,9 +190,11 @@ export default {
class="user-item" class="user-item"
v-for="(user, index) in users" v-for="(user, index) in users"
v-if="renderAssignee(index)" v-if="renderAssignee(index)"
:key="user.id"
> >
<a <a
class="user-link has-tooltip" class="user-link has-tooltip"
data-container="body"
data-placement="bottom" data-placement="bottom"
:href="assigneeUrl(user)" :href="assigneeUrl(user)"
:data-title="user.name" :data-title="user.name"
...@@ -220,5 +228,5 @@ export default { ...@@ -220,5 +228,5 @@ export default {
</template> </template>
</div> </div>
</div> </div>
`, </template>
};
import Flash from '../../../flash'; import Flash from '../../../flash';
import AssigneeTitle from './assignee_title'; import AssigneeTitle from './assignee_title';
import Assignees from './assignees'; import Assignees from './assignees.vue';
import Store from '../../stores/sidebar_store'; import Store from '../../stores/sidebar_store';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
...@@ -28,8 +28,8 @@ export default { ...@@ -28,8 +28,8 @@ export default {
}, },
}, },
components: { components: {
'assignee-title': AssigneeTitle, AssigneeTitle,
assignees: Assignees, Assignees,
}, },
methods: { methods: {
assignSelf() { assignSelf() {
......
...@@ -103,6 +103,7 @@ ...@@ -103,6 +103,7 @@
.issuable-show-labels { .issuable-show-labels {
a { a {
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 5px;
display: inline-block; display: inline-block;
.color-label { .color-label {
...@@ -116,6 +117,12 @@ ...@@ -116,6 +117,12 @@
} }
&.has-labels { &.has-labels {
// this font size is a fix to
// prevent unintended spacing between labels
// which shows up when rendering markup has white-space
// characters present.
// see: https://css-tricks.com/fighting-the-space-between-inline-block-elements/#article-header-id-3
font-size: 0;
margin-bottom: -5px; margin-bottom: -5px;
} }
} }
......
...@@ -3,7 +3,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController ...@@ -3,7 +3,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :require_pages_enabled! before_action :require_pages_enabled!
before_action :authorize_update_pages!, except: [:show] before_action :authorize_update_pages!, except: [:show]
before_action :domain, only: [:show, :destroy, :verify] before_action :domain, except: [:new, :create]
def show def show
end end
...@@ -24,8 +24,11 @@ class Projects::PagesDomainsController < Projects::ApplicationController ...@@ -24,8 +24,11 @@ class Projects::PagesDomainsController < Projects::ApplicationController
redirect_to project_pages_domain_path(@project, @domain) redirect_to project_pages_domain_path(@project, @domain)
end end
def edit
end
def create def create
@domain = @project.pages_domains.create(pages_domain_params) @domain = @project.pages_domains.create(create_params)
if @domain.valid? if @domain.valid?
redirect_to project_pages_domain_path(@project, @domain) redirect_to project_pages_domain_path(@project, @domain)
...@@ -34,6 +37,16 @@ class Projects::PagesDomainsController < Projects::ApplicationController ...@@ -34,6 +37,16 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end end
end end
def update
if @domain.update(update_params)
redirect_to project_pages_path(@project),
status: 302,
notice: 'Domain was updated'
else
render 'edit'
end
end
def destroy def destroy
@domain.destroy @domain.destroy
...@@ -49,12 +62,12 @@ class Projects::PagesDomainsController < Projects::ApplicationController ...@@ -49,12 +62,12 @@ class Projects::PagesDomainsController < Projects::ApplicationController
private private
def pages_domain_params def create_params
params.require(:pages_domain).permit( params.require(:pages_domain).permit(:key, :certificate, :domain)
:certificate, end
:key,
:domain def update_params
) params.require(:pages_domain).permit(:key, :certificate)
end end
def domain def domain
......
...@@ -49,7 +49,7 @@ module Ci ...@@ -49,7 +49,7 @@ module Ci
ref_protected: 1 ref_protected: 1
} }
cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at, :ip_address
# Searches for runners matching the given query. # Searches for runners matching the given query.
# #
...@@ -157,7 +157,7 @@ module Ci ...@@ -157,7 +157,7 @@ module Ci
end end
def update_cached_info(values) def update_cached_info(values)
values = values&.slice(:version, :revision, :platform, :architecture) || {} values = values&.slice(:version, :revision, :platform, :architecture, :ip_address) || {}
values[:contacted_at] = Time.now values[:contacted_at] = Time.now
cache_attributes(values) cache_attributes(values)
......
...@@ -15,6 +15,8 @@ module Projects ...@@ -15,6 +15,8 @@ module Projects
return error("Could not set the default branch") unless project.change_head(params[:default_branch]) return error("Could not set the default branch") unless project.change_head(params[:default_branch])
end end
ensure_wiki_exists if enabling_wiki?
if project.update_attributes(params.except(:default_branch)) if project.update_attributes(params.except(:default_branch))
if project.previous_changes.include?('path') if project.previous_changes.include?('path')
project.rename_repo project.rename_repo
...@@ -52,5 +54,18 @@ module Projects ...@@ -52,5 +54,18 @@ module Projects
project.repository.exists? && project.repository.exists? &&
new_branch && new_branch != project.default_branch new_branch && new_branch != project.default_branch
end end
def enabling_wiki?
return false if @project.wiki_enabled?
params[:project_feature_attributes][:wiki_access_level].to_i > ProjectFeature::DISABLED
end
def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
log_error("Could not create wiki for #{project.full_name}")
Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
end
end end
end end
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
= runner.description = runner.description
%td %td
= runner.version = runner.version
%td
= runner.ip_address
%td %td
- if runner.shared? - if runner.shared?
n/a n/a
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
%th Runner token %th Runner token
%th Description %th Description
%th Version %th Version
%th IP Address
%th Projects %th Projects
%th Jobs %th Jobs
%th Tags %th Tags
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
- page_title "Cycle Analytics" - page_title "Cycle Analytics"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag('common_vue') = webpack_bundle_tag('common_vue')
= webpack_bundle_tag('cycle_analytics')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data - if @cycle_analytics_no_data
......
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f| - if @domain.errors.any?
- if @domain.errors.any?
#error_explanation #error_explanation
.alert.alert-danger .alert.alert-danger
- @domain.errors.full_messages.each do |msg| - @domain.errors.full_messages.each do |msg|
%p= msg %p= msg
.form-group .form-group
= f.label :domain, class: 'control-label' do = f.label :domain, class: 'control-label' do
Domain Domain
.col-sm-10 .col-sm-10
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control' = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted?
- if Gitlab.config.pages.external_https - if Gitlab.config.pages.external_https
.form-group .form-group
= f.label :certificate, class: 'control-label' do = f.label :certificate, class: 'control-label' do
Certificate (PEM) Certificate (PEM)
...@@ -25,10 +24,7 @@ ...@@ -25,10 +24,7 @@
.col-sm-10 .col-sm-10
= f.text_area :key, rows: 5, class: 'form-control' = f.text_area :key, rows: 5, class: 'form-control'
%span.help-inline Upload a private key for your certificate %span.help-inline Upload a private key for your certificate
- else - else
.nothing-here-block .nothing-here-block
Support for custom certificates is disabled. Support for custom certificates is disabled.
Ask your system's administrator to enable it. Ask your system's administrator to enable it.
.form-actions
= f.submit 'Create New Domain', class: "btn btn-save"
- add_to_breadcrumbs "Pages", project_pages_path(@project)
- breadcrumb_title @domain.domain
- page_title @domain.domain
%h3.page_title
= @domain.domain
%hr.clearfix
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
= f.submit 'Save Changes', class: "btn btn-save"
- add_to_breadcrumbs "Pages", project_pages_path(@project)
- page_title 'New Pages Domain' - page_title 'New Pages Domain'
%h3.page_title %h3.page_title
New Pages Domain New Pages Domain
%hr.clearfix %hr.clearfix
%div %div
= render 'form' = form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
= f.submit 'Create New Domain', class: "btn btn-save"
- add_to_breadcrumbs "Pages", project_pages_path(@project)
- breadcrumb_title @domain.domain
- page_title "#{@domain.domain}", 'Pages Domains' - page_title "#{@domain.domain}", 'Pages Domains'
- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled? - verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
- if verification_enabled && @domain.unverified? - if verification_enabled && @domain.unverified?
%p.alert.alert-warning %p.alert.alert-warning
...@@ -8,6 +11,7 @@ ...@@ -8,6 +11,7 @@
%h3.page-title %h3.page-title
Pages Domain Pages Domain
= link_to 'Edit', edit_project_pages_domain_path(@project, @domain), class: 'btn btn-success pull-right'
.table-holder .table-holder
%table.table %table.table
......
...@@ -29,6 +29,11 @@ ...@@ -29,6 +29,11 @@
Token Token
.col-sm-10 .col-sm-10
= f.text_field :token, class: 'form-control', readonly: true = f.text_field :token, class: 'form-control', readonly: true
.form-group
= label_tag :ip_address, class: 'control-label' do
IP Address
.col-sm-10
= f.text_field :ip_address, class: 'form-control', readonly: true
.form-group .form-group
= label_tag :description, class: 'control-label' do = label_tag :description, class: 'control-label' do
Description Description
......
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
%tr %tr
%td Version %td Version
%td= @runner.version %td= @runner.version
%tr
%td IP Address
%td= @runner.ip_address
%tr %tr
%td Revision %td Revision
%td= @runner.revision %td= @runner.revision
......
---
title: Display Runner IP Address
merge_request: 17286
author:
type: added
---
title: Fix code and wiki search results pages when non-ASCII text is displayed
merge_request: 17413
author:
type: fixed
---
title: Enable filtering MR list based on clicked label in MR sidebar
merge_request: 17390
author:
type: fixed
---
title: Make sure wiki exists when it's enabled
merge_request:
author:
type: fixed
---
title: Add Assignees vue component missing data container
merge_request: 17426
author: George Tsiolis
type: fixed
---
title: 'Pages custom domain: allow update of key/certificate'
merge_request: 17376
author: rfwatson
type: changed
---
title: Move Assignees vue component
merge_request: 16952
author: George Tsiolis
type: performance
...@@ -103,4 +103,6 @@ Doorkeeper.configure do ...@@ -103,4 +103,6 @@ Doorkeeper.configure do
# Some applications require dynamic query parameters on their request_uri # Some applications require dynamic query parameters on their request_uri
# set to true if you want this to be allowed # set to true if you want this to be allowed
# wildcard_redirect_uri false # wildcard_redirect_uri false
base_controller 'ApplicationController'
end end
...@@ -12,9 +12,14 @@ unless Sidekiq.server? ...@@ -12,9 +12,14 @@ unless Sidekiq.server?
config.lograge.logger = ActiveSupport::Logger.new(filename) config.lograge.logger = ActiveSupport::Logger.new(filename)
# Add request parameters to log output # Add request parameters to log output
config.lograge.custom_options = lambda do |event| config.lograge.custom_options = lambda do |event|
params = event.payload[:params]
.except(*%w(controller action format))
.each_pair
.map { |k, v| { key: k, value: v } }
payload = { payload = {
time: event.time.utc.iso8601(3), time: event.time.utc.iso8601(3),
params: event.payload[:params].except(*%w(controller action format)), params: params,
remote_ip: event.payload[:remote_ip], remote_ip: event.payload[:remote_ip],
user_id: event.payload[:user_id], user_id: event.payload[:user_id],
username: event.payload[:username] username: event.payload[:username]
......
...@@ -55,7 +55,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -55,7 +55,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
resource :pages, only: [:show, :destroy] do resource :pages, only: [:show, :destroy] do
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: %r{[^/]+} } do resources :domains, except: :index, controller: 'pages_domains', constraints: { id: %r{[^/]+} } do
member do member do
post :verify post :verify
end end
......
...@@ -44,7 +44,6 @@ function generateEntries() { ...@@ -44,7 +44,6 @@ function generateEntries() {
const manualEntries = { const manualEntries = {
balsamiq_viewer: './blob/balsamiq_viewer.js', balsamiq_viewer: './blob/balsamiq_viewer.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
environments: './environments/environments_bundle.js', environments: './environments/environments_bundle.js',
monitoring: './monitoring/monitoring_bundle.js', monitoring: './monitoring/monitoring_bundle.js',
mr_notes: './mr_notes/index.js', mr_notes: './mr_notes/index.js',
...@@ -59,7 +58,6 @@ function generateEntries() { ...@@ -59,7 +58,6 @@ function generateEntries() {
terminal: './terminal/terminal_bundle.js', terminal: './terminal/terminal_bundle.js',
two_factor_auth: './two_factor_auth.js', two_factor_auth: './two_factor_auth.js',
common: './commons/index.js', common: './commons/index.js',
common_vue: './vue_shared/vue_resource_interceptor.js', common_vue: './vue_shared/vue_resource_interceptor.js',
locale: './locale/index.js', locale: './locale/index.js',
...@@ -225,6 +223,33 @@ const config = { ...@@ -225,6 +223,33 @@ const config = {
return `${moduleNames[0]}-${hash.substr(0, 6)}`; return `${moduleNames[0]}-${hash.substr(0, 6)}`;
}), }),
// create cacheable common library bundle for all vue chunks
new webpack.optimize.CommonsChunkPlugin({
name: 'common_vue',
chunks: [
'boards',
'deploy_keys',
'environments',
'filtered_search',
'groups',
'monitoring',
'mr_notes',
'notebook_viewer',
'pdf_viewer',
'pipelines',
'pipelines_details',
'registry_list',
'ide',
'schedule_form',
'schedules_index',
'sidebar',
'vue_merge_request_widget',
],
minChunks: function(module, count) {
return module.resource && (/vue_shared/).test(module.resource);
},
}),
// create cacheable common library bundles // create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
names: ['main', 'common', 'webpack_runtime'], names: ['main', 'common', 'webpack_runtime'],
......
class AddIpAddressToRunner < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_runners, :ip_address, :string
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180216121030) do ActiveRecord::Schema.define(version: 20180222043024) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -437,6 +437,7 @@ ActiveRecord::Schema.define(version: 20180216121030) do ...@@ -437,6 +437,7 @@ ActiveRecord::Schema.define(version: 20180216121030) do
t.boolean "run_untagged", default: true, null: false t.boolean "run_untagged", default: true, null: false
t.boolean "locked", default: false, null: false t.boolean "locked", default: false, null: false
t.integer "access_level", default: 0, null: false t.integer "access_level", default: 0, null: false
t.string "ip_address"
end end
add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree
......
--- ---
toc: false
comments: false comments: false
--- ---
...@@ -8,15 +7,9 @@ comments: false ...@@ -8,15 +7,9 @@ comments: false
Welcome to [GitLab](https://about.gitlab.com/), a Git-based fully featured Welcome to [GitLab](https://about.gitlab.com/), a Git-based fully featured
platform for software development! platform for software development!
GitLab offers the most scalable Git-based fully integrated platform for software development, with flexible products and subscription plans. GitLab offers the most scalable Git-based fully integrated platform for
software development, with flexible products and subscriptions.
With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Libre, Starter, Premium, and Ultimate. To understand what features you have access to, check the [GitLab subscriptions](#gitlab-subscriptions) below.
Every feature available in Libre is also available in Starter, Premium, and Ultimate.
Starter features are also available in Premium and Ultimate, and Premium features are also
available in Ultimate.
GitLab.com is our SaaS offering. It's hosted, managed, and administered by GitLab, with [free and paid plans](https://about.gitlab.com/gitlab-com/) for individuals and teams: Free, Bronze, Silver, and Gold.
## Shortcuts to GitLab's most visited docs ## Shortcuts to GitLab's most visited docs
...@@ -124,8 +117,6 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i ...@@ -124,8 +117,6 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
- [GitLab Integration](integration/README.md): Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication. - [GitLab Integration](integration/README.md): Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication.
- [Trello Power-Up](integration/trello_power_up.md): Integrate with GitLab's Trello Power-Up - [Trello Power-Up](integration/trello_power_up.md): Integrate with GitLab's Trello Power-Up
----
## Administrator documentation ## Administrator documentation
[Administration documentation](administration/index.md) applies to admin users of GitLab [Administration documentation](administration/index.md) applies to admin users of GitLab
...@@ -143,3 +134,42 @@ Learn how to contribute to GitLab: ...@@ -143,3 +134,42 @@ Learn how to contribute to GitLab:
- [Development](development/README.md): All styleguides and explanations how to contribute. - [Development](development/README.md): All styleguides and explanations how to contribute.
- [Legal](legal/README.md): Contributor license agreements. - [Legal](legal/README.md): Contributor license agreements.
- [Writing documentation](development/writing_documentation.md): Contributing to GitLab Docs. - [Writing documentation](development/writing_documentation.md): Contributing to GitLab Docs.
## GitLab subscriptions
You have two options to use GitLab:
- GitLab self-hosted: Install, administer, and maintain your own GitLab instance.
- GitLab.com: GitLab's SaaS offering. You don't need to install anything to use GitLab.com,
you only need to [sign up](https://gitlab.com/users/sign_in) and start using GitLab
straight away.
### GitLab self-hosted
With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Libre, Starter, Premium, and Ultimate.
Every feature available in Libre is also available in Starter, Premium, and Ultimate.
Starter features are also available in Premium and Ultimate, and Premium features are also
available in Ultimate.
### GitLab.com
GitLab.com is hosted, managed, and administered by GitLab, Inc., with
[free and paid subscriptions](https://about.gitlab.com/gitlab-com/) for individuals
and teams: Free, Bronze, Silver, and Gold.
GitLab.com subscriptions grants access
to the same features available in GitLab self-hosted, **expect
[administration](administration/index.md) tools and settings**:
- GitLab.com Free includes the same features available in GitLab Libre
- GitLab.com Bronze includes the same features available in GitLab Starter
- GitLab.com Silver includes the same features available in GitLab Premium
- GitLab.com Gold includes the same features available in GitLab Ultimate
For supporting the open source community and encouraging the development of
open source projects, GitLab grants access to **Gold** features
for all GitLab.com **public** projects, regardless of the subscription.
To know more about GitLab subscriptions and licensing, please refer to the
[GitLab Product Marketing Handbook](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers).
...@@ -56,6 +56,9 @@ future GitLab releases.** ...@@ -56,6 +56,9 @@ future GitLab releases.**
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | | **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used | | **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | | **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
| **CI_RUNNER_VERSION** | all | 10.6 | GitLab Runner version that is executing the current job |
| **CI_RUNNER_REVISION** | all | 10.6 | GitLab Runner revision that is executing the current job |
| **CI_RUNNER_EXECUTABLE_ARCH** | all | 10.6 | The OS/architecture of the GitLab Runner executable (note that this is not necessarily the same as the environment of the executor) |
| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | | **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | | **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] |
| **CI_PIPELINE_SOURCE** | 10.0 | all | The source for this pipeline, one of: push, web, trigger, schedule, api, external. Pipelines created before 9.5 will have unknown as source | | **CI_PIPELINE_SOURCE** | 10.0 | all | The source for this pipeline, one of: push, web, trigger, schedule, api, external. Pipelines created before 9.5 will have unknown as source |
......
...@@ -507,6 +507,7 @@ This is the entry point for our store. You can use the following as a guide: ...@@ -507,6 +507,7 @@ This is the entry point for our store. You can use the following as a guide:
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as actions from './actions'; import * as actions from './actions';
import * as getters from './getters';
import * as mutations from './mutations'; import * as mutations from './mutations';
Vue.use(Vuex); Vue.use(Vuex);
...@@ -514,6 +515,7 @@ Vue.use(Vuex); ...@@ -514,6 +515,7 @@ Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
actions, actions,
getters, getters,
mutations,
state: { state: {
users: [], users: [],
}, },
......
...@@ -109,8 +109,7 @@ in your SAML IdP: ...@@ -109,8 +109,7 @@ in your SAML IdP:
1. Change the value of `issuer` to a unique name, which will identify the application 1. Change the value of `issuer` to a unique name, which will identify the application
to the IdP. to the IdP.
1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you 1. For the changes to take effect, you must [reconfigure][] GitLab if you installed via Omnibus or [restart GitLab][] if you installed from source.
installed GitLab via Omnibus or from source respectively.
1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified 1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified
in `issuer`. in `issuer`.
......
...@@ -36,11 +36,15 @@ GFM honors the markdown specification in how [paragraphs and line breaks are han ...@@ -36,11 +36,15 @@ GFM honors the markdown specification in how [paragraphs and line breaks are han
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
Line-breaks, or softreturns, are rendered if you end a line with two or more spaces: Line-breaks, or softreturns, are rendered if you end a line with two or more spaces:
[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
[//]: # (They are needed for the Markdown text to render correctly.)
Roses are red [followed by two or more spaces] Roses are red [followed by two or more spaces]
Violets are blue Violets are blue
Sugar is sweet Sugar is sweet
[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
[//]: # (They are needed for the Markdown text to render correctly.)
Roses are red Roses are red
Violets are blue Violets are blue
......
...@@ -34,7 +34,7 @@ With **[GitLab Enterprise Edition][ee]**, you can also: ...@@ -34,7 +34,7 @@ With **[GitLab Enterprise Edition][ee]**, you can also:
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Premium) - View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Premium)
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Starter) - Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Starter)
- [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Starter) - [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Starter)
- Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Starter) - Analyze the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Starter)
## Use cases ## Use cases
......
...@@ -9,16 +9,22 @@ module API ...@@ -9,16 +9,22 @@ module API
Gitlab::CurrentSettings.runners_registration_token) Gitlab::CurrentSettings.runners_registration_token)
end end
def get_runner_version_from_params def authenticate_runner!
return unless params['info'].present? forbidden! unless current_runner
attributes_for_keys(%w(name version revision platform architecture), params['info']) current_runner
.update_cached_info(get_runner_details_from_request)
end end
def authenticate_runner! def get_runner_details_from_request
forbidden! unless current_runner return get_runner_ip unless params['info'].present?
attributes_for_keys(%w(name version revision platform architecture), params['info'])
.merge(get_runner_ip)
end
current_runner.update_cached_info(get_runner_version_from_params) def get_runner_ip
{ ip_address: request.ip }
end end
def current_runner def current_runner
......
...@@ -16,7 +16,8 @@ module API ...@@ -16,7 +16,8 @@ module API
optional :tag_list, type: Array[String], desc: %q(List of Runner's tags) optional :tag_list, type: Array[String], desc: %q(List of Runner's tags)
end end
post '/' do post '/' do
attributes = attributes_for_keys [:description, :locked, :run_untagged, :tag_list] attributes = attributes_for_keys([:description, :locked, :run_untagged, :tag_list])
.merge(get_runner_details_from_request)
runner = runner =
if runner_registration_token_valid? if runner_registration_token_valid?
...@@ -30,7 +31,6 @@ module API ...@@ -30,7 +31,6 @@ module API
return forbidden! unless runner return forbidden! unless runner
if runner.id if runner.id
runner.update(get_runner_version_from_params)
present runner, with: Entities::RunnerRegistrationDetails present runner, with: Entities::RunnerRegistrationDetails
else else
not_found! not_found!
......
...@@ -59,7 +59,7 @@ module Gitlab ...@@ -59,7 +59,7 @@ module Gitlab
end end
def pages(limit: nil) def pages(limit: nil)
@repository.gitaly_migrate(:wiki_get_all_pages, status: Gitlab::GitalyClient::MigrationStatus::DISABLED) do |is_enabled| @repository.gitaly_migrate(:wiki_get_all_pages) do |is_enabled|
if is_enabled if is_enabled
gitaly_get_all_pages gitaly_get_all_pages
else else
...@@ -68,9 +68,8 @@ module Gitlab ...@@ -68,9 +68,8 @@ module Gitlab
end end
end end
# Disable because of https://gitlab.com/gitlab-org/gitlab-ce/issues/42039
def page(title:, version: nil, dir: nil) def page(title:, version: nil, dir: nil)
@repository.gitaly_migrate(:wiki_find_page, status: Gitlab::GitalyClient::MigrationStatus::DISABLED) do |is_enabled| @repository.gitaly_migrate(:wiki_find_page) do |is_enabled|
if is_enabled if is_enabled
gitaly_find_page(title: title, version: version, dir: dir) gitaly_find_page(title: title, version: version, dir: dir)
else else
......
module Gitlab module Gitlab
class SearchResults class SearchResults
class FoundBlob class FoundBlob
include EncodingHelper
attr_reader :id, :filename, :basename, :ref, :startline, :data, :project_id attr_reader :id, :filename, :basename, :ref, :startline, :data, :project_id
def initialize(opts = {}) def initialize(opts = {})
...@@ -9,7 +11,7 @@ module Gitlab ...@@ -9,7 +11,7 @@ module Gitlab
@basename = opts.fetch(:basename, nil) @basename = opts.fetch(:basename, nil)
@ref = opts.fetch(:ref, nil) @ref = opts.fetch(:ref, nil)
@startline = opts.fetch(:startline, nil) @startline = opts.fetch(:startline, nil)
@data = opts.fetch(:data, nil) @data = encode_utf8(opts.fetch(:data, nil))
@per_page = opts.fetch(:per_page, 20) @per_page = opts.fetch(:per_page, 20)
@project_id = opts.fetch(:project_id, nil) @project_id = opts.fetch(:project_id, nil)
end end
......
...@@ -21,6 +21,8 @@ else ...@@ -21,6 +21,8 @@ else
File.open(hook_path, 'w') do |file| File.open(hook_path, 'w') do |file|
IO.copy_stream(DATA, file) IO.copy_stream(DATA, file)
end end
File.chmod(0755, hook_path)
end end
# Toggle the harness on or off # Toggle the harness on or off
......
...@@ -34,6 +34,8 @@ describe Oauth::AuthorizationsController do ...@@ -34,6 +34,8 @@ describe Oauth::AuthorizationsController do
end end
context 'with valid params' do context 'with valid params' do
render_views
it 'returns 200 code and renders view' do it 'returns 200 code and renders view' do
get :new, params get :new, params
......
...@@ -53,6 +53,66 @@ describe Projects::PagesDomainsController do ...@@ -53,6 +53,66 @@ describe Projects::PagesDomainsController do
end end
end end
describe 'GET edit' do
it "displays the 'edit' page" do
get(:edit, request_params.merge(id: pages_domain.domain))
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template('edit')
end
end
describe 'PATCH update' do
before do
controller.instance_variable_set(:@domain, pages_domain)
end
let(:pages_domain_params) do
attributes_for(:pages_domain, :with_certificate, :with_key).slice(:key, :certificate)
end
let(:params) do
request_params.merge(id: pages_domain.domain, pages_domain: pages_domain_params)
end
it 'updates the domain' do
expect(pages_domain)
.to receive(:update)
.with(pages_domain_params)
.and_return(true)
patch(:update, params)
end
it 'redirects to the project page' do
patch(:update, params)
expect(flash[:notice]).to eq 'Domain was updated'
expect(response).to redirect_to(project_pages_path(project))
end
context 'the domain is invalid' do
it 'renders the edit action' do
allow(pages_domain).to receive(:update).and_return(false)
patch(:update, params)
expect(response).to render_template('edit')
end
end
context 'the parameters include the domain' do
it 'renders 400 Bad Request' do
expect(pages_domain)
.to receive(:update)
.with(hash_not_including(:domain))
.and_return(true)
patch(:update, params.deep_merge(pages_domain: { domain: 'abc' }))
end
end
end
describe 'POST verify' do describe 'POST verify' do
let(:params) { request_params.merge(id: pages_domain.domain) } let(:params) { request_params.merge(id: pages_domain.domain) }
......
...@@ -160,6 +160,37 @@ feature 'Pages' do ...@@ -160,6 +160,37 @@ feature 'Pages' do
expect(page).to have_content('my.test.domain.com') expect(page).to have_content('my.test.domain.com')
end end
describe 'updating the certificate for an existing domain' do
let!(:domain) do
create(:pages_domain, :with_key, :with_certificate, project: project)
end
it 'allows the certificate to be updated' do
visit project_pages_path(project)
within('#content-body') { click_link 'Details' }
click_link 'Edit'
click_button 'Save Changes'
expect(page).to have_content('Domain was updated')
end
context 'when the certificate is invalid' do
it 'tells the user what the problem is' do
visit project_pages_path(project)
within('#content-body') { click_link 'Details' }
click_link 'Edit'
fill_in 'Certificate (PEM)', with: 'invalid data'
click_button 'Save Changes'
expect(page).to have_content('Certificate must be a valid PEM certificate')
expect(page).to have_content('Certificate misses intermediates')
expect(page).to have_content("Key doesn't match the certificate")
end
end
end
end end
end end
......
...@@ -367,23 +367,6 @@ describe 'Pipelines', :js do ...@@ -367,23 +367,6 @@ describe 'Pipelines', :js do
expect(build.reload).to be_canceled expect(build.reload).to be_canceled
end end
end end
context 'dropdown jobs list' do
it 'should keep the dropdown open when the user ctr/cmd + clicks in the job name' do
find('.js-builds-dropdown-button').click
dropdown_item = find('.mini-pipeline-graph-dropdown-item').native
%i(alt control).each do |meta_key|
page.driver.browser.action
.key_down(meta_key)
.click(dropdown_item)
.key_up(meta_key)
.perform
end
expect(page).to have_selector('.js-ci-action-icon')
end
end
end end
context 'with pagination' do context 'with pagination' do
......
import LabelsSelect from '~/labels_select';
const mockUrl = '/foo/bar/url';
const mockLabels = [
{
id: 26,
title: 'Foo Label',
description: 'Foobar',
color: '#BADA55',
text_color: '#FFFFFF',
},
];
describe('LabelsSelect', () => {
describe('getLabelTemplate', () => {
const label = mockLabels[0];
let $labelEl;
beforeEach(() => {
$labelEl = $(LabelsSelect.getLabelTemplate({
labels: mockLabels,
issueUpdateURL: mockUrl,
}));
});
it('generated label item template has correct label URL', () => {
expect($labelEl.attr('href')).toBe('/foo/bar?label_name[]=Foo%20Label');
});
it('generated label item template has correct label title', () => {
expect($labelEl.find('span.label').text()).toBe(label.title);
});
it('generated label item template has label description as title attribute', () => {
expect($labelEl.find('span.label').attr('title')).toBe(label.description);
});
it('generated label item template has correct label styles', () => {
expect($labelEl.find('span.label').attr('style')).toBe(`background-color: ${label.color}; color: ${label.text_color};`);
});
});
});
import Vue from 'vue'; import Vue from 'vue';
import Assignee from '~/sidebar/components/assignees/assignees'; import Assignee from '~/sidebar/components/assignees/assignees.vue';
import UsersMock from './mock_data'; import UsersMock from './mock_data';
import UsersMockHelper from '../helpers/user_mock_data_helper'; import UsersMockHelper from '../helpers/user_mock_data_helper';
......
# coding: utf-8
require 'spec_helper' require 'spec_helper'
describe Gitlab::ProjectSearchResults do describe Gitlab::ProjectSearchResults do
...@@ -105,6 +106,32 @@ describe Gitlab::ProjectSearchResults do ...@@ -105,6 +106,32 @@ describe Gitlab::ProjectSearchResults do
end end
end end
context 'when the search returns non-ASCII data' do
context 'with UTF-8' do
let(:results) { project.repository.search_files_by_content("файл", 'master') }
it 'returns results as UTF-8' do
expect(subject.filename).to eq('encoding/russian.rb')
expect(subject.basename).to eq('encoding/russian')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
expect(subject.data).to eq("Хороший файл")
end
end
context 'with ISO-8859-1' do
let(:search_result) { "master:encoding/iso8859.txt\x001\x00\xC4\xFC\nmaster:encoding/iso8859.txt\x002\x00\nmaster:encoding/iso8859.txt\x003\x00foo\n".force_encoding(Encoding::ASCII_8BIT) }
it 'returns results as UTF-8' do
expect(subject.filename).to eq('encoding/iso8859.txt')
expect(subject.basename).to eq('encoding/iso8859')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
expect(subject.data).to eq("Äü\n\nfoo")
end
end
end
context "when filename has extension" do context "when filename has extension" do
let(:search_result) { "master:CONTRIBUTE.md\x005\x00- [Contribute to GitLab](#contribute-to-gitlab)\n" } let(:search_result) { "master:CONTRIBUTE.md\x005\x00- [Contribute to GitLab](#contribute-to-gitlab)\n" }
......
...@@ -122,6 +122,15 @@ describe API::Runner do ...@@ -122,6 +122,15 @@ describe API::Runner do
end end
end end
end end
it "sets the runner's ip_address" do
post api('/runners'),
{ token: registration_token },
{ 'REMOTE_ADDR' => '123.111.123.111' }
expect(response).to have_gitlab_http_status 201
expect(Ci::Runner.first.ip_address).to eq('123.111.123.111')
end
end end
describe 'DELETE /api/v4/runners' do describe 'DELETE /api/v4/runners' do
...@@ -422,6 +431,15 @@ describe API::Runner do ...@@ -422,6 +431,15 @@ describe API::Runner do
end end
end end
it "sets the runner's ip_address" do
post api('/jobs/request'),
{ token: runner.token },
{ 'User-Agent' => user_agent, 'REMOTE_ADDR' => '123.222.123.222' }
expect(response).to have_gitlab_http_status 201
expect(runner.reload.ip_address).to eq('123.222.123.222')
end
context 'when concurrently updating a job' do context 'when concurrently updating a job' do
before do before do
expect_any_instance_of(Ci::Build).to receive(:run!) expect_any_instance_of(Ci::Build).to receive(:run!)
......
...@@ -123,6 +123,40 @@ describe Projects::UpdateService do ...@@ -123,6 +123,40 @@ describe Projects::UpdateService do
end end
end end
context 'when we update project but not enabling a wiki' do
it 'does not try to create an empty wiki' do
FileUtils.rm_rf(project.wiki.repository.path)
result = update_project(project, user, { name: 'test1' })
expect(result).to eq({ status: :success })
expect(project.wiki_repository_exists?).to be false
end
end
context 'when enabling a wiki' do
it 'creates a wiki' do
project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED)
FileUtils.rm_rf(project.wiki.repository.path)
result = update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED })
expect(result).to eq({ status: :success })
expect(project.wiki_repository_exists?).to be true
expect(project.wiki_enabled?).to be true
end
it 'logs an error and creates a metric when wiki can not be created' do
project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED)
expect_any_instance_of(ProjectWiki).to receive(:wiki).and_raise(ProjectWiki::CouldNotCreateWikiError)
expect_any_instance_of(described_class).to receive(:log_error).with("Could not create wiki for #{project.full_name}")
expect(Gitlab::Metrics).to receive(:counter)
update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED })
end
end
context 'when updating a project that contains container images' do context 'when updating a project that contains container images' do
before do before do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
......
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