Merge branch 'master' into 48237-toggle-file-comments

* master: (38 commits)
  Update bitbucket.md to fix typo and small grammar touchups.
  Update time_helper.rb to fix output for exact minutes.
  Document update for GitLab Pages users.
  Support manually stopping any environment from the UI
  Fix link to frontend in handbook
  Shorten CHANGELOG filename to avoid breaking ecryptfs users
  Replace 48976-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-gitlab-backgroundmigration-fixcrossprojectlabellinks-namespace.yml
  Fix mountComponent helper path in docs
  Fix #48537 - Update avatar only via the projects API
  Add additional headline for Adding SSH keys to GitLab
  Request to be a proofreader.
  Fix specs
  Fix search bar text input alignment
  Fix API docs on unauthenticated projects return
  Adds with_projects optional parameter to /groups/:id API endpoint
  Docs: make it clear that you need a completely separate domain for Pages
  Document that we don't want to wait in tests
  Removes unused store in diffs mr refactor Removes double export for actions in diffs module in mr refactor
  Remove useless return
  Use latest gitlab-styles
  ...
This commit is contained in:
Filipa Lacerda 2018-07-10 12:05:06 +01:00
commit 135ead1d83
No known key found for this signature in database
GPG Key ID: 9CA3FDE4D1E2F1C8
319 changed files with 2721 additions and 1981 deletions

View File

@ -327,7 +327,7 @@ cloud-native-image:
cache: {}
script:
- gem install gitlab --no-ri --no-rdoc
- ./trigger-build cng
- ./scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee

View File

@ -199,12 +199,6 @@ Naming/HeredocDelimiterCase:
Naming/HeredocDelimiterNaming:
Enabled: false
# Offense count: 27
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect.
Performance/HashEachMethods:
Enabled: false
# Offense count: 1
Performance/UnfreezeString:
Exclude:

View File

@ -104,7 +104,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0'
# HAML
gem 'hamlit', '~> 2.6.1'
gem 'hamlit', '~> 2.8.8'
# Files attachments
gem 'carrierwave', '~> 1.2'
@ -351,9 +351,9 @@ group :development, :test do
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'gitlab-styles', '~> 2.3', require: false
gem 'gitlab-styles', '~> 2.4', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
gem 'rubocop', '~> 0.52.1'
gem 'rubocop', '~> 0.54.0'
gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false

View File

@ -312,8 +312,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@ -381,8 +381,8 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
hamlit (2.8.8)
temple (>= 0.8.0)
thor
tilt
hashdiff (0.3.4)
@ -776,16 +776,16 @@ GEM
pg
rails
sqlite3
rubocop (0.52.1)
rubocop (0.54.0)
parallel (~> 1.10)
parser (>= 2.4.0.2, < 3.0)
parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
rubocop-rspec (1.22.1)
rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@ -889,7 +889,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
temple (0.7.7)
temple (0.8.0)
test-prof (0.2.5)
test_after_commit (1.1.0)
activerecord (>= 3.2)
@ -900,7 +900,7 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
tilt (2.0.6)
tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
@ -1043,7 +1043,7 @@ DEPENDENCIES
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@ -1057,7 +1057,7 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hamlit (~> 2.8.8)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@ -1143,7 +1143,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
rubocop (~> 0.52.1)
rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)

View File

@ -79,7 +79,7 @@ GEM
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.2.1)
bcrypt (3.1.11)
bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
better_errors (2.1.1)
@ -111,7 +111,7 @@ GEM
capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
carrierwave (1.2.1)
carrierwave (1.2.3)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
@ -315,8 +315,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@ -384,8 +384,8 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
hamlit (2.8.8)
temple (>= 0.8.0)
thor
tilt
hashdiff (0.3.4)
@ -514,7 +514,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.8.2)
nokogiri (1.8.3)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@ -785,16 +785,16 @@ GEM
pg
rails
sqlite3
rubocop (0.52.1)
rubocop (0.54.0)
parallel (~> 1.10)
parser (>= 2.4.0.2, < 3.0)
parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
rubocop-rspec (1.22.1)
rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@ -811,7 +811,7 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.27.1)
rugged (0.27.2)
safe_yaml (1.0.4)
sanitize (4.6.5)
crass (~> 1.0.2)
@ -877,7 +877,7 @@ GEM
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
sprockets (3.7.1)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@ -898,7 +898,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
temple (0.7.7)
temple (0.8.0)
test-prof (0.2.5)
text (1.3.1)
thin (1.7.0)
@ -1053,7 +1053,7 @@ DEPENDENCIES
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@ -1067,7 +1067,7 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hamlit (~> 2.8.8)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@ -1154,7 +1154,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
rubocop (~> 0.52.1)
rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)

View File

