Commit 36019301 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-09-27

# Conflicts:
#	app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
#	app/assets/javascripts/monitoring/components/dashboard.vue

[ci skip]
parents 07a8cea2 0d84dd22
...@@ -92,7 +92,7 @@ gem 'gitlab-gollum-rugged_adapter', '~> 0.4.4', require: false ...@@ -92,7 +92,7 @@ gem 'gitlab-gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 5.3.3', require: 'linguist' gem 'github-linguist', '~> 5.3.3', require: 'linguist'
# API # API
gem 'grape', '~> 1.0' gem 'grape', '~> 1.1'
gem 'grape-entity', '~> 0.7.1' gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors' gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
......
...@@ -368,7 +368,7 @@ GEM ...@@ -368,7 +368,7 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (1.0.3) grape (1.1.0)
activesupport activesupport
builder builder
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
...@@ -529,7 +529,7 @@ GEM ...@@ -529,7 +529,7 @@ GEM
multi_json (1.13.1) multi_json (1.13.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (1.0.2) mustermann (1.0.3)
mustermann-grape (1.0.0) mustermann-grape (1.0.0)
mustermann (~> 1.0.0) mustermann (~> 1.0.0)
mysql2 (0.4.10) mysql2 (0.4.10)
...@@ -1078,7 +1078,7 @@ DEPENDENCIES ...@@ -1078,7 +1078,7 @@ DEPENDENCIES
google-api-client (~> 0.23) google-api-client (~> 0.23)
google-protobuf (= 3.5.1) google-protobuf (= 3.5.1)
gpgme gpgme
grape (~> 1.0) grape (~> 1.1)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0) grape-path-helpers (~> 1.0)
grape_logging (~> 1.7) grape_logging (~> 1.7)
......
...@@ -371,7 +371,7 @@ GEM ...@@ -371,7 +371,7 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (1.0.3) grape (1.1.0)
activesupport activesupport
builder builder
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
...@@ -532,7 +532,7 @@ GEM ...@@ -532,7 +532,7 @@ GEM
multi_json (1.13.1) multi_json (1.13.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (1.0.2) mustermann (1.0.3)
mustermann-grape (1.0.0) mustermann-grape (1.0.0)
mustermann (~> 1.0.0) mustermann (~> 1.0.0)
mysql2 (0.4.10) mysql2 (0.4.10)
...@@ -1087,7 +1087,7 @@ DEPENDENCIES ...@@ -1087,7 +1087,7 @@ DEPENDENCIES
google-api-client (~> 0.23) google-api-client (~> 0.23)
google-protobuf (= 3.5.1) google-protobuf (= 3.5.1)
gpgme gpgme
grape (~> 1.0) grape (~> 1.1)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0) grape-path-helpers (~> 1.0)
grape_logging (~> 1.7) grape_logging (~> 1.7)
......
<script> <script>
import Link from '@gitlab-org/gitlab-ui/dist/components/base/link'; import { Link } from '@gitlab-org/gitlab-ui';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
export default { export default {
......
import Vue from 'vue'; import Vue from 'vue';
import Pagination from '@gitlab-org/gitlab-ui/dist/components/base/pagination'; import {
import progressBar from '@gitlab-org/gitlab-ui/dist/components/base/progress_bar'; Pagination,
import modal from '@gitlab-org/gitlab-ui/dist/components/base/modal'; ProgressBar,
import loadingIcon from '@gitlab-org/gitlab-ui/dist/components/base/loading_icon'; Modal,
LoadingIcon,
import dModal from '@gitlab-org/gitlab-ui/dist/directives/modal'; ModalDirective,
import dTooltip from '@gitlab-org/gitlab-ui/dist/directives/tooltip'; TooltipDirective,
} from '@gitlab-org/gitlab-ui';
Vue.component('gl-pagination', Pagination); Vue.component('gl-pagination', Pagination);
Vue.component('gl-progress-bar', progressBar); Vue.component('gl-progress-bar', ProgressBar);
Vue.component('gl-ui-modal', modal); Vue.component('gl-ui-modal', Modal);
Vue.component('gl-loading-icon', loadingIcon); Vue.component('gl-loading-icon', LoadingIcon);
Vue.directive('gl-modal', dModal); Vue.directive('gl-modal', ModalDirective);
Vue.directive('gl-tooltip', dTooltip); Vue.directive('gl-tooltip', TooltipDirective);
...@@ -7,6 +7,13 @@ const tokenKeys = [{ ...@@ -7,6 +7,13 @@ const tokenKeys = [{
symbol: '', symbol: '',
icon: 'messages', icon: 'messages',
tag: 'status', tag: 'status',
}, {
key: 'type',
type: 'string',
param: 'type',
symbol: '',
icon: 'cube',
tag: 'type',
}]; }];
const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys); const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
......
...@@ -97,11 +97,18 @@ export default class FilteredSearchDropdownManager { ...@@ -97,11 +97,18 @@ export default class FilteredSearchDropdownManager {
gl: NullDropdown, gl: NullDropdown,
element: this.container.querySelector('#js-dropdown-admin-runner-status'), element: this.container.querySelector('#js-dropdown-admin-runner-status'),
}, },
<<<<<<< HEAD
weight: { weight: {
reference: null, reference: null,
gl: DropdownWeight, gl: DropdownWeight,
element: this.container.querySelector('#js-dropdown-weight'), element: this.container.querySelector('#js-dropdown-weight'),
=======
type: {
reference: null,
gl: NullDropdown,
element: this.container.querySelector('#js-dropdown-admin-runner-type'),
>>>>>>> upstream/master
}, },
}; };
......
<script> <script>
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
import IdeTree from './ide_tree.vue'; import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue'; import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue'; import ActivityBar from './activity_bar.vue';
......
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
import FileRow from '~/vue_shared/components/file_row.vue'; import FileRow from '~/vue_shared/components/file_row.vue';
import NavDropdown from './nav_dropdown.vue'; import NavDropdown from './nav_dropdown.vue';
import FileRowExtra from './file_row_extra.vue'; import FileRowExtra from './file_row_extra.vue';
......
...@@ -138,6 +138,7 @@ export default { ...@@ -138,6 +138,7 @@ export default {
}, },
mounted() { mounted() {
this.resizeThrottled = _.debounce(this.resize, 100); this.resizeThrottled = _.debounce(this.resize, 100);
<<<<<<< HEAD
this.servicePromises = [ this.servicePromises = [
this.service this.service
.getGraphsData() .getGraphsData()
...@@ -148,6 +149,8 @@ export default { ...@@ -148,6 +149,8 @@ export default {
.then(data => this.store.storeDeploymentData(data)) .then(data => this.store.storeDeploymentData(data))
.catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))), .catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
]; ];
=======
>>>>>>> upstream/master
if (!this.hasMetrics) { if (!this.hasMetrics) {
this.state = 'gettingStarted'; this.state = 'gettingStarted';
} else { } else {
...@@ -195,7 +198,11 @@ export default { ...@@ -195,7 +198,11 @@ export default {
<template> <template>
<div <div
v-if="!showEmptyState" v-if="!showEmptyState"
<<<<<<< HEAD
:key="updateDashboardKey" :key="updateDashboardKey"
=======
:key="forceRedraw"
>>>>>>> upstream/master
class="prometheus-graphs prepend-top-default" class="prometheus-graphs prepend-top-default"
> >
<div <div
......
...@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho'; ...@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache'; import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue'; import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight'; import syntaxHighlight from '~/syntax_highlight';
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility'; import { getLocationHash } from './lib/utils/url_utility';
import Flash from './flash'; import Flash from './flash';
......
...@@ -3,7 +3,7 @@ import { mapState, mapActions } from 'vuex'; ...@@ -3,7 +3,7 @@ import { mapState, mapActions } from 'vuex';
import imageDiffHelper from '~/image_diff/helpers/index'; import imageDiffHelper from '~/image_diff/helpers/index';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
import { trimFirstCharOfLineContent } from '~/diffs/store/utils'; import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
export default { export default {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import $ from 'jquery'; import $ from 'jquery';
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
const { CancelToken } = axios; const { CancelToken } = axios;
let axiosSource; let axiosSource;
......
<script> <script>
import Link from '@gitlab-org/gitlab-ui/dist/components/base/link'; import { Link } from '@gitlab-org/gitlab-ui';
export default { export default {
components: { components: {
......
<script> <script>
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading'; import { SkeletonLoading } from '@gitlab-org/gitlab-ui';
export default { export default {
name: 'SkeletonNote', name: 'SkeletonNote',
......
...@@ -286,7 +286,7 @@ body { ...@@ -286,7 +286,7 @@ body {
} }
.page-title { .page-title {
margin-top: $gl-padding; margin: #{2 * $grid-size} 0;
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
......
...@@ -10,6 +10,7 @@ class Admin::RunnersFinder < UnionFinder ...@@ -10,6 +10,7 @@ class Admin::RunnersFinder < UnionFinder
def execute def execute
search! search!
filter_by_status! filter_by_status!
filter_by_runner_type!
sort! sort!
paginate! paginate!
...@@ -36,10 +37,11 @@ class Admin::RunnersFinder < UnionFinder ...@@ -36,10 +37,11 @@ class Admin::RunnersFinder < UnionFinder
end end
def filter_by_status! def filter_by_status!
status = @params[:status_status] filter_by!(:status_status, Ci::Runner::AVAILABLE_STATUSES)
if status.present? && Ci::Runner::AVAILABLE_STATUSES.include?(status) end
@runners = @runners.public_send(status) # rubocop:disable GitlabSecurity/PublicSend
end def filter_by_runner_type!
filter_by!(:type_type, Ci::Runner::AVAILABLE_TYPES)
end end
def sort! def sort!
...@@ -49,4 +51,12 @@ class Admin::RunnersFinder < UnionFinder ...@@ -49,4 +51,12 @@ class Admin::RunnersFinder < UnionFinder
def paginate! def paginate!
@runners = @runners.page(@params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE) @runners = @runners.page(@params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
end end
def filter_by!(scope_name, available_scopes)
scope = @params[scope_name]
if scope.present? && available_scopes.include?(scope)
@runners = @runners.public_send(scope) # rubocop:disable GitlabSecurity/PublicSend
end
end
end end
...@@ -10,12 +10,24 @@ module Ci ...@@ -10,12 +10,24 @@ module Ci
include FromUnion include FromUnion
prepend EE::Ci::Runner prepend EE::Ci::Runner
enum access_level: {
not_protected: 0,
ref_protected: 1
}
enum runner_type: {
instance_type: 1,
group_type: 2,
project_type: 3
}
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour ONLINE_CONTACT_TIMEOUT = 1.hour
UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes
AVAILABLE_TYPES = %w[specific shared].freeze AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
AVAILABLE_TYPES = runner_types.keys.freeze
AVAILABLE_STATUSES = %w[active paused online offline].freeze AVAILABLE_STATUSES = %w[active paused online offline].freeze
AVAILABLE_SCOPES = (AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze AVAILABLE_SCOPES = (AVAILABLE_TYPES_LEGACY + AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
ignore_column :is_shared ignore_column :is_shared
...@@ -98,17 +110,6 @@ module Ci ...@@ -98,17 +110,6 @@ module Ci
after_destroy :cleanup_runner_queue after_destroy :cleanup_runner_queue
enum access_level: {
not_protected: 0,
ref_protected: 1
}
enum runner_type: {
instance_type: 1,
group_type: 2,
project_type: 3
}
cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at
chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout
......
...@@ -83,12 +83,21 @@ ...@@ -83,12 +83,21 @@
{{hint}} {{hint}}
%span.js-filter-tag.dropdown-light-content %span.js-filter-tag.dropdown-light-content
{{tag}} {{tag}}
#js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu #js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_STATUSES.each do |status| - Ci::Runner::AVAILABLE_STATUSES.each do |status|
%li.filter-dropdown-item{ data: { value: status } } %li.filter-dropdown-item{ data: { value: status } }
= button_tag class: %w[btn btn-link] do = button_tag class: %w[btn btn-link] do
= status.titleize = status.titleize
#js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
%li.filter-dropdown-item{ data: { value: runner_type } }
= button_tag class: %w[btn btn-link] do
= runner_type.titleize
= button_tag class: %w[clear-search hidden] do = button_tag class: %w[clear-search hidden] do
= icon('times') = icon('times')
.filter-dropdown-container .filter-dropdown-container
......
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
- new_project_label = _("New project") - new_project_label = _("New project")
- new_subgroup_label = _("New subgroup") - new_subgroup_label = _("New subgroup")
- if can_create_subgroups - if can_create_subgroups
.btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } } .btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup.qa-new-project-or-subgroup-dropdown{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } }
%input.btn.btn-success.dropdown-primary.js-new-group-child{ type: "button", value: new_project_label, data: { action: "new-project" } } %input.btn.btn-success.dropdown-primary.js-new-group-child.qa-new-in-group-button{ type: "button", value: new_project_label, data: { action: "new-project" } }
%button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } } %button.btn.btn-success.dropdown-toggle.js-dropdown-toggle.qa-new-project-or-subgroup-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } }
= icon("caret-down", class: "dropdown-btn-icon") = icon("caret-down", class: "dropdown-btn-icon")
%ul#new-project-or-subgroup-dropdown.dropdown-menu.dropdown-menu-right{ data: { dropdown: true } } %ul#new-project-or-subgroup-dropdown.dropdown-menu.dropdown-menu-right{ data: { dropdown: true } }
%li.droplab-item-selected{ role: "button", data: { value: "new-project", text: new_project_label } } %li.droplab-item-selected.qa-new-project-option{ role: "button", data: { value: "new-project", text: new_project_label } }
.menu-item .menu-item
.icon-container .icon-container
= icon("check", class: "list-item-checkmark") = icon("check", class: "list-item-checkmark")
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%strong= new_project_label %strong= new_project_label
%span= s_("GroupsTree|Create a project in this group.") %span= s_("GroupsTree|Create a project in this group.")
%li.divider.droplap-item-ignore %li.divider.droplap-item-ignore
%li{ role: "button", data: { value: "new-subgroup", text: new_subgroup_label } } %li.qa-new-subgroup-option{ role: "button", data: { value: "new-subgroup", text: new_subgroup_label } }
.menu-item .menu-item
.icon-container .icon-container
= icon("check", class: "list-item-checkmark") = icon("check", class: "list-item-checkmark")
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.row .row
.form-group.project-name.col-sm-12 .form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold' = label_tag :name, _('Project name'), class: 'label-bold'
= text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control input-lg", autofocus: true, required: true = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control input-lg", autofocus: true
.form-group.col-12.col-sm-6 .form-group.col-12.col-sm-6
= label_tag :namespace_id, _('Project URL'), class: 'label-bold' = label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group .form-group
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.form-group.project-name.col-sm-12 .form-group.project-name.col-sm-12
= f.label :name, class: 'label-bold' do = f.label :name, class: 'label-bold' do
%span= _("Project name") %span= _("Project name")
= f.text_field :name, placeholder: "My awesome project", class: "form-control input-lg", autofocus: true, required: true = f.text_field :name, placeholder: "My awesome project", class: "form-control input-lg", autofocus: true
.form-group.project-path.col-sm-6 .form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do = f.label :namespace_id, class: 'label-bold' do
%span= s_("Project URL") %span= s_("Project URL")
......
---
title: Removes the 'required' attribute from the 'project name' field
merge_request: 21770
author:
type: other
---
title: Add a type filter to the admin runners view
merge_request: 19649
author: Alexis Reigel
type: added
---
title: Change vertical margin of page titles to 16px
merge_request: 21888
author:
type: changed
...@@ -11,11 +11,15 @@ Get a list of specific runners available to the user. ...@@ -11,11 +11,15 @@ Get a list of specific runners available to the user.
``` ```
GET /runners GET /runners
GET /runners?scope=active GET /runners?scope=active
GET /runners?type=project_type
GET /runners?status=active
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------| |-----------|---------|----------|---------------------|
| `scope` | string | no | The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided | | `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
``` ```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners" curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners"
...@@ -56,11 +60,15 @@ is restricted to users with `admin` privileges. ...@@ -56,11 +60,15 @@ is restricted to users with `admin` privileges.
``` ```
GET /runners/all GET /runners/all
GET /runners/all?scope=online GET /runners/all?scope=online
GET /runners/all?type=project_type
GET /runners/all?status=active
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------| |-----------|---------|----------|---------------------|
| `scope` | string | no | The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided | | `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
``` ```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/all" curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/all"
...@@ -336,11 +344,17 @@ usage is enabled in the project's settings. ...@@ -336,11 +344,17 @@ usage is enabled in the project's settings.
``` ```
GET /projects/:id/runners GET /projects/:id/runners
GET /projects/:id/runners?scope=active
GET /projects/:id/runners?type=project_type
GET /projects/:id/runners?status=active
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------| |-----------|----------------|----------|---------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
``` ```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners" curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners"
......
...@@ -139,5 +139,3 @@ your whole GitLab instance as well as in each project. ...@@ -139,5 +139,3 @@ your whole GitLab instance as well as in each project.
- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md) - [New CI job permissions model](../user/project/new_ci_build_permissions_model.md)
Read about what changed in GitLab 8.12 and how that affects your jobs. Read about what changed in GitLab 8.12 and how that affects your jobs.
There's a new way to access your Git submodules and LFS objects in jobs. There's a new way to access your Git submodules and LFS objects in jobs.
[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml
...@@ -178,8 +178,8 @@ runs of jobs for things like dependencies and commonly used libraries ...@@ -178,8 +178,8 @@ runs of jobs for things like dependencies and commonly used libraries
so they don't have to be re-fetched from the public internet. so they don't have to be re-fetched from the public internet.
NOTE: **Note:** NOTE: **Note:**
For more examples, check the [GitLab CI Yml](https://gitlab.com/gitlab-org/gitlab-ci-yml) For more examples, check out our [GitLab CI/CD
project. templates](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates).
### Caching Nodejs dependencies ### Caching Nodejs dependencies
...@@ -190,7 +190,7 @@ Nodejs modules are installed in `node_modules/` and are cached per-branch: ...@@ -190,7 +190,7 @@ Nodejs modules are installed in `node_modules/` and are cached per-branch:
```yaml ```yaml
# #
# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Nodejs.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
# #
image: node:latest image: node:latest
...@@ -217,7 +217,7 @@ are cached per-branch: ...@@ -217,7 +217,7 @@ are cached per-branch:
```yaml ```yaml
# #
# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/PHP.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
# #
image: php:7.2 image: php:7.2
...@@ -246,7 +246,7 @@ pip's cache is defined under `.cache/pip/` and both are cached per-branch: ...@@ -246,7 +246,7 @@ pip's cache is defined under `.cache/pip/` and both are cached per-branch:
```yaml ```yaml
# #
# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Python.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml
# #
image: python:latest image: python:latest
...@@ -286,7 +286,7 @@ jobs inherit it. Gems are installed in `vendor/ruby/` and are cached per-branch: ...@@ -286,7 +286,7 @@ jobs inherit it. Gems are installed in `vendor/ruby/` and are cached per-branch:
```yaml ```yaml
# #
# https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Ruby.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
# #
image: ruby:2.5 image: ruby:2.5
......
...@@ -4,8 +4,8 @@ comments: false ...@@ -4,8 +4,8 @@ comments: false
# GitLab CI/CD Examples # GitLab CI/CD Examples
A collection of `.gitlab-ci.yml` template files is maintained at the [GitLab CI/CD YAML project][gitlab-ci-templates]. When you create a new file via the UI, A collection of [`.gitlab-ci.yml` template files][gitlab-ci-templates] is maintained in GitLab. When you create a new file via the UI,
GitLab will give you the option to choose one of the templates existent on this project. GitLab will give you the option to choose one of these templates.
If your favorite programming language or framework are missing we would love your If your favorite programming language or framework are missing we would love your
help by sending a merge request with a new `.gitlab-ci.yml` to this project. help by sending a merge request with a new `.gitlab-ci.yml` to this project.
...@@ -95,4 +95,4 @@ language users and GitLab by sending a merge request with a guide for that langu ...@@ -95,4 +95,4 @@ language users and GitLab by sending a merge request with a guide for that langu
You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/) You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
to get paid for writing complete articles for GitLab. to get paid for writing complete articles for GitLab.
[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml [gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates
...@@ -110,4 +110,4 @@ performance: ...@@ -110,4 +110,4 @@ performance:
- sitespeed-results/ - sitespeed-results/
``` ```
A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml). A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml).
...@@ -74,7 +74,7 @@ on making Review Apps automatically deployed by each pipeline, both in CE and EE ...@@ -74,7 +74,7 @@ on making Review Apps automatically deployed by each pipeline, both in CE and EE
[review-apps-ee]: https://console.cloud.google.com/kubernetes/clusters/details/us-central1-b/review-apps-ee?project=gitlab-review-apps [review-apps-ee]: https://console.cloud.google.com/kubernetes/clusters/details/us-central1-b/review-apps-ee?project=gitlab-review-apps
[review-apps.sh]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/review-apps.sh [review-apps.sh]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/review-apps.sh
[automated_cleanup.rb]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/automated_cleanup.rb [automated_cleanup.rb]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/review_apps/automated_cleanup.rb
[Auto-DevOps.gitlab-ci.yml]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml [Auto-DevOps.gitlab-ci.yml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
[gitlab-k8s-integration]: https://docs.gitlab.com/ee/user/project/clusters/index.html [gitlab-k8s-integration]: https://docs.gitlab.com/ee/user/project/clusters/index.html
--- ---
......
...@@ -145,7 +145,7 @@ When using Auto DevOps, you may want to deploy different environments to ...@@ -145,7 +145,7 @@ When using Auto DevOps, you may want to deploy different environments to
different Kubernetes clusters. This is possible due to the 1:1 connection that different Kubernetes clusters. This is possible due to the 1:1 connection that
[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters). [exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters).
In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml) In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know: (used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know:
- `review/` (every environment starting with `review/`) - `review/` (every environment starting with `review/`)
...@@ -832,6 +832,6 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/ ...@@ -832,6 +832,6 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[review-app]: ../../ci/review_apps/index.md [review-app]: ../../ci/review_apps/index.md
[container-registry]: ../../user/project/container_registry.md [container-registry]: ../../user/project/container_registry.md
[postgresql]: https://www.postgresql.org/ [postgresql]: https://www.postgresql.org/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml [Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
[ee]: https://about.gitlab.com/pricing/ [ee]: https://about.gitlab.com/pricing/
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507 [ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
...@@ -11,10 +11,18 @@ module API ...@@ -11,10 +11,18 @@ module API
params do params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_STATUSES, optional :scope, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The scope of specific runners to show' desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
use :pagination use :pagination
end end
get do get do
runners = filter_runners(current_user.ci_owned_runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES) runners = current_user.ci_owned_runners
runners = filter_runners(runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
...@@ -24,11 +32,20 @@ module API ...@@ -24,11 +32,20 @@ module API
params do params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES, optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show' desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
use :pagination use :pagination
end end
get 'all' do get 'all' do
authenticated_as_admin! authenticated_as_admin!
runners = filter_runners(Ci::Runner.all, params[:scope])
runners = Ci::Runner.all
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
...@@ -116,10 +133,18 @@ module API ...@@ -116,10 +133,18 @@ module API
params do params do
optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES, optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES,
desc: 'The scope of specific runners to show' desc: 'The scope of specific runners to show'
optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES,
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
use :pagination use :pagination
end end
get ':id/runners' do get ':id/runners' do
runners = filter_runners(Ci::Runner.owned_or_instance_wide(user_project.id), params[:scope]) runners = Ci::Runner.owned_or_instance_wide(user_project.id)
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
present paginate(runners), with: Entities::Runner present paginate(runners), with: Entities::Runner
end end
......
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
end end
def base_dir def base_dir
Rails.root.join('vendor/gitlab-ci-yml') Rails.root.join('lib/gitlab/ci/templates')
end end
def finder(project = nil) def finder(project = nil)
......
...@@ -99,10 +99,6 @@ namespace :gitlab do ...@@ -99,10 +99,6 @@ namespace :gitlab do
"https://github.com/github/gitignore.git", "https://github.com/github/gitignore.git",
/(\.{1,2}|LICENSE|Global|\.gitignore)\z/ /(\.{1,2}|LICENSE|Global|\.gitignore)\z/
), ),
Template.new(
"https://gitlab.com/gitlab-org/gitlab-ci-yml.git",
/(\.{1,2}|LICENSE|CONTRIBUTING.md|Pages|autodeploy|\.gitlab-ci.yml)\z/
),
Template.new( Template.new(
"https://gitlab.com/gitlab-org/Dockerfile.git", "https://gitlab.com/gitlab-org/Dockerfile.git",
/(\.{1,2}|LICENSE|CONTRIBUTING.md|\.Dockerfile)\z/ /(\.{1,2}|LICENSE|CONTRIBUTING.md|\.Dockerfile)\z/
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
}, },
"dependencies": { "dependencies": {
"@gitlab-org/gitlab-svgs": "^1.29.0", "@gitlab-org/gitlab-svgs": "^1.29.0",
"@gitlab-org/gitlab-ui": "^1.7.0", "@gitlab-org/gitlab-ui": "^1.7.1",
"autosize": "^4.0.0", "autosize": "^4.0.0",
"axios": "^0.17.1", "axios": "^0.17.1",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
......
...@@ -5,14 +5,11 @@ module QA ...@@ -5,14 +5,11 @@ module QA
include Page::Component::GroupsFilter include Page::Component::GroupsFilter
view 'app/views/groups/show.html.haml' do view 'app/views/groups/show.html.haml' do
element :new_project_or_subgroup_dropdown, '.new-project-subgroup' element :new_project_or_subgroup_dropdown
element :new_project_or_subgroup_dropdown_toggle, '.dropdown-toggle' element :new_project_or_subgroup_dropdown_toggle
element :new_project_option, /%li.*data:.*value: "new-project"/ element :new_project_option
element :new_project_button, /%input.*data:.*action: "new-project"/ element :new_subgroup_option
element :new_subgroup_option, /%li.*data:.*value: "new-subgroup"/ element :new_in_group_button
# data-value and data-action get modified by JS for subgroup
element :new_subgroup_button, /%input.*\.js-new-group-child/
end end
view 'app/assets/javascripts/groups/constants.js' do view 'app/assets/javascripts/groups/constants.js' do
...@@ -24,7 +21,7 @@ module QA ...@@ -24,7 +21,7 @@ module QA
end end
def has_new_project_or_subgroup_dropdown? def has_new_project_or_subgroup_dropdown?
page.has_css?(element_selector_css(:new_project_or_subgroup_dropdown)) has_element?(:new_project_or_subgroup_dropdown)
end end
def has_subgroup?(name) def has_subgroup?(name)
...@@ -36,31 +33,29 @@ module QA ...@@ -36,31 +33,29 @@ module QA
end end
def go_to_new_subgroup def go_to_new_subgroup
click_new('subgroup') select_kind :new_subgroup_option
find("input[data-action='new-subgroup']").click click_element :new_in_group_button
end end
def go_to_new_project def go_to_new_project
click_new('project') select_kind :new_project_option
find("input[data-action='new-project']").click click_element :new_in_group_button
end end
private private
def click_new(kind) def select_kind(kind)
within '.new-project-subgroup' do within_element(:new_project_or_subgroup_dropdown) do
css = "li[data-value='new-#{kind}']"
# May need to click again because it is possible to click the button quicker than the JS is bound # May need to click again because it is possible to click the button quicker than the JS is bound
wait(reload: false) do wait(reload: false) do
find('.dropdown-toggle').click click_element :new_project_or_subgroup_dropdown_toggle
page.has_css?(css) has_element?(kind)
end end
find(css).click click_element kind
end end
end end
end end
......
...@@ -73,24 +73,72 @@ describe "Admin Runners" do ...@@ -73,24 +73,72 @@ describe "Admin Runners" do
expect(page).to have_text 'No runners found' expect(page).to have_text 'No runners found'
end end
it 'shows correct runner when status is selected and search term is entered' do
create(:ci_runner, description: 'runner-a-1', active: true)
create(:ci_runner, description: 'runner-a-2', active: false)
create(:ci_runner, description: 'runner-b-1', active: true)
visit admin_runners_path
input_filtered_search_keys('status:active')
expect(page).to have_content 'runner-a-1'
expect(page).to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
input_filtered_search_keys('status:active runner-a')
expect(page).to have_content 'runner-a-1'
expect(page).not_to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
end
end end
it 'shows correct runner when status is selected and search term is entered', :js do describe 'filter by type', :js do
create(:ci_runner, description: 'runner-a-1', active: true) it 'shows correct runner when type matches' do
create(:ci_runner, description: 'runner-a-2', active: false) create :ci_runner, :project, description: 'runner-project'
create(:ci_runner, description: 'runner-b-1', active: true) create :ci_runner, :group, description: 'runner-group'
visit admin_runners_path visit admin_runners_path
expect(page).to have_content 'runner-project'
expect(page).to have_content 'runner-group'
input_filtered_search_keys('status:active') input_filtered_search_keys('type:project_type')
expect(page).to have_content 'runner-a-1' expect(page).to have_content 'runner-project'
expect(page).to have_content 'runner-b-1' expect(page).not_to have_content 'runner-group'
expect(page).not_to have_content 'runner-a-2' end
it 'shows no runner when type does not match' do
create :ci_runner, :project, description: 'runner-project'
create :ci_runner, :group, description: 'runner-group'
input_filtered_search_keys('status:active runner-a') visit admin_runners_path
expect(page).to have_content 'runner-a-1'
expect(page).not_to have_content 'runner-b-1' input_filtered_search_keys('type:instance_type')
expect(page).not_to have_content 'runner-a-2'
expect(page).not_to have_content 'runner-project'
expect(page).not_to have_content 'runner-group'
expect(page).to have_text 'No runners found'
end
it 'shows correct runner when type is selected and search term is entered' do
create :ci_runner, :project, description: 'runner-a-1'
create :ci_runner, :instance, description: 'runner-a-2'
create :ci_runner, :project, description: 'runner-b-1'
visit admin_runners_path
input_filtered_search_keys('type:project_type')
expect(page).to have_content 'runner-a-1'
expect(page).to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
input_filtered_search_keys('type:project_type runner-a')
expect(page).to have_content 'runner-a-1'
expect(page).not_to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
end
end end
it 'sorts by last contact date', :js do it 'sorts by last contact date', :js do
......
...@@ -29,6 +29,14 @@ describe Admin::RunnersFinder do ...@@ -29,6 +29,14 @@ describe Admin::RunnersFinder do
end end
end end
context 'filter by runner type' do
it 'calls the corresponding scope on Ci::Runner' do
expect(Ci::Runner).to receive(:project_type).and_call_original
described_class.new(params: { type_type: 'project_type' }).execute
end
end
context 'sort' do context 'sort' do
context 'without sort param' do context 'without sort param' do
it 'sorts by created_at' do it 'sorts by created_at' do
......
...@@ -185,4 +185,41 @@ describe('Dashboard', () => { ...@@ -185,4 +185,41 @@ describe('Dashboard', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('when the window resizes', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
jasmine.clock().install();
});
afterEach(() => {
mock.restore();
jasmine.clock().uninstall();
});
it('rerenders the dashboard when the sidebar is resized', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
});
expect(component.forceRedraw).toEqual(0);
const navSidebarEl = document.querySelector('.nav-sidebar');
navSidebarEl.classList.add('nav-sidebar-collapsed');
Vue.nextTick()
.then(() => {
jasmine.clock().tick(1000);
return Vue.nextTick();
})
.then(() => {
expect(component.forceRedraw).toEqual(component.elWidth);
done();
})
.catch(done.fail);
});
});
}); });
...@@ -8,7 +8,7 @@ describe Gitlab::Ci::External::File::Local do ...@@ -8,7 +8,7 @@ describe Gitlab::Ci::External::File::Local do
describe '#valid?' do describe '#valid?' do
context 'when is a valid local path' do context 'when is a valid local path' do
let(:location) { '/vendor/gitlab-ci-yml/existent-file.yml' } let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' }
before do before do
allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("image: 'ruby2:2'") allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("image: 'ruby2:2'")
...@@ -20,7 +20,7 @@ describe Gitlab::Ci::External::File::Local do ...@@ -20,7 +20,7 @@ describe Gitlab::Ci::External::File::Local do
end end
context 'when is not a valid local path' do context 'when is not a valid local path' do
let(:location) { '/vendor/gitlab-ci-yml/non-existent-file.yml' } let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
it 'should return false' do it 'should return false' do
expect(local_file.valid?).to be_falsy expect(local_file.valid?).to be_falsy
...@@ -48,7 +48,7 @@ describe Gitlab::Ci::External::File::Local do ...@@ -48,7 +48,7 @@ describe Gitlab::Ci::External::File::Local do
- bundle install --jobs $(nproc) "${FLAGS[@]}" - bundle install --jobs $(nproc) "${FLAGS[@]}"
HEREDOC HEREDOC
end end
let(:location) { '/vendor/gitlab-ci-yml/existent-file.yml' } let(:location) { '/lib/gitlab/ci/templates/existent-file.yml' }
before do before do
allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return(local_file_content) allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return(local_file_content)
...@@ -60,7 +60,7 @@ describe Gitlab::Ci::External::File::Local do ...@@ -60,7 +60,7 @@ describe Gitlab::Ci::External::File::Local do
end end
context 'with an invalid file' do context 'with an invalid file' do
let(:location) { '/vendor/gitlab-ci-yml/non-existent-file.yml' } let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
it 'should be nil' do it 'should be nil' do
expect(local_file.content).to be_nil expect(local_file.content).to be_nil
...@@ -69,7 +69,7 @@ describe Gitlab::Ci::External::File::Local do ...@@ -69,7 +69,7 @@ describe Gitlab::Ci::External::File::Local do
end end
describe '#error_message' do describe '#error_message' do
let(:location) { '/vendor/gitlab-ci-yml/non-existent-file.yml' } let(:location) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
it 'should return an error message' do it 'should return an error message' do
expect(local_file.error_message).to eq("Local file '#{location}' is not valid.") expect(local_file.error_message).to eq("Local file '#{location}' is not valid.")
......
...@@ -17,7 +17,7 @@ describe Gitlab::Ci::External::Mapper do ...@@ -17,7 +17,7 @@ describe Gitlab::Ci::External::Mapper do
context 'when the string is a local file' do context 'when the string is a local file' do
let(:values) do let(:values) do
{ {
include: '/vendor/gitlab-ci-yml/non-existent-file.yml', include: '/lib/gitlab/ci/templates/non-existent-file.yml',
image: 'ruby:2.2' image: 'ruby:2.2'
} }
end end
...@@ -61,7 +61,7 @@ describe Gitlab::Ci::External::Mapper do ...@@ -61,7 +61,7 @@ describe Gitlab::Ci::External::Mapper do
include: include:
[ [
remote_url, remote_url,
'/vendor/gitlab-ci-yml/template.yml' '/lib/gitlab/ci/templates/template.yml'
], ],
image: 'ruby:2.2' image: 'ruby:2.2'
} }
......
...@@ -16,12 +16,12 @@ describe Gitlab::Ci::External::Processor do ...@@ -16,12 +16,12 @@ describe Gitlab::Ci::External::Processor do
end end
context 'when an invalid local file is defined' do context 'when an invalid local file is defined' do
let(:values) { { include: '/vendor/gitlab-ci-yml/non-existent-file.yml', image: 'ruby:2.2' } } let(:values) { { include: '/lib/gitlab/ci/templates/non-existent-file.yml', image: 'ruby:2.2' } }
it 'should raise an error' do it 'should raise an error' do
expect { processor.perform }.to raise_error( expect { processor.perform }.to raise_error(
described_class::FileError, described_class::FileError,
"Local file '/vendor/gitlab-ci-yml/non-existent-file.yml' is not valid." "Local file '/lib/gitlab/ci/templates/non-existent-file.yml' is not valid."
) )
end end
end end
...@@ -79,7 +79,7 @@ describe Gitlab::Ci::External::Processor do ...@@ -79,7 +79,7 @@ describe Gitlab::Ci::External::Processor do
end end
context 'with a valid local external file is defined' do context 'with a valid local external file is defined' do
let(:values) { { include: '/vendor/gitlab-ci-yml/template.yml', image: 'ruby:2.2' } } let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }
let(:local_file_content) do let(:local_file_content) do
<<-HEREDOC <<-HEREDOC
before_script: before_script:
...@@ -145,7 +145,7 @@ describe Gitlab::Ci::External::Processor do ...@@ -145,7 +145,7 @@ describe Gitlab::Ci::External::Processor do
end end
context 'when external files are defined but not valid' do context 'when external files are defined but not valid' do
let(:values) { { include: '/vendor/gitlab-ci-yml/template.yml', image: 'ruby:2.2' } } let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'ruby:2.2' } }
let(:local_file_content) { 'invalid content file ////' } let(:local_file_content) { 'invalid content file ////' }
......
# frozen_string_literal: true
require 'spec_helper'
describe "CI YML Templates" do
Gitlab::Template::GitlabCiYmlTemplate.all.each do |template|
it "#{template.name} should be valid" do
expect { Gitlab::Ci::YamlProcessor.new(template.content) }.not_to raise_error
end
end
end
...@@ -1371,18 +1371,6 @@ module Gitlab ...@@ -1371,18 +1371,6 @@ module Gitlab
end end
end end
describe "Validate configuration templates" do
templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml")
templates.each do |file|
it "does not return errors for #{file}" do
file = File.read(file)
expect { Gitlab::Ci::YamlProcessor.new(file) }.not_to raise_error
end
end
end
describe "#validation_message" do describe "#validation_message" do
subject { Gitlab::Ci::YamlProcessor.validation_message(content) } subject { Gitlab::Ci::YamlProcessor.validation_message(content) }
......
...@@ -40,7 +40,7 @@ describe Gitlab::Template::GitlabCiYmlTemplate do ...@@ -40,7 +40,7 @@ describe Gitlab::Template::GitlabCiYmlTemplate do
describe '#content' do describe '#content' do
it 'loads the full file' do it 'loads the full file' do
gitignore = subject.new(Rails.root.join('vendor/gitlab-ci-yml/Ruby.gitlab-ci.yml')) gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml'))
expect(gitignore.name).to eq 'Ruby' expect(gitignore.name).to eq 'Ruby'
expect(gitignore.content).to start_with('#') expect(gitignore.content).to start_with('#')
......
...@@ -25,36 +25,71 @@ describe API::Runners do ...@@ -25,36 +25,71 @@ describe API::Runners do
describe 'GET /runners' do describe 'GET /runners' do
context 'authorized user' do context 'authorized user' do
it 'returns user available runners' do it 'returns response status and headers' do
get api('/runners', user) get api('/runners', user)
shared = json_response.any? { |r| r['is_shared'] }
descriptions = json_response.map { |runner| runner['description'] }
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array end
expect(json_response[0]).to have_key('ip_address')
expect(descriptions).to contain_exactly( it 'returns user available runners' do
'Project runner', 'Two projects runner', 'Group runner' get api('/runners', user)
)
expect(shared).to be_falsey expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner')
]
end end
it 'filters runners by scope' do it 'filters runners by scope' do
get api('/runners?scope=active', user) create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
get api('/runners?scope=paused', user)
shared = json_response.any? { |r| r['is_shared'] }
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response[0]).to have_key('ip_address') expect(json_response).to match_array [
expect(shared).to be_falsey a_hash_including('description' => 'Inactive project runner')
]
end end
it 'avoids filtering if scope is invalid' do it 'avoids filtering if scope is invalid' do
get api('/runners?scope=unknown', user) get api('/runners?scope=unknown', user)
expect(response).to have_gitlab_http_status(400) expect(response).to have_gitlab_http_status(400)
end end
it 'filters runners by type' do
get api('/runners?type=project_type', user)
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner')
]
end
it 'does not filter by invalid type' do
get api('/runners?type=bogus', user)
expect(response).to have_gitlab_http_status(400)
end
it 'filters runners by status' do
create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
get api('/runners?status=paused', user)
expect(json_response).to match_array [
a_hash_including('description' => 'Inactive project runner')
]
end
it 'does not filter by invalid status' do
get api('/runners?status=bogus', user)
expect(response).to have_gitlab_http_status(400)
end
end end
context 'unauthorized user' do context 'unauthorized user' do
...@@ -69,51 +104,91 @@ describe API::Runners do ...@@ -69,51 +104,91 @@ describe API::Runners do
describe 'GET /runners/all' do describe 'GET /runners/all' do
context 'authorized user' do context 'authorized user' do
context 'with admin privileges' do context 'with admin privileges' do
it 'returns response status and headers' do
get api('/runners/all', admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
end
it 'returns all runners' do it 'returns all runners' do
get api('/runners/all', admin) get api('/runners/all', admin)
shared = json_response.any? { |r| r['is_shared'] } expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner'),
a_hash_including('description' => 'Shared runner')
]
end
it 'filters runners by scope' do
get api('/runners/all?scope=shared', admin)
shared = json_response.all? { |r| r['is_shared'] }
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response[0]).to have_key('ip_address') expect(json_response[0]).to have_key('ip_address')
expect(shared).to be_truthy expect(shared).to be_truthy
end end
end
context 'without admin privileges' do it 'filters runners by scope' do
it 'does not return runners list' do get api('/runners/all?scope=specific', admin)
get api('/runners/all', user)
expect(response).to have_gitlab_http_status(403) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner')
]
end end
end
it 'filters runners by scope' do it 'avoids filtering if scope is invalid' do
get api('/runners/all?scope=shared', admin) get api('/runners/all?scope=unknown', admin)
expect(response).to have_gitlab_http_status(400)
end
shared = json_response.all? { |r| r['is_shared'] } it 'filters runners by type' do
expect(response).to have_gitlab_http_status(200) get api('/runners/all?type=project_type', admin)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response[0]).to have_key('ip_address')
expect(shared).to be_truthy
end
it 'filters runners by scope' do expect(json_response).to match_array [
get api('/runners/all?scope=specific', admin) a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner')
]
end
shared = json_response.any? { |r| r['is_shared'] } it 'does not filter by invalid type' do
expect(response).to have_gitlab_http_status(200) get api('/runners/all?type=bogus', admin)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(response).to have_gitlab_http_status(400)
expect(json_response[0]).to have_key('ip_address') end
expect(shared).to be_falsey
it 'filters runners by status' do
create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
get api('/runners/all?status=paused', admin)
expect(json_response).to match_array [
a_hash_including('description' => 'Inactive project runner')
]
end
it 'does not filter by invalid status' do
get api('/runners/all?status=bogus', admin)
expect(response).to have_gitlab_http_status(400)
end
end end
it 'avoids filtering if scope is invalid' do context 'without admin privileges' do
get api('/runners?scope=unknown', admin) it 'does not return runners list' do
expect(response).to have_gitlab_http_status(400) get api('/runners/all', user)
expect(response).to have_gitlab_http_status(403)
end
end end
end end
...@@ -577,15 +652,69 @@ describe API::Runners do ...@@ -577,15 +652,69 @@ describe API::Runners do
describe 'GET /projects/:id/runners' do describe 'GET /projects/:id/runners' do
context 'authorized user with maintainer privileges' do context 'authorized user with maintainer privileges' do
it "returns project's runners" do it 'returns response status and headers' do
get api('/runners/all', admin)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
end
it 'returns all runners' do
get api("/projects/#{project.id}/runners", user) get api("/projects/#{project.id}/runners", user)
shared = json_response.any? { |r| r['is_shared'] } expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Shared runner')
]
end
it 'filters runners by scope' do
get api("/projects/#{project.id}/runners?scope=specific", user)
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response[0]).to have_key('ip_address') expect(json_response).to match_array [
expect(shared).to be_truthy a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner')
]
end
it 'avoids filtering if scope is invalid' do
get api("/projects/#{project.id}/runners?scope=unknown", user)
expect(response).to have_gitlab_http_status(400)
end
it 'filters runners by type' do
get api("/projects/#{project.id}/runners?type=project_type", user)
expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner')
]
end
it 'does not filter by invalid type' do
get api("/projects/#{project.id}/runners?type=bogus", user)
expect(response).to have_gitlab_http_status(400)
end
it 'filters runners by status' do
create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project])
get api("/projects/#{project.id}/runners?status=paused", user)
expect(json_response).to match_array [
a_hash_including('description' => 'Inactive project runner')
]
end
it 'does not filter by invalid status' do
get api("/projects/#{project.id}/runners?status=bogus", user)
expect(response).to have_gitlab_http_status(400)
end end
end end
......
image: ruby:2.4-alpine
test:
script: ./verify_templates.rb
## Developer Certificate of Origin + License
By contributing to GitLab B.V., You accept and agree to the following terms and
conditions for Your present and future Contributions submitted to GitLab B.V.
Except for the license granted herein to GitLab B.V. and recipients of software
distributed by GitLab B.V., You reserve all right, title, and interest in and to
Your Contributions. All Contributions are subject to the following DCO + License
terms.
[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
_This notice should stay as the first item in the CONTRIBUTING.md file._
## Code of conduct
As contributors and maintainers of this project, we pledge to respect all people
who contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual
language or imagery, derogatory comments or personal attacks, trolling, public
or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct. Project maintainers who do not follow the
Code of Conduct may be removed from the project team.
This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior can be
reported by emailing contact@gitlab.com.
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
[contributor-covenant]: http://contributor-covenant.org
[individual-agreement]: https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html
[corporate-agreement]: https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html
Copyright (c) 2011-2017 GitLab B.V.
With regard to the GitLab Software:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
For all third party components incorporated into the GitLab Software, those
components are licensed under the original license provided by the owner of the
applicable component.
...@@ -164,9 +164,9 @@ ...@@ -164,9 +164,9 @@
version "1.29.0" version "1.29.0"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70"
"@gitlab-org/gitlab-ui@^1.7.0": "@gitlab-org/gitlab-ui@^1.7.1":
version "1.7.0" version "1.7.1"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.7.0.tgz#cf37b7262f90af42664d4d1600917271a8f8fb28" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.7.1.tgz#e9cce86cb7e34311405e705c1de674276b453f17"
dependencies: dependencies:
"@gitlab-org/gitlab-svgs" "^1.23.0" "@gitlab-org/gitlab-svgs" "^1.23.0"
bootstrap-vue "^2.0.0-rc.11" bootstrap-vue "^2.0.0-rc.11"
......
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