Commit 3420c6cc authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into orderable-issues

parents 9895d670 7f2819b7
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 8.17.3 (2017-03-07)
- Fix the redirect to custom home page URL. !9518
- Fix broken migration when upgrading straight to 8.17.1. !9613
- Make projects dropdown only show projects you are a member of. !9614
- Fix creating a file in an empty repo using the API. !9632
- Don't copy tooltip when copying GFM.
- Fix cherry-picking or reverting through an MR.
## 8.17.2 (2017-03-01) ## 8.17.2 (2017-03-01)
- Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602 - Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602
......
...@@ -18,25 +18,26 @@ gem 'pg', '~> 0.18.2', group: :postgres ...@@ -18,25 +18,26 @@ gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.24.0' gem 'rugged', '~> 0.24.0'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.2' gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0' gem 'doorkeeper', '~> 4.2.0'
gem 'omniauth', '~> 1.4.2' gem 'doorkeeper-openid_connect', '~> 1.1.0'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth', '~> 1.4.2'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.2'
gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2' gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.7.0' gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.0' gem 'omniauth-authentiq', '~> 0.3.0'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6' gem 'jwt', '~> 1.5.6'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails' gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
...@@ -68,9 +69,9 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false ...@@ -68,9 +69,9 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', 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.0'
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'
# Pagination # Pagination
gem 'kaminari', '~> 0.17.0' gem 'kaminari', '~> 0.17.0'
...@@ -102,19 +103,19 @@ gem 'unf', '~> 0.1.4' ...@@ -102,19 +103,19 @@ gem 'unf', '~> 0.1.4'
gem 'seed-fu', '~> 2.3.5' gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie' gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie'
gem 'gitlab-markup', '~> 1.5.1' gem 'gitlab-markup', '~> 1.5.1'
gem 'redcarpet', '~> 3.4' gem 'redcarpet', '~> 3.4'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 4.2' gem 'rdoc', '~> 4.2'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.7' gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0' gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8' gem 'truncato', '~> 0.7.8'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
...@@ -229,18 +230,18 @@ gem 'sass-rails', '~> 5.0.6' ...@@ -229,18 +230,18 @@ gem 'sass-rails', '~> 5.0.6'
gem 'coffee-rails', '~> 4.1.0' gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2' gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0' gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.7' gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.0' gem 'gemojione', '~> 3.0'
gem 'gon', '~> 6.1.0' gem 'gon', '~> 6.1.0'
gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0' gem 'jquery-rails', '~> 4.1.0'
gem 'request_store', '~> 1.3' gem 'request_store', '~> 1.3'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1' gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1' gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0' gem 'base32', '~> 0.3.0'
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 2.0.0' gem 'sentry-raven', '~> 2.0.0'
...@@ -278,13 +279,13 @@ group :development, :test do ...@@ -278,13 +279,13 @@ group :development, :test do
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0' gem 'fuubar', '~> 2.0.0'
gem 'database_cleaner', '~> 1.5.0' gem 'database_cleaner', '~> 1.5.0'
gem 'factory_girl_rails', '~> 4.7.0' gem 'factory_girl_rails', '~> 4.7.0'
gem 'rspec-rails', '~> 3.5.0' gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry', '~> 0.4.5' gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling', '~> 0.0.5' gem 'rspec_profiling', '~> 0.0.5'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0' gem 'minitest', '~> 5.7.0'
...@@ -292,13 +293,13 @@ group :development, :test do ...@@ -292,13 +293,13 @@ group :development, :test do
# Generate Fake data # Generate Fake data
gem 'ffaker', '~> 2.4' gem 'ffaker', '~> 2.4'
gem 'capybara', '~> 2.6.2' gem 'capybara', '~> 2.6.2'
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0' gem 'poltergeist', '~> 1.9.0'
gem 'spring', '~> 1.7.0' gem 'spring', '~> 1.7.0'
gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'rubocop', '~> 0.47.1', require: false gem 'rubocop', '~> 0.47.1', require: false
gem 'rubocop-rspec', '~> 1.12.0', require: false gem 'rubocop-rspec', '~> 1.12.0', require: false
......
...@@ -78,6 +78,7 @@ GEM ...@@ -78,6 +78,7 @@ GEM
better_errors (1.0.1) better_errors (1.0.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubis (>= 2.6.6) erubis (>= 2.6.6)
bindata (2.3.5)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
...@@ -167,6 +168,9 @@ GEM ...@@ -167,6 +168,9 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.0) doorkeeper (4.2.0)
railties (>= 4.2) railties (>= 4.2)
doorkeeper-openid_connect (1.1.2)
doorkeeper (~> 4.0)
json-jwt (~> 1.6)
dropzonejs-rails (0.7.2) dropzonejs-rails (0.7.2)
rails (> 3.1) rails (> 3.1)
email_reply_trimmer (0.1.6) email_reply_trimmer (0.1.6)
...@@ -376,6 +380,12 @@ GEM ...@@ -376,6 +380,12 @@ GEM
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (1.8.6) json (1.8.6)
json-jwt (1.7.1)
activesupport
bindata
multi_json (>= 1.3)
securecompare
url_safe_base64
json-schema (2.6.2) json-schema (2.6.2)
addressable (~> 2.3.8) addressable (~> 2.3.8)
jwt (1.5.6) jwt (1.5.6)
...@@ -684,6 +694,7 @@ GEM ...@@ -684,6 +694,7 @@ GEM
scss_lint (0.47.1) scss_lint (0.47.1)
rake (>= 0.9, < 11) rake (>= 0.9, < 11)
sass (~> 3.4.15) sass (~> 3.4.15)
securecompare (1.0.0)
seed-fu (2.3.6) seed-fu (2.3.6)
activerecord (>= 3.1) activerecord (>= 3.1)
activesupport (>= 3.1) activesupport (>= 3.1)
...@@ -789,6 +800,7 @@ GEM ...@@ -789,6 +800,7 @@ GEM
get_process_mem (~> 0) get_process_mem (~> 0)
unicorn (>= 4, < 6) unicorn (>= 4, < 6)
uniform_notifier (1.10.0) uniform_notifier (1.10.0)
url_safe_base64 (0.2.2)
validates_hostname (1.0.6) validates_hostname (1.0.6)
activerecord (>= 3.0) activerecord (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
...@@ -866,6 +878,7 @@ DEPENDENCIES ...@@ -866,6 +878,7 @@ DEPENDENCIES
devise-two-factor (~> 3.0.0) devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0) diffy (~> 3.1.0)
doorkeeper (~> 4.2.0) doorkeeper (~> 4.2.0)
doorkeeper-openid_connect (~> 1.1.0)
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
email_reply_trimmer (~> 0.1) email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
......
import PrometheusGraph from './monitoring/prometheus_graph'; // TODO: Maybe Make this a bundle
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
/* global UsernameValidator */ /* global UsernameValidator */
/* global ActiveTabMemoizer */ /* global ActiveTabMemoizer */
...@@ -286,7 +287,7 @@ const UserCallout = require('./user_callout'); ...@@ -286,7 +287,7 @@ const UserCallout = require('./user_callout');
case 'search:show': case 'search:show':
new Search(); new Search();
break; break;
case 'projects:protected_branches:index': case 'projects:repository:show':
new gl.ProtectedBranchCreate(); new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList(); new gl.ProtectedBranchEditList();
break; break;
...@@ -297,6 +298,8 @@ const UserCallout = require('./user_callout'); ...@@ -297,6 +298,8 @@ const UserCallout = require('./user_callout');
case 'ci:lints:show': case 'ci:lints:show':
new gl.CILintEditor(); new gl.CILintEditor();
break; break;
case 'projects:environments:metrics':
new PrometheusGraph();
case 'users:show': case 'users:show':
new UserCallout(); new UserCallout();
break; break;
......
This diff is collapsed.
...@@ -4,6 +4,21 @@ ...@@ -4,6 +4,21 @@
&.reset-filters { &.reset-filters {
padding: 7px; padding: 7px;
} }
&.update-issues-btn {
float: right;
margin-right: 0;
@media (max-width: $screen-xs-max) {
float: none;
}
}
}
.filters-section {
@media (max-width: $screen-xs-max) {
display: inline-block;
}
} }
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
...@@ -34,6 +49,11 @@ ...@@ -34,6 +49,11 @@
display: block; display: block;
margin: 0 0 10px; margin: 0 0 10px;
} }
.dropdown-menu-toggle,
.update-issues-btn .btn {
width: 100%;
}
} }
.filtered-search-container { .filtered-search-container {
...@@ -111,7 +131,15 @@ ...@@ -111,7 +131,15 @@
overflow: auto; overflow: auto;
} }
@media (max-width: $screen-xs-min) { @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
.issues-details-filters {
.dropdown-menu-toggle {
width: 100px;
}
}
}
@media (max-width: $screen-xs-max) {
.issues-details-filters { .issues-details-filters {
padding: 0 0 10px; padding: 0 0 10px;
background-color: $white-light; background-color: $white-light;
...@@ -205,4 +233,4 @@ ...@@ -205,4 +233,4 @@
.filter-dropdown-loading { .filter-dropdown-loading {
padding: 8px 16px; padding: 8px 16px;
} }
\ No newline at end of file
...@@ -229,44 +229,6 @@ ul.content-list { ...@@ -229,44 +229,6 @@ ul.content-list {
} }
} }
// Table list
.table-list {
display: table;
width: 100%;
.table-list-row {
display: table-row;
}
.table-list-cell {
display: table-cell;
vertical-align: top;
padding: 10px 16px;
border-bottom: 1px solid $gray-darker;
&.avatar-cell {
width: 36px;
padding-right: 0;
img {
margin-right: 0;
}
}
}
&.table-wide {
.table-list-cell {
&:last-of-type {
padding-right: 0;
}
&:first-of-type {
padding-left: 0;
}
}
}
}
.panel > .content-list > li { .panel > .content-list > li {
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
} }
......
...@@ -100,8 +100,7 @@ ...@@ -100,8 +100,7 @@
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
.issues-filters { .issues-filters {
.milestone-filter, .milestone-filter {
.labels-filter {
display: none; display: none;
} }
} }
......
...@@ -48,11 +48,3 @@ ...@@ -48,11 +48,3 @@
line-height: inherit; line-height: inherit;
} }
} }
.panel-default {
.table-list-row:last-child {
.table-list-cell {
border-bottom: 0;
}
}
}
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
padding: 5px 10px; padding: 5px 10px;
background-color: $gray-light; background-color: $gray-light;
border-bottom: 1px solid $gray-darker; border-bottom: 1px solid $gray-darker;
border-top: 1px solid $gray-darker;
font-size: 14px; font-size: 14px;
&:first-child { &:first-child {
...@@ -117,10 +118,37 @@ ...@@ -117,10 +118,37 @@
} }
} }
.commit.flex-list {
display: flex;
}
.avatar-cell {
width: 46px;
padding-left: 10px;
img {
margin-right: 0;
}
}
.commit-detail {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-grow: 1;
padding-left: 10px;
.merge-request-branches & {
flex-direction: column;
}
}
.commit-content {
padding-right: 10px;
}
.commit-actions { .commit-actions {
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
width: 300px;
text-align: right;
font-size: 0; font-size: 0;
} }
......
...@@ -143,3 +143,71 @@ ...@@ -143,3 +143,71 @@
} }
} }
} }
.prometheus-graph {
text {
fill: $stat-graph-axis-fill;
}
}
.x-axis path,
.y-axis path,
.label-x-axis-line,
.label-y-axis-line {
fill: none;
stroke-width: 1;
shape-rendering: crispEdges;
}
.x-axis path,
.y-axis path {
stroke: $stat-graph-axis-fill;
}
.label-x-axis-line,
.label-y-axis-line {
stroke: $border-color;
}
.y-axis {
line {
stroke: $stat-graph-axis-fill;
stroke-width: 1;
}
}
.metric-area {
opacity: 0.8;
}
.prometheus-graph-overlay {
fill: none;
opacity: 0.0;
pointer-events: all;
}
.rect-text-metric {
fill: $white-light;
stroke-width: 1;
stroke: $black;
}
.rect-axis-text {
fill: $white-light;
}
.text-metric,
.text-median-metric,
.text-metric-usage,
.text-metric-date {
fill: $black;
}
.text-metric-date {
font-weight: 200;
}
.selected-metric-line {
stroke: $black;
stroke-width: 1;
}
...@@ -240,8 +240,7 @@ ...@@ -240,8 +240,7 @@
.commit { .commit {
margin: 0; margin: 0;
padding-top: 2px; padding: 10px 0;
padding-bottom: 2px;
list-style: none; list-style: none;
&:hover { &:hover {
...@@ -409,7 +408,7 @@ ...@@ -409,7 +408,7 @@
} }
.panel-footer { .panel-footer {
padding: 5px 10px; padding: 0;
.btn { .btn {
min-width: auto; min-width: auto;
......
...@@ -331,6 +331,10 @@ ul.notes { ...@@ -331,6 +331,10 @@ ul.notes {
&:hover { &:hover {
color: $gl-link-color; color: $gl-link-color;
}
&:focus,
&:hover {
text-decoration: none; text-decoration: none;
} }
} }
......
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
.table.ci-table { .table.ci-table {
&.builds-page tr { &.builds-page tbody tr {
height: 71px; height: 71px;
} }
......
...@@ -746,6 +746,8 @@ pre.light-well { ...@@ -746,6 +746,8 @@ pre.light-well {
} }
.protected-branches-list { .protected-branches-list {
margin-bottom: 30px;
a { a {
color: $gl-text-color; color: $gl-text-color;
......
...@@ -24,3 +24,14 @@ ...@@ -24,3 +24,14 @@
.service-settings .control-label { .service-settings .control-label {
padding-top: 0; padding-top: 0;
} }
.token-token-container {
#impersonation-token-token {
width: 80%;
display: inline;
}
.btn-clipboard {
margin-left: 5px;
}
}
.triggers-container {
.label-container {
display: inline-block;
margin-left: 10px;
}
}
.trigger-actions {
.btn {
margin-left: 10px;
}
}
...@@ -139,18 +139,10 @@ ...@@ -139,18 +139,10 @@
.blob-commit-info { .blob-commit-info {
list-style: none; list-style: none;
background: $gray-light; background: $gray-light;
padding: 6px 0; padding: 16px 16px 16px 6px;
border: 1px solid $border-color; border: 1px solid $border-color;
border-bottom: none; border-bottom: none;
margin: 0; margin: 0;
.table-list-cell {
border-bottom: none;
}
.commit-actions {
width: 260px;
}
} }
#modal-remove-blob > .modal-dialog { width: 850px; } #modal-remove-blob > .modal-dialog { width: 850px; }
......
...@@ -2,7 +2,7 @@ class Admin::ApplicationsController < Admin::ApplicationController ...@@ -2,7 +2,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
include OauthApplications include OauthApplications
before_action :set_application, only: [:show, :edit, :update, :destroy] before_action :set_application, only: [:show, :edit, :update, :destroy]
before_action :load_scopes, only: [:new, :edit] before_action :load_scopes, only: [:new, :create, :edit, :update]
def index def index
@applications = Doorkeeper::Application.where("owner_id IS NULL") @applications = Doorkeeper::Application.where("owner_id IS NULL")
......
class Admin::ImpersonationTokensController < Admin::ApplicationController
before_action :user
def index
set_index_vars
end
def create
@impersonation_token = finder.build(impersonation_token_params)
if @impersonation_token.save
flash[:impersonation_token] = @impersonation_token.token
redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created."
else
set_index_vars
render :index
end
end
def revoke
@impersonation_token = finder.find(params[:id])
if @impersonation_token.revoke!
flash[:notice] = "Revoked impersonation token #{@impersonation_token.name}!"
else
flash[:alert] = "Could not revoke impersonation token #{@impersonation_token.name}."
end
redirect_to admin_user_impersonation_tokens_path
end
private
def user
@user ||= User.find_by!(username: params[:user_id])
end
def finder(options = {})
PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options))
end
def impersonation_token_params
params.require(:personal_access_token).permit(:name, :expires_at, :impersonation, scopes: [])
end
def set_index_vars
@scopes = Gitlab::Auth::API_SCOPES
@impersonation_token ||= finder.build
@inactive_impersonation_tokens = finder(state: 'inactive').execute
@active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at)
end
end
module RepositorySettingsRedirect
extend ActiveSupport::Concern
def redirect_to_repository_settings(project)
redirect_to namespace_project_settings_repository_path(project.namespace, project)
end
end
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :authenticate_resource_owner!
layout 'profile' layout 'profile'
# Overriden from Doorkeeper::AuthorizationsController to
# include the call to session.delete
def new def new
if pre_auth.authorizable? if pre_auth.authorizable?
if skip_authorization? || matching_token? if skip_authorization? || matching_token?
...@@ -16,44 +16,4 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController ...@@ -16,44 +16,4 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
render "doorkeeper/authorizations/error" render "doorkeeper/authorizations/error"
end end
end end
# TODO: Handle raise invalid authorization
def create
redirect_or_render authorization.authorize
end
def destroy
redirect_or_render authorization.deny
end
private
def matching_token?
Doorkeeper::AccessToken.matching_token_for(pre_auth.client,
current_resource_owner.id,
pre_auth.scopes)
end
def redirect_or_render(auth)
if auth.redirectable?
redirect_to auth.redirect_uri
else
render json: auth.body, status: auth.status
end
end
def pre_auth
@pre_auth ||=
Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration,
server.client_via_uid,
params)
end
def authorization
@authorization ||= strategy.request
end
def strategy
@strategy ||= server.authorization_request(pre_auth.response_type)
end
end end
...@@ -4,7 +4,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController ...@@ -4,7 +4,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
end end
def create def create
@personal_access_token = current_user.personal_access_tokens.generate(personal_access_token_params) @personal_access_token = finder.build(personal_access_token_params)
if @personal_access_token.save if @personal_access_token.save
flash[:personal_access_token] = @personal_access_token.token flash[:personal_access_token] = @personal_access_token.token
...@@ -16,7 +16,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController ...@@ -16,7 +16,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
end end
def revoke def revoke
@personal_access_token = current_user.personal_access_tokens.find(params[:id]) @personal_access_token = finder.find(params[:id])
if @personal_access_token.revoke! if @personal_access_token.revoke!
flash[:notice] = "Revoked personal access token #{@personal_access_token.name}!" flash[:notice] = "Revoked personal access token #{@personal_access_token.name}!"
...@@ -29,14 +29,19 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController ...@@ -29,14 +29,19 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
private private
def finder(options = {})
PersonalAccessTokensFinder.new({ user: current_user, impersonation: false }.merge(options))
end
def personal_access_token_params def personal_access_token_params
params.require(:personal_access_token).permit(:name, :expires_at, scopes: []) params.require(:personal_access_token).permit(:name, :expires_at, scopes: [])
end end
def set_index_vars def set_index_vars
@personal_access_token ||= current_user.personal_access_tokens.build @scopes = Gitlab::Auth::API_SCOPES
@scopes = Gitlab::Auth::SCOPES
@active_personal_access_tokens = current_user.personal_access_tokens.active.order(:expires_at) @personal_access_token = finder.build
@inactive_personal_access_tokens = current_user.personal_access_tokens.inactive @inactive_personal_access_tokens = finder(state: 'inactive').execute
@active_personal_access_tokens = finder(state: 'active').execute.order(:expires_at)
end end
end end
class Projects::DeployKeysController < Projects::ApplicationController class Projects::DeployKeysController < Projects::ApplicationController
include RepositorySettingsRedirect
respond_to :html respond_to :html
# Authorize # Authorize
...@@ -7,51 +8,36 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -7,51 +8,36 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index def index
@key = DeployKey.new redirect_to_repository_settings(@project)
set_index_vars
end end
def new def new
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) redirect_to_repository_settings(@project)
end end
def create def create
@key = DeployKey.new(deploy_key_params.merge(user: current_user)) @key = DeployKey.new(deploy_key_params.merge(user: current_user))
set_index_vars
if @key.valid? && @project.deploy_keys << @key unless @key.valid? && @project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) flash[:alert] = @key.errors.full_messages.join(', ').html_safe
else
render "index"
end end
redirect_to_repository_settings(@project)
end end
def enable def enable
Projects::EnableDeployKeyService.new(@project, current_user, params).execute Projects::EnableDeployKeyService.new(@project, current_user, params).execute
redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) redirect_to_repository_settings(@project)
end end
def disable def disable
@project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy
redirect_back_or_default(default: { action: 'index' }) redirect_to_repository_settings(@project)
end end
protected protected
def set_index_vars
@enabled_keys ||= @project.deploy_keys
@available_keys ||= current_user.accessible_deploy_keys - @enabled_keys
@available_project_keys ||= current_user.project_deploy_keys - @enabled_keys
@available_public_keys ||= DeployKey.are_public - @enabled_keys
# Public keys that are already used by another accessible project are already
# in @available_project_keys.
@available_public_keys -= @available_project_keys
end
def deploy_key_params def deploy_key_params
params.require(:deploy_key).permit(:key, :title, :can_push) params.require(:deploy_key).permit(:key, :title, :can_push)
end end
......
...@@ -5,7 +5,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -5,7 +5,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :authorize_create_deployment!, only: [:stop] before_action :authorize_create_deployment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update] before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize] before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize] before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :verify_api_request!, only: :terminal_websocket_authorize
def index def index
...@@ -109,6 +109,19 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -109,6 +109,19 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end end
end end
def metrics
# Currently, this acts as a hint to load the metrics details into the cache
# if they aren't there already
@metrics = environment.metrics || {}
respond_to do |format|
format.html
format.json do
render json: @metrics, status: @metrics.any? ? :ok : :no_content
end
end
end
private private
def verify_api_request! def verify_api_request!
......
class Projects::ProtectedBranchesController < Projects::ApplicationController class Projects::ProtectedBranchesController < Projects::ApplicationController
include RepositorySettingsRedirect
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :load_protected_branch, only: [:show, :update, :destroy] before_action :load_protected_branch, only: [:show, :update, :destroy]
before_action :load_protected_branches, only: [:index]
layout "project_settings" layout "project_settings"
def index def index
@protected_branch = @project.protected_branches.new redirect_to_repository_settings(@project)
load_gon_index
end end
def create def create
@protected_branch = ::ProtectedBranches::CreateService.new(@project, current_user, protected_branch_params).execute @protected_branch = ::ProtectedBranches::CreateService.new(@project, current_user, protected_branch_params).execute
if @protected_branch.persisted? unless @protected_branch.persisted?
redirect_to namespace_project_protected_branches_path(@project.namespace, @project) flash[:alert] = @protected_branches.errors.full_messages.join(', ').html_safe
else
load_protected_branches
load_gon_index
render :index
end end
redirect_to_repository_settings(@project)
end end
def show def show
...@@ -45,7 +41,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -45,7 +41,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
@protected_branch.destroy @protected_branch.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to namespace_project_protected_branches_path } format.html { redirect_to_repository_settings(@project) }
format.js { head :ok } format.js { head :ok }
end end
end end
...@@ -61,24 +57,4 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -61,24 +57,4 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
merge_access_levels_attributes: [:access_level, :id], merge_access_levels_attributes: [:access_level, :id],
push_access_levels_attributes: [:access_level, :id]) push_access_levels_attributes: [:access_level, :id])
end end
def load_protected_branches
@protected_branches = @project.protected_branches.order(:name).page(params[:page])
end
def access_levels_options
{
push_access_levels: {
roles: ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } },
},
merge_access_levels: {
roles: ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }
}
}
end
def load_gon_index
params = { open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } } }
gon.push(params.merge(access_levels_options))
end
end end
module Projects
module Settings
class RepositoryController < Projects::ApplicationController
before_action :authorize_admin_project!
def show
@deploy_keys = DeployKeysPresenter
.new(@project, current_user: current_user)
define_protected_branches
end
private
def define_protected_branches
load_protected_branches
@protected_branch = @project.protected_branches.new
load_gon_index
end
def load_protected_branches
@protected_branches = @project.protected_branches.order(:name).page(params[:page])
end
def access_levels_options
{
push_access_levels: {
roles: ProtectedBranch::PushAccessLevel.human_access_levels.map do |id, text|
{ id: id, text: text, before_divider: true }
end
},
merge_access_levels: {
roles: ProtectedBranch::MergeAccessLevel.human_access_levels.map do |id, text|
{ id: id, text: text, before_divider: true }
end
}
}
end
def open_branches
branches = @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } }
{ open_branches: branches }
end
def load_gon_index
gon.push(open_branches.merge(access_levels_options))
end
end
end
end
class Projects::TriggersController < Projects::ApplicationController class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_build! before_action :authorize_admin_build!
before_action :authorize_manage_trigger!, except: [:index, :create]
before_action :authorize_admin_trigger!, only: [:edit, :update]
before_action :trigger, only: [:take_ownership, :edit, :update, :destroy]
layout 'project_settings' layout 'project_settings'
...@@ -8,27 +11,67 @@ class Projects::TriggersController < Projects::ApplicationController ...@@ -8,27 +11,67 @@ class Projects::TriggersController < Projects::ApplicationController
end end
def create def create
@trigger = project.triggers.new @trigger = project.triggers.create(create_params.merge(owner: current_user))
@trigger.save
if @trigger.valid? if @trigger.valid?
redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Trigger was created successfully.' flash[:notice] = 'Trigger was created successfully.'
else else
@triggers = project.triggers.select(&:persisted?) flash[:alert] = 'You could not create a new trigger.'
render action: "show" end
redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
def take_ownership
if trigger.update(owner: current_user)
flash[:notice] = 'Trigger was re-assigned.'
else
flash[:alert] = 'You could not take ownership of trigger.'
end
redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
def edit
end
def update
if trigger.update(update_params)
redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project), notice: 'Trigger was successfully updated.'
else
render action: "edit"
end end
end end
def destroy def destroy
trigger.destroy if trigger.destroy
flash[:alert] = "Trigger removed" flash[:notice] = "Trigger removed."
else
flash[:alert] = "Could not remove the trigger."
end
redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project) redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end end
private private
def authorize_manage_trigger!
access_denied! unless can?(current_user, :manage_trigger, trigger)
end
def authorize_admin_trigger!
access_denied! unless can?(current_user, :admin_trigger, trigger)
end
def trigger def trigger
@trigger ||= project.triggers.find(params[:id]) @trigger ||= project.triggers.find(params[:id]) || render_404
end
def create_params
params.require(:trigger).permit(:description)
end
def update_params
params.require(:trigger).permit(:description)
end end
end end
...@@ -14,6 +14,8 @@ class UploadsController < ApplicationController ...@@ -14,6 +14,8 @@ class UploadsController < ApplicationController
end end
disposition = uploader.image? ? 'inline' : 'attachment' disposition = uploader.image? ? 'inline' : 'attachment'
expires_in 0.seconds, must_revalidate: true, private: true
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
end end
......
class PersonalAccessTokensFinder
attr_accessor :params
delegate :build, :find, :find_by, to: :execute
def initialize(params = {})
@params = params
end
def execute
tokens = PersonalAccessToken.all
tokens = by_user(tokens)
tokens = by_impersonation(tokens)
by_state(tokens)
end
private
def by_user(tokens)
return tokens unless @params[:user]
tokens.where(user: @params[:user])
end
def by_impersonation(tokens)
case @params[:impersonation]
when true
tokens.with_impersonation
when false
tokens.without_impersonation
else
tokens
end
end
def by_state(tokens)
case @params[:state]
when 'active'
tokens.active
when 'inactive'
tokens.inactive
else
tokens
end
end
end
...@@ -81,8 +81,8 @@ module ApplicationSettingsHelper ...@@ -81,8 +81,8 @@ module ApplicationSettingsHelper
end end
def repository_storages_options_for_select def repository_storages_options_for_select
options = Gitlab.config.repositories.storages.map do |name, path| options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{path}", name] ["#{name} - #{storage['path']}", name]
end end
options_for_select(options, @application_setting.repository_storages) options_for_select(options, @application_setting.repository_storages)
......
...@@ -48,6 +48,8 @@ module CiStatusHelper ...@@ -48,6 +48,8 @@ module CiStatusHelper
'icon_status_created' 'icon_status_created'
when 'skipped' when 'skipped'
'icon_status_skipped' 'icon_status_skipped'
when 'manual'
'icon_status_manual'
else else
'icon_status_canceled' 'icon_status_canceled'
end end
......
...@@ -162,7 +162,12 @@ module EventsHelper ...@@ -162,7 +162,12 @@ module EventsHelper
def event_note(text, options = {}) def event_note(text, options = {})
text = first_line_in_markdown(text, 150, options) text = first_line_in_markdown(text, 150, options)
sanitize(text, tags: %w(a img b pre code p span))
sanitize(
text,
tags: %w(a img b pre code p span),
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style']
)
end end
def event_commit_title(message) def event_commit_title(message)
......
...@@ -74,6 +74,10 @@ module GitlabRoutingHelper ...@@ -74,6 +74,10 @@ module GitlabRoutingHelper
namespace_project_environment_path(environment.project.namespace, environment.project, environment, *args) namespace_project_environment_path(environment.project.namespace, environment.project, environment, *args)
end end
def environment_metrics_path(environment, *args)
metrics_namespace_project_environment_path(environment.project.namespace, environment.project, environment, *args)
end
def issue_path(entity, *args) def issue_path(entity, *args)
namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args)
end end
......
...@@ -50,7 +50,7 @@ module SortingHelper ...@@ -50,7 +50,7 @@ module SortingHelper
end end
def sort_title_priority def sort_title_priority
'Priority' 'Label priority'
end end
def sort_title_oldest_updated def sort_title_oldest_updated
......
class ChatTeam < ActiveRecord::Base class ChatTeam < ActiveRecord::Base
validates :team_id, presence: true validates :team_id, presence: true
validates :namespace, uniqueness: true
belongs_to :namespace belongs_to :namespace
end end
...@@ -517,6 +517,27 @@ module Ci ...@@ -517,6 +517,27 @@ module Ci
] ]
end end
def steps
[Gitlab::Ci::Build::Step.from_commands(self),
Gitlab::Ci::Build::Step.from_after_script(self)].compact
end
def image
Gitlab::Ci::Build::Image.from_image(self)
end
def services
Gitlab::Ci::Build::Image.from_services(self)
end
def artifacts
[options[:artifacts]]
end
def cache
[options[:cache]]
end
def credentials def credentials
Gitlab::Ci::Build::Credentials::Factory.new(self).create! Gitlab::Ci::Build::Credentials::Factory.new(self).create!
end end
...@@ -543,10 +564,35 @@ module Ci ...@@ -543,10 +564,35 @@ module Ci
@unscoped_project ||= Project.unscoped.find_by(id: gl_project_id) @unscoped_project ||= Project.unscoped.find_by(id: gl_project_id)
end end
CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
def predefined_variables def predefined_variables
variables = [ variables = [
{ key: 'CI', value: 'true', public: true }, { key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true }, { key: 'GITLAB_CI', value: 'true', public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
{ key: 'CI_JOB_ID', value: id.to_s, public: true },
{ key: 'CI_JOB_NAME', value: name, public: true },
{ key: 'CI_JOB_STAGE', value: stage, public: true },
{ key: 'CI_JOB_TOKEN', value: token, public: false },
{ key: 'CI_COMMIT_SHA', value: sha, public: true },
{ key: 'CI_COMMIT_REF_NAME', value: ref, public: true },
{ key: 'CI_COMMIT_REF_SLUG', value: ref_slug, public: true },
{ key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER, public: true },
{ key: 'CI_REGISTRY_PASSWORD', value: token, public: false },
{ key: 'CI_REPOSITORY_URL', value: repo_url, public: false }
]
variables << { key: "CI_COMMIT_TAG", value: ref, public: true } if tag?
variables << { key: "CI_PIPELINE_TRIGGERED", value: 'true', public: true } if trigger_request
variables << { key: "CI_JOB_MANUAL", value: 'true', public: true } if action?
variables.concat(legacy_variables)
end
def legacy_variables
variables = [
{ key: 'CI_BUILD_ID', value: id.to_s, public: true }, { key: 'CI_BUILD_ID', value: id.to_s, public: true },
{ key: 'CI_BUILD_TOKEN', value: token, public: false }, { key: 'CI_BUILD_TOKEN', value: token, public: false },
{ key: 'CI_BUILD_REF', value: sha, public: true }, { key: 'CI_BUILD_REF', value: sha, public: true },
...@@ -554,14 +600,12 @@ module Ci ...@@ -554,14 +600,12 @@ module Ci
{ key: 'CI_BUILD_REF_NAME', value: ref, public: true }, { key: 'CI_BUILD_REF_NAME', value: ref, public: true },
{ key: 'CI_BUILD_REF_SLUG', value: ref_slug, public: true }, { key: 'CI_BUILD_REF_SLUG', value: ref_slug, public: true },
{ key: 'CI_BUILD_NAME', value: name, public: true }, { key: 'CI_BUILD_NAME', value: name, public: true },
{ key: 'CI_BUILD_STAGE', value: stage, public: true }, { key: 'CI_BUILD_STAGE', value: stage, public: true }
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }
] ]
variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request variables << { key: "CI_BUILD_TAG", value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if action? variables << { key: "CI_BUILD_TRIGGERED", value: 'true', public: true } if trigger_request
variables << { key: "CI_BUILD_MANUAL", value: 'true', public: true } if action?
variables variables
end end
......
...@@ -29,8 +29,12 @@ module Ci ...@@ -29,8 +29,12 @@ module Ci
token[0...4] token[0...4]
end end
def can_show_token?(user) def legacy?
owner.blank? || owner == user self.owner_id.blank?
end
def can_access_project?
self.owner_id.blank? || Ability.allowed?(self.owner, :create_build, project)
end end
end end
end end
...@@ -7,7 +7,7 @@ module HasStatus ...@@ -7,7 +7,7 @@ module HasStatus
STARTED_STATUSES = %w[running success failed skipped manual].freeze STARTED_STATUSES = %w[running success failed skipped manual].freeze
ACTIVE_STATUSES = %w[pending running].freeze ACTIVE_STATUSES = %w[pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
ORDERED_STATUSES = %w[manual failed pending running canceled success skipped].freeze ORDERED_STATUSES = %w[failed pending running manual canceled success skipped created].freeze
class_methods do class_methods do
def status_sql def status_sql
......
...@@ -145,6 +145,14 @@ class Environment < ActiveRecord::Base ...@@ -145,6 +145,14 @@ class Environment < ActiveRecord::Base
project.deployment_service.terminals(self) if has_terminals? project.deployment_service.terminals(self) if has_terminals?
end end
def has_metrics?
project.monitoring_service.present? && available? && last_deployment.present?
end
def metrics
project.monitoring_service.metrics(self) if has_metrics?
end
# An environment name is not necessarily suitable for use in URLs, DNS # An environment name is not necessarily suitable for use in URLs, DNS
# or other third-party contexts, so provide a slugified version. A slug has # or other third-party contexts, so provide a slugified version. A slug has
# the following properties: # the following properties:
......
class OauthAccessGrant < Doorkeeper::AccessGrant
belongs_to :resource_owner, class_name: 'User'
belongs_to :application, class_name: 'Doorkeeper::Application'
end
class OauthAccessToken < ActiveRecord::Base class OauthAccessToken < Doorkeeper::AccessToken
belongs_to :resource_owner, class_name: 'User' belongs_to :resource_owner, class_name: 'User'
belongs_to :application, class_name: 'Doorkeeper::Application' belongs_to :application, class_name: 'Doorkeeper::Application'
end end
class PersonalAccessToken < ActiveRecord::Base class PersonalAccessToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable include TokenAuthenticatable
add_authentication_token_field :token add_authentication_token_field :token
...@@ -6,17 +7,30 @@ class PersonalAccessToken < ActiveRecord::Base ...@@ -6,17 +7,30 @@ class PersonalAccessToken < ActiveRecord::Base
belongs_to :user belongs_to :user
scope :active, -> { where(revoked: false).where("expires_at >= NOW() OR expires_at IS NULL") } before_save :ensure_token
scope :active, -> { where("revoked = false AND (expires_at >= NOW() OR expires_at IS NULL)") }
scope :inactive, -> { where("revoked = true OR expires_at < NOW()") } scope :inactive, -> { where("revoked = true OR expires_at < NOW()") }
scope :with_impersonation, -> { where(impersonation: true) }
scope :without_impersonation, -> { where(impersonation: false) }
def self.generate(params) validates :scopes, presence: true
personal_access_token = self.new(params) validate :validate_api_scopes
personal_access_token.ensure_token
personal_access_token
end
def revoke! def revoke!
self.revoked = true self.revoked = true
self.save self.save
end end
def active?
!revoked? && !expired?
end
protected
def validate_api_scopes
unless scopes.all? { |scope| Gitlab::Auth::API_SCOPES.include?(scope.to_sym) }
errors.add :scopes, "can only contain API scopes"
end
end
end end
...@@ -113,6 +113,7 @@ class Project < ActiveRecord::Base ...@@ -113,6 +113,7 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
has_one :external_wiki_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
has_one :prometheus_service, dependent: :destroy, inverse_of: :project
has_one :mock_ci_service, dependent: :destroy has_one :mock_ci_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
...@@ -392,7 +393,7 @@ class Project < ActiveRecord::Base ...@@ -392,7 +393,7 @@ class Project < ActiveRecord::Base
end end
def repository_storage_path def repository_storage_path
Gitlab.config.repositories.storages[repository_storage] Gitlab.config.repositories.storages[repository_storage]['path']
end end
def team def team
...@@ -771,6 +772,14 @@ class Project < ActiveRecord::Base ...@@ -771,6 +772,14 @@ class Project < ActiveRecord::Base
@deployment_service ||= deployment_services.reorder(nil).find_by(active: true) @deployment_service ||= deployment_services.reorder(nil).find_by(active: true)
end end
def monitoring_services
services.where(category: :monitoring)
end
def monitoring_service
@monitoring_service ||= monitoring_services.reorder(nil).find_by(active: true)
end
def jira_tracker? def jira_tracker?
issues_tracker.to_param == 'jira' issues_tracker.to_param == 'jira'
end end
......
# Base class for monitoring services
#
# These services integrate with a deployment solution like Prometheus
# to provide additional features for environments.
class MonitoringService < Service
default_value_for :category, 'monitoring'
def self.supported_events
%w()
end
# Environments have a number of metrics
def metrics(environment)
raise NotImplementedError
end
end
class PrometheusService < MonitoringService
include ReactiveCaching
self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] }
self.reactive_cache_lease_timeout = 30.seconds
self.reactive_cache_refresh_interval = 30.seconds
self.reactive_cache_lifetime = 1.minute
# Access to prometheus is directly through the API
prop_accessor :api_url
with_options presence: true, if: :activated? do
validates :api_url, url: true
end
after_save :clear_reactive_cache!
def initialize_properties
if properties.nil?
self.properties = {}
end
end
def title
'Prometheus'
end
def description
'Prometheus monitoring'
end
def help
'Retrieves `container_cpu_usage_seconds_total` and `container_memory_usage_bytes` from the configured Prometheus server. An `environment` label is required on each metric to identify the Environment.'
end
def self.to_param
'prometheus'
end
def fields
[
{
type: 'text',
name: 'api_url',
title: 'API URL',
placeholder: 'Prometheus API Base URL, like http://prometheus.example.com/'
}
]
end
# Check we can connect to the Prometheus API
def test(*args)
client.ping
{ success: true, result: 'Checked API endpoint' }
rescue Gitlab::PrometheusError => err
{ success: false, result: err }
end
def metrics(environment)
with_reactive_cache(environment.slug) do |data|
data
end
end
# Cache metrics for specific environment
def calculate_reactive_cache(environment_slug)
return unless active? && project && !project.pending_delete?
memory_query = %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
{
success: true,
metrics: {
# Memory used in MB
memory_values: client.query_range(memory_query, start: 8.hours.ago),
memory_current: client.query(memory_query),
# CPU Usage rate in cores.
cpu_values: client.query_range(cpu_query, start: 8.hours.ago),
cpu_current: client.query(cpu_query)
},
last_update: Time.now.utc
}
rescue Gitlab::PrometheusError => err
{ success: false, result: err.message }
end
def client
@prometheus ||= Gitlab::Prometheus.new(api_url: api_url)
end
end
...@@ -50,10 +50,6 @@ class Repository ...@@ -50,10 +50,6 @@ class Repository
end end
end end
def self.storages
Gitlab.config.repositories.storages
end
def initialize(path_with_namespace, project) def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace @path_with_namespace = path_with_namespace
@project = project @project = project
......
...@@ -232,6 +232,7 @@ class Service < ActiveRecord::Base ...@@ -232,6 +232,7 @@ class Service < ActiveRecord::Base
mattermost mattermost
pipelines_email pipelines_email
pivotaltracker pivotaltracker
prometheus
pushover pushover
redmine redmine
slack_slash_commands slack_slash_commands
......
...@@ -325,8 +325,7 @@ class User < ActiveRecord::Base ...@@ -325,8 +325,7 @@ class User < ActiveRecord::Base
end end
def find_by_personal_access_token(token_string) def find_by_personal_access_token(token_string)
personal_access_token = PersonalAccessToken.active.find_by_token(token_string) if token_string PersonalAccessTokensFinder.new(state: 'active').find_by(token: token_string)&.user
personal_access_token&.user
end end
# Returns a user for the given SSH key. # Returns a user for the given SSH key.
......
module Ci
class TriggerPolicy < BasePolicy
def rules
delegate! @subject.project
if can?(:admin_build)
can! :admin_trigger if @subject.owner.blank? ||
@subject.owner == @user
can! :manage_trigger
end
end
end
end
module Projects
module Settings
class DeployKeysPresenter < Gitlab::View::Presenter::Simple
presents :project
delegate :size, to: :enabled_keys, prefix: true
delegate :size, to: :available_project_keys, prefix: true
delegate :size, to: :available_public_keys, prefix: true
def new_key
@key ||= DeployKey.new
end
def enabled_keys
@enabled_keys ||= project.deploy_keys
end
def any_keys_enabled?
enabled_keys.any?
end
def available_keys
@available_keys ||= current_user.accessible_deploy_keys - enabled_keys
end
def available_project_keys
@available_project_keys ||= current_user.project_deploy_keys - enabled_keys
end
def any_available_project_keys_enabled?
available_project_keys.any?
end
def key_available?(deploy_key)
available_keys.include?(deploy_key)
end
def available_public_keys
return @available_public_keys if defined?(@available_public_keys)
@available_public_keys ||= DeployKey.are_public - enabled_keys
# Public keys that are already used by another accessible project are already
# in @available_project_keys.
@available_public_keys -= available_project_keys
end
def any_available_public_keys_enabled?
available_public_keys.any?
end
def to_partial_path
'projects/deploy_keys/index'
end
def form_partial_path
'projects/deploy_keys/form'
end
end
end
end
module Ci module Ci
# This class responsible for assigning # This class responsible for assigning
# proper pending build to runner on runner API request # proper pending build to runner on runner API request
class RegisterBuildService class RegisterJobService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
attr_reader :runner attr_reader :runner
......
...@@ -99,6 +99,8 @@ class GitPushService < BaseService ...@@ -99,6 +99,8 @@ class GitPushService < BaseService
UpdateMergeRequestsWorker UpdateMergeRequestsWorker
.perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
EventCreateService.new.push(@project, current_user, build_push_data) EventCreateService.new.push(@project, current_user, build_push_data)
@project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks)
......
- page_title "Impersonation Tokens", @user.name, "Users"
= render 'admin/users/head'
.row.prepend-top-default
.col-lg-12
= render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes
= render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens
...@@ -21,4 +21,6 @@ ...@@ -21,4 +21,6 @@
= link_to "SSH keys", keys_admin_user_path(@user) = link_to "SSH keys", keys_admin_user_path(@user)
= nav_link(controller: :identities) do = nav_link(controller: :identities) do
= link_to "Identities", admin_user_identities_path(@user) = link_to "Identities", admin_user_identities_path(@user)
= nav_link(controller: :impersonation_tokens) do
= link_to "Impersonation Tokens", admin_user_impersonation_tokens_path(@user)
.append-bottom-default .append-bottom-default
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
= hidden_field_tag :state, @pre_auth.state = hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type = hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope = hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
= submit_tag "Authorize", class: "btn btn-success wide pull-left" = submit_tag "Authorize", class: "btn btn-success wide pull-left"
= form_tag oauth_authorization_path, method: :delete do = form_tag oauth_authorization_path, method: :delete do
= hidden_field_tag :client_id, @pre_auth.client.uid = hidden_field_tag :client_id, @pre_auth.client.uid
...@@ -34,4 +35,5 @@ ...@@ -34,4 +35,5 @@
= hidden_field_tag :state, @pre_auth.state = hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type = hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope = hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
= submit_tag "Deny", class: "btn btn-danger prepend-left-10" = submit_tag "Deny", class: "btn btn-danger prepend-left-10"
...@@ -4,18 +4,14 @@ ...@@ -4,18 +4,14 @@
%span %span
Members Members
- if can_edit - if can_edit
= nav_link(controller: :deploy_keys) do = nav_link(controller: :repository) do
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do = link_to namespace_project_settings_repository_path(@project.namespace, @project), title: 'Repository' do
%span %span
Deploy Keys Repository
= nav_link(controller: :integrations) do = nav_link(controller: :integrations) do
= link_to namespace_project_settings_integrations_path(@project.namespace, @project), title: 'Integrations' do = link_to namespace_project_settings_integrations_path(@project.namespace, @project), title: 'Integrations' do
%span %span
Integrations Integrations
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%span
Protected Branches
- if @project.feature_available?(:builds, current_user) - if @project.feature_available?(:builds, current_user)
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
......
...@@ -24,80 +24,11 @@ ...@@ -24,80 +24,11 @@
%hr %hr
%h5.prepend-top-0 = render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
Add a Personal Access Token
%p.profile-settings-content
Pick a name for the application, and we'll give you a unique token.
= render "form", personal_access_token: @personal_access_token, scopes: @scopes
%hr
%h5 Active Personal Access Tokens (#{@active_personal_access_tokens.length})
- if @active_personal_access_tokens.present?
.table-responsive
%table.table.active-personal-access-tokens
%thead
%tr
%th Name
%th Created
%th Expires
%th Scopes
%th
%tbody
- @active_personal_access_tokens.each do |token|
%tr
%td= token.name
%td= token.created_at.to_date.to_s(:medium)
%td
- if token.expires_at.present?
= token.expires_at.to_date.to_s(:medium)
- else
%span.personal-access-tokens-never-expires-label Never
%td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
%td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." }
- else
.settings-message.text-center
You don't have any active tokens yet.
%hr
%h5 Inactive Personal Access Tokens (#{@inactive_personal_access_tokens.length})
- if @inactive_personal_access_tokens.present?
.table-responsive
%table.table.inactive-personal-access-tokens
%thead
%tr
%th Name
%th Created
%tbody
- @inactive_personal_access_tokens.each do |token|
%tr
%td= token.name
%td= token.created_at.to_date.to_s(:medium)
- else
.settings-message.text-center
There are no inactive tokens.
= render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens
:javascript :javascript
var $dateField = $('#personal_access_token_expires_at');
var date = $dateField.val();
new Pikaday({
field: $dateField.get(0),
theme: 'gitlab-theme',
format: 'yyyy-mm-dd',
minDate: new Date(),
onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
$("#created-personal-access-token").click(function() { $("#created-personal-access-token").click(function() {
this.select(); this.select();
}); });
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
- else - else
= link_to title, '#' = link_to title, '#'
%ul.blob-commit-info.table-list.hidden-xs %ul.blob-commit-info.hidden-xs
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project, ref: @ref = render blob_commit, project: @project, ref: @ref
......
...@@ -9,33 +9,34 @@ ...@@ -9,33 +9,34 @@
- cache_key.push(commit.status(ref)) if commit.status(ref) - cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do = cache(cache_key, expires_in: 1.day) do
%li.commit.table-list-row.js-toggle-container{ id: "commit-#{commit.short_id}" } %li.commit.flex-list.js-toggle-container{ id: "commit-#{commit.short_id}" }
.table-list-cell.avatar-cell.hidden-xs .avatar-cell.hidden-xs
= author_avatar(commit, size: 36) = author_avatar(commit, size: 36)
.table-list-cell.commit-content .commit-detail
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message item-title" .commit-content
%span.commit-row-message.visible-xs-inline = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message item-title"
&middot; %span.commit-row-message.visible-xs-inline
= commit.short_id &middot;
- if commit.status(ref) = commit.short_id
.visible-xs-inline - if commit.status(ref)
= render_commit_status(commit, ref: ref) .visible-xs-inline
- if commit.description? = render_commit_status(commit, ref: ref)
%a.text-expander.hidden-xs.js-toggle-button ... - if commit.description?
%a.text-expander.hidden-xs.js-toggle-button ...
- if commit.description? - if commit.description?
%pre.commit-row-description.js-toggle-content %pre.commit-row-description.js-toggle-content
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
.commiter .commiter
= commit_author_link(commit, avatar: false, size: 24) = commit_author_link(commit, avatar: false, size: 24)
committed committed
#{time_ago_with_tooltip(commit.committed_date)} #{time_ago_with_tooltip(commit.committed_date)}
.table-list-cell.commit-actions.hidden-xs .commit-actions.flex-row.hidden-xs
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
= clipboard_button(clipboard_text: commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(clipboard_text: commit.id, title: "Copy commit SHA to clipboard")
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
...@@ -11,4 +11,4 @@ ...@@ -11,4 +11,4 @@
%li.warning-row.unstyled %li.warning-row.unstyled
#{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues. #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
- else - else
%ul.content-list.table-list= render commits, project: @project, ref: @ref %ul.content-list= render commits, project: @project, ref: @ref
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits| - commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
%li.commit-header #{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')} %li.commit-header #{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}
%li.commits-row %li.commits-row
%ul.content-list.commit-list.table-list.table-wide %ul.content-list.commit-list
= render commits, project: project, ref: ref = render commits, project: project, ref: ref
- if hidden > 0 - if hidden > 0
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
%span.key-created-at %span.key-created-at
created #{time_ago_with_tooltip(deploy_key.created_at)} created #{time_ago_with_tooltip(deploy_key.created_at)}
.visible-xs-block.visible-sm-block .visible-xs-block.visible-sm-block
- if @available_keys.include?(deploy_key) - if @deploy_keys.key_available?(deploy_key)
= link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-sm prepend-left-10", method: :put do = link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-sm prepend-left-10", method: :put do
Enable Enable
- else - else
......
= form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input" } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input" } do |f|
= form_errors(@key) = form_errors(@deploy_keys.new_key)
.form-group .form-group
= f.label :title, class: "label-light" = f.label :title, class: "label-light"
= f.text_field :title, class: 'form-control', autofocus: true, required: true = f.text_field :title, class: 'form-control', autofocus: true, required: true
......
- page_title "Deploy Keys"
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
= page_title Deploy Keys
%p %p
Deploy keys allow read-only access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one. Deploy keys allow read-only access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
.col-lg-9 .col-lg-9
%h5.prepend-top-0 %h5.prepend-top-0
Create a new deploy key for this project Create a new deploy key for this project
= render "form" = render @deploy_keys.form_partial_path
.col-lg-9.col-lg-offset-3 .col-lg-9.col-lg-offset-3
%hr %hr
.col-lg-9.col-lg-offset-3.append-bottom-default.deploy-keys .col-lg-9.col-lg-offset-3.append-bottom-default.deploy-keys
%h5.prepend-top-0 %h5.prepend-top-0
Enabled deploy keys for this project (#{@enabled_keys.size}) Enabled deploy keys for this project (#{@deploy_keys.enabled_keys_size})
- if @enabled_keys.any? - if @deploy_keys.any_keys_enabled?
%ul.well-list %ul.well-list
= render @enabled_keys = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.enabled_keys, as: :deploy_key
- else - else
.settings-message.text-center .settings-message.text-center
No deploy keys found. Create one with the form above or add existing one below. No deploy keys found. Create one with the form above.
%h5.prepend-top-default %h5.prepend-top-default
Deploy keys from projects you have access to (#{@available_project_keys.size}) Deploy keys from projects you have access to (#{@deploy_keys.available_project_keys_size})
- if @available_project_keys.any? - if @deploy_keys.any_available_project_keys_enabled?
%ul.well-list %ul.well-list
= render @available_project_keys = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.available_project_keys, as: :deploy_key
- else - else
.settings-message.text-center .settings-message.text-center
No deploy keys from your projects could be found. Create one with the form above or add existing one below. No deploy keys from your projects could be found. Create one with the form above or add existing one below.
- if @available_public_keys.any? - if @deploy_keys.any_available_public_keys_enabled?
%h5.prepend-top-default %h5.prepend-top-default
Public deploy keys available to any project (#{@available_public_keys.size}) Public deploy keys available to any project (#{@deploy_keys.available_public_keys_size})
%ul.well-list %ul.well-list
= render @available_public_keys = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.available_public_keys, as: :deploy_key
- environment = local_assigns.fetch(:environment)
- return unless environment.has_metrics? && can?(current_user, :read_environment, environment)
= link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do
= icon('area-chart')
- @no_container = true
- page_title "Metrics for environment", @environment.name
= render "projects/pipelines/head"
%div{ class: container_class }
.top-area
.row
.col-sm-6
%h3.page-title
Environment:
= @environment.name
.col-sm-6
.nav-controls
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.row
.col-sm-12
%svg.prometheus-graph{ 'graph-type' => 'cpu_values' }
.row
.col-sm-12
%svg.prometheus-graph{ 'graph-type' => 'memory_values' }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
%h3.page-title= @environment.name %h3.page-title= @environment.name
.col-md-3 .col-md-3
.nav-controls .nav-controls
= render 'projects/environments/metrics_button', environment: @environment
= render 'projects/environments/terminal_button', environment: @environment = render 'projects/environments/terminal_button', environment: @environment
= render 'projects/environments/external_url', environment: @environment = render 'projects/environments/external_url', environment: @environment
- if can?(current_user, :update_environment, @environment) - if can?(current_user, :update_environment, @environment)
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
selected: f.object.source_project_id selected: f.object.source_project_id
.merge-request-select.dropdown .merge-request-select.dropdown
= f.hidden_field :source_branch = f.hidden_field :source_branch
= dropdown_toggle f.object.source_branch || "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" } = dropdown_toggle local_assigns.fetch(f.object.source_branch, "Select source branch"), { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-branch .dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
= dropdown_title("Select source branch") = dropdown_title("Select source branch")
= dropdown_filter("Search branches") = dropdown_filter("Search branches")
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
branches: @merge_request.source_branches, branches: @merge_request.source_branches,
selected: f.object.source_branch selected: f.object.source_branch
.panel-footer .panel-footer
= icon('spinner spin', class: 'js-source-loading') .text-center= icon('spinner spin', class: 'js-source-loading')
%ul.list-unstyled.mr_source_commit %ul.list-unstyled.mr_source_commit
.col-md-6 .col-md-6
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
branches: @merge_request.target_branches, branches: @merge_request.target_branches,
selected: f.object.target_branch selected: f.object.target_branch
.panel-footer .panel-footer
= icon('spinner spin', class: "js-target-loading") .text-center= icon('spinner spin', class: "js-target-loading")
%ul.list-unstyled.mr_target_commit %ul.list-unstyled.mr_target_commit
- if @merge_request.errors.any? - if @merge_request.errors.any?
......
- if @pipeline - if @pipeline
.mr-widget-heading .mr-widget-heading
- %w[success success_with_warnings skipped canceled failed running pending].each do |status| - %w[success success_with_warnings skipped manual canceled failed running pending].each do |status|
.ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) } .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) }
%div{ class: "ci-status-icon ci-status-icon-#{status}" } %div{ class: "ci-status-icon ci-status-icon-#{status}" }
= link_to namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'icon-link' do = link_to namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'icon-link' do
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
= render 'projects/merge_requests/widget/open/build_failed' = render 'projects/merge_requests/widget/open/build_failed'
- elsif !@merge_request.mergeable_discussions_state? - elsif !@merge_request.mergeable_discussions_state?
= render 'projects/merge_requests/widget/open/unresolved_discussions' = render 'projects/merge_requests/widget/open/unresolved_discussions'
- elsif @pipeline&.blocked?
= render 'projects/merge_requests/widget/open/manual'
- elsif @merge_request.can_be_merged? || resolved_conflicts - elsif @merge_request.can_be_merged? || resolved_conflicts
= render 'projects/merge_requests/widget/open/accept' = render 'projects/merge_requests/widget/open/accept'
......
%h4
Pipeline blocked
%p
The pipeline for this merge request requires a manual action to proceed.
- @stage.statuses.latest.each do |status| - grouped_statuses = @stage.statuses.latest_ordered.group_by(&:status)
%li - HasStatus::ORDERED_STATUSES.each do |ordered_status|
= render 'ci/status/dropdown_graph_badge', subject: status - grouped_statuses.fetch(ordered_status, []).each do |status|
%li
= render 'ci/status/dropdown_graph_badge', subject: status
...@@ -23,6 +23,6 @@ ...@@ -23,6 +23,6 @@
- if can_admin_project - if can_admin_project
%th %th
%tbody %tbody
= render partial: @protected_branches, locals: { can_admin_project: can_admin_project } = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches, locals: { can_admin_project: can_admin_project}
= paginate @protected_branches, theme: 'gitlab' = paginate @protected_branches, theme: 'gitlab'
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
= f.label :name, class: 'col-md-2 text-right' do = f.label :name, class: 'col-md-2 text-right' do
Branch: Branch:
.col-md-10 .col-md-10
= render partial: "dropdown", locals: { f: f } = render partial: "projects/protected_branches/dropdown", locals: { f: f }
.help-block .help-block
= link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches') = link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches')
such as such as
......
- page_title "Protected branches"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('protected_branches') = page_specific_javascript_bundle_tag('protected_branches')
.row.prepend-top-default.append-bottom-default .row.prepend-top-default.append-bottom-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
= page_title Protected Branches
%p Keep stable branches secure and force developers to use merge requests. %p Keep stable branches secure and force developers to use merge requests.
%p.prepend-top-20 %p.prepend-top-20
By default, protected branches are designed to: By default, protected branches are designed to:
...@@ -17,6 +16,6 @@ ...@@ -17,6 +16,6 @@
%p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. %p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
.col-lg-9 .col-lg-9
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
= render 'create_protected_branch' = render 'projects/protected_branches/create_protected_branch'
= render "branches_list" = render "projects/protected_branches/branches_list"
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- else - else
(branch was removed from repository) (branch was removed from repository)
= render partial: 'update_protected_branch', locals: { protected_branch: protected_branch } = render partial: 'projects/protected_branches/update_protected_branch', locals: { protected_branch: protected_branch }
- if can_admin_project - if can_admin_project
%td %td
......
- page_title "Repository"
= render @deploy_keys
= render "projects/protected_branches/index"
%h4.prepend-top-0
Triggers
%p.prepend-top-20
Triggers can force a specific branch or tag to get rebuilt with an API call. These tokens will
impersonate their associated user including their access to projects and their project
permissions.
%p.prepend-top-20
Triggers with the
%span.label.label-primary legacy
label do not have an associated user and only have access to the current project.
%p.append-bottom-0
= succeed '.' do
Learn more in the
= link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
= form_for [@project.namespace.becomes(Namespace), @project, @trigger], html: { class: 'gl-show-field-errors' } do |f|
= form_errors(@trigger)
- if @trigger.token
.form-group
%label.label-light Token
%p.form-control-static= @trigger.token
.form-group
= f.label :key, "Description", class: "label-light"
= f.text_field :description, class: "form-control", required: true, title: 'Trigger description is required.', placeholder: "Trigger description"
= f.submit btn_text, class: "btn btn-save"
.row.prepend-top-default.append-bottom-default .row.prepend-top-default.append-bottom-default.triggers-container
.col-lg-3 .col-lg-3
%h4.prepend-top-0 = render "projects/triggers/content"
Triggers
%p.prepend-top-20
Triggers can force a specific branch or tag to get rebuilt with an API call.
%p.append-bottom-0
= succeed '.' do
Learn more in the
= link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
.col-lg-9 .col-lg-9
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%h4.panel-title %h4.panel-title
Manage your project's triggers Manage your project's triggers
.panel-body .panel-body
= render "projects/triggers/form", btn_text: "Add trigger"
%hr
- if @triggers.any? - if @triggers.any?
.table-responsive .table-responsive.triggers-list
%table.table %table.table
%thead %thead
%th %th
%strong Token %strong Token
%th
%strong Description
%th
%strong Owner
%th %th
%strong Last used %strong Last used
%th %th
= render partial: 'projects/triggers/trigger', collection: @triggers, as: :trigger = render partial: 'projects/triggers/trigger', collection: @triggers, as: :trigger
- else - else
%p.settings-message.text-center.append-bottom-default %p.settings-message.text-center.append-bottom-default
No triggers have been created yet. Add one using the button below. No triggers have been created yet. Add one using the form above.
= form_for @trigger, url: url_for(controller: '/projects/triggers', action: 'create') do |f|
= f.submit "Add trigger", class: 'btn btn-success'
.panel-footer .panel-footer
......
%tr %tr
%td %td
%span.monospace= trigger.token - if can?(current_user, :admin_trigger, trigger)
%span= trigger.token
= clipboard_button(clipboard_text: trigger.token, title: "Copy trigger token to clipboard")
- else
%span= trigger.short_token
.label-container
- if trigger.legacy?
%span.label.label-primary.has-tooltip{ title: "Trigger makes use of deprecated functionality" } legacy
- if !trigger.can_access_project?
%span.label.label-danger.has-tooltip{ title: "Trigger user has insufficient permissions to project" } invalid
%td
- if trigger.description? && trigger.description.length > 15
%span.has-tooltip{ title: trigger.description }= truncate(trigger.description, length: 15)
- else
= trigger.description
%td
- if trigger.owner
.trigger-owner.sr-only= trigger.owner.name
= user_avatar(user: trigger.owner, size: 20)
%td %td
- if trigger.last_trigger_request - if trigger.last_used
#{time_ago_in_words(trigger.last_trigger_request.created_at)} ago #{time_ago_in_words(trigger.last_used)} ago
- else - else
Never Never
%td.text-right %td.text-right.trigger-actions
= link_to 'Revoke', namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-warning btn-sm" - take_ownership_confirmation = "By taking ownership you will bind this trigger to your user account. With this the trigger will have access to all your projects as if it was you. Are you sure?"
- revoke_trigger_confirmation = "By revoking a trigger you will break any processes making use of it. Are you sure?"
- if trigger.owner != current_user && can?(current_user, :manage_trigger, trigger)
= link_to 'Take ownership', take_ownership_namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: take_ownership_confirmation }, method: :post, class: "btn btn-default btn-sm btn-trigger-take-ownership"
- if can?(current_user, :admin_trigger, trigger)
= link_to edit_namespace_project_trigger_path(@project.namespace, @project, trigger), method: :get, title: "Edit", class: "btn btn-default btn-sm" do
%i.fa.fa-pencil
- if can?(current_user, :manage_trigger, trigger)
= link_to namespace_project_trigger_path(@project.namespace, @project, trigger), data: { confirm: revoke_trigger_confirmation }, method: :delete, title: "Revoke", class: "btn btn-default btn-warning btn-sm btn-trigger-revoke" do
%i.fa.fa-trash
- page_title "Trigger"
.row.prepend-top-default.append-bottom-default
.col-lg-3
= render "content"
.col-lg-9
%h4.prepend-top-0
Update trigger
= render "form", btn_text: "Save trigger"
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
.results.prepend-top-10 .results.prepend-top-10
- if @scope == 'commits' - if @scope == 'commits'
%ul.content-list.commit-list.table-list.table-wide %ul.content-list.commit-list
= render partial: "search/results/commit", collection: @search_objects = render partial: "search/results/commit", collection: @search_objects
- else - else
.search-results .search-results
......
- personal_access_token = local_assigns.fetch(:personal_access_token) - type = impersonation ? "Impersonation" : "Personal Access"
- scopes = local_assigns.fetch(:scopes)
= form_for [:profile, personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f| %h5.prepend-top-0
Add a #{type} Token
%p.profile-settings-content
Pick a name for the application, and we'll give you a unique #{type} Token.
= form_errors(personal_access_token) = form_for token, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
= form_errors(token)
.form-group .form-group
= f.label :name, class: 'label-light' = f.label :name, class: 'label-light'
...@@ -15,7 +19,21 @@ ...@@ -15,7 +19,21 @@
.form-group .form-group
= f.label :scopes, class: 'label-light' = f.label :scopes, class: 'label-light'
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: personal_access_token, scopes: scopes = render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes
.prepend-top-default .prepend-top-default
= f.submit 'Create Personal Access Token', class: "btn btn-create" = f.submit "Create #{type} Token", class: "btn btn-create"
:javascript
var $dateField = $('.datepicker');
var date = $dateField.val();
new Pikaday({
field: $dateField.get(0),
theme: 'gitlab-theme',
format: 'yyyy-mm-dd',
minDate: new Date(),
onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
- type = impersonation ? "Impersonation" : "Personal Access"
%hr
%h5 Active #{type} Tokens (#{active_tokens.length})
- if impersonation
%p.profile-settings-content
To see all the user's personal access tokens you must impersonate them first.
- if active_tokens.present?
.table-responsive
%table.table.active-tokens
%thead
%tr
%th Name
%th Created
%th Expires
%th Scopes
- if impersonation
%th Token
%th
%tbody
- active_tokens.each do |token|
%tr
%td= token.name
%td= token.created_at.to_date.to_s(:medium)
%td
- if token.expires?
%span{ class: ('text-warning' if token.expires_soon?) }
In #{distance_of_time_in_words_to_now(token.expires_at)}
- else
%span.token-never-expires-label Never
%td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
- if impersonation
%td.token-token-container
= text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control"
= clipboard_button(clipboard_text: token.token)
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
%td= link_to "Revoke", path, method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." }
- else
.settings-message.text-center
This user has no active #{type} Tokens.
%hr
%h5 Inactive #{type} Tokens (#{inactive_tokens.length})
- if inactive_tokens.present?
.table-responsive
%table.table.inactive-tokens
%thead
%tr
%th Name
%th Created
%tbody
- inactive_tokens.each do |token|
%tr
%td= token.name
%td= token.created_at.to_date.to_s(:medium)
- else
.settings-message.text-center
This user has no inactive #{type} Tokens.
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
= hidden_field_tag 'update[issuable_ids]', [] = hidden_field_tag 'update[issuable_ids]', []
= hidden_field_tag :state_event, params[:state_event] = hidden_field_tag :state_event, params[:state_event]
.filter-item.inline .filter-item.inline.update-issues-btn
= button_tag "Update #{type.to_s.humanize(capitalize: false)}", class: "btn update_selected_issues btn-save" = button_tag "Update #{type.to_s.humanize(capitalize: false)}", class: "btn update_selected_issues btn-save"
:javascript :javascript
......
...@@ -3,8 +3,8 @@ class PostReceive ...@@ -3,8 +3,8 @@ class PostReceive
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
def perform(repo_path, identifier, changes) def perform(repo_path, identifier, changes)
if path = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1].to_s) } if repository_storage = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1]['path'].to_s) }
repo_path.gsub!(path[1].to_s, "") repo_path.gsub!(repository_storage[1]['path'].to_s, "")
else else
log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"") log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"")
end end
......
class SystemHookPushWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(push_data, hook_id)
SystemHooksService.new.execute_hooks(push_data, hook_id)
end
end
...@@ -10,8 +10,5 @@ class UpdateMergeRequestsWorker ...@@ -10,8 +10,5 @@ class UpdateMergeRequestsWorker
return unless user return unless user
MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref) MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
push_data = Gitlab::DataBuilder::Push.build(project, user, oldrev, newrev, ref, [])
SystemHooksService.new.execute_hooks(push_data, :push_hooks)
end end
end end
--- ---
title: Fix cherry-picking or reverting through an MR title: Align bulk update issues button to the right
merge_request: merge_request:
author: author:
---
title: Manage user personal access tokens through api and add impersonation tokens
merge_request: 9099
author: Simon Vocella
---
title: Combined deploy keys, push rules, protect branches and mirror repository settings options into a single one called
Repository
merge_request:
author:
---
title: fix background color for labels mention in todo
merge_request: 9155
author: mhasbini
---
title: Refactor dropdown_assignee_spec
merge_request: 9711
author: George Andrinopoulos
---
title: Uploaded files which content can change now require revalidation on each page load
merge_request: 9453
author:
---
title: Update storage settings to allow extra values per repository storage
merge_request: 9597
author:
---
title: Fix the redirect to custom home page URL
merge_request: 9518
author:
--- ---
title: Don't copy tooltip when copying GFM title: Fix jobs table header height
merge_request: merge_request:
author: author:
---
title: Fix broken migration when upgrading straight to 8.17.1
merge_request: 9613
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.
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