@ -1,11 +1,5 @@
# GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
[![Gitter](https://badges.gitter.im/gitlabhq/gitlabhq.svg)](https://gitter.im/gitlabhq/gitlabhq?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby

View File

@ -2,9 +2,9 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
require File.expand_path('config/application', __dir__)
relative_url_conf = File.expand_path('../config/initializers/relative_url', __FILE__)
relative_url_conf = File.expand_path('config/initializers/relative_url', __dir__)
require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks

View File

@ -39,11 +39,6 @@ export default {
required: false,
default: true,
},
discussionsExpanded: {
type: Boolean,
required: false,
default: true,
},
currentUser: {
type: Object,
required: true,
@ -116,7 +111,7 @@ export default {
},
methods: {
...mapActions('diffs', ['toggleFileDiscussions']),
handleToggle(e, checkTarget) {
handleToggleFile(e, checkTarget) {
if (
!checkTarget ||
e.target === this.$refs.header ||
@ -128,7 +123,7 @@ export default {
showForkMessage() {
this.$emit('showForkMessage');
},
handleToggleDiscussions(){
handleToggleDiscussions() {
this.toggleFileDiscussions(this.diffFile);
},
},
@ -139,7 +134,7 @@ export default {
<div
ref="header"
class="js-file-title file-title file-title-flex-parent"
@click="handleToggle($event, true)"
@click="handleToggleFile($event, true)"
>
<div class="file-header-content">
<icon
@ -224,9 +219,9 @@ export default {
<button
:class="{ active: hasExpandedDiscussions }"
:title="s__('MergeRequests|Toggle comments for this file')"
@click="handleToggleDiscussions"
class="btn"
class="js-btn-vue-toggle-comments btn"
type="button"
@click="handleToggleDiscussions"
>
<icon name="comment" />
</button>

View File

@ -112,15 +112,5 @@ export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
});
};
export default {
setBaseConfig,
fetchDiffFiles,
setInlineDiffViewType,
setParallelDiffViewType,
showCommentForm,
cancelCommentForm,
loadMoreLines,
loadCollapsedDiff,
expandAllFiles,
toggleFileDiscussions,
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View File

@ -31,6 +31,20 @@ export const diffHasAllCollpasedDiscussions = (state, getters) => diff => {
return (discussions.length && discussions.every(discussion => !discussion.expanded)) || false;
};
/**
* Checks if the diff has any open discussions
* @param {Object} diff
* @returns {Boolean}
*/
export const diffHasExpandedDiscussions = (state, getters) => diff => {
const discussions = getters.getDiffFileDiscussions(diff);
return (
(discussions.length && discussions.find(discussion => discussion.expanded) !== undefined) ||
false
);
};
/**
* Returns an array with the discussions of the given diff
* @param {Object} diff

View File

@ -1,11 +0,0 @@
import Vue from 'vue';
import Vuex from 'vuex';
import diffsModule from './modules';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
diffs: diffsModule,
},
});

View File

@ -1,4 +1,4 @@
import actions from '../actions';
import * as actions from '../actions';
import * as getters from '../getters';
import mutations from '../mutations';
import createState from './diff_state';

View File

@ -171,6 +171,8 @@ export default class DueDateSelectors {
initMilestoneDatePicker() {
$('.datepicker').each(function initPikadayMilestone() {
const $datePicker = $(this);
const datePickerVal = $datePicker.val();
const calendar = new Pikaday({
field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker',
@ -183,7 +185,7 @@ export default class DueDateSelectors {
},
});
calendar.setDate(parsePikadayDate($datePicker.val()));
calendar.setDate(parsePikadayDate(datePickerVal));
$datePicker.data('pikaday', calendar);
});

View File

@ -1,50 +1,50 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
export default {
directives: {
tooltip,
},
components: {
loadingIcon,
Icon,
},
props: {
actions: {
type: Array,
required: false,
default: () => [],
},
components: {
loadingIcon,
Icon,
},
data() {
return {
isLoading: false,
};
},
computed: {
title() {
return 'Deploy to...';
},
props: {
actions: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
isLoading: false,
};
},
computed: {
title() {
return 'Deploy to...';
},
},
methods: {
onClickAction(endpoint) {
this.isLoading = true;
},
methods: {
onClickAction(endpoint) {
this.isLoading = true;
eventHub.$emit('postAction', endpoint);
},
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
}
return !action.playable;
},
eventHub.$emit('postAction', { endpoint });
},
};
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
}
return !action.playable;
},
},
};
</script>
<template>
<div
@ -61,10 +61,7 @@
data-toggle="dropdown"
>
<span>
<icon
:size="12"
name="play"
/>
<icon name="play" />
<i
class="fa fa-caret-down"
aria-hidden="true"
@ -85,10 +82,6 @@
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
>
<icon
:size="12"
name="play"
/>
<span>
{{ action.name }}
</span>

View File

@ -1,30 +1,30 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import { s__ } from '../../locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import { s__ } from '../../locale';
/**
* Renders the external url link in environments table.
*/
export default {
components: {
Icon,
/**
* Renders the external url link in environments table.
*/
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
externalUrl: {
type: String,
required: true,
},
directives: {
tooltip,
},
computed: {
title() {
return s__('Environments|Open live environment');
},
props: {
externalUrl: {
type: String,
required: true,
},
},
computed: {
title() {
return s__('Environments|Open');
},
},
};
},
};
</script>
<template>
<a
@ -37,9 +37,6 @@
target="_blank"
rel="noopener noreferrer nofollow"
>
<icon
:size="12"
name="external-link"
/>
<icon name="external-link" />
</a>
</template>

View File

@ -1,429 +1,450 @@
<script>
import Timeago from 'timeago.js';
import _ from 'underscore';
import tooltip from '~/vue_shared/directives/tooltip';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { humanize } from '~/lib/utils/text_utility';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback.vue';
import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
import Timeago from 'timeago.js';
import _ from 'underscore';
import tooltip from '~/vue_shared/directives/tooltip';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { humanize } from '~/lib/utils/text_utility';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback.vue';
import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
/**
* Envrionment Item Component
*
* Renders a table row for each environment.
*/
const timeagoInstance = new Timeago();
/**
* Envrionment Item Component
*
* Renders a table row for each environment.
*/
const timeagoInstance = new Timeago();
export default {
components: {
UserAvatarLink,
CommitComponent,
ActionsComponent,
ExternalUrlComponent,
StopComponent,
RollbackComponent,
TerminalButtonComponent,
MonitoringButtonComponent,
export default {
components: {
UserAvatarLink,
CommitComponent,
ActionsComponent,
ExternalUrlComponent,
StopComponent,
RollbackComponent,
TerminalButtonComponent,
MonitoringButtonComponent,
},
directives: {
tooltip,
},
props: {
model: {
type: Object,
required: true,
default: () => ({}),
},
directives: {
tooltip,
canCreateDeployment: {
type: Boolean,
required: false,
default: false,
},
props: {
model: {
type: Object,
required: true,
default: () => ({}),
},
canReadEnvironment: {
type: Boolean,
required: false,
default: false,
},
},
canCreateDeployment: {
type: Boolean,
required: false,
default: false,
},
canReadEnvironment: {
type: Boolean,
required: false,
default: false,
},
computed: {
/**
* Verifies if `last_deployment` key exists in the current Envrionment.
* This key is required to render most of the html - this method works has
* an helper.
*
* @returns {Boolean}
*/
hasLastDeploymentKey() {
if (this.model && this.model.last_deployment && !_.isEmpty(this.model.last_deployment)) {
return true;
}
return false;
},
computed: {
/**
* Verifies if `last_deployment` key exists in the current Envrionment.
* This key is required to render most of the html - this method works has
* an helper.
*
* @returns {Boolean}
*/
hasLastDeploymentKey() {
if (this.model &&
this.model.last_deployment &&
!_.isEmpty(this.model.last_deployment)) {
return true;
}
return false;
},
/**
* Verifies is the given environment has manual actions.
* Used to verify if we should render them or nor.
*
* @returns {Boolean|Undefined}
*/
hasManualActions() {
return this.model &&
this.model.last_deployment &&
this.model.last_deployment.manual_actions &&
this.model.last_deployment.manual_actions.length > 0;
},
/**
* Returns the value of the `stop_action?` key provided in the response.
*
* @returns {Boolean}
*/
hasStopAction() {
return this.model && this.model['stop_action?'];
},
/**
* Verifies if the `deployable` key is present in `last_deployment` key.
* Used to verify whether we should or not render the rollback partial.
*
* @returns {Boolean|Undefined}
*/
canRetry() {
return this.model &&
this.hasLastDeploymentKey &&
this.model.last_deployment &&
this.model.last_deployment.deployable;
},
/**
* Verifies if the date to be shown is present.
*
* @returns {Boolean|Undefined}
*/
canShowDate() {
return this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable !== undefined;
},
/**
* Human readable date.
*
* @returns {String}
*/
createdDate() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.created_at) {
return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
}
return '';
},
/**
* Returns the manual actions with the name parsed.
*
* @returns {Array.<Object>|Undefined}
*/
manualActions() {
if (this.hasManualActions) {
return this.model.last_deployment.manual_actions.map((action) => {
const parsedAction = {
name: humanize(action.name),
play_path: action.play_path,
playable: action.playable,
};
return parsedAction;
});
}
return [];
},
/**
* Builds the string used in the user image alt attribute.
*
* @returns {String}
*/
userImageAltDescription() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.user &&
this.model.last_deployment.user.username) {
return `${this.model.last_deployment.user.username}'s avatar'`;
}
return '';
},
/**
* If provided, returns the commit tag.
*
* @returns {String|Undefined}
*/
commitTag() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.tag) {
return this.model.last_deployment.tag;
}
return undefined;
},
/**
* If provided, returns the commit ref.
*
* @returns {Object|Undefined}
*/
commitRef() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.ref) {
return this.model.last_deployment.ref;
}
return undefined;
},
/**
* If provided, returns the commit url.
*
* @returns {String|Undefined}
*/
commitUrl() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.commit_path) {
return this.model.last_deployment.commit.commit_path;
}
return undefined;
},
/**
* If provided, returns the commit short sha.
*
* @returns {String|Undefined}
*/
commitShortSha() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.short_id) {
return this.model.last_deployment.commit.short_id;
}
return undefined;
},
/**
* If provided, returns the commit title.
*
* @returns {String|Undefined}
*/
commitTitle() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.title) {
return this.model.last_deployment.commit.title;
}
return undefined;
},
/**
* If provided, returns the commit tag.
*
* @returns {Object|Undefined}
*/
commitAuthor() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.author) {
return this.model.last_deployment.commit.author;
}
return undefined;
},
/**
* Verifies if the `retry_path` key is present and returns its value.
*
* @returns {String|Undefined}
*/
retryUrl() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.retry_path) {
return this.model.last_deployment.deployable.retry_path;
}
return undefined;
},
/**
* Verifies if the `last?` key is present and returns its value.
*
* @returns {Boolean|Undefined}
*/
isLastDeployment() {
return this.model && this.model.last_deployment &&
this.model.last_deployment['last?'];
},
/**
* Builds the name of the builds needed to display both the name and the id.
*
* @returns {String}
*/
buildName() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable) {
const { deployable } = this.model.last_deployment;
return `${deployable.name} #${deployable.id}`;
}
return '';
},
/**
* Builds the needed string to show the internal id.
*
* @returns {String}
*/
deploymentInternalId() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.iid) {
return `#${this.model.last_deployment.iid}`;
}
return '';
},
/**
* Verifies if the user object is present under last_deployment object.
*
* @returns {Boolean}
*/
deploymentHasUser() {
return this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user);
},
/**
* Returns the user object nested with the last_deployment object.
* Used to render the template.
*
* @returns {Object}
*/
deploymentUser() {
if (this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)) {
return this.model.last_deployment.user;
}
return {};
},
/**
* Verifies if the build name column should be rendered by verifing
* if all the information needed is present
* and if the environment is not a folder.
*
* @returns {Boolean}
*/
shouldRenderBuildName() {
return !this.model.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.deployable);
},
/**
* Verifies the presence of all the keys needed to render the buil_path.
*
* @return {String}
*/
buildPath() {
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.build_path) {
return this.model.last_deployment.deployable.build_path;
}
return '';
},
/**
* Verifies the presence of all the keys needed to render the external_url.
*
* @return {String}
*/
externalURL() {
if (this.model && this.model.external_url) {
return this.model.external_url;
}
return '';
},
/**
* Verifies if deplyment internal ID should be rendered by verifing
* if all the information needed is present
* and if the environment is not a folder.
*
* @returns {Boolean}
*/
shouldRenderDeploymentID() {
return !this.model.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined;
},
environmentPath() {
if (this.model && this.model.environment_path) {
return this.model.environment_path;
}
return '';
},
monitoringUrl() {
if (this.model && this.model.metrics_path) {
return this.model.metrics_path;
}
return '';
},
displayEnvironmentActions() {
return this.hasManualActions ||
this.externalURL ||
this.monitoringUrl ||
this.hasStopAction ||
this.canRetry;
},
/**
* Verifies is the given environment has manual actions.
* Used to verify if we should render them or nor.
*
* @returns {Boolean|Undefined}
*/
hasManualActions() {
return (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.manual_actions &&
this.model.last_deployment.manual_actions.length > 0
);
},
methods: {
onClickFolder() {
eventHub.$emit('toggleFolder', this.model);
},
/**
* Returns whether the environment can be stopped.
*
* @returns {Boolean}
*/
canStopEnvironment() {
return this.model && this.model.can_stop;
},
};
/**
* Verifies if the `deployable` key is present in `last_deployment` key.
* Used to verify whether we should or not render the rollback partial.
*
* @returns {Boolean|Undefined}
*/
canRetry() {
return (
this.model &&
this.hasLastDeploymentKey &&
this.model.last_deployment &&
this.model.last_deployment.deployable
);
},
/**
* Verifies if the date to be shown is present.
*
* @returns {Boolean|Undefined}
*/
canShowDate() {
return (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable !== undefined
);
},
/**
* Human readable date.
*
* @returns {String}
*/
createdDate() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.created_at
) {
return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
}
return '';
},
/**
* Returns the manual actions with the name parsed.
*
* @returns {Array.<Object>|Undefined}
*/
manualActions() {
if (this.hasManualActions) {
return this.model.last_deployment.manual_actions.map(action => {
const parsedAction = {
name: humanize(action.name),
play_path: action.play_path,
playable: action.playable,
};
return parsedAction;
});
}
return [];
},
/**
* Builds the string used in the user image alt attribute.
*
* @returns {String}
*/
userImageAltDescription() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.user &&
this.model.last_deployment.user.username
) {
return `${this.model.last_deployment.user.username}'s avatar'`;
}
return '';
},
/**
* If provided, returns the commit tag.
*
* @returns {String|Undefined}
*/
commitTag() {
if (this.model && this.model.last_deployment && this.model.last_deployment.tag) {
return this.model.last_deployment.tag;
}
return undefined;
},
/**
* If provided, returns the commit ref.
*
* @returns {Object|Undefined}
*/
commitRef() {
if (this.model && this.model.last_deployment && this.model.last_deployment.ref) {
return this.model.last_deployment.ref;
}
return undefined;
},
/**
* If provided, returns the commit url.
*
* @returns {String|Undefined}
*/
commitUrl() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.commit_path
) {
return this.model.last_deployment.commit.commit_path;
}
return undefined;
},
/**
* If provided, returns the commit short sha.
*
* @returns {String|Undefined}
*/
commitShortSha() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.short_id
) {
return this.model.last_deployment.commit.short_id;
}
return undefined;
},
/**
* If provided, returns the commit title.
*
* @returns {String|Undefined}
*/
commitTitle() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.title
) {
return this.model.last_deployment.commit.title;
}
return undefined;
},
/**
* If provided, returns the commit tag.
*
* @returns {Object|Undefined}
*/
commitAuthor() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.commit &&
this.model.last_deployment.commit.author
) {
return this.model.last_deployment.commit.author;
}
return undefined;
},
/**
* Verifies if the `retry_path` key is present and returns its value.
*
* @returns {String|Undefined}
*/
retryUrl() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.retry_path
) {
return this.model.last_deployment.deployable.retry_path;
}
return undefined;
},
/**
* Verifies if the `last?` key is present and returns its value.
*
* @returns {Boolean|Undefined}
*/
isLastDeployment() {
return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
},
/**
* Builds the name of the builds needed to display both the name and the id.
*
* @returns {String}
*/
buildName() {
if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) {
const { deployable } = this.model.last_deployment;
return `${deployable.name} #${deployable.id}`;
}
return '';
},
/**
* Builds the needed string to show the internal id.
*
* @returns {String}
*/
deploymentInternalId() {
if (this.model && this.model.last_deployment && this.model.last_deployment.iid) {
return `#${this.model.last_deployment.iid}`;
}
return '';
},
/**
* Verifies if the user object is present under last_deployment object.
*
* @returns {Boolean}
*/
deploymentHasUser() {
return (
this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)
);
},
/**
* Returns the user object nested with the last_deployment object.
* Used to render the template.
*
* @returns {Object}
*/
deploymentUser() {
if (
this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)
) {
return this.model.last_deployment.user;
}
return {};
},
/**
* Verifies if the build name column should be rendered by verifing
* if all the information needed is present
* and if the environment is not a folder.
*
* @returns {Boolean}
*/
shouldRenderBuildName() {
return (
!this.model.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.deployable)
);
},
/**
* Verifies the presence of all the keys needed to render the buil_path.
*
* @return {String}
*/
buildPath() {
if (
this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable.build_path
) {
return this.model.last_deployment.deployable.build_path;
}
return '';
},
/**
* Verifies the presence of all the keys needed to render the external_url.
*
* @return {String}
*/
externalURL() {
if (this.model && this.model.external_url) {
return this.model.external_url;
}
return '';
},
/**
* Verifies if deplyment internal ID should be rendered by verifing
* if all the information needed is present
* and if the environment is not a folder.
*
* @returns {Boolean}
*/
shouldRenderDeploymentID() {
return (
!this.model.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined
);
},
environmentPath() {
if (this.model && this.model.environment_path) {
return this.model.environment_path;
}
return '';
},
monitoringUrl() {
if (this.model && this.model.metrics_path) {
return this.model.metrics_path;
}
return '';
},
displayEnvironmentActions() {
return (
this.hasManualActions ||
this.externalURL ||
this.monitoringUrl ||
this.canStopEnvironment ||
this.canRetry
);
},
},
methods: {
onClickFolder() {
eventHub.$emit('toggleFolder', this.model);
},
},
};
</script>
<template>
<div
@ -580,11 +601,6 @@
class="btn-group table-action-buttons"
role="group">
<actions-component
v-if="hasManualActions && canCreateDeployment"
:actions="manualActions"
/>
<external-url-component
v-if="externalURL && canReadEnvironment"
:external-url="externalURL"
@ -595,21 +611,26 @@
:monitoring-url="monitoringUrl"
/>
<actions-component
v-if="hasManualActions && canCreateDeployment"
:actions="manualActions"
/>
<terminal-button-component
v-if="model && model.terminal_path"
:terminal-path="model.terminal_path"
/>
<stop-component
v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path"
/>
<rollback-component
v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl"
/>
<stop-component
v-if="canStopEnvironment"
:environment="model"
/>
</div>
</div>
</div>

View File

@ -1,29 +1,29 @@
<script>
/**
* Renders the Monitoring (Metrics) link in environments table.
*/
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
/**
* Renders the Monitoring (Metrics) link in environments table.
*/
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
Icon,
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
monitoringUrl: {
type: String,
required: true,
},
directives: {
tooltip,
},
computed: {
title() {
return 'Monitoring';
},
props: {
monitoringUrl: {
type: String,
required: true,
},
},
computed: {
title() {
return 'Monitoring';
},
},
};
},
};
</script>
<template>
<a
@ -35,9 +35,6 @@
data-container="body"
rel="noopener noreferrer nofollow"
>
<icon
:size="12"
name="chart"
/>
<icon name="chart" />
</a>
</template>

View File

@ -1,56 +1,74 @@
<script>
/**
* Renders Rollback or Re deploy button in environments table depending
* of the provided property `isLastDeployment`.
*
* Makes a post request when the button is clicked.
*/
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
/**
* Renders Rollback or Re deploy button in environments table depending
* of the provided property `isLastDeployment`.
*
* Makes a post request when the button is clicked.
*/
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../event_hub';
import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
loadingIcon,
},
props: {
retryUrl: {
type: String,
default: '',
},
export default {
components: {
Icon,
LoadingIcon,
},
isLastDeployment: {
type: Boolean,
default: true,
},
},
data() {
return {
isLoading: false,
};
},
methods: {
onClick() {
this.isLoading = true;
directives: {
tooltip,
},
eventHub.$emit('postAction', this.retryUrl);
},
props: {
retryUrl: {
type: String,
default: '',
},
};
isLastDeployment: {
type: Boolean,
default: true,
},
},
data() {
return {
isLoading: false,
};
},
computed: {
title() {
return this.isLastDeployment ? s__('Environments|Re-deploy to environment') : s__('Environments|Rollback environment');
},
},
methods: {
onClick() {
this.isLoading = true;
eventHub.$emit('postAction', { endpoint: this.retryUrl });
},
},
};
</script>
<template>
<button
v-tooltip
:disabled="isLoading"
:title="title"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
>
<span v-if="isLastDeployment">
{{ s__("Environments|Re-deploy") }}
</span>
<span v-else>
{{ s__("Environments|Rollback") }}
</span>
<icon
v-if="isLastDeployment"
name="repeat" />
<icon
v-else
name="redo"/>
<loading-icon v-if="isLoading" />
</button>

View File

@ -1,72 +1,78 @@
<script>
/**
* Renders the stop "button" that allows stop an environment.
* Used in environments table.
*/
/**
* Renders the stop "button" that allows stop an environment.
* Used in environments table.
*/
import $ from 'jquery';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import $ from 'jquery';
import Icon from '~/vue_shared/components/icon.vue';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
loadingIcon,
export default {
components: {
Icon,
LoadingButton,
},
directives: {
tooltip,
},
props: {
environment: {
type: Object,
required: true,
},
},
directives: {
tooltip,
data() {
return {
isLoading: false,
};
},
computed: {
title() {
return s__('Environments|Stop environment');
},
},
props: {
stopUrl: {
type: String,
default: '',
},
mounted() {
eventHub.$on('stopEnvironment', this.onStopEnvironment);
},
beforeDestroy() {
eventHub.$off('stopEnvironment', this.onStopEnvironment);
},
methods: {
onClick() {
$(this.$el).tooltip('dispose');
eventHub.$emit('requestStopEnvironment', this.environment);
},
data() {
return {
isLoading: false,
};
onStopEnvironment(environment) {
if (this.environment.id === environment.id) {
this.isLoading = true;
}
},
computed: {
title() {
return 'Stop';
},
},
methods: {
onClick() {
// eslint-disable-next-line no-alert
if (window.confirm('Are you sure you want to stop this environment?')) {
this.isLoading = true;
$(this.$el).tooltip('dispose');
eventHub.$emit('postAction', this.stopUrl);
}
},
},
};
},
};
</script>
<template>
<button
<loading-button
v-tooltip
:disabled="isLoading"
:loading="isLoading"
:title="title"
:aria-label="title"
type="button"
class="btn stop-env-link d-none d-sm-none d-md-block"
container-class="btn btn-danger d-none d-sm-none d-md-block"
data-container="body"
data-toggle="modal"
data-target="#stop-environment-modal"
@click="onClick"
>
<i
class="fa fa-stop stop-env-icon"
aria-hidden="true"
>
</i>
<loading-icon v-if="isLoading" />
</button>
<icon name="stop"/>
</loading-button>
</template>

View File

@ -1,31 +1,31 @@
<script>
/**
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
/**
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
Icon,
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
terminalPath: {
type: String,
required: false,
default: '',
},
directives: {
tooltip,
},
computed: {
title() {
return 'Terminal';
},
props: {
terminalPath: {
type: String,
required: false,
default: '',
},
},
computed: {
title() {
return 'Terminal';
},
},
};
},
};
</script>
<template>
<a
@ -36,9 +36,6 @@
class="btn terminal-button d-none d-sm-none d-md-block"
data-container="body"
>
<icon
:size="12"
name="terminal"
/>
<icon name="terminal" />
</a>
</template>

View File

@ -5,10 +5,12 @@
import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import StopEnvironmentModal from './stop_environment_modal.vue';
export default {
components: {
emptyState,
StopEnvironmentModal,
},
mixins: [
@ -90,6 +92,8 @@
</script>
<template>
<div :class="cssContainerClass">
<stop-environment-modal :environment="environmentInStopModal" />
<div class="top-area">
<tabs
:tabs="tabs"

View File

@ -0,0 +1,92 @@
<script>
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '../event_hub';
export default {
id: 'stop-environment-modal',
name: 'StopEnvironmentModal',
components: {
GlModal,
LoadingButton,
},
directives: {
tooltip,
},
props: {
environment: {
type: Object,
required: true,
},
},
computed: {
noStopActionMessage() {
return sprintf(
s__(
`Environments|Note that this action will stop the environment,
but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment
due to no stop environment action being defined
in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file.`,
),
{
emphasisStart: '<strong>',
emphasisEnd: '</strong>',
ciConfigLinkStart:
'<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">',
ciConfigLinkEnd: '</a>',
},
false,
);
},
},
methods: {
onSubmit() {
eventHub.$emit('stopEnvironment', this.environment);
},
},
};
</script>
<template>
<gl-modal
:id="$options.id"
:footer-primary-button-text="s__('Environments|Stop environment')"
footer-primary-button-variant="danger"
@submit="onSubmit"
>
<template slot="header">
<h4
class="modal-title d-flex mw-100"
>
Stopping
<span
v-tooltip
:title="environment.name"
class="text-truncate ml-1 mr-1 flex-fill"
>{{ environment.name }}</span>
?
</h4>
</template>
<p>{{ s__('Environments|Are you sure you want to stop this environment?') }}</p>
<div
v-if="!environment.has_stop_action"
class="warning_message"
>
<p v-html="noStopActionMessage"></p>
<a
href="https://docs.gitlab.com/ee/ci/environments.html#stopping-an-environment"
target="_blank"
rel="noopener noreferrer"
>{{ s__('Environments|Learn more about stopping environments') }}</a>
</div>
</gl-modal>
</template>

View File

@ -1,12 +1,18 @@
<script>
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import StopEnvironmentModal from '../components/stop_environment_modal.vue';
export default {
components: {
StopEnvironmentModal,
},
mixins: [
environmentsMixin,
CIPaginationMixin,
],
props: {
endpoint: {
type: String,
@ -38,6 +44,8 @@
</script>
<template>
<div :class="cssContainerClass">
<stop-environment-modal :environment="environmentInStopModal" />
<div
v-if="!isLoading"
class="top-area"

View File

@ -40,6 +40,7 @@ export default {
scope: getParameterByName('scope') || 'available',
page: getParameterByName('page') || '1',
requestData: {},
environmentInStopModal: {},
};
},
@ -85,7 +86,7 @@ export default {
Flash(s__('Environments|An error occurred while fetching the environments.'));
},
postAction(endpoint) {
postAction({ endpoint, errorMessage }) {
if (!this.isMakingRequest) {
this.isLoading = true;
@ -93,7 +94,7 @@ export default {
.then(() => this.fetchEnvironments())
.catch(() => {
this.isLoading = false;
Flash(s__('Environments|An error occurred while making the request.'));
Flash(errorMessage || s__('Environments|An error occurred while making the request.'));
});
}
},
@ -106,6 +107,15 @@ export default {
.catch(this.errorCallback);
},
updateStopModal(environment) {
this.environmentInStopModal = environment;
},
stopEnvironment(environment) {
const endpoint = environment.stop_path;
const errorMessage = s__('Environments|An error occurred while stopping the environment, please try again');
this.postAction({ endpoint, errorMessage });
},
},
computed: {
@ -162,9 +172,13 @@ export default {
});
eventHub.$on('postAction', this.postAction);
eventHub.$on('requestStopEnvironment', this.updateStopModal);
eventHub.$on('stopEnvironment', this.stopEnvironment);
},
beforeDestroyed() {
eventHub.$off('postAction');
beforeDestroy() {
eventHub.$off('postAction', this.postAction);
eventHub.$off('requestStopEnvironment', this.updateStopModal);
eventHub.$off('stopEnvironment', this.stopEnvironment);
},
};

View File

@ -13,7 +13,7 @@ export default class EnvironmentsService {
// eslint-disable-next-line class-methods-use-this
postAction(endpoint) {
return axios.post(endpoint, {}, { emulateJSON: true });
return axios.post(endpoint, {});
}
getFolderContent(folderUrl) {

View File

@ -1,7 +1,6 @@
export const ADD_NEW_NOTE = 'ADD_NEW_NOTE';
export const ADD_NEW_REPLY_TO_DISCUSSION = 'ADD_NEW_REPLY_TO_DISCUSSION';
export const DELETE_NOTE = 'DELETE_NOTE';
export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES';
export const SET_NOTES_DATA = 'SET_NOTES_DATA';
export const SET_NOTEABLE_DATA = 'SET_NOTEABLE_DATA';
@ -11,15 +10,15 @@ export const SET_LAST_FETCHED_AT = 'SET_LAST_FETCHED_AT';
export const SET_TARGET_NOTE_HASH = 'SET_TARGET_NOTE_HASH';
export const SHOW_PLACEHOLDER_NOTE = 'SHOW_PLACEHOLDER_NOTE';
export const TOGGLE_AWARD = 'TOGGLE_AWARD';
export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
export const UPDATE_NOTE = 'UPDATE_NOTE';
export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
// DISCUSSION
export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';

View File

@ -224,7 +224,10 @@
.form-control {
position: relative;
min-width: 200px;
padding: 5px 25px 6px 0;
padding-right: 25px;
padding-left: 0;
height: $input-height;
line-height: inherit;
border-color: transparent;
&:focus,

View File

@ -23,7 +23,7 @@
}
.btn-group {
> a {
> .btn:not(.btn-danger) {
color: $gl-text-color-secondary;
}

View File

@ -22,7 +22,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def update
if deploy_key.update_attributes(update_params)
if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
redirect_to admin_deploy_keys_path
else
@ -34,7 +34,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
deploy_key.destroy
respond_to do |format|
format.html { redirect_to admin_deploy_keys_path, status: 302 }
format.html { redirect_to admin_deploy_keys_path, status: :found }
format.json { head :ok }
end
end

View File

@ -39,7 +39,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
if @group.update_attributes(group_params)
if @group.update(group_params)
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"

View File

@ -23,7 +23,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def update
if hook.update_attributes(hook_params)
if hook.update(hook_params)
flash[:notice] = 'System hook was successfully updated.'
redirect_to admin_hooks_path
else
@ -34,7 +34,7 @@ class Admin::HooksController < Admin::ApplicationController
def destroy
hook.destroy
redirect_to admin_hooks_path, status: 302
redirect_to admin_hooks_path, status: :found
end
def test

View File

@ -25,7 +25,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
end
def update
if @identity.update_attributes(identity_params)
if @identity.update(identity_params)
RepairLdapBlockedUserService.new(@user).execute
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
else

View File

@ -11,7 +11,7 @@ class Admin::ImpersonationsController < Admin::ApplicationController
session[:impersonator_id] = nil
redirect_to admin_user_path(original_user), status: 302
redirect_to admin_user_path(original_user), status: :found
end
private

View File

@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController
def cancel_all
Ci::Build.running_or_pending.each(&:cancel)
redirect_to admin_jobs_path, status: 303
redirect_to admin_jobs_path, status: :see_other
end
end

View File

@ -16,7 +16,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
runner = rp.runner
rp.destroy
redirect_to admin_runner_path(runner), status: 302
redirect_to admin_runner_path(runner), status: :found
end
private

View File

@ -28,7 +28,7 @@ class Admin::RunnersController < Admin::ApplicationController
def destroy
@runner.destroy
redirect_to admin_runners_path, status: 302
redirect_to admin_runners_path, status: :found
end
def resume

View File

@ -16,7 +16,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
if service.update_attributes(service_params[:service])
if service.update(service_params[:service])
PropagateServiceTemplateWorker.perform_async(service.id) if service.active?
redirect_to admin_application_settings_services_path,

View File

@ -163,7 +163,7 @@ class Admin::UsersController < Admin::ApplicationController
format.json { head :ok }
else
format.html { redirect_back_or_admin_user(alert: 'There was an error removing the e-mail.') }
format.json { render json: 'There was an error removing the e-mail.', status: 400 }
format.json { render json: 'There was an error removing the e-mail.', status: :bad_request }
end
end
end

View File

@ -127,7 +127,7 @@ module IssuableActions
errors: [
"Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
]
}, status: 409
}, status: :conflict
end
end
end

View File

@ -27,7 +27,7 @@ module LfsRequest
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
documentation_url: help_url
},
status: 501
status: :not_implemented
)
end

View File

@ -7,6 +7,6 @@ class Groups::AvatarsController < Groups::ApplicationController
@group.remove_avatar!
@group.save
redirect_to edit_group_path(@group), status: 302
redirect_to edit_group_path(@group), status: :found
end
end

View File

@ -23,7 +23,7 @@ class Groups::RunnersController < Groups::ApplicationController
def destroy
@runner.destroy
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: 302
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found
end
def resume

View File

@ -41,7 +41,7 @@ class JwtController < ApplicationController
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}" }
]
}, status: 401
}, status: :unauthorized
end
def render_unauthorized
@ -50,7 +50,7 @@ class JwtController < ApplicationController
{ code: 'UNAUTHORIZED',
message: 'HTTP Basic: Access denied' }
]
}, status: 401
}, status: :unauthorized
end
def auth_params

View File

@ -5,14 +5,14 @@ class NotificationSettingsController < ApplicationController
return render_404 unless can_read?(resource)
@notification_setting = current_user.notification_settings_for(resource)
@saved = @notification_setting.update_attributes(notification_setting_params)
@saved = @notification_setting.update(notification_setting_params)
render_response
end
def update
@notification_setting = current_user.notification_settings.find(params[:id])
@saved = @notification_setting.update_attributes(notification_setting_params)
@saved = @notification_setting.update(notification_setting_params)
render_response
end

View File

@ -7,7 +7,7 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController
ActiveSession.destroy(current_user, params[:id])
respond_to do |format|
format.html { redirect_to profile_active_sessions_url, status: 302 }
format.html { redirect_to profile_active_sessions_url, status: :found }
format.js { head :ok }
end
end

View File

@ -4,6 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
Users::UpdateService.new(current_user, user: @user).execute { |user| user.remove_avatar! }
redirect_to profile_path, status: 302
redirect_to profile_path, status: :found
end
end

View File

@ -39,7 +39,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}."
end
redirect_to profile_chat_names_path, status: 302
redirect_to profile_chat_names_path, status: :found
end
private

View File

@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
Emails::DestroyService.new(current_user, user: current_user).execute(@email)
respond_to do |format|
format.html { redirect_to profile_emails_url, status: 302 }
format.html { redirect_to profile_emails_url, status: :found }
format.js { head :ok }
end
end

View File

@ -21,7 +21,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.destroy
respond_to do |format|
format.html { redirect_to profile_gpg_keys_url, status: 302 }
format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end
@ -30,7 +30,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.revoke
respond_to do |format|
format.html { redirect_to profile_gpg_keys_url, status: 302 }
format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end

View File

@ -26,7 +26,7 @@ class Profiles::KeysController < Profiles::ApplicationController
Keys::DestroyService.new(current_user).execute(@key)
respond_to do |format|
format.html { redirect_to profile_keys_url, status: 302 }
format.html { redirect_to profile_keys_url, status: :found }
format.js { head :ok }
end
end

View File

@ -78,7 +78,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy
current_user.disable_two_factor!
redirect_to profile_account_path, status: 302
redirect_to profile_account_path, status: :found
end
def skip

View File

@ -61,7 +61,7 @@ class Projects::ApplicationController < ApplicationController
def require_non_empty_project
# Be sure to return status code 303 to avoid a double DELETE:
# http://api.rubyonrails.org/classes/ActionController/Redirecting.html
redirect_to project_path(@project), status: 303 if @project.empty_repo?
redirect_to project_path(@project), status: :see_other if @project.empty_repo?
end
def require_branch_head

View File

@ -21,6 +21,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.save
redirect_to edit_project_path(@project), status: 302
redirect_to edit_project_path(@project), status: :found
end
end

View File

@ -98,7 +98,7 @@ class Projects::BranchesController < Projects::ApplicationController
flash_type = result[:status] == :error ? :alert : :notice
flash[flash_type] = result[:message]
redirect_to project_branches_path(@project), status: 303
redirect_to project_branches_path(@project), status: :see_other
end
format.js { render nothing: true, status: result[:return_code] }

View File

@ -62,7 +62,7 @@ class Projects::ClustersController < Projects::ApplicationController
def destroy
if cluster.destroy
flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
redirect_to project_clusters_path(project), status: 302
redirect_to project_clusters_path(project), status: :found
else
flash[:notice] = _('Kubernetes cluster integration was not removed.')
render :show

View File

@ -35,7 +35,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def update
if deploy_key.update_attributes(update_params)
if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
redirect_to_repository_settings(@project)
else

View File

@ -2,7 +2,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_create_deployment!, only: [:stop]
before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
@ -116,7 +116,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.terminal_websocket(terminal)
else
render text: 'Not found', status: 404
render text: 'Not found', status: :not_found
end
end
@ -175,4 +175,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def environment
@environment ||= project.environments.find(params[:id])
end
def authorize_stop_environment!
access_denied! unless can?(current_user, :stop_environment, environment)
end
end

View File

@ -53,7 +53,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
render plain: "HTTP Basic: Access denied\n", status: :unauthorized
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_missing_personal_access_token
end
@ -83,7 +83,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
render plain: "HTTP Basic: Access denied\n" \
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}",
status: 401
status: :unauthorized
end
def repository

View File

@ -24,7 +24,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
def update
@group_link = @project.project_group_links.find(params[:id])
@group_link.update_attributes(group_link_params)
@group_link.update(group_link_params)
end
def destroy
@ -34,7 +34,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
respond_to do |format|
format.html do
redirect_to project_project_members_path(project), status: 302
redirect_to project_project_members_path(project), status: :found
end
format.js { head :ok }
end

View File

@ -29,7 +29,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def update
if hook.update_attributes(hook_params)
if hook.update(hook_params)
flash[:notice] = 'Hook was successfully updated.'
redirect_to project_settings_integrations_path(@project)
else
@ -48,7 +48,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
redirect_to project_settings_integrations_path(@project), status: 302
redirect_to project_settings_integrations_path(@project), status: :found
end
private

View File

@ -39,7 +39,7 @@ class Projects::LabelsController < Projects::ApplicationController
else
respond_to do |format|
format.html { render :new }
format.json { render json: { message: @label.errors.messages }, status: 400 }
format.json { render json: { message: @label.errors.messages }, status: :bad_request }
end
end
end
@ -115,7 +115,7 @@ class Projects::LabelsController < Projects::ApplicationController
flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe
respond_to do |format|
format.html do
redirect_to(project_labels_path(@project), status: 303)
redirect_to(project_labels_path(@project), status: :see_other)
end
format.json do
render json: { url: project_labels_path(@project) }

View File

@ -25,7 +25,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
documentation_url: "#{Gitlab.config.gitlab.url}/help"
},
status: 501
status: :not_implemented
)
end

View File

@ -28,7 +28,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
if store_file!(oid, size)
head 200
else
render plain: 'Unprocessable entity', status: 422
render plain: 'Unprocessable entity', status: :unprocessable_entity
end
rescue ActiveRecord::RecordInvalid
render_lfs_forbidden

View File

@ -192,7 +192,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
stop_url =
if environment.stop_action? && can?(current_user, :create_deployment, environment)
if can?(current_user, :stop_environment, environment)
stop_project_environment_path(project, environment)
end
@ -227,7 +227,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def rebase
RebaseWorker.perform_async(@merge_request.id, current_user.id)
render nothing: true, status: 200
render nothing: true, status: :ok
end
protected

View File

@ -96,7 +96,7 @@ class Projects::MilestonesController < Projects::ApplicationController
Milestones::DestroyService.new(project, current_user).execute(milestone)
respond_to do |format|
format.html { redirect_to namespace_project_milestones_path, status: 303 }
format.html { redirect_to namespace_project_milestones_path, status: :see_other }
format.js { head :ok }
end
end

View File

@ -13,7 +13,7 @@ class Projects::MirrorsController < Projects::ApplicationController
end
def update
if project.update_attributes(mirror_params)
if project.update(mirror_params)
flash[:notice] = 'Mirroring settings were successfully updated.'
else
flash[:alert] = project.errors.full_messages.join(', ').html_safe

View File

@ -64,7 +64,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def destroy
if schedule.destroy
redirect_to pipeline_schedules_path(@project), status: 302
redirect_to pipeline_schedules_path(@project), status: :found
else
redirect_to pipeline_schedules_path(@project),
status: :forbidden,

View File

@ -14,7 +14,7 @@ class Projects::ReleasesController < Projects::ApplicationController
# it exists only to save a description to each Tag.
# If description is empty we should destroy the existing record.
if release_params[:description].present?
release.update_attributes(release_params)
release.update(release_params)
else
release.destroy
end

View File

@ -24,7 +24,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
send_git_archive @repository, ref: @ref, format: params[:format], append_sha: append_sha
rescue => ex
logger.error("#{self.class.name}: #{ex}")
return git_not_found!
git_not_found!
end
def assign_archive_vars

View File

@ -21,6 +21,6 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
runner_project = project.runner_projects.find(params[:id])
runner_project.destroy
redirect_to project_runners_path(project), status: 302
redirect_to project_runners_path(project), status: :found
end
end

View File

@ -24,7 +24,7 @@ class Projects::RunnersController < Projects::ApplicationController
@runner.destroy
end
redirect_to project_runners_path(@project), status: 302
redirect_to project_runners_path(@project), status: :found
end
def resume

View File

@ -34,7 +34,7 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service_test_response
if @service.update_attributes(service_params[:service])
if @service.update(service_params[:service])
data = @service.test_data(project, current_user)
outcome = @service.test(data)

View File

@ -82,7 +82,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.destroy
redirect_to project_snippets_path(@project), status: 302
redirect_to project_snippets_path(@project), status: :found
end
protected

View File

@ -50,7 +50,7 @@ class Projects::TagsController < Projects::ApplicationController
respond_to do |format|
if result[:status] == :success
format.html do
redirect_to project_tags_path(@project), status: 303
redirect_to project_tags_path(@project), status: :see_other
end
format.js

View File

@ -14,6 +14,6 @@ class Projects::TemplatesController < Projects::ApplicationController
def get_template_class
template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
@template_type = template_types[params[:template_type]]
render json: [], status: 404 unless @template_type
render json: [], status: :not_found unless @template_type
end
end

View File

@ -50,7 +50,7 @@ class Projects::TriggersController < Projects::ApplicationController
flash[:alert] = "Could not remove the trigger."
end
redirect_to project_settings_ci_cd_path(@project), status: 302
redirect_to project_settings_ci_cd_path(@project), status: :found
end
private

View File

@ -120,7 +120,7 @@ class Projects::WikisController < Projects::ApplicationController
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
return false
false
end
def wiki_params
@ -129,7 +129,7 @@ class Projects::WikisController < Projects::ApplicationController
def build_page(args)
WikiPage.new(@project_wiki).tap do |page|
page.update_attributes(args)
page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
end
end
end

View File

@ -133,7 +133,7 @@ class ProjectsController < Projects::ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:notice] = _("Project '%{project_name}' is in the process of being deleted.") % { project_name: @project.full_name }
redirect_to dashboard_projects_path, status: 302
redirect_to dashboard_projects_path, status: :found
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), status: 302, alert: ex.message
end

View File

@ -32,8 +32,8 @@ class SessionsController < Devise::SessionsController
super do |resource|
# User has successfully signed in, so clear any unused reset token
if resource.reset_password_token.present?
resource.update_attributes(reset_password_token: nil,
reset_password_sent_at: nil)
resource.update(reset_password_token: nil,
reset_password_sent_at: nil)
end
# hide the signed-in notification

View File

@ -13,7 +13,7 @@ module Sherlock
def destroy_all
Gitlab::Sherlock.collection.clear
redirect_to :back, status: 302
redirect_to :back, status: :found
end
end
end

View File

@ -89,7 +89,7 @@ class SnippetsController < ApplicationController
@snippet.destroy
redirect_to snippets_path, status: 302
redirect_to snippets_path, status: :found
end
protected

View File

@ -5,9 +5,13 @@ module TimeHelper
seconds = interval_in_seconds - minutes * 60
if minutes >= 1
"#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
if seconds % 60 == 0
pluralize(minutes, "minute")
else
[pluralize(minutes, "minute"), pluralize(seconds, "second")].to_sentence
end
else
"#{pluralize(seconds, "second")}"
pluralize(seconds, "second")
end
end

View File

@ -371,7 +371,7 @@ module Ci
def update_coverage
coverage = trace.extract_coverage(coverage_regex)
update_attributes(coverage: coverage) if coverage.present?
update(coverage: coverage) if coverage.present?
end
def parse_trace_sections!
@ -437,9 +437,9 @@ module Ci
end
def artifacts_metadata_entry(path, **options)
artifacts_metadata.use_file do |metadata_path|
artifacts_metadata.open do |metadata_stream|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
metadata_path,
metadata_stream,
path,
**options)

View File

@ -23,7 +23,7 @@ module ProtectedRef
# If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations
# to fail.
has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
has_many :"#{type}_access_levels", inverse_of: self.model_name.singular
validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }

View File

@ -11,8 +11,8 @@ module Network
@parent_spaces = []
end
def method_missing(m, *args, &block)
@commit.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
def method_missing(msg, *args, &block)
@commit.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def space

View File

@ -107,7 +107,7 @@ class ProjectWiki
update_project_activity
rescue Gitlab::Git::Wiki::DuplicatePageError => e
@error_message = "Duplicate page: #{e.message}"
return false
false
end
def update_page(page, content:, title: nil, format: :markdown, message: nil)

View File

@ -57,7 +57,7 @@ class RemoteMirror < ActiveRecord::Base
Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
timestamp = Time.now
remote_mirror.update_attributes!(
remote_mirror.update!(
last_update_at: timestamp, last_successful_update_at: timestamp, last_error: nil
)
end

View File

@ -462,12 +462,12 @@ class Repository
expire_branches_cache
end
def method_missing(m, *args, &block)
if m == :lookup && !block_given?
lookup_cache[m] ||= {}
lookup_cache[m][args.join(":")] ||= raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
def method_missing(msg, *args, &block)
if msg == :lookup && !block_given?
lookup_cache[msg] ||= {}
lookup_cache[msg][args.join(":")] ||= raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
else
raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end

View File

@ -496,7 +496,7 @@ class User < ActiveRecord::Base
def disable_two_factor!
transaction do
update_attributes(
update(
otp_required_for_login: false,
encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil,
@ -1053,7 +1053,7 @@ class User < ActiveRecord::Base
return @global_notification_setting if defined?(@global_notification_setting)
@global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
@global_notification_setting.update_attributes(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
@global_notification_setting.update(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
@global_notification_setting
end
@ -1333,8 +1333,8 @@ class User < ActiveRecord::Base
end
end
def self.unique_internal(scope, username, email_pattern, &b)
scope.first || create_unique_internal(scope, username, email_pattern, &b)
def self.unique_internal(scope, username, email_pattern, &block)
scope.first || create_unique_internal(scope, username, email_pattern, &block)
end
def self.create_unique_internal(scope, username, email_pattern, &creation_block)

View File

@ -1,3 +1,4 @@
# rubocop:disable Rails/ActiveRecordAliases
class WikiPage
PageChangedError = Class.new(StandardError)
PageRenameError = Class.new(StandardError)

View File

@ -1,9 +1,13 @@
class EnvironmentPolicy < BasePolicy
delegate { @subject.project }
condition(:stop_action_allowed) do
@subject.stop_action? && can?(:update_build, @subject.stop_action)
condition(:stop_with_deployment_allowed) do
@subject.stop_action? && can?(:create_deployment) && can?(:update_build, @subject.stop_action)
end
rule { can?(:create_deployment) & stop_action_allowed }.enable :stop_environment
condition(:stop_with_update_allowed) do
!@subject.stop_action? && can?(:update_environment, @subject)
end
rule { stop_with_deployment_allowed | stop_with_update_allowed }.enable :stop_environment
end

View File

@ -7,7 +7,7 @@ class EnvironmentEntity < Grape::Entity
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
expose :stop_action?
expose :stop_action?, as: :has_stop_action
expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
@ -31,4 +31,14 @@ class EnvironmentEntity < Grape::Entity
end
expose :created_at, :updated_at
expose :can_stop do |environment|
environment.available? && can?(current_user, :stop_environment, environment)
end
private
def current_user
request.current_user
end
end

View File

@ -3,7 +3,7 @@ module Badges
# returns the updated badge
def execute(badge)
if params.present?
badge.update_attributes(params)
badge.update(params)
end
badge

View File

@ -13,8 +13,6 @@ module Commits
# rubocop:disable GitlabSecurity/PublicSend
message = @commit.public_send(:"#{action}_message", current_user)
# rubocop:disable GitlabSecurity/PublicSend
repository.public_send(
action,
current_user,

View File

@ -130,7 +130,7 @@ class IssuableBaseService < BaseService
def create_issuable(issuable, attributes, label_ids:)
issuable.with_transaction_returning_status do
if issuable.save
issuable.update_attributes(label_ids: label_ids)
issuable.update(label_ids: label_ids)
end
end
end

View File

@ -6,7 +6,7 @@ module Members
old_access_level = member.human_access
if member.update_attributes(params)
if member.update(params)
after_execute(action: permission, old_access_level: old_access_level, member: member)
end

View File

@ -26,7 +26,7 @@ module MergeRequests
Gitlab::GitLogger.info("#{log_prefix} rebased to #{rebase_sha}")
merge_request.update_attributes(rebase_commit_sha: rebase_sha)
merge_request.update(rebase_commit_sha: rebase_sha)
Gitlab::GitLogger.info("#{log_prefix} rebase SHA saved: #{rebase_sha}")

View File

@ -11,7 +11,7 @@ module Milestones
end
if params.present?
milestone.update_attributes(params.except(:state_event))
milestone.update(params.except(:state_event))
end
milestone

View File

@ -5,7 +5,7 @@ module Notes
old_mentioned_users = note.mentioned_users.to_a
note.update_attributes(params.merge(updated_by: current_user))
note.update(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user)
if note.previous_changes.include?('note')

View File

@ -10,16 +10,16 @@ module NotificationRecipientService
NotificationRecipient.new(user, *args).notifiable?
end
def self.build_recipients(*a)
Builder::Default.new(*a).notification_recipients
def self.build_recipients(*args)
Builder::Default.new(*args).notification_recipients
end
def self.build_new_note_recipients(*a)
Builder::NewNote.new(*a).notification_recipients
def self.build_new_note_recipients(*args)
Builder::NewNote.new(*args).notification_recipients
end
def self.build_merge_request_unmergeable_recipients(*a)
Builder::MergeRequestUnmergeable.new(*a).notification_recipients
def self.build_merge_request_unmergeable_recipients(*args)
Builder::MergeRequestUnmergeable.new(*args).notification_recipients
end
module Builder
@ -44,7 +44,6 @@ module NotificationRecipientService
raise 'abstract'
end
# rubocop:disable Rails/Delegate
def project
target.project
end

Some files were not shown because too many files have changed in this diff Show More