diff options
38 files changed, 298 insertions, 125 deletions
@@ -33,7 +33,7 @@ gem 'grape-route-helpers', '~> 2.1.0' gem 'faraday', '~> 0.12' # Authentication libraries -gem 'devise', '~> 4.2' +gem 'devise', '~> 4.4' gem 'doorkeeper', '~> 4.3' gem 'doorkeeper-openid_connect', '~> 1.3' gem 'omniauth', '~> 1.8' diff --git a/Gemfile.lock b/Gemfile.lock index f11df6a283e..87ee15e9e76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -143,7 +143,7 @@ GEM connection_pool (2.2.1) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.3) + crass (1.0.4) creole (0.5.0) css_parser (1.5.0) addressable @@ -162,10 +162,10 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) device_detector (1.0.0) - devise (4.2.0) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.1) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) devise-two-factor (3.0.0) @@ -646,7 +646,7 @@ GEM pry (>= 0.9.10) public_suffix (3.0.2) pyu-ruby-sasl (0.0.3.3) - rack (1.6.9) + rack (1.6.10) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.4.1) @@ -694,7 +694,7 @@ GEM rainbow (2.2.2) rake raindrops (0.18.0) - rake (12.3.0) + rake (12.3.1) rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) @@ -735,8 +735,9 @@ GEM declarative-option (< 0.2.0) uber (< 0.2.0) request_store (1.3.1) - responders (2.3.0) - railties (>= 4.2.0, < 5.1) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) rest-client (2.0.2) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) @@ -966,7 +967,7 @@ GEM descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) vmstat (2.3.0) - warden (1.2.6) + warden (1.2.7) rack (>= 1.0) webmock (2.3.2) addressable (>= 2.3.6) @@ -1028,7 +1029,7 @@ DEPENDENCIES deckar01-task_list (= 2.0.0) default_value_for (~> 3.0.0) device_detector - devise (~> 4.2) + devise (~> 4.4) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) doorkeeper (~> 4.3) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 10d5cb6a23f..3056b97ccd5 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -162,6 +162,7 @@ GEM activerecord (>= 3.2.0, < 5.2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + device_detector (1.0.1) devise (4.4.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -375,7 +376,7 @@ GEM rake grape_logging (1.7.0) grape - grpc (1.10.0) + grpc (1.11.0) google-protobuf (~> 3.1) googleapis-common-protos-types (~> 1.0.0) googleauth (>= 0.5.1, < 0.7) @@ -554,9 +555,6 @@ GEM jwt (>= 1.5) omniauth (>= 1.1.1) omniauth-oauth2 (>= 1.5) - omniauth-jwt (0.0.2) - jwt - omniauth (~> 1.1) omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) @@ -1033,6 +1031,7 @@ DEPENDENCIES database_cleaner (~> 1.5.0) deckar01-task_list (= 2.0.0) default_value_for (~> 3.0.5) + device_detector devise (~> 4.2) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) @@ -1080,7 +1079,7 @@ DEPENDENCIES grape-entity (~> 0.6.0) grape-route-helpers (~> 2.1.0) grape_logging (~> 1.7) - grpc (~> 1.10.0) + grpc (~> 1.11.0) haml_lint (~> 0.26.0) hamlit (~> 2.6.1) hashie-forbidden_attributes @@ -1121,7 +1120,6 @@ DEPENDENCIES omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.2) omniauth-google-oauth2 (~> 0.5.3) - omniauth-jwt (~> 0.0.2) omniauth-kerberos (~> 0.3.0) omniauth-oauth2-generic (~> 0.2.2) omniauth-saml (~> 1.10) diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index 16bd2f5feb3..ab9e22037d0 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -1,5 +1,5 @@ <script> - import playIconSvg from 'icons/_icon_play.svg'; + 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'; @@ -8,9 +8,9 @@ directives: { tooltip, }, - components: { loadingIcon, + Icon, }, props: { actions: { @@ -19,20 +19,16 @@ default: () => [], }, }, - data() { return { - playIconSvg, isLoading: false, }; }, - computed: { title() { return 'Deploy to...'; }, }, - methods: { onClickAction(endpoint) { this.isLoading = true; @@ -65,7 +61,10 @@ :disabled="isLoading" > <span> - <span v-html="playIconSvg"></span> + <icon + name="play" + :size="12" + /> <i class="fa fa-caret-down" aria-hidden="true" @@ -86,7 +85,10 @@ :class="{ disabled: isActionDisabled(action) }" :disabled="isActionDisabled(action)" > - <span v-html="playIconSvg"></span> + <icon + name="play" + :size="12" + /> <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 c9a68cface6..ea6f1168c68 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.vue +++ b/app/assets/javascripts/environments/components/environment_external_url.vue @@ -1,4 +1,5 @@ <script> + import Icon from '~/vue_shared/components/icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; import { s__ } from '../../locale'; @@ -6,6 +7,9 @@ * Renders the external url link in environments table. */ export default { + components: { + Icon, + }, directives: { tooltip, }, @@ -15,7 +19,6 @@ required: true, }, }, - computed: { title() { return s__('Environments|Open'); @@ -34,10 +37,9 @@ :aria-label="title" :href="externalUrl" > - <i - class="fa fa-external-link" - aria-hidden="true" - > - </i> + <icon + name="external-link" + :size="12" + /> </a> </template> diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue index 081537cf218..deada134b27 100644 --- a/app/assets/javascripts/environments/components/environment_monitoring.vue +++ b/app/assets/javascripts/environments/components/environment_monitoring.vue @@ -2,20 +2,22 @@ /** * 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, + }, directives: { tooltip, }, - props: { monitoringUrl: { type: String, required: true, }, }, - computed: { title() { return 'Monitoring'; @@ -33,10 +35,9 @@ :title="title" :aria-label="title" > - <i - class="fa fa-area-chart" - aria-hidden="true" - > - </i> + <icon + name="chart" + :size="12" + /> </a> </template> diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 605a88e997e..c822fb1574c 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -12,7 +12,6 @@ components: { loadingIcon, }, - props: { retryUrl: { type: String, @@ -24,13 +23,11 @@ default: true, }, }, - data() { return { isLoading: false, }; }, - methods: { onClick() { this.isLoading = true; diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue index 407d5333c0e..e8469d088ef 100644 --- a/app/assets/javascripts/environments/components/environment_terminal_button.vue +++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue @@ -3,14 +3,16 @@ * Renders a terminal button to open a web terminal. * Used in environments table. */ - import terminalIconSvg from 'icons/_icon_terminal.svg'; + import Icon from '~/vue_shared/components/icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; export default { + components: { + Icon, + }, directives: { tooltip, }, - props: { terminalPath: { type: String, @@ -18,13 +20,6 @@ default: '', }, }, - - data() { - return { - terminalIconSvg, - }; - }, - computed: { title() { return 'Terminal'; @@ -40,7 +35,10 @@ :title="title" :aria-label="title" :href="terminalPath" - v-html="terminalIconSvg" > + <icon + name="terminal" + :size="12" + /> </a> </template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue index 6424b93ce54..1f6bbca13b5 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue @@ -15,17 +15,10 @@ export default { type: String, required: true, }, - committedStateSvgPath: { - type: String, - required: true, - }, }, computed: { - ...mapState(['lastCommitMsg', 'rightPanelCollapsed']), + ...mapState(['lastCommitMsg', 'rightPanelCollapsed', 'changedFiles', 'stagedFiles']), ...mapGetters(['collapseButtonIcon', 'collapseButtonTooltip']), - statusSvg() { - return this.lastCommitMsg ? this.committedStateSvgPath : this.noChangesStateSvgPath; - }, }, methods: { ...mapActions(['toggleRightPanelCollapsed']), @@ -35,6 +28,7 @@ export default { <template> <div + v-if="!lastCommitMsg" class="multi-file-commit-panel-section ide-commit-empty-state js-empty-state" > <header @@ -64,12 +58,11 @@ export default { v-if="!rightPanelCollapsed" > <div class="svg-content svg-80"> - <img :src="statusSvg" /> + <img :src="noChangesStateSvgPath" /> </div> <div class="append-right-default prepend-left-default"> <div class="text-content text-center" - v-if="!lastCommitMsg" > <h4> {{ __('No changes') }} @@ -78,15 +71,6 @@ export default { {{ __('Edit files in the editor and commit changes here') }} </p> </div> - <div - class="text-content text-center" - v-else - > - <h4> - {{ __('All changes are committed') }} - </h4> - <p v-html="lastCommitMsg"></p> - </div> </div> </div> </div> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue new file mode 100644 index 00000000000..628a17eddca --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue @@ -0,0 +1,39 @@ +<script> +import { mapState } from 'vuex'; + +export default { + props: { + committedStateSvgPath: { + type: String, + required: true, + }, + }, + computed: { + ...mapState(['lastCommitMsg']), + }, +}; +</script> + +<template> + <div + class="multi-file-commit-panel-success-message" + aria-live="assertive" + > + <div class="svg-content svg-80"> + <img + :src="committedStateSvgPath" + alt="" + /> + </div> + <div class="append-right-default prepend-left-default"> + <div + class="text-content text-center" + > + <h4> + {{ __('All changes are committed') }} + </h4> + <p v-html="lastCommitMsg"></p> + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 877d1b5e026..fa929381744 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -7,6 +7,7 @@ import LoadingButton from '~/vue_shared/components/loading_button.vue'; import CommitFilesList from './commit_sidebar/list.vue'; import EmptyState from './commit_sidebar/empty_state.vue'; import CommitMessageField from './commit_sidebar/message_field.vue'; +import SuccessMessage from './commit_sidebar/success_message.vue'; import * as consts from '../stores/modules/commit/constants'; import Actions from './commit_sidebar/actions.vue'; @@ -16,6 +17,7 @@ export default { Icon, CommitFilesList, EmptyState, + SuccessMessage, Actions, LoadingButton, CommitMessageField, @@ -34,9 +36,15 @@ export default { }, }, computed: { - ...mapState(['changedFiles', 'stagedFiles', 'rightPanelCollapsed']), + showStageUnstageArea() { + return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal); + }, + someUncommitedChanges() { + return !!(this.changedFiles.length || this.stagedFiles.length); + }, + ...mapState(['changedFiles', 'stagedFiles', 'rightPanelCollapsed', 'lastCommitMsg', 'unusedSeal']), ...mapState('commit', ['commitMessage', 'submitCommitLoading']), - ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled', 'branchName']), + ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']), }, methods: { ...mapActions('commit', [ @@ -69,7 +77,7 @@ export default { </template> </deprecated-modal> <template - v-if="changedFiles.length || stagedFiles.length" + v-if="showStageUnstageArea" > <commit-files-list icon-name="unstaged" @@ -89,11 +97,23 @@ export default { :show-toggle="false" :staged-list="true" /> + </template> + <empty-state + v-if="unusedSeal" + :no-changes-state-svg-path="noChangesStateSvgPath" + /> + <div + class="multi-file-commit-panel-bottom" + > <form class="form-horizontal multi-file-commit-form" @submit.prevent.stop="commitChanges" v-if="!rightPanelCollapsed" > + <success-message + v-if="lastCommitMsg && !someUncommitedChanges" + :committed-state-svg-path="committedStateSvgPath" + /> <commit-message-field :text="commitMessage" @input="updateCommitMessage" @@ -117,11 +137,6 @@ export default { </button> </div> </form> - </template> - <empty-state - v-else - :no-changes-state-svg-path="noChangesStateSvgPath" - :committed-state-svg-path="committedStateSvgPath" - /> + </div> </div> </template> diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index 4c8c997e376..7358dd9ef92 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -149,6 +149,12 @@ export const updateTempFlagForEntry = ({ commit, dispatch, state }, { file, temp export const toggleFileFinder = ({ commit }, fileFindVisible) => commit(types.TOGGLE_FILE_FINDER, fileFindVisible); +export const burstUnusedSeal = ({ state, commit }) => { + if (state.unusedSeal) { + commit(types.BURST_UNUSED_SEAL); + } +}; + export * from './actions/tree'; export * from './actions/file'; export * from './actions/project'; diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index fcdb3b753b2..861830badee 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -117,7 +117,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) = }); }; -export const changeFileContent = ({ state, commit }, { path, content }) => { +export const changeFileContent = ({ commit, dispatch, state }, { path, content }) => { const file = state.entries[path]; commit(types.UPDATE_FILE_CONTENT, { path, content }); @@ -128,6 +128,8 @@ export const changeFileContent = ({ state, commit }, { path, content }) => { } else if (!file.changed && indexOfChangedFile !== -1) { commit(types.REMOVE_FILE_FROM_CHANGED, path); } + + dispatch('burstUnusedSeal', {}, { root: true }); }; export const setFileLanguage = ({ getters, commit }, { fileLanguage }) => { diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 349ff68f1e3..4fbc97d053e 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -182,6 +182,10 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState }) = } commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true }); + + setTimeout(() => { + commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true }); + }, 5000); }) .then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH)); }) diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index f5c12db6db0..87b39379338 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -61,3 +61,4 @@ export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB'; export const UPDATE_TEMP_FLAG = 'UPDATE_TEMP_FLAG'; export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER'; +export const BURST_UNUSED_SEAL = 'BURST_UNUSED_SEAL'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 0c1d720df09..539a07116b3 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -128,6 +128,11 @@ export default { }), }); }, + [types.BURST_UNUSED_SEAL](state) { + Object.assign(state, { + unusedSeal: false, + }); + }, ...projectMutations, ...mergeRequestMutation, ...fileMutations, diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 3470bb9aec0..0976d278559 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -18,5 +18,6 @@ export default () => ({ entries: {}, viewer: 'editor', delayViewerUpdated: false, + unusedSeal: true, fileFindVisible: false, }); diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index b54ecd2d543..5e786ee6935 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -74,7 +74,11 @@ export function capitalizeFirstCharacter(text) { * @param {*} replace * @returns {String} */ -export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace); +export const stripHtml = (string, replace = '') => { + if (!string) return string; + + return string.replace(/<[^>]*>/g, replace); +}; /** * Converts snake_case string to camelCase diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 11052be40a8..70ce5de6a6c 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -44,6 +44,12 @@ } } + .note-text { + table { + font-family: $font-family-sans-serif; + } + } + table { width: 100%; font-family: $monospace_font; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 6342042374f..2c0b20809ba 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -549,6 +549,7 @@ margin-bottom: 0; border-bottom: 1px solid $white-dark; padding: $gl-btn-padding 0; + min-height: 56px; } .multi-file-commit-panel-header-title { @@ -673,6 +674,24 @@ } } +.multi-file-commit-panel-bottom { + position: relative; + + .multi-file-commit-panel-success-message { + position: absolute; + top: 1px; + left: 3px; + bottom: 0; + right: 0; + z-index: 10; + background: $gray-light; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: center; + } +} + .dirty-diff { // !important need to override monaco inline style width: 4px !important; diff --git a/app/models/identity.rb b/app/models/identity.rb index 1011b9f1109..3fd0c5e751d 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,12 +1,16 @@ class Identity < ActiveRecord::Base + def self.uniqueness_scope + :provider + end + include Sortable include CaseSensitivity belongs_to :user validates :provider, presence: true - validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider, case_sensitive: false } - validates :user_id, uniqueness: { scope: :provider } + validates :extern_uid, allow_blank: true, uniqueness: { scope: uniqueness_scope, case_sensitive: false } + validates :user_id, uniqueness: { scope: uniqueness_scope } before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8f964a488aa..63c6ada86e1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -323,7 +323,7 @@ class MergeRequest < ActiveRecord::Base # updates `merge_jid` with the MergeWorker#jid. # This helps tracking enqueued and ongoing merge jobs. def merge_async(user_id, params) - jid = MergeWorker.perform_async(id, user_id, params) + jid = MergeWorker.perform_async(id, user_id, params.to_h) update_column(:merge_jid, jid) end diff --git a/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml b/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml new file mode 100644 index 00000000000..f2810fab208 --- /dev/null +++ b/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml @@ -0,0 +1,5 @@ +--- +title: Change font for tables inside diff discussions +merge_request: 18660 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml b/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml new file mode 100644 index 00000000000..aa23a89a175 --- /dev/null +++ b/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml @@ -0,0 +1,6 @@ +--- +title: Add documentation about how to use variables to define deploy policies for + staging/production environments +merge_request: 18675 +author: +type: other diff --git a/changelogs/unreleased/ide-improve-commit-panel.yml b/changelogs/unreleased/ide-improve-commit-panel.yml new file mode 100644 index 00000000000..f6237db3039 --- /dev/null +++ b/changelogs/unreleased/ide-improve-commit-panel.yml @@ -0,0 +1,5 @@ +--- +title: Improve interaction on WebIDE commit panel +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/update-environment-item-action-buttons-icons.yml b/changelogs/unreleased/update-environment-item-action-buttons-icons.yml new file mode 100644 index 00000000000..7f06022be3e --- /dev/null +++ b/changelogs/unreleased/update-environment-item-action-buttons-icons.yml @@ -0,0 +1,5 @@ +--- +title: Update environment item action buttons icons +merge_request: 18632 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/zj-fork-opt-out.yml b/changelogs/unreleased/zj-fork-opt-out.yml new file mode 100644 index 00000000000..56bf6b8b0f6 --- /dev/null +++ b/changelogs/unreleased/zj-fork-opt-out.yml @@ -0,0 +1,5 @@ +--- +title: Gitaly handles repository forks by default +merge_request: +author: +type: other diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb index 0c32528311e..ca2eed664ed 100644 --- a/config/initializers/trusted_proxies.rb +++ b/config/initializers/trusted_proxies.rb @@ -22,3 +22,16 @@ end.compact Rails.application.config.action_dispatch.trusted_proxies = ( ['127.0.0.1', '::1'] + gitlab_trusted_proxies) + +# A monkey patch to make trusted proxies work with Rails 5.0. +# Inspired by https://github.com/rails/rails/issues/5223#issuecomment-263778719 +# Remove this monkey patch when upstream is fixed. +if Gitlab.rails5? + module TrustedProxyMonkeyPatch + def ip + @ip ||= (get_header("action_dispatch.remote_ip") || super).to_s + end + end + + ActionDispatch::Request.send(:include, TrustedProxyMonkeyPatch) +end diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 7c0cd2c40d2..882ddf4d2c5 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -495,6 +495,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. | | `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-variables-environment-variables). Set it to use a custom database name. | | `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` | +| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | TIP: **Tip:** Set up the replica variables using a @@ -561,6 +562,22 @@ service: internalPort: 5000 ``` +#### Deploy policy for staging and production environments + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/160) +in GitLab 10.8. + +The normal behavior of Auto DevOps is to use Continuous Deployment, pushing +automatically to the `production` environment every time a new pipeline is run +on the default branch. However, there are cases where you might want to use a +staging environment and deploy to production manually. For this scenario, the +`STAGING_ENABLED` environment variable was introduced. + +If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to +`1` as a secret variable), then the application will be automatically deployed +to a `staging` environment, and a `production_manual` job will be created for +you when you're ready to manually deploy to production. + ## Currently supported languages NOTE: **Note:** diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index 0a0588346b1..0126ce39c5a 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -5,10 +5,6 @@ module SharedGroup is_member_of(current_user.name, "Owned", Gitlab::Access::DEVELOPER) end - step '"John Doe" is owner of group "Owned"' do - is_member_of("John Doe", "Owned", Gitlab::Access::OWNER) - end - step '"John Doe" is guest of group "Guest"' do is_member_of("John Doe", "Guest", Gitlab::Access::GUEST) end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 014e6ad625b..b6c648a707d 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -48,10 +48,6 @@ module SharedPaths visit group_group_members_path(Group.find_by(name: "Owned")) end - step 'I visit group "Owned" settings page' do - visit edit_group_path(Group.find_by(name: "Owned")) - end - step 'I visit group "Owned" projects page' do visit projects_group_path(Group.find_by(name: "Owned")) end diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb index 099709620b3..68373460d23 100644 --- a/lib/gitlab/git/gitlab_projects.rb +++ b/lib/gitlab/git/gitlab_projects.rb @@ -63,7 +63,8 @@ module Gitlab end def fork_repository(new_shard_name, new_repository_relative_path) - Gitlab::GitalyClient.migrate(:fork_repository) do |is_enabled| + Gitlab::GitalyClient.migrate(:fork_repository, + status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| if is_enabled gitaly_fork_repository(new_shard_name, new_repository_relative_path) else diff --git a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js index b80d08de7b1..53275b78da5 100644 --- a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js @@ -24,42 +24,10 @@ describe('IDE commit panel empty state', () => { resetStore(vm.$store); }); - describe('statusSvg', () => { - it('uses noChangesStateSvgPath when commit message is empty', () => { - expect(vm.statusSvg).toBe('no-changes'); - expect(vm.$el.querySelector('img').getAttribute('src')).toBe( - 'no-changes', - ); - }); - - it('uses committedStateSvgPath when commit message exists', done => { - vm.$store.state.lastCommitMsg = 'testing'; - - Vue.nextTick(() => { - expect(vm.statusSvg).toBe('committed-state'); - expect(vm.$el.querySelector('img').getAttribute('src')).toBe( - 'committed-state', - ); - - done(); - }); - }); - }); - it('renders no changes text when last commit message is empty', () => { expect(vm.$el.textContent).toContain('No changes'); }); - it('renders last commit message when it exists', done => { - vm.$store.state.lastCommitMsg = 'testing commit message'; - - Vue.nextTick(() => { - expect(vm.$el.textContent).toContain('testing commit message'); - - done(); - }); - }); - describe('toggle button', () => { it('calls store action', () => { spyOn(vm, 'toggleRightPanelCollapsed'); diff --git a/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js b/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js new file mode 100644 index 00000000000..e1a432b81be --- /dev/null +++ b/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js @@ -0,0 +1,35 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import successMessage from '~/ide/components/commit_sidebar/success_message.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { resetStore } from '../../helpers'; + +describe('IDE commit panel successful commit state', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(successMessage); + + vm = createComponentWithStore(Component, store, { + committedStateSvgPath: 'committed-state', + }); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('renders last commit message when it exists', done => { + vm.$store.state.lastCommitMsg = 'testing commit message'; + + Vue.nextTick(() => { + expect(vm.$el.textContent).toContain('testing commit message'); + + done(); + }); + }); +}); diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index ce5c525bed7..3ee11bd2f03 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -398,6 +398,20 @@ describe('IDE store file actions', () => { }) .catch(done.fail); }); + + it('bursts unused seal', done => { + store + .dispatch('changeFileContent', { + path: tmpFile.path, + content: 'content', + }) + .then(() => { + expect(store.state.unusedSeal).toBe(false); + + done(); + }) + .catch(done.fail); + }); }); describe('discardFileChanges', () => { diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js index 997711d1e19..61efb6372c9 100644 --- a/spec/javascripts/ide/stores/mutations_spec.js +++ b/spec/javascripts/ide/stores/mutations_spec.js @@ -116,4 +116,14 @@ describe('Multi-file store mutations', () => { expect(localState.fileFindVisible).toBe(true); }); }); + + describe('BURST_UNUSED_SEAL', () => { + it('updates unusedSeal', () => { + expect(localState.unusedSeal).toBe(true); + + mutations.BURST_UNUSED_SEAL(localState); + + expect(localState.unusedSeal).toBe(false); + }); + }); }); diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js index ae00fb76714..eab5c24406a 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js +++ b/spec/javascripts/lib/utils/text_utility_spec.js @@ -75,6 +75,14 @@ describe('text_utility', () => { 'This is a text with html .', ); }); + + it('passes through with null string input', () => { + expect(textUtils.stripHtml(null, ' ')).toEqual(null); + }); + + it('passes through with undefined string input', () => { + expect(textUtils.stripHtml(undefined, ' ')).toEqual(undefined); + }); }); describe('convertToCamelCase', () => { diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index becb146422e..5a9aa7c7d1b 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1213,7 +1213,7 @@ describe MergeRequest do it 'enqueues MergeWorker job and updates merge_jid' do merge_request = create(:merge_request) user_id = double(:user_id) - params = double(:params) + params = {} merge_jid = 'hash-123' expect(MergeWorker).to receive(:perform_async).with(merge_request.id, user_id, params) do |