Commit 0aefb516 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into backstage/gb/migrate-stages-statuses

* master: (97 commits)
  Bulk update sidebar UI polish
  Fix margin in mini graph for commits box
  Add space between words in language dropdown
  Fix support for old CI API when image or services are not specified
  Short-circuit build coverage extraction for empty regexes
  Update VERSION to 9.5.0-pre
  Update CHANGELOG.md for 9.4.0
  Compress gitlab svg logo
  Fix the gcovr coverage regex by removing line separators before scanning
  Capitalize Sidekiq word in dev doc
  Add 1px to breadcrumbs min height
  Update nginx docs
  Use custom font SVG for logo
  Port spinach tests to rspec feature specs
  Merge branch 'fix-re2-infinite-loop-nick' into 'security-9-3'
  Copy-edit background migrations guidelines
  Add Traditional Chinese in Taiwan translations of Pipeline Schedules
  Fix new project selectors in GitLab QA
  Reword success to passing for pipeline badges
  Rename build to pipeline for status badges
  ...
parents 5505795e 883488e0
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
cache: .default-cache: &default-cache
key: "ruby-233-with-yarn" key: "ruby-233-with-yarn"
paths: paths:
- vendor/ruby - vendor/ruby
- .yarn-cache/ - .yarn-cache/
.push-cache: &push-cache
cache:
<<: *default-cache
policy: push
.pull-cache: &pull-cache
cache:
<<: *default-cache
policy: pull
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
...@@ -24,11 +34,11 @@ before_script: ...@@ -24,11 +34,11 @@ before_script:
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
stages: stages:
- build - build
- prepare - prepare
- test - test
- post-test - post-test
- pages - pages
# Predefined scopes # Predefined scopes
.dedicated-runner: &dedicated-runner .dedicated-runner: &dedicated-runner
...@@ -41,10 +51,6 @@ stages: ...@@ -41,10 +51,6 @@ stages:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
KNAPSACK_S3_BUCKET: "gitlab-ce-cache" KNAPSACK_S3_BUCKET: "gitlab-ce-cache"
cache:
key: "knapsack"
paths:
- knapsack/
artifacts: artifacts:
expire_in: 31d expire_in: 31d
paths: paths:
...@@ -79,8 +85,9 @@ stages: ...@@ -79,8 +85,9 @@ stages:
- /(^docs[\/-].*|.*-docs$)/ - /(^docs[\/-].*|.*-docs$)/
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
...@@ -110,8 +117,9 @@ stages: ...@@ -110,8 +117,9 @@ stages:
<<: *except-docs <<: *except-docs
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
...@@ -157,6 +165,7 @@ build-package: ...@@ -157,6 +165,7 @@ build-package:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
stage: build stage: build
cache: {}
when: manual when: manual
script: script:
- scripts/trigger-build - scripts/trigger-build
...@@ -170,6 +179,11 @@ knapsack: ...@@ -170,6 +179,11 @@ knapsack:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
stage: prepare stage: prepare
cache:
key: knapsack
paths:
- knapsack/
policy: pull
script: script:
- mkdir -p knapsack/${CI_PROJECT_NAME}/ - mkdir -p knapsack/${CI_PROJECT_NAME}/
- wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
...@@ -182,6 +196,11 @@ update-knapsack: ...@@ -182,6 +196,11 @@ update-knapsack:
<<: *dedicated-runner <<: *dedicated-runner
<<: *only-canonical-masters <<: *only-canonical-masters
stage: post-test stage: post-test
cache:
key: knapsack
paths:
- knapsack/
policy: push
script: script:
- retry gem install fog-aws mime-types - retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
...@@ -194,6 +213,8 @@ setup-test-env: ...@@ -194,6 +213,8 @@ setup-test-env:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
stage: prepare stage: prepare
cache:
<<: *default-cache
script: script:
- node --version - node --version
- yarn install --pure-lockfile --cache-folder .yarn-cache - yarn install --pure-lockfile --cache-folder .yarn-cache
...@@ -273,6 +294,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql ...@@ -273,6 +294,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
# Static analysis jobs # Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
<<: *pull-cache
variables: variables:
SIMPLECOV: "false" SIMPLECOV: "false"
SETUP_DB: "false" SETUP_DB: "false"
...@@ -281,6 +303,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql ...@@ -281,6 +303,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test stage: test
script: script:
- bundle exec rake $CI_JOB_NAME - bundle exec rake $CI_JOB_NAME
...@@ -297,9 +320,9 @@ static-analysis: ...@@ -297,9 +320,9 @@ static-analysis:
# - Check validity of relative links # - Check validity of relative links
# - Make sure cURL examples in API docs use the full switches # - Make sure cURL examples in API docs use the full switches
docs lint: docs lint:
<<: *dedicated-runner
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test stage: test
<<: *dedicated-runner
cache: {} cache: {}
dependencies: [] dependencies: []
before_script: [] before_script: []
...@@ -342,9 +365,10 @@ ee_compat_check: ...@@ -342,9 +365,10 @@ ee_compat_check:
# DB migration, rollback, and seed jobs # DB migration, rollback, and seed jobs
.db-migrate-reset: &db-migrate-reset .db-migrate-reset: &db-migrate-reset
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
script: script:
- bundle exec rake db:migrate:reset - bundle exec rake db:migrate:reset
...@@ -357,11 +381,12 @@ db:migrate:reset-mysql: ...@@ -357,11 +381,12 @@ db:migrate:reset-mysql:
<<: *use-mysql <<: *use-mysql
.migration-paths: &migration-paths .migration-paths: &migration-paths
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *only-canonical-masters
<<: *pull-cache
stage: test
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
<<: *only-canonical-masters
script: script:
- git fetch origin v8.14.10 - git fetch origin v8.14.10
- git checkout -f FETCH_HEAD - git checkout -f FETCH_HEAD
...@@ -382,9 +407,10 @@ migration:path-mysql: ...@@ -382,9 +407,10 @@ migration:path-mysql:
<<: *use-mysql <<: *use-mysql
.db-rollback: &db-rollback .db-rollback: &db-rollback
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
script: script:
- bundle exec rake db:rollback STEP=120 - bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate - bundle exec rake db:migrate
...@@ -398,9 +424,10 @@ db:rollback-mysql: ...@@ -398,9 +424,10 @@ db:rollback-mysql:
<<: *use-mysql <<: *use-mysql
.db-seed_fu: &db-seed_fu .db-seed_fu: &db-seed_fu
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
variables: variables:
SIZE: "1" SIZE: "1"
SETUP_DB: "false" SETUP_DB: "false"
...@@ -425,9 +452,10 @@ db:seed_fu-mysql: ...@@ -425,9 +452,10 @@ db:seed_fu-mysql:
# Frontend-related jobs # Frontend-related jobs
gitlab:assets:compile: gitlab:assets:compile:
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
dependencies: [] dependencies: []
variables: variables:
NODE_ENV: "production" NODE_ENV: "production"
...@@ -445,14 +473,15 @@ gitlab:assets:compile: ...@@ -445,14 +473,15 @@ gitlab:assets:compile:
name: webpack-report name: webpack-report
expire_in: 31d expire_in: 31d
paths: paths:
- webpack-report/ - webpack-report/
karma: karma:
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
<<: *use-pg <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
CHROME_LOG_FILE: "chrome_debug.log" CHROME_LOG_FILE: "chrome_debug.log"
...@@ -470,6 +499,7 @@ karma: ...@@ -470,6 +499,7 @@ karma:
codeclimate: codeclimate:
<<: *except-docs <<: *except-docs
<<: *pull-cache
before_script: [] before_script: []
image: docker:latest image: docker:latest
stage: test stage: test
...@@ -485,10 +515,11 @@ codeclimate: ...@@ -485,10 +515,11 @@ codeclimate:
paths: [codeclimate.json] paths: [codeclimate.json]
coverage: coverage:
stage: post-test
services: []
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: post-test
services: []
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
...@@ -505,7 +536,10 @@ coverage: ...@@ -505,7 +536,10 @@ coverage:
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: post-test stage: post-test
dependencies:
- setup-test-env
before_script: [] before_script: []
script: script:
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
...@@ -517,9 +551,10 @@ lint:javascript:report: ...@@ -517,9 +551,10 @@ lint:javascript:report:
- eslint-report.html - eslint-report.html
pages: pages:
<<: *dedicated-runner
<<: *pull-cache
before_script: [] before_script: []
stage: pages stage: pages
<<: *dedicated-runner
dependencies: dependencies:
- coverage - coverage
- karma - karma
...@@ -543,6 +578,7 @@ pages: ...@@ -543,6 +578,7 @@ pages:
# rubygems.org in the future. # rubygems.org in the future.
cache gems: cache gems:
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
only: only:
- tags - tags
variables: variables:
...@@ -557,8 +593,9 @@ cache gems: ...@@ -557,8 +593,9 @@ cache gems:
- master@gitlab-org/gitlab-ee - master@gitlab-org/gitlab-ee
gitlab_git_test: gitlab_git_test:
<<: *pull-cache
<<: *except-docs
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
script: script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes - spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
<<: *except-docs
This diff is collapsed.
...@@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false ...@@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.19.0' gem 'grape', '~> 0.19.2'
gem 'grape-entity', '~> 0.6.0' gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
...@@ -386,7 +386,7 @@ gem 'vmstat', '~> 2.3.0' ...@@ -386,7 +386,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.14.0' gem 'gitaly', '~> 0.18.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -269,7 +269,7 @@ GEM ...@@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.14.0) gitaly (0.18.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -332,13 +332,13 @@ GEM ...@@ -332,13 +332,13 @@ GEM
multi_json (~> 1.11) multi_json (~> 1.11)
os (~> 0.9) os (~> 0.9)
signet (~> 0.7) signet (~> 0.7)
grape (0.19.1) grape (0.19.2)
activesupport activesupport
builder builder
hashie (>= 2.1.0) hashie (>= 2.1.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
mustermann-grape (~> 0.4.0) mustermann-grape (~> 1.0.0)
rack (>= 1.3.0) rack (>= 1.3.0)
rack-accept rack-accept
virtus (>= 1.0.0) virtus (>= 1.0.0)
...@@ -463,10 +463,9 @@ GEM ...@@ -463,10 +463,9 @@ GEM
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (0.4.0) mustermann (1.0.0)
tool (~> 0.2) mustermann-grape (1.0.0)
mustermann-grape (0.4.0) mustermann (~> 1.0.0)
mustermann (= 0.4.0)
mysql2 (0.4.5) mysql2 (0.4.5)
net-ldap (0.12.1) net-ldap (0.12.1)
netrc (0.11.0) netrc (0.11.0)
...@@ -850,7 +849,6 @@ GEM ...@@ -850,7 +849,6 @@ GEM
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
toml-rb (0.3.15) toml-rb (0.3.15)
citrus (~> 3.0, > 3.0) citrus (~> 3.0, > 3.0)
tool (0.2.3)
truncato (0.7.8) truncato (0.7.8)
htmlentities (~> 4.3.1) htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1) nokogiri (~> 1.6.1)
...@@ -972,7 +970,7 @@ DEPENDENCIES ...@@ -972,7 +970,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.14.0) gitaly (~> 0.18.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -981,7 +979,7 @@ DEPENDENCIES ...@@ -981,7 +979,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
grape (~> 0.19.0) grape (~> 0.19.2)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.21.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
......
9.4.0-pre 9.5.0-pre
...@@ -41,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; ...@@ -41,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing'; import Landing from './landing';
import BlobForkSuggestion from './blob/blob_fork_suggestion'; import BlobForkSuggestion from './blob/blob_fork_suggestion';
import UserCallout from './user_callout'; import UserCallout from './user_callout';
import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags';
import ShortcutsWiki from './shortcuts_wiki'; import ShortcutsWiki from './shortcuts_wiki';
import Pipelines from './pipelines'; import Pipelines from './pipelines';
import BlobViewer from './blob/viewer/index'; import BlobViewer from './blob/viewer/index';
...@@ -396,12 +395,6 @@ import PerformanceBar from './performance_bar'; ...@@ -396,12 +395,6 @@ import PerformanceBar from './performance_bar';
new Search(); new Search();
break; break;
case 'projects:settings:repository:show': case 'projects:settings:repository:show':
// Initialize Protected Branch Settings
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
// Initialize Protected Tag Settings
new ProtectedTagCreate();
new ProtectedTagEditList();
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
break; break;
......
...@@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown { class DropdownHint extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { input, tokenKeys } = options;
super(options);
this.config = { this.config = {
Filter: { Filter: {
template: 'hint', template: 'hint',
......
...@@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown { class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { input, endpoint, symbol } = options;
super(options);
this.symbol = symbol; this.symbol = symbol;
this.config = { this.config = {
Ajax: { Ajax: {
......
...@@ -5,8 +5,9 @@ import './filtered_search_dropdown'; ...@@ -5,8 +5,9 @@ import './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils'; import { addClassIfElementExists } from '../lib/utils/dom_utils';
class DropdownUser extends gl.FilteredSearchDropdown { class DropdownUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { tokenKeys } = options;
super(options);
this.config = { this.config = {
AjaxFilter: { AjaxFilter: {
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`, endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
......
const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
class FilteredSearchDropdown { class FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) { constructor({ droplab, dropdown, input, filter }) {
this.droplab = droplab; this.droplab = droplab;
this.hookId = input && input.id; this.hookId = input && input.id;
this.input = input; this.input = input;
......
...@@ -42,13 +42,19 @@ class FilteredSearchDropdownManager { ...@@ -42,13 +42,19 @@ class FilteredSearchDropdownManager {
milestone: { milestone: {
reference: null, reference: null,
gl: 'DropdownNonUser', gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'], extraArguments: {
endpoint: `${this.baseEndpoint}/milestones.json`,
symbol: '%',
},
element: this.container.querySelector('#js-dropdown-milestone'), element: this.container.querySelector('#js-dropdown-milestone'),
}, },
label: { label: {
reference: null, reference: null,
gl: 'DropdownNonUser', gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'], extraArguments: {
endpoint: `${this.baseEndpoint}/labels.json`,
symbol: '~',
},
element: this.container.querySelector('#js-dropdown-label'), element: this.container.querySelector('#js-dropdown-label'),
}, },
hint: { hint: {
...@@ -97,13 +103,19 @@ class FilteredSearchDropdownManager { ...@@ -97,13 +103,19 @@ class FilteredSearchDropdownManager {
let forceShowList = false; let forceShowList = false;
if (!mappingKey.reference) { if (!mappingKey.reference) {
const dl = this.droplab; const defaultArguments = {
const defaultArguments = droplab: this.droplab,
[null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key]; dropdown: element,
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []); input: this.filteredSearchInput,
tokenKeys: this.filteredSearchTokenKeys,
filter: key,
};
const extraArguments = mappingKey.extraArguments || {};
const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new gl[glClass](<arguments>)` // Passing glArguments to `new gl[glClass](<arguments>)`
mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))(); mappingKey.reference =
new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))();
} }
if (firstLoad) { if (firstLoad) {
......
...@@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar { ...@@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar {
this.toggleCheckboxDisplay(enable); this.toggleCheckboxDisplay(enable);
if (enable) { if (enable) {
this.initAffix();
SidebarHeightManager.init(); SidebarHeightManager.init();
} }
} }
initAffix() {
if (!this.$sidebar.hasClass('affix-top')) {
const offsetTop = $('.scrolling-tabs-container').outerHeight() + $('.sub-nav-scroll').outerHeight();
this.$sidebar.affix({
offset: {
top: offsetTop,
},
});
}
}
updateSelectedIssuableIds() { updateSelectedIssuableIds() {
this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds());
} }
......
/* eslint-disable no-unused-vars */
import ProtectedBranchCreate from './protected_branch_create';
import ProtectedBranchEditList from './protected_branch_edit_list';
$(() => {
const protectedBranchCreate = new ProtectedBranchCreate();
const protectedBranchEditList = new ProtectedBranchEditList();
});
/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */ export default class ProtectedBranchAccessDropdown {
constructor(options) {
this.options = options;
this.initDropdown();
}
(global => { initDropdown() {
global.gl = global.gl || {}; const { $dropdown, data, onSelect } = this.options;
$dropdown.glDropdown({
gl.ProtectedBranchAccessDropdown = class { data,
constructor(options) { selectable: true,
const { $dropdown, data, onSelect } = options; inputId: $dropdown.data('input-id'),
fieldName: $dropdown.data('field-name'),
$dropdown.glDropdown({ toggleLabel(item, $el) {
data: data, if ($el.is('.is-active')) {
selectable: true, return item.text;
inputId: $dropdown.data('input-id'),
fieldName: $dropdown.data('field-name'),
toggleLabel(item, el) {
if (el.is('.is-active')) {
return item.text;
} else {
return 'Select';
}
},
clicked(opts) {
const { e } = opts;
e.preventDefault();
onSelect();
} }
}); return 'Select';
} },
}; clicked(options) {
})(window); options.e.preventDefault();
onSelect();
},
});
}
}
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
/* global ProtectedBranchDropdown */ import ProtectedBranchDropdown from './protected_branch_dropdown';
(global => { export default class ProtectedBranchCreate {
global.gl = global.gl || {}; constructor() {
this.$form = $('.js-new-protected-branch');
gl.ProtectedBranchCreate = class { this.buildDropdowns();
constructor() { }
this.$wrap = this.$form = $('#new_protected_branch');
this.buildDropdowns(); buildDropdowns() {
} const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push');
buildDropdowns() {
const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); // Cache callback
const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); this.onSelectCallback = this.onSelect.bind(this);
// Cache callback // Allowed to Merge dropdown
this.onSelectCallback = this.onSelect.bind(this); this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: $allowedToMergeDropdown,
// Allowed to Merge dropdown data: gon.merge_access_levels,
new gl.ProtectedBranchAccessDropdown({ onSelect: this.onSelectCallback,
$dropdown: $allowedToMergeDropdown, });
data: gon.merge_access_levels,
onSelect: this.onSelectCallback // Allowed to Push dropdown
}); this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: $allowedToPushDropdown,
// Allowed to Push dropdown data: gon.push_access_levels,
new gl.ProtectedBranchAccessDropdown({ onSelect: this.onSelectCallback,
$dropdown: $allowedToPushDropdown, });
data: gon.push_access_levels,
onSelect: this.onSelectCallback // Select default
}); $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
// Select default
$allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); // Protected branch dropdown
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); this.protectedBranchDropdown = new ProtectedBranchDropdown({
$dropdown: this.$form.find('.js-protected-branch-select'),
// Protected branch dropdown onSelect: this.onSelectCallback,
new ProtectedBranchDropdown({ });
$dropdown: this.$wrap.find('.js-protected-branch-select'), }
onSelect: this.onSelectCallback
}); // This will run after clicked callback
} onSelect() {
// Enable submit button
// This will run after clicked callback const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
onSelect() { const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
// Enable submit button const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); }
}
this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
}
};
})(window);
/* eslint-disable comma-dangle, no-unused-vars */ export default class ProtectedBranchDropdown {
/**
class ProtectedBranchDropdown { * @param {Object} options containing
* `$dropdown` target element
* `onSelect` event callback
* $dropdown must be an element created using `dropdown_branch()` rails helper
*/
constructor(options) { constructor(options) {
this.onSelect = options.onSelect; this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown; this.$dropdown = options.$dropdown;
...@@ -12,7 +16,7 @@ class ProtectedBranchDropdown { ...@@ -12,7 +16,7 @@ class ProtectedBranchDropdown {
this.bindEvents(); this.bindEvents();
// Hide footer // Hide footer
this.$dropdownFooter.addClass('hidden'); this.toggleFooter(true);
} }
buildDropdown() { buildDropdown() {
...@@ -21,7 +25,7 @@ class ProtectedBranchDropdown { ...@@ -21,7 +25,7 @@ class ProtectedBranchDropdown {
filterable: true, filterable: true,
remote: false, remote: false,
search: { search: {
fields: ['title'] fields: ['title'],
}, },
selectable: true, selectable: true,
toggleLabel(selected) { toggleLabel(selected) {
...@@ -36,10 +40,9 @@ class ProtectedBranchDropdown { ...@@ -36,10 +40,9 @@ class ProtectedBranchDropdown {
}, },
onFilter: this.toggleCreateNewButton.bind(this), onFilter: this.toggleCreateNewButton.bind(this),
clicked: (options) => { clicked: (options) => {
const { $el, e } = options; options.e.preventDefault();
e.preventDefault();
this.onSelect(); this.onSelect();
} },
}); });
} }
...@@ -64,20 +67,22 @@ class ProtectedBranchDropdown { ...@@ -64,20 +67,22 @@ class ProtectedBranchDropdown {
} }
toggleCreateNewButton(branchName) { toggleCreateNewButton(branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName
};
if (branchName) { if (branchName) {
this.selectedBranch = {
title: branchName,
id: branchName,
text: branchName,
};
this.$dropdownContainer this.$dropdownContainer
.find('.js-create-new-protected-branch code') .find('.js-create-new-protected-branch code')
.text(branchName); .text(branchName);
} }
this.$dropdownFooter.toggleClass('hidden', !branchName); this.toggleFooter(!branchName);
} }
}
window.ProtectedBranchDropdown = ProtectedBranchDropdown; toggleFooter(toggleState) {
this.$dropdownFooter.toggleClass('hidden', toggleState);
}
}
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ /* eslint-disable no-new */
/* global Flash */ /* global Flash */
(global => { import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
global.gl = global.gl || {};
gl.ProtectedBranchEdit = class { export default class ProtectedBranchEdit {
constructor(options) { constructor(options) {
this.$wrap = options.$wrap; this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.onSelectCallback = this.onSelect.bind(this);
this.buildDropdowns(); this.buildDropdowns();
} }
buildDropdowns() { buildDropdowns() {
// Allowed to merge dropdown // Allowed to merge dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToMergeDropdown, $dropdown: this.$allowedToMergeDropdown,
data: gon.merge_access_levels, data: gon.merge_access_levels,
onSelect: this.onSelect.bind(this) onSelect: this.onSelectCallback,
}); });
// Allowed to push dropdown // Allowed to push dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToPushDropdown, $dropdown: this.$allowedToPushDropdown,
data: gon.push_access_levels, data: gon.push_access_levels,
onSelect: this.onSelect.bind(this) onSelect: this.onSelectCallback,
}); });
} }
onSelect() { onSelect() {
const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
// Do not update if one dropdown has not selected any option // Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
this.$allowedToMergeDropdown.disable(); this.$allowedToMergeDropdown.disable();
this.$allowedToPushDropdown.disable(); this.$allowedToPushDropdown.disable();
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: this.$wrap.data('url'), url: this.$wrap.data('url'),
dataType: 'json', dataType: 'json',
data: { data: {
_method: 'PATCH', _method: 'PATCH',
protected_branch: { protected_branch: {
merge_access_levels_attributes: [{ merge_access_levels_attributes: [{
id: this.$allowedToMergeDropdown.data('access-level-id'), id: this.$allowedToMergeDropdown.data('access-level-id'),
access_level: $allowedToMergeInput.val() access_level: $allowedToMergeInput.val(),
}], }],
push_access_levels_attributes: [{ push_access_levels_attributes: [{
id: this.$allowedToPushDropdown.data('access-level-id'), id: this.$allowedToPushDropdown.data('access-level-id'),
access_level: $allowedToPushInput.val() access_level: $allowedToPushInput.val(),
}] }],
}
}, },
error() { },
$.scrollTo(0); error() {
new Flash('Failed to update branch!'); new Flash('Failed to update branch!', null, $('.js-protected-branches-list'));
} },
}).always(() => { }).always(() => {
this.$allowedToMergeDropdown.enable(); this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable(); this.$allowedToPushDropdown.enable();
}); });
} }
}; }
})(window);
/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */ /* eslint-disable no-new */
(global => { import ProtectedBranchEdit from './protected_branch_edit';
global.gl = global.gl || {};
gl.ProtectedBranchEditList = class { export default class ProtectedBranchEditList {
constructor() { constructor() {
this.$wrap = $('.protected-branches-list'); this.$wrap = $('.protected-branches-list');
this.initEditForm();
}
// Build edit forms initEditForm() {
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new gl.ProtectedBranchEdit({ new ProtectedBranchEdit({
$wrap: $(el) $wrap: $(el),
});
}); });
} });
}; }
})(window); }
import './protected_branch_access_dropdown';
import './protected_branch_create';
import './protected_branch_dropdown';
import './protected_branch_edit';
import './protected_branch_edit_list';
export { default as ProtectedTagCreate } from './protected_tag_create'; /* eslint-disable no-unused-vars */
export { default as ProtectedTagEditList } from './protected_tag_edit_list';
import ProtectedTagCreate from './protected_tag_create';
import ProtectedTagEditList from './protected_tag_edit_list';
$(() => {
const protectedtTagCreate = new ProtectedTagCreate();
const protectedtTagEditList = new ProtectedTagEditList();
});
...@@ -190,14 +190,6 @@ ...@@ -190,14 +190,6 @@
display: none; display: none;
} }
.btn,
.dropdown,
.dropdown-toggle,
input,
form {
height: 35px;
}
input { input {
display: inline-block; display: inline-block;
position: relative; position: relative;
......
...@@ -92,7 +92,6 @@ ...@@ -92,7 +92,6 @@
@mixin maintain-sidebar-dimensions { @mixin maintain-sidebar-dimensions {
display: block; display: block;
width: $gutter-width; width: $gutter-width;
padding: 10px 0;
} }
.issues-bulk-update.right-sidebar { .issues-bulk-update.right-sidebar {
...@@ -104,6 +103,15 @@ ...@@ -104,6 +103,15 @@
&.right-sidebar-expanded { &.right-sidebar-expanded {
@include maintain-sidebar-dimensions; @include maintain-sidebar-dimensions;
width: $gutter-width; width: $gutter-width;
.issuable-sidebar-header {
// matches `.top-area .nav-controls` for issuable index pages
padding: 11px 0;
}
.block:last-of-type {
border: none;
}
} }
&.right-sidebar-collapsed { &.right-sidebar-collapsed {
......
...@@ -41,10 +41,22 @@ header.navbar-gitlab-new { ...@@ -41,10 +41,22 @@ header.navbar-gitlab-new {
} }
} }
.logo-text {
line-height: initial;
svg {
width: 55px;
height: 15px;
margin: 0;
fill: $white-light;
}
}
&:hover, &:hover,
&:focus { &:focus {
color: $tanuki-yellow; .logo-text svg {
text-decoration: none; fill: $tanuki-yellow;
}
} }
} }
} }
...@@ -274,7 +286,7 @@ header.navbar-gitlab-new { ...@@ -274,7 +286,7 @@ header.navbar-gitlab-new {
.breadcrumbs { .breadcrumbs {
display: flex; display: flex;
min-height: 60px; min-height: 61px;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
......
...@@ -23,6 +23,10 @@ $new-sidebar-width: 220px; ...@@ -23,6 +23,10 @@ $new-sidebar-width: 220px;
position: fixed; position: fixed;
height: 100%; height: 100%;
} }
.issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
padding: 10px 0 15px;
}
} }
.context-header { .context-header {
...@@ -165,7 +169,6 @@ $new-sidebar-width: 220px; ...@@ -165,7 +169,6 @@ $new-sidebar-width: 220px;
> li { > li {
a { a {
font-size: 12px;
padding: 8px 16px 8px 24px; padding: 8px 16px 8px 24px;
&:hover, &:hover,
...@@ -262,7 +265,7 @@ $new-sidebar-width: 220px; ...@@ -262,7 +265,7 @@ $new-sidebar-width: 220px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty // scss-lint:disable DuplicateProperty
height: calc(100vh - 120px); height: calc(100vh - 180px);
// scss-lint:enable DuplicateProperty // scss-lint:enable DuplicateProperty
} }
} }
......
...@@ -54,7 +54,11 @@ ...@@ -54,7 +54,11 @@
.mr-widget-pipeline-graph { .mr-widget-pipeline-graph {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin: 0 -6px 0 0; margin-right: 4px;
.stage-cell .stage-container {
margin: 3px 3px 3px 0;
}
.dropdown-menu { .dropdown-menu {
margin-top: 11px; margin-top: 11px;
......
...@@ -742,7 +742,8 @@ pre.light-well { ...@@ -742,7 +742,8 @@ pre.light-well {
} }
} }
.protected-tags-list { .protected-tags-list,
.protected-branches-list {
.dropdown-menu-toggle { .dropdown-menu-toggle {
width: 100%; width: 100%;
max-width: 300px; max-width: 300px;
......
...@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController ...@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController
end end
def retry def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger) result = hook.execute(hook_log.request_data, hook_log.trigger)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_to edit_admin_hook_path(@hook) redirect_to edit_admin_hook_path(@hook)
end end
......
...@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController
end end
def test def test
status, message = hook.execute(sample_hook_data, 'system_hooks') result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_back_or_default redirect_back_or_default
end end
...@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController
:url :url
) )
end end
def sample_hook_data
{
event_name: "project_create",
name: "Ruby",
path: "ruby",
project_id: 1,
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
end
end end
...@@ -3,11 +3,14 @@ module HooksExecution ...@@ -3,11 +3,14 @@ module HooksExecution
private private
def set_hook_execution_notice(status, message) def set_hook_execution_notice(result)
if status && status >= 200 && status < 400 http_status = result[:http_status]
flash[:notice] = "Hook executed successfully: HTTP #{status}" message = result[:message]
elsif status
flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}" if http_status && http_status >= 200 && http_status < 400
flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
elsif http_status
flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
else else
flash[:alert] = "Hook execution failed: #{message}" flash[:alert] = "Hook execution failed: #{message}"
end end
......
...@@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController ...@@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index] before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index] before_action :no_cache_headers, except: [:index]
def build def pipeline
build_status = Gitlab::Badge::Build::Status pipeline_status = Gitlab::Badge::Pipeline::Status
.new(project, params[:ref]) .new(project, params[:ref])
render_badge build_status render_badge pipeline_status
end end
def coverage def coverage
......
...@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController ...@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController
end end
def retry def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger) result = hook.execute(hook_log.request_data, hook_log.trigger)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_to edit_project_hook_path(@project, @hook) redirect_to edit_project_hook_path(@project, @hook)
end end
......
...@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index
redirect_to project_settings_integrations_path(@project)
end
def create def create
@hook = @project.hooks.new(hook_params) @hook = @project.hooks.new(hook_params)
@hook.save @hook.save
...@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController
end end
def test def test
if !@project.empty_repo? result = TestHooks::ProjectService.new(hook, current_user, params[:trigger]).execute
status, message = TestHookService.new.execute(hook, current_user)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
end
redirect_back_or_default(default: { action: 'index' }) redirect_back_or_default(default: { action: 'index' })
end end
......
...@@ -35,7 +35,7 @@ module Projects ...@@ -35,7 +35,7 @@ module Projects
def define_badges_variables def define_badges_variables
@ref = params[:ref] || @project.default_branch || 'master' @ref = params[:ref] || @project.default_branch || 'master'
@badges = [Gitlab::Badge::Build::Status, @badges = [Gitlab::Badge::Pipeline::Status,
Gitlab::Badge::Coverage::Report] Gitlab::Badge::Coverage::Report]
@badges.map! do |badge| @badges.map! do |badge|
......
module HooksHelper
def link_to_test_hook(hook, trigger)
path = case hook
when ProjectHook
project = hook.project
test_project_hook_path(project, hook, trigger: trigger)
when SystemHook
test_admin_hook_path(hook, trigger: trigger)
end
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
link_to path, rel: 'nofollow' do
content_tag(:span, trigger_human_name)
end
end
end
...@@ -8,6 +8,6 @@ module TriggersHelper ...@@ -8,6 +8,6 @@ module TriggersHelper
end end
def service_trigger_url(service) def service_trigger_url(service)
"#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger"
end end
end end
...@@ -96,6 +96,14 @@ module Ci ...@@ -96,6 +96,14 @@ module Ci
BuildSuccessWorker.perform_async(id) BuildSuccessWorker.perform_async(id)
end end
end end
before_transition any => [:failed] do |build|
next if build.retries_max.zero?
if build.retries_count < build.retries_max
Ci::Build.retry(build, build.user)
end
end
end end
def detailed_status(current_user) def detailed_status(current_user)
...@@ -130,6 +138,14 @@ module Ci ...@@ -130,6 +138,14 @@ module Ci
success? || failed? || canceled? success? || failed? || canceled?
end end
def retries_count
pipeline.builds.retried.where(name: self.name).count
end
def retries_max
self.options.fetch(:retry, 0).to_i
end
def latest? def latest?
!retried? !retried?
end end
......
class ProjectHook < WebHook class ProjectHook < WebHook
belongs_to :project TRIGGERS = {
push_hooks: :push_events,
tag_push_hooks: :tag_push_events,
issue_hooks: :issues_events,
confidential_issue_hooks: :confidential_issues_events,
note_hooks: :note_events,
merge_request_hooks: :merge_requests_events,
job_hooks: :job_events,
pipeline_hooks: :pipeline_events,
wiki_page_hooks: :wiki_page_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
scope :issue_hooks, -> { where(issues_events: true) } belongs_to :project
scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) } validates :project, presence: true
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :job_hooks, -> { where(job_events: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end end
class ServiceHook < WebHook class ServiceHook < WebHook
belongs_to :service belongs_to :service
validates :service, presence: true
def execute(data) def execute(data)
WebHookService.new(self, data, 'service_hook').execute WebHookService.new(self, data, 'service_hook').execute
......
class SystemHook < WebHook class SystemHook < WebHook
scope :repository_update_hooks, -> { where(repository_update_events: true) } TRIGGERS = {
repository_update_hooks: :repository_update_events,
push_hooks: :push_events,
tag_push_hooks: :tag_push_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
default_value_for :push_events, false default_value_for :push_events, false
default_value_for :repository_update_events, true default_value_for :repository_update_events, true
......
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
include Sortable include Sortable
default_value_for :push_events, true
default_value_for :issues_events, false
default_value_for :confidential_issues_events, false
default_value_for :note_events, false
default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false
default_value_for :job_events, false
default_value_for :pipeline_events, false
default_value_for :repository_update_events, false
default_value_for :enable_ssl_verification, true
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
validates :url, presence: true, url: true validates :url, presence: true, url: true
def execute(data, hook_name) def execute(data, hook_name)
......
...@@ -4,7 +4,7 @@ class SystemHooksService ...@@ -4,7 +4,7 @@ class SystemHooksService
end end
def execute_hooks(data, hooks_scope = :all) def execute_hooks(data, hooks_scope = :all)
SystemHook.send(hooks_scope).each do |hook| SystemHook.public_send(hooks_scope).find_each do |hook|
hook.async_execute(data, 'system_hooks') hook.async_execute(data, 'system_hooks')
end end
end end
......
class TestHookService
def execute(hook, current_user)
data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
hook.execute(data, 'push_hooks')
end
end
module TestHooks
class BaseService
attr_accessor :hook, :current_user, :trigger
def initialize(hook, current_user, trigger)
@hook = hook
@current_user = current_user
@trigger = trigger
end
def execute
trigger_data_method = "#{trigger}_data"
if !self.respond_to?(trigger_data_method, true) ||
!hook.class::TRIGGERS.value?(trigger.to_sym)
return error('Testing not available for this hook')
end
error_message = catch(:validation_error) do
sample_data = self.__send__(trigger_data_method)
return hook.execute(sample_data, trigger)
end
error(error_message)
end
private
def error(message, http_status = nil)
result = {
message: message,
status: :error
}
result[:http_status] = http_status if http_status
result
end
end
end
module TestHooks
class ProjectService < TestHooks::BaseService
private
def project
@project ||= hook.project
end
def push_events_data
throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo?
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
alias_method :tag_push_events_data, :push_events_data
def note_events_data
note = project.notes.first
throw(:validation_error, 'Ensure the project has notes.') unless note.present?
Gitlab::DataBuilder::Note.build(note, current_user)
end
def issues_events_data
issue = project.issues.first
throw(:validation_error, 'Ensure the project has issues.') unless issue.present?
issue.to_hook_data(current_user)
end
alias_method :confidential_issues_events_data, :issues_events_data
def merge_requests_events_data
merge_request = project.merge_requests.first
throw(:validation_error, 'Ensure the project has merge requests.') unless merge_request.present?
merge_request.to_hook_data(current_user)
end
def job_events_data
build = project.builds.first
throw(:validation_error, 'Ensure the project has CI jobs.') unless build.present?
Gitlab::DataBuilder::Build.build(build)
end
def pipeline_events_data
pipeline = project.pipelines.first
throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present?
Gitlab::DataBuilder::Pipeline.build(pipeline)
end
def wiki_page_events_data
page = project.wiki.pages.first
if !project.wiki_enabled? || page.blank?
throw(:validation_error, 'Ensure the wiki is enabled and has pages.')
end
Gitlab::DataBuilder::WikiPage.build(page, current_user, 'create')
end
end
end
module TestHooks
class SystemService < TestHooks::BaseService
private
def project
@project ||= begin
project = Project.first
throw(:validation_error, 'Ensure that at least one project exists.') unless project
project
end
end
def push_events_data
if project.empty_repo?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def tag_push_events_data
if project.repository.tags.empty?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def repository_update_events_data
commit = project.commit
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
unless commit
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
change = Gitlab::DataBuilder::Repository.single_change(
commit.parent_id || Gitlab::Git::BLANK_SHA,
commit.id,
ref
)
Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
end
end
end
...@@ -39,7 +39,11 @@ class WebHookService ...@@ -39,7 +39,11 @@ class WebHookService
execution_duration: Time.now - start_time execution_duration: Time.now - start_time
) )
[response.code, response.to_s] {
status: :success,
http_status: response.code,
message: response.to_s
}
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
log_execution( log_execution(
trigger: hook_name, trigger: hook_name,
...@@ -52,7 +56,10 @@ class WebHookService ...@@ -52,7 +56,10 @@ class WebHookService
Rails.logger.error("WebHook Error => #{e}") Rails.logger.error("WebHook Error => #{e}")
[nil, e.to_s] {
status: :error,
message: e.to_s
}
end end
def async_execute def async_execute
......
module WikiPages module WikiPages
class BaseService < ::BaseService class BaseService < ::BaseService
def hook_data(page, action)
hook_data = {
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
wiki: @project.wiki.hook_attrs,
object_attributes: page.hook_attrs
}
page_url = Gitlab::UrlBuilder.build(page)
hook_data[:object_attributes].merge!(url: page_url, action: action)
hook_data
end
private private
def execute_hooks(page, action = 'create') def execute_hooks(page, action = 'create')
page_data = hook_data(page, action) page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
@project.execute_hooks(page_data, :wiki_page_hooks) @project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks) @project.execute_services(page_data, :wiki_page_hooks)
end end
......
...@@ -315,7 +315,9 @@ ...@@ -315,7 +315,9 @@
%fieldset %fieldset
%legend Metrics - Prometheus %legend Metrics - Prometheus
%p %p
Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available Enable a Prometheus metrics endpoint at
%code= metrics_path
to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
= link_to 'here', admin_health_check_path = link_to 'here', admin_health_check_path
\. This setting requires a \. This setting requires a
= link_to 'restart', help_page_path('administration/restart_gitlab') = link_to 'restart', help_page_path('administration/restart_gitlab')
...@@ -327,10 +329,13 @@ ...@@ -327,10 +329,13 @@
= f.label :prometheus_metrics_enabled do = f.label :prometheus_metrics_enabled do
= f.check_box :prometheus_metrics_enabled = f.check_box :prometheus_metrics_enabled
Enable Prometheus Metrics Enable Prometheus Metrics
- unless Gitlab::Metrics.metrics_folder_present? - unless Gitlab::Metrics.metrics_folder_present?
.help-block .help-block
%strong.cred WARNING: %strong.cred WARNING:
Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory. Environment variable
%code prometheus_multiproc_dir
does not exist or is not pointing to a valid directory.
= link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
%fieldset %fieldset
%legend Profiling - Performance Bar %legend Profiling - Performance Bar
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= render partial: 'form', locals: { form: f, hook: @hook } = render partial: 'form', locals: { form: f, hook: @hook }
.form-actions .form-actions
= f.submit 'Save changes', class: 'btn btn-create' = f.submit 'Save changes', class: 'btn btn-create'
= link_to 'Test hook', test_admin_hook_path(@hook), class: 'btn btn-default' = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } = link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr %hr
......
...@@ -22,12 +22,12 @@ ...@@ -22,12 +22,12 @@
- @hooks.each do |hook| - @hooks.each do |hook|
%li %li
.controls .controls
= link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm' = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-small'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm' = link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm' = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url .monospace= hook.url
%div %div
- %w(repository_update_events push_events tag_push_events issues_events note_events merge_requests_events job_events).each do |trigger| - SystemHook::TRIGGERS.each_value do |event|
- if hook.send(trigger) - if hook.public_send(event)
%span.label.label-gray= trigger.titleize %span.label.label-gray= event.to_s.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'} %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
...@@ -2,26 +2,6 @@ ...@@ -2,26 +2,6 @@
= render "admin/dashboard/head" = render "admin/dashboard/head"
%div{ class: container_class } %div{ class: container_class }
%p.prepend-top-default
%span
To register a new Runner you should enter the following registration
token.
With this token the Runner will request a unique Runner token and use
that for future communication.
%br
Registration token is
%code#runners-token= current_application_settings.runners_registration_token
.bs-callout.clearfix
.pull-left
%p
You can reset runners registration token by pressing a button below.
.prepend-top-10
= button_to "Reset runners registration token", reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' }
.bs-callout .bs-callout
%p %p
A 'Runner' is a process which runs a job. A 'Runner' is a process which runs a job.
...@@ -46,6 +26,19 @@ ...@@ -46,6 +26,19 @@
%span.label.label-danger paused %span.label.label-danger paused
\- Runner will not receive any new jobs \- Runner will not receive any new jobs
.bs-callout.clearfix
.pull-left
%p
You can reset runners registration token by pressing a button below.
.prepend-top-10
= button_to _("Reset runners registration token"), reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: _("Are you sure you want to reset registration token?") }
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: current_application_settings.runners_registration_token,
type: 'shared' }
.append-bottom-20.clearfix .append-bottom-20.clearfix
.pull-left .pull-left
= form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
......
- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
.bs-callout.help-callout
%h4= _("How to setup a #{type} Runner for a new project")
%ol
%li
= _("Install a Runner compatible with GitLab CI")
= (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code= root_url(only_path: false)
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
%li
= _("Start the Runner!")
...@@ -7,6 +7,6 @@ ...@@ -7,6 +7,6 @@
%span.light %span.light
- has_icon = provider_has_icon?(provider) - has_icon = provider_has_icon?(provider)
= link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
%fieldset %fieldset.prepend-top-10
= check_box_tag :remember_me = check_box_tag :remember_me
= label_tag :remember_me, 'Remember Me' = label_tag :remember_me, 'Remember me'
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
%h1.title %h1.title
= link_to root_path, title: 'Dashboard' do = link_to root_path, title: 'Dashboard' do
= brand_header_logo = brand_header_logo
%span.hidden-xs %span.logo-text.hidden-xs
GitLab = render 'shared/logo_type.svg'
- if current_user - if current_user
= render "layouts/nav/new_dashboard" = render "layouts/nav/new_dashboard"
......
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Save changes', class: 'btn btn-create' = f.submit 'Save changes', class: 'btn btn-create'
= link_to 'Test hook', test_project_hook_path(@project, @hook), class: 'btn btn-default' = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: @hook
= link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } = link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr %hr
= render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project } = render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= pipeline_schedule.description = pipeline_schedule.description
%td.branch-name-cell %td.branch-name-cell
= icon('code-fork') = icon('code-fork')
- if pipeline_schedule.ref - if pipeline_schedule.ref.present?
= link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name"
%td %td
- if pipeline_schedule.last_pipeline - if pipeline_schedule.last_pipeline
......
.panel.panel-default.protected-branches-list .panel.panel-default.protected-branches-list.js-protected-branches-list
- if @protected_branches.empty? - if @protected_branches.empty?
.panel-heading .panel-heading
%h3.panel-title %h3.panel-title
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
- if can_admin_project - if can_admin_project
%th %th
%tbody %tbody
%tr
%td.flash-container{ colspan: 5 }
= yield = yield
= paginate @protected_branches, theme: 'gitlab' = paginate @protected_branches, theme: 'gitlab'
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%h3.panel-title %h3.panel-title
......
.panel.panel-default.protected-tags-list .panel.panel-default.protected-tags-list.js-protected-tags-list
- if @protected_tags.empty? - if @protected_tags.empty?
.panel-heading .panel-heading
%h3.panel-title %h3.panel-title
......
%h3 Specific Runners %h3 Specific Runners
.bs-callout.help-callout = render partial: 'ci/runner/how_to_setup_runner',
%h4 How to setup a specific Runner for a new project locals: { registration_token: @project.runners_token,
type: 'specific' }
%ol
%li
Install a Runner compatible with GitLab CI
(checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it).
%li
Specify the following URL during the Runner setup:
%code= root_url(only_path: false)
%li
Use the following registration token during setup:
%code= @project.runners_token
%li
Start the Runner!
- if @project_runners.any? - if @project_runners.any?
%h4.underlined-title Runners activated for this project %h4.underlined-title Runners activated for this project
......
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
.col-md-8.col-lg-7 .col-md-8.col-lg-7
%strong.light-header= hook.url %strong.light-header= hook.url
%div %div
- %w(push_events tag_push_events issues_events confidential_issues_events note_events merge_requests_events job_events pipeline_events wiki_page_events).each do |trigger| - ProjectHook::TRIGGERS.each_value do |event|
- if hook.send(trigger) - if hook.public_send(event)
%span.label.label-gray.deploy-project-label= trigger.titleize %span.label.label-gray.deploy-project-label= event.to_s.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5 .col-md-4.col-lg-5.text-right-lg.prepend-top-5
%span.append-right-10.inline %span.append-right-10.inline
SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
= link_to "Edit", edit_project_hook_path(@project, hook), class: "btn btn-sm" = link_to 'Edit', edit_project_hook_path(@project, hook), class: 'btn btn-sm'
= link_to "Test", test_project_hook_path(@project, hook), class: "btn btn-sm" = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: hook, button_class: 'btn-small'
= link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do = link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-transparent' do
%span.sr-only Remove %span.sr-only Remove
= icon('trash') = icon('trash')
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 617 169"><path d="M315.26 2.97h-21.8l.1 162.5h88.3v-20.1h-66.5l-.1-142.4M465.89 136.95c-5.5 5.7-14.6 11.4-27 11.4-16.6 0-23.3-8.2-23.3-18.9 0-16.1 11.2-23.8 35-23.8 4.5 0 11.7.5 15.4 1.2v30.1h-.1m-22.6-98.5c-17.6 0-33.8 6.2-46.4 16.7l7.7 13.4c8.9-5.2 19.8-10.4 35.5-10.4 17.9 0 25.8 9.2 25.8 24.6v7.9c-3.5-.7-10.7-1.2-15.1-1.2-38.2 0-57.6 13.4-57.6 41.4 0 25.1 15.4 37.7 38.7 37.7 15.7 0 30.8-7.2 36-18.9l4 15.9h15.4v-83.2c-.1-26.3-11.5-43.9-44-43.9M557.63 149.1c-8.2 0-15.4-1-20.8-3.5V70.5c7.4-6.2 16.6-10.7 28.3-10.7 21.1 0 29.2 14.9 29.2 39 0 34.2-13.1 50.3-36.7 50.3m9.2-110.6c-19.5 0-30 13.3-30 13.3v-21l-.1-27.8h-21.3l.1 158.5c10.7 4.5 25.3 6.9 41.2 6.9 40.7 0 60.3-26 60.3-70.9-.1-35.5-18.2-59-50.2-59M77.9 20.6c19.3 0 31.8 6.4 39.9 12.9l9.4-16.3C114.5 6 97.3 0 78.9 0 32.5 0 0 28.3 0 85.4c0 59.8 35.1 83.1 75.2 83.1 20.1 0 37.2-4.7 48.4-9.4l-.5-63.9V75.1H63.6v20.1h38l.5 48.5c-5 2.5-13.6 4.5-25.3 4.5-32.2 0-53.8-20.3-53.8-63-.1-43.5 22.2-64.6 54.9-64.6M231.43 2.95h-21.3l.1 27.3v94.3c0 26.3 11.4 43.9 43.9 43.9 4.5 0 8.9-.4 13.1-1.2v-19.1c-3.1.5-6.4.7-9.9.7-17.9 0-25.8-9.2-25.8-24.6v-65h35.7v-17.8h-35.7l-.1-38.5M155.96 165.47h21.3v-124h-21.3v124M155.96 24.37h21.3V3.07h-21.3v21.3"/></svg>
- type = local_assigns.fetch(:type) - type = local_assigns.fetch(:type)
%aside.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top{ data: { "offset-top" => "50", "spy" => "affix" }, "aria-live" => "polite" } %aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
.issuable-sidebar.hidden .issuable-sidebar.hidden
= form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do = form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do
.block .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.pull-left .filter-item.inline.update-issues-btn.pull-left
= button_tag "Update all", class: "btn update-selected-issues btn-info", disabled: true = button_tag "Update all", class: "btn update-selected-issues btn-info", disabled: true
= button_tag "Cancel", class: "btn btn-default js-bulk-update-menu-hide pull-right" = button_tag "Cancel", class: "btn btn-default js-bulk-update-menu-hide pull-right"
......
- triggers = local_assigns.fetch(:triggers)
- button_class = local_assigns.fetch(:button_class, '')
- hook = local_assigns.fetch(:hook)
.hook-test-button.dropdown.inline
%button.btn{ 'data-toggle' => 'dropdown', class: button_class }
Test
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
- triggers.each_value do |event|
%li
= link_to_test_hook(hook, event)
---
title: Added "created_after" and "created_before" params to issuables
merge_request: 12151
author: Kyle Bishop @kybishop
---
title: "Adding French translations"
merge_request: 12200
author : Erwan "Dremor" Georget
---
title: Display all current broadcast messages, not just the last one
merge_request: 11113
author: rickettm
---
title: Honor the "Remember me" parameter for OAuth-based login
merge_request: 11963
author:
---
title: "#20628 Enable implicit grant in GitLab as OAuth Provider"
merge_request: 12384
author: Mateusz Pytel
---
title: Add coordinator url to admin area runner page
merge_request: 11603
author:
---
title: Replace 'dashboard/new-project.feature' spinach with rspec
merge_request: 12550
author: Alexander Randa (@randaalex)
---
title: Replace 'dashboard/todos' spinach with rspec
merge_request: 12453
author: Alexander Randa (@randaalex)
---
title: Replace 'snippets/snippets.feature' spinach with rspec
merge_request: 12385
author: Alexander Randa @randaalex
---
title: Allow creation of files and directories with spaces through Web UI
merge_request: 12608
author:
---
title: Add blame view age mapping
merge_request: 7198
author: Jeff Stubler
---
title: Update welcome page UX for new users
merge_request: 12662
author:
---
title: Fix mobile view of files view buttons
merge_request:
author:
---
title: Improve members view on mobile
merge_request: 12619
author:
---
title: Disable fork button on project limit
merge_request: 12145
author: Ivan Chernov
---
title: Inserts exact matches of name, username and email to the top of the search
list
merge_request: 12525
author:
---
title: Accept image for avatar in user API
merge_request: 12143
author: Ivan Chernov
---
title: Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor
of "slash commands"
merge_request:
author:
---
title: Center dropdown for mini graph
merge_request:
author:
---
title: Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients
merge_request: 9045
author: jneen
---
title: Use fa-chevron-down on dropdown arrows for consistency
merge_request: 9659
author: TM Lee
---
title: Use color inputs for broadcast messages
merge_request:
author:
---
title: Additional Prometheus metrics support
merge_request: 11712
author:
---
title: Moved "Members in a project" menu entry and path locations
merge_request: 11560
---
title: Rollback project repo move if there is an error in Projects::TransferService
merge_request: 11877
author:
---
title: Removes deleted_at and pending_delete occurrences in Project related queries
merge_request: 12091
author:
---
title: Ensures default user limits when external user is unchecked
merge_request: 12218
author:
---
title: Adds realtime feature to job show view header and sidebar info. Updates UX.
merge_request:
author:
---
title: Create responsive mobile view for pipelines table
merge_request:
author:
---
title: Change order of monospace fonts to fix bug on some linux distros
merge_request:
author:
---
title: Fix spacing on runner buttons.
merge_request: !12535
author:
---
title: Add database helpers 'add_timestamps_with_timezone' and 'timestamps_with_timezone'
merge_request: 11229
author: @blackst0ne
---
title: Filter archived project in API v3 only if param present
merge_request: 12245
author: Ivan Chernov
---
title: Allow admins to disable all restricted visibility levels
merge_request: 12649
author:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment