diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2018-07-10 12:05:06 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-07-10 18:11:19 +0100 |
commit | 135ead1d83d774a1bc0ed3ed947896b64d1755aa (patch) | |
tree | 7625ab36e07b70af088d5584bc5fd082b857d77f /app | |
parent | ac71675d3096ac9317ea230a408d69029bdadc73 (diff) | |
parent | f3edbafe6310ca1584002b8a351048a3fb3309e3 (diff) | |
download | gitlab-ce-135ead1d83d774a1bc0ed3ed947896b64d1755aa.tar.gz |
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
...
Diffstat (limited to 'app')
108 files changed, 1022 insertions, 826 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index ac4f37512ec..c5abd0a9568 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -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> diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 2e6092d7e87..18caf87d6da 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -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 () => {}; diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index 9a8d27d0b04..f89acb73ed8 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -32,6 +32,20 @@ export const diffHasAllCollpasedDiscussions = (state, getters) => diff => { }; /** + * 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 * @returns {Array} diff --git a/app/assets/javascripts/diffs/store/index.js b/app/assets/javascripts/diffs/store/index.js deleted file mode 100644 index e6aa8f5b12a..00000000000 --- a/app/assets/javascripts/diffs/store/index.js +++ /dev/null @@ -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, - }, -}); diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js index 90505f83b60..20d1ebbe049 100644 --- a/app/assets/javascripts/diffs/store/modules/index.js +++ b/app/assets/javascripts/diffs/store/modules/index.js @@ -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'; diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index 17ea3bdb179..8abd8bc581a 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -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); }); diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index e3652fe739e..63d83e307ee 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -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); - }, + eventHub.$emit('postAction', { endpoint }); + }, - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } + isActionDisabled(action) { + if (action.playable === undefined) { + return false; + } - return !action.playable; - }, + 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> diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue index 68195225d50..7446196de13 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.vue +++ b/app/assets/javascripts/environments/components/environment_external_url.vue @@ -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> diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 5ecdccf63ad..39f3790a286 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -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'; - - /** - * 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, +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(); + +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: () => ({}), - }, - - canCreateDeployment: { - type: Boolean, - required: false, - default: false, - }, - - canReadEnvironment: { - 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; + }, + + /** + * 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 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 ''; }, - 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 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) + ); }, - methods: { - onClickFolder() { - eventHub.$emit('toggleFolder', this.model); - }, + /** + * 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> diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue index 947e8c901e9..ccc8419ca6d 100644 --- a/app/assets/javascripts/environments/components/environment_monitoring.vue +++ b/app/assets/javascripts/environments/components/environment_monitoring.vue @@ -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> diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 310835c5ea9..4deeef4beb9 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -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'; - - export default { - components: { - loadingIcon, +/** + * 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: { + Icon, + LoadingIcon, + }, + + directives: { + tooltip, + }, + + props: { + retryUrl: { + type: String, + default: '', }, - props: { - retryUrl: { - type: String, - default: '', - }, - - isLastDeployment: { - type: Boolean, - default: true, - }, + + isLastDeployment: { + type: Boolean, + default: true, }, - data() { - return { - isLoading: false, - }; + }, + data() { + return { + isLoading: false, + }; + }, + + computed: { + title() { + return this.isLastDeployment ? s__('Environments|Re-deploy to environment') : s__('Environments|Rollback environment'); }, - methods: { - onClick() { - this.isLoading = true; + }, + + methods: { + onClick() { + this.isLoading = true; - eventHub.$emit('postAction', this.retryUrl); - }, + 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> diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue index eba58bedd6d..a814b3405f5 100644 --- a/app/assets/javascripts/environments/components/environment_stop.vue +++ b/app/assets/javascripts/environments/components/environment_stop.vue @@ -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, - }, + directives: { + tooltip, + }, - props: { - stopUrl: { - type: String, - default: '', - }, + props: { + environment: { + type: Object, + required: true, }, + }, - data() { - return { - isLoading: false, - }; - }, + data() { + return { + isLoading: false, + }; + }, - computed: { - title() { - return 'Stop'; - }, + computed: { + title() { + return s__('Environments|Stop environment'); }, + }, - methods: { - onClick() { - // eslint-disable-next-line no-alert - if (window.confirm('Are you sure you want to stop this environment?')) { - this.isLoading = true; + mounted() { + eventHub.$on('stopEnvironment', this.onStopEnvironment); + }, - $(this.$el).tooltip('dispose'); + beforeDestroy() { + eventHub.$off('stopEnvironment', this.onStopEnvironment); + }, - eventHub.$emit('postAction', this.stopUrl); - } - }, + methods: { + onClick() { + $(this.$el).tooltip('dispose'); + eventHub.$emit('requestStopEnvironment', this.environment); + }, + onStopEnvironment(environment) { + if (this.environment.id === environment.id) { + this.isLoading = true; + } }, - }; + }, +}; </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> diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue index f8e3165f8cd..350417e5ad0 100644 --- a/app/assets/javascripts/environments/components/environment_terminal_button.vue +++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue @@ -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> diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index b18f02343d6..8efdfb8abe0 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -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" diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue new file mode 100644 index 00000000000..657cc8cd1aa --- /dev/null +++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue @@ -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> diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue index 5f72a39c5cb..e69bfa0b2cc 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.vue +++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue @@ -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" diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index a7a79dbca70..d88624f7f8d 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -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); }, }; diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js index 3b121551aca..4e07ccba91a 100644 --- a/app/assets/javascripts/environments/services/environments_service.js +++ b/app/assets/javascripts/environments/services/environments_service.js @@ -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) { diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js index 6c1ffdf266b..6f374f78691 100644 --- a/app/assets/javascripts/notes/stores/mutation_types.js +++ b/app/assets/javascripts/notes/stores/mutation_types.js @@ -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'; diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 551a7e852ae..5d79610b21e 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -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, diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 199039f38f7..3144dcc4dc0 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -23,7 +23,7 @@ } .btn-group { - > a { + > .btn:not(.btn-danger) { color: $gl-text-color-secondary; } diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb index b0c4c31cffc..5c2025c1988 100644 --- a/app/controllers/admin/deploy_keys_controller.rb +++ b/app/controllers/admin/deploy_keys_controller.rb @@ -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 diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 96b7bc65ac9..d7a5b745d3f 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -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" diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 6944857bd33..a98c355c7ba 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -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 diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb index 43b4e3a2cc3..ceb45865804 100644 --- a/app/controllers/admin/identities_controller.rb +++ b/app/controllers/admin/identities_controller.rb @@ -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 diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 39dbf85f6c0..d2f947d2c66 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -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 diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb index ae7a7f6279c..ac1ae0f16b3 100644 --- a/app/controllers/admin/jobs_controller.rb +++ b/app/controllers/admin/jobs_controller.rb @@ -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 diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 7aba77d8129..51d5799cd89 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -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 diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 4b01904f2a1..6c76c55a9d4 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -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 diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index a7025b62ad7..e70aa549140 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -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, diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 653f3dfffc4..a51a8c3ed4a 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -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 diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index ba510968684..37e03d70b6f 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -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 diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index 5e4e8a87153..79ee5b2f91e 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -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 diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index cc5ba5878f8..35a61b359c8 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -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 diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 78992ec7f46..1036b4e6ed3 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -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 diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 67057b5b126..3cb9e46b548 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -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 diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb index 8ec4bb1233f..ed20302487c 100644 --- a/app/controllers/notification_settings_controller.rb +++ b/app/controllers/notification_settings_controller.rb @@ -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 diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb index f0cdc228366..f1e77d68acd 100644 --- a/app/controllers/profiles/active_sessions_controller.rb +++ b/app/controllers/profiles/active_sessions_controller.rb @@ -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 diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb index 39b9f8a84d1..4f030ded80f 100644 --- a/app/controllers/profiles/avatars_controller.rb +++ b/app/controllers/profiles/avatars_controller.rb @@ -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 diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 2353f0840d6..a186c5f36a8 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -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 diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index bbd7ba49d77..a39824ec9c8 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -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 diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb index 38e3eacd229..c32507756e8 100644 --- a/app/controllers/profiles/gpg_keys_controller.rb +++ b/app/controllers/profiles/gpg_keys_controller.rb @@ -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 diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index 12a6cd11f80..6035258667e 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -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 diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index aa9789f8a0f..29ff18a1219 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -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 diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 5ab6d103c89..b4f814fd3a4 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -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 diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 21a403f3765..a13d552dbd8 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -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 diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index cd7250b10fc..d1dc9fe9600 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -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] } diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 62193257940..358fe59618b 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -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 diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index f43ef2e5f2f..06739d8fd4a 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -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 diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 27b7425b965..68353e6a210 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -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 diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb index 07249fe3182..a52814e6e52 100644 --- a/app/controllers/projects/git_http_client_controller.rb +++ b/app/controllers/projects/git_http_client_controller.rb @@ -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 diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index f58ee3e9109..bc5f38f3c2b 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -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 diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 6800d742b0a..2da2aad9b33 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -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 diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 91016f6494e..21d3c918581 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -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) } diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb index 3f4962b543d..c64ccc3d473 100644 --- a/app/controllers/projects/lfs_api_controller.rb +++ b/app/controllers/projects/lfs_api_controller.rb @@ -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 diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb index 45c98d60822..dd7e673ec75 100644 --- a/app/controllers/projects/lfs_storage_controller.rb +++ b/app/controllers/projects/lfs_storage_controller.rb @@ -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 diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index a7c5f858c42..dc6551fc761 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -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 diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 594563d1f6f..5e86ec93f34 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -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 diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb index 5698ff4e706..3b24d231f3d 100644 --- a/app/controllers/projects/mirrors_controller.rb +++ b/app/controllers/projects/mirrors_controller.rb @@ -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 diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index fa258f3d9af..aeda7b3edf5 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -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, diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 3e0a530fdb9..19e09b3af6f 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -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 diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index d01f324e6fd..ecb2ece7532 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -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 diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index a080724634b..c098c82081e 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -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 diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index bef94cea989..cc7cce887bf 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -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 diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 690596b12db..d55046047ae 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -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) diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 208a1d19862..f742d7edf83 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -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 diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index b62d7d9b7c5..b17753222a0 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -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 diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb index 694b468c8d3..52d6fb82093 100644 --- a/app/controllers/projects/templates_controller.rb +++ b/app/controllers/projects/templates_controller.rb @@ -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 diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index e04145dd0b3..6f3de43f85a 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -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 diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index aa844e94d89..c01066c688a 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -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 diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index f2abe27f60e..9d1c44db137 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -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 diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 1de6ae24622..9dd652206fe 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -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 diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb index cb6c3a7cd98..ae4953c3259 100644 --- a/app/controllers/sherlock/transactions_controller.rb +++ b/app/controllers/sherlock/transactions_controller.rb @@ -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 diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 3d51520ddf4..1d6d0943674 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -89,7 +89,7 @@ class SnippetsController < ApplicationController @snippet.destroy - redirect_to snippets_path, status: 302 + redirect_to snippets_path, status: :found end protected diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb index 271e839692a..336385f6798 100644 --- a/app/helpers/time_helper.rb +++ b/app/helpers/time_helper.rb @@ -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 diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 19949f83351..d8ddb4bc667 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -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) diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index 94eef4ff7cd..dbe8d31de37 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -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}." } diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb index 22d48c9e661..d667948deae 100644 --- a/app/models/network/commit.rb +++ b/app/models/network/commit.rb @@ -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 diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index a6f94b3e3b0..fc868c3ebb7 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -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) diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index c4b5dd2dc96..976b501e297 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -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 diff --git a/app/models/repository.rb b/app/models/repository.rb index 7cd600fec5b..5ed2a7b4068 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 27a5d0278b7..1c5d39db118 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index cde79b95062..4b49edb01a5 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,3 +1,4 @@ +# rubocop:disable Rails/ActiveRecordAliases class WikiPage PageChangedError = Class.new(StandardError) PageRenameError = Class.new(StandardError) diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb index 375a5535359..978dc3a7c81 100644 --- a/app/policies/environment_policy.rb +++ b/app/policies/environment_policy.rb @@ -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 diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index ba0ae6ba8a0..0fc3f92b151 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -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 diff --git a/app/services/badges/update_service.rb b/app/services/badges/update_service.rb index 7ca84b5df31..495a4a2c99d 100644 --- a/app/services/badges/update_service.rb +++ b/app/services/badges/update_service.rb @@ -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 diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb index b9d0173a2d0..1ce6ab36cbf 100644 --- a/app/services/commits/change_service.rb +++ b/app/services/commits/change_service.rb @@ -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, diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 683f64e82ad..5e06e0c61cf 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -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 diff --git a/app/services/members/update_service.rb b/app/services/members/update_service.rb index 48b3d59f7bd..cb19cf01dd7 100644 --- a/app/services/members/update_service.rb +++ b/app/services/members/update_service.rb @@ -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 diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb index 5b4bc86b9ba..c741e913860 100644 --- a/app/services/merge_requests/rebase_service.rb +++ b/app/services/merge_requests/rebase_service.rb @@ -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}") diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb index 31b441ed476..74edbf9b41d 100644 --- a/app/services/milestones/update_service.rb +++ b/app/services/milestones/update_service.rb @@ -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 diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb index 75fd08ea0a9..e16ef398184 100644 --- a/app/services/notes/update_service.rb +++ b/app/services/notes/update_service.rb @@ -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') diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb index 4fa38665abc..d9834fd0ccc 100644 --- a/app/services/notification_recipient_service.rb +++ b/app/services/notification_recipient_service.rb @@ -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 diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 02769e72229..87173cc79ec 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -124,7 +124,7 @@ module Projects # It's possible that the project was destroyed, but some after_commit # hook failed and caused us to end up here. A destroyed model will be a frozen hash, # which cannot be altered. - project.update_attributes(delete_error: message, pending_delete: false) unless project.destroyed? + project.update(delete_error: message, pending_delete: false) unless project.destroyed? log_error("Deletion failed on #{project.full_path} with the following message: #{message}") end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 348eb0bf8d8..a8aafa9fb4f 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -37,7 +37,7 @@ module Projects return new_project unless new_project.persisted? builds_access_level = @project.project_feature.builds_access_level - new_project.project_feature.update_attributes(builds_access_level: builds_access_level) + new_project.project_feature.update(builds_access_level: builds_access_level) link_fork_network(new_project) diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb index 6ea43561d61..618c30b971f 100644 --- a/app/services/projects/lfs_pointers/lfs_download_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_service.rb @@ -22,7 +22,7 @@ module Projects private def download_and_save_file(file, sanitized_uri) - IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file) + IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file) # rubocop:disable Security/Open end def headers(sanitized_uri) diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index d8250cd8102..f4fbaacc08b 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -22,7 +22,7 @@ module Projects # If the block added errors, don't try to save the project return validation_failed! if project.errors.any? - if project.update_attributes(params.except(:default_branch)) + if project.update(params.except(:default_branch)) if project.previous_changes.include?('path') project.rename_repo else diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb index b7c36651968..dc696e9c440 100644 --- a/app/services/update_release_service.rb +++ b/app/services/update_release_service.rb @@ -7,7 +7,7 @@ class UpdateReleaseService < BaseService release = project.releases.find_by(tag: tag_name) if release - release.update_attributes(description: release_description) + release.update(description: release_description) success(release) else diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb index 7919f126075..719bd6ef418 100644 --- a/app/uploaders/gitlab_uploader.rb +++ b/app/uploaders/gitlab_uploader.rb @@ -71,6 +71,28 @@ class GitlabUploader < CarrierWave::Uploader::Base File.join('/', self.class.base_dir, dynamic_segment, filename) end + def cached_size + size + end + + def open + stream = + if file_storage? + File.open(path, "rb") if path + else + ::Gitlab::HttpIO.new(url, cached_size) if url + end + + return unless stream + return stream unless block_given? + + begin + yield(stream) + ensure + stream.close + end + end + private # Designed to be overridden by child uploaders that have a dynamic path diff --git a/app/uploaders/job_artifact_uploader.rb b/app/uploaders/job_artifact_uploader.rb index 855cf2fc21c..f6af023e0f9 100644 --- a/app/uploaders/job_artifact_uploader.rb +++ b/app/uploaders/job_artifact_uploader.rb @@ -18,14 +18,6 @@ class JobArtifactUploader < GitlabUploader dynamic_segment end - def open - if file_storage? - File.open(path, "rb") if path - else - ::Gitlab::Ci::Trace::HttpIO.new(url, cached_size) if url - end - end - private def dynamic_segment diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml index e0ecf56525a..f4c91377ecb 100644 --- a/app/views/projects/deployments/_actions.haml +++ b/app/views/projects/deployments/_actions.haml @@ -3,13 +3,12 @@ - if actions.present? .btn-group .dropdown - %button.dropdown.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' } - = custom_icon('icon_play') + %button.dropdown.dropdown-new.btn.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') } + = sprite_icon('play') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-right - actions.each do |action| - next unless can?(current_user, :update_build, action) %li - = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow' do - = custom_icon('icon_play') + = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow', class: 'btn' do %span= action.name.humanize diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml index 95f950948ab..281e042c915 100644 --- a/app/views/projects/deployments/_rollback.haml +++ b/app/views/projects/deployments/_rollback.haml @@ -1,6 +1,7 @@ - if can?(current_user, :create_deployment, deployment) && deployment.deployable - = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do + - tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment') + = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do - if deployment.last? - = _("Re-deploy") + = sprite_icon('repeat') - else - = _("Rollback") + = sprite_icon('redo') diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml index a264252e095..4694bc39d54 100644 --- a/app/views/projects/environments/_external_url.html.haml +++ b/app/views/projects/environments/_external_url.html.haml @@ -1,4 +1,4 @@ - if environment.external_url && can?(current_user, :read_environment, environment) - = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do + = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip', title: s_('Environments|Open live environment') do = sprite_icon('external-link') View deployment diff --git a/app/views/projects/environments/_stop.html.haml b/app/views/projects/environments/_stop.html.haml deleted file mode 100644 index c35f9af2873..00000000000 --- a/app/views/projects/environments/_stop.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -- if can?(current_user, :create_deployment, environment) && environment.stop_action? - .inline - = link_to stop_project_environment_path(@project, environment), method: :post, - class: 'btn stop-env-link', rel: 'nofollow', data: { confirm: 'Are you sure you want to stop this environment?' } do - = icon('stop', class: 'stop-env-icon') diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index add394a6356..a33bc9d4ce6 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -4,6 +4,33 @@ - page_title "Environments" %div{ class: container_class } + - if can?(current_user, :stop_environment, @environment) + #stop-environment-modal.modal.fade{ tabindex: -1 } + .modal-dialog + .modal-content + .modal-header + %h4.modal-title.d-flex.mw-100 + Stopping + %span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } } + = @environment.name + ? + .modal-body + %p= s_('Environments|Are you sure you want to stop this environment?') + - unless @environment.stop_action? + .warning_message + %p= s_('Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file.').html_safe % { emphasis_start: '<strong>'.html_safe, + emphasis_end: '</strong>'.html_safe, + ci_config_link_start: '<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">'.html_safe, + ci_config_link_end: '</a>'.html_safe } + %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') + .modal-footer + = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' } + = button_to stop_project_environment_path(@project, @environment), class: 'btn btn-danger has-tooltip', method: :post do + = s_('Environments|Stop environment') + .row.top-area.adjust .col-md-7 %h3.page-title= @environment.name @@ -15,7 +42,10 @@ - if can?(current_user, :update_environment, @environment) = link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn' - if can?(current_user, :stop_environment, @environment) - = link_to 'Stop', stop_project_environment_path(@project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post + = button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal', + target: '#stop-environment-modal' } do + = sprite_icon('stop') + = s_('Environments|Stop') .environments-container - if @deployments.blank? diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml index 532712ee6d1..f3b56df0c96 100644 --- a/app/views/shared/hook_logs/_content.html.haml +++ b/app/views/shared/hook_logs/_content.html.haml @@ -30,7 +30,7 @@ %h5 Request body: %pre - :plain + :escaped #{JSON.pretty_generate(hook_log.request_data)} %h5 Response headers: %pre @@ -40,5 +40,5 @@ %h5 Response body: %pre - :plain + :escaped #{hook_log.response_body} diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index f9f0efb302a..12706613ac2 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -15,14 +15,14 @@ class EmailReceiverWorker private - def handle_failure(raw, e) - Rails.logger.warn("Email can not be processed: #{e}\n\n#{raw}") + def handle_failure(raw, error) + Rails.logger.warn("Email can not be processed: #{error}\n\n#{raw}") return unless raw.present? can_retry = false reason = - case e + case error when Gitlab::Email::UnknownIncomingEmail "We couldn't figure out what the email is for. Please create your issue or comment through the web interface." when Gitlab::Email::SentNotificationNotFoundError @@ -42,7 +42,7 @@ class EmailReceiverWorker "The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member." when Gitlab::Email::InvalidRecordError can_retry = true - e.message + error.message end if reason diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb index a3ecfa8e711..01d03ec7888 100644 --- a/app/workers/object_storage/migrate_uploads_worker.rb +++ b/app/workers/object_storage/migrate_uploads_worker.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -# rubocop:disable Metrics/LineLength -# rubocop:disable Style/Documentation module ObjectStorage class MigrateUploadsWorker |