From 2ddcd634fc74d894b243694582fdf58cf5fb3c2a Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 30 Nov 2020 21:09:16 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/assets/javascripts/boards/index.js | 2 +- .../edit/components/integration_form.vue | 13 +- .../pipelines/components/graph/job_item.vue | 6 +- .../graph/stage_column_component.vue | 9 +- .../pipelines/components/header_component.vue | 1 + .../components/service_desk_root.vue | 5 + .../components/service_desk_setting.vue | 6 +- .../projects/settings_service_desk/index.js | 2 + .../stylesheets/page_bundles/pipeline.scss | 15 -- app/controllers/projects/boards_controller.rb | 3 + app/controllers/projects_controller.rb | 1 - app/models/project.rb | 7 +- .../projects/_service_desk_settings.html.haml | 1 + .../shared/access_tokens/_table.html.haml | 2 +- .../shared/deploy_tokens/_table.html.haml | 2 +- ...476-remove-angle-brackets-in-no-scopes.yml | 5 + ...wn-not-shown-in-boards-add-issue-modal.yml | 5 + ...r-dropdowns-gitlab-ui-integration-test.yml | 5 + ...-modal-showing-on-project-integrations.yml | 5 + ...le-fix-button-spacing-on-pipeline-page.yml | 5 + .../development/add_issues_button.yml | 8 + config/gitlab.yml.example | 4 + config/initializers/01_secret_token.rb | 3 + config/initializers/1_settings.rb | 10 +- config/settings.rb | 8 + doc/development/application_secrets.md | 1 + .../documentation/styleguide/index.md | 59 ++++--- .../issue_boards_add_issues_modal_v13_6.png | Bin 10189 -> 0 bytes doc/user/project/issue_board.md | 26 +++- .../project/merge_requests/code_quality.md | 8 +- doc/user/project/releases/index.md | 10 ++ lib/gitlab/encrypted_configuration.rb | 121 +++++++++++++++ lib/tasks/gitlab/user_management.rake | 4 +- locale/gitlab.pot | 6 +- package.json | 2 +- spec/config/settings_spec.rb | 16 ++ spec/features/boards/add_issues_modal_spec.rb | 5 + .../settings/service_desk_setting_spec.rb | 55 +++++-- .../remove_cluster_confirmation_spec.js.snap | 4 +- .../design_version_dropdown_spec.js.snap | 4 +- .../__snapshots__/alerts_form_spec.js.snap | 2 +- .../jira_import_form_spec.js.snap | 4 +- .../dashboard_template_spec.js.snap | 2 +- .../__snapshots__/code_coverage_spec.js.snap | 2 +- .../__snapshots__/split_button_spec.js.snap | 2 +- spec/initializers/secret_token_spec.rb | 27 +++- .../gitlab/encrypted_configuration_spec.rb | 145 ++++++++++++++++++ spec/models/project_spec.rb | 36 +++++ yarn.lock | 8 +- 49 files changed, 580 insertions(+), 102 deletions(-) create mode 100644 changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml create mode 100644 changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml create mode 100644 changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml create mode 100644 changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml create mode 100644 changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml create mode 100644 config/feature_flags/development/add_issues_button.yml delete mode 100644 doc/user/project/img/issue_boards_add_issues_modal_v13_6.png create mode 100644 lib/gitlab/encrypted_configuration.rb create mode 100644 spec/lib/gitlab/encrypted_configuration_spec.rb diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index bda581f5a4d..86d80d88737 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -306,7 +306,7 @@ export default () => { const issueBoardsModal = document.getElementById('js-add-issues-btn'); - if (issueBoardsModal) { + if (issueBoardsModal && gon.features.addIssuesButton) { // eslint-disable-next-line no-new new Vue({ el: issueBoardsModal, diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue index bbfa865905a..32366c5068f 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_form.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue @@ -33,7 +33,14 @@ export default { mixins: [glFeatureFlagsMixin()], computed: { ...mapGetters(['currentKey', 'propsSource', 'isDisabled']), - ...mapState(['defaultState', 'override', 'isSaving', 'isTesting', 'isResetting']), + ...mapState([ + 'defaultState', + 'customState', + 'override', + 'isSaving', + 'isTesting', + 'isResetting', + ]), isEditable() { return this.propsSource.editable; }, @@ -42,8 +49,8 @@ export default { }, isInstanceOrGroupLevel() { return ( - this.propsSource.integrationLevel === integrationLevels.INSTANCE || - this.propsSource.integrationLevel === integrationLevels.GROUP + this.customState.integrationLevel === integrationLevels.INSTANCE || + this.customState.integrationLevel === integrationLevels.GROUP ); }, showJiraIssuesFields() { diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue index f099372d6db..90ffa258884 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_item.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue @@ -4,7 +4,7 @@ import ActionComponent from './action_component.vue'; import JobNameComponent from './job_name_component.vue'; import { sprintf } from '~/locale'; import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin'; -import { accessors } from './accessors'; +import { accessValue } from './accessors'; import { REST } from './constants'; /** @@ -79,10 +79,10 @@ export default { return this.dropdownLength === 1 ? 'viewport' : 'scrollParent'; }, detailsPath() { - return this.status[accessors[this.dataMethod].detailsPath]; + return accessValue(this.dataMethod, 'detailsPath', this.status); }, hasDetails() { - return this.status[accessors[this.dataMethod].hasDetails]; + return accessValue(this.dataMethod, 'hasDetails', this.status); }, status() { return this.job && this.job.status ? this.job.status : {}; diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index 5e2d79758e1..830ad219574 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -5,7 +5,7 @@ import JobItem from './job_item.vue'; import JobGroupDropdown from './job_group_dropdown.vue'; import ActionComponent from './action_component.vue'; import { GRAPHQL } from './constants'; -import { accessors } from './accessors'; +import { accessValue } from './accessors'; export default { components: { @@ -39,7 +39,6 @@ export default { default: () => ({}), }, }, - accessors, titleClasses: [ 'gl-font-weight-bold', 'gl-pipeline-job-width', @@ -56,8 +55,8 @@ export default { }, }, methods: { - getAccessor(property) { - return accessors[GRAPHQL][property]; + getGroupId(group) { + return accessValue(GRAPHQL, 'groupId', group); }, groupId(group) { return `ci-badge-${escape(group.name)}`; @@ -87,7 +86,7 @@ export default {
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 741609c908a..af7c0d0ec3f 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -229,6 +229,7 @@ export default { v-if="pipeline.cancelable" :loading="isCanceling" :disabled="isCanceling" + class="gl-ml-3" variant="danger" data-testid="cancelPipeline" @click="cancelPipeline()" diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue index df7d9b56aed..a07c57c42cb 100644 --- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue @@ -30,6 +30,10 @@ export default { required: false, default: '', }, + customEmailEnabled: { + type: Boolean, + required: false, + }, selectedTemplate: { type: String, required: false, @@ -140,6 +144,7 @@ export default { :is-enabled="isEnabled" :incoming-email="incomingEmail" :custom-email="updatedCustomEmail" + :custom-email-enabled="customEmailEnabled" :initial-selected-template="selectedTemplate" :initial-outgoing-name="outgoingName" :initial-project-key="projectKey" diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue index 5d120fd0b3f..2896cb491b5 100644 --- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue @@ -31,6 +31,10 @@ export default { required: false, default: '', }, + customEmailEnabled: { + type: Boolean, + required: false, + }, initialSelectedTemplate: { type: String, required: false, @@ -69,7 +73,7 @@ export default { return [''].concat(this.templates); }, hasProjectKeySupport() { - return Boolean(this.glFeatures.serviceDeskCustomAddress); + return Boolean(this.customEmailEnabled); }, email() { return this.customEmail || this.incomingEmail; diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js index c73163788ef..8f9828dd73d 100644 --- a/app/assets/javascripts/projects/settings_service_desk/index.js +++ b/app/assets/javascripts/projects/settings_service_desk/index.js @@ -18,6 +18,7 @@ export default () => { endpoint: dataset.endpoint, incomingEmail: dataset.incomingEmail, customEmail: dataset.customEmail, + customEmailEnabled: parseBoolean(dataset.customEmailEnabled), selectedTemplate: dataset.selectedTemplate, outgoingName: dataset.outgoingName, projectKey: dataset.projectKey, @@ -31,6 +32,7 @@ export default () => { endpoint: this.endpoint, incomingEmail: this.incomingEmail, customEmail: this.customEmail, + customEmailEnabled: this.customEmailEnabled, selectedTemplate: this.selectedTemplate, outgoingName: this.outgoingName, projectKey: this.projectKey, diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss index 493a9093361..7b424882ffa 100644 --- a/app/assets/stylesheets/page_bundles/pipeline.scss +++ b/app/assets/stylesheets/page_bundles/pipeline.scss @@ -129,17 +129,6 @@ overflow: auto; } -// Move to Gitlab UI -.gl-font-weight-100 { - font-weight: 100; -} - -.gl-active-text-decoration-none:active, -.gl-focus-text-decoration-none:focus, -.gl-hover-text-decoration-none:hover { - text-decoration: none; -} - // These are single-value classes to use with utility-class style CSS // but to still access this variable. Do not add other styles. .gl-pipeline-min-h { @@ -150,10 +139,6 @@ width: 186px; } -.gl-pipeline-title-width { - width: 176px; -} - .gl-build-content { @include build-content(); } diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 0d92127910d..51c9bf3699a 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -7,6 +7,9 @@ class Projects::BoardsController < Projects::ApplicationController before_action :check_issues_available! before_action :authorize_read_board!, only: [:index, :show] before_action :assign_endpoint_vars + before_action do + push_frontend_feature_flag(:add_issues_button) + end feature_category :boards diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 161d59fdd85..f4c81ef65ab 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -35,7 +35,6 @@ class ProjectsController < Projects::ApplicationController before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export] before_action only: [:edit] do - push_frontend_feature_flag(:service_desk_custom_address, @project) push_frontend_feature_flag(:approval_suggestions, @project, default_enabled: true) end diff --git a/app/models/project.rb b/app/models/project.rb index 96863de6e4c..69032b3d762 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2507,8 +2507,7 @@ class Project < ApplicationRecord end def service_desk_custom_address - return unless ::Gitlab::ServiceDeskEmail.enabled? - return unless ::Feature.enabled?(:service_desk_custom_address, self) + return unless service_desk_custom_address_enabled? key = service_desk_setting&.project_key return unless key.present? @@ -2516,6 +2515,10 @@ class Project < ApplicationRecord ::Gitlab::ServiceDeskEmail.address_for_key("#{full_path_slug}-#{key}") end + def service_desk_custom_address_enabled? + ::Gitlab::ServiceDeskEmail.enabled? && ::Feature.enabled?(:service_desk_custom_address, self) + end + def root_namespace if namespace.has_parent? namespace.root_ancestor diff --git a/app/views/projects/_service_desk_settings.html.haml b/app/views/projects/_service_desk_settings.html.haml index 7c08955983a..3b2b3a2ba67 100644 --- a/app/views/projects/_service_desk_settings.html.haml +++ b/app/views/projects/_service_desk_settings.html.haml @@ -12,6 +12,7 @@ enabled: "#{@project.service_desk_enabled}", incoming_email: (@project.service_desk_incoming_address if @project.service_desk_enabled), custom_email: (@project.service_desk_custom_address if @project.service_desk_enabled), + custom_email_enabled: "#{@project.service_desk_custom_address_enabled?}", selected_template: "#{@project.service_desk_setting&.issue_template_key}", outgoing_name: "#{@project.service_desk_setting&.outgoing_name}", project_key: "#{@project.service_desk_setting&.project_key}", diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml index 255ec9995db..50daa400e6c 100644 --- a/app/views/shared/access_tokens/_table.html.haml +++ b/app/views/shared/access_tokens/_table.html.haml @@ -42,7 +42,7 @@ = _('In %{time_to_now}') % { time_to_now: distance_of_time_in_words_to_now(token.expires_at) } - else %span.token-never-expires-label= _('Never') - %td= token.scopes.present? ? token.scopes.join(', ') : html_escape_once(_('<no scopes selected>')).html_safe + %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected') %td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'btn btn-danger float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } } - else .settings-message.text-center diff --git a/app/views/shared/deploy_tokens/_table.html.haml b/app/views/shared/deploy_tokens/_table.html.haml index ad73442807e..361471af0ad 100644 --- a/app/views/shared/deploy_tokens/_table.html.haml +++ b/app/views/shared/deploy_tokens/_table.html.haml @@ -23,7 +23,7 @@ In #{distance_of_time_in_words_to_now(token.expires_at)} - else %span.token-never-expires-label= _('Never') - %td= token.scopes.present? ? token.scopes.join(", ") : html_escape_once(_('<no scopes selected>')).html_safe + %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected') %td= link_to s_('DeployTokens|Revoke'), "#", class: "btn btn-danger float-right", data: { toggle: "modal", target: "#revoke-modal-#{token.id}"} = render 'shared/deploy_tokens/revoke_modal', token: token, group_or_project: group_or_project - else diff --git a/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml b/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml new file mode 100644 index 00000000000..bb13a2fc782 --- /dev/null +++ b/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml @@ -0,0 +1,5 @@ +--- +title: Remove brackets in no scopes selected message in access and deploy tokens lists +merge_request: 47628 +author: +type: changed diff --git a/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml b/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml new file mode 100644 index 00000000000..858a7dac2e3 --- /dev/null +++ b/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml @@ -0,0 +1,5 @@ +--- +title: Remove `Add Issues` button and a related modal +merge_request: 47898 +author: +type: changed diff --git a/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml b/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml new file mode 100644 index 00000000000..a15b3d57583 --- /dev/null +++ b/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml @@ -0,0 +1,5 @@ +--- +title: Fix styling of various dropdowns +merge_request: 48800 +author: +type: fixed diff --git a/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml b/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml new file mode 100644 index 00000000000..2fa121dc15d --- /dev/null +++ b/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml @@ -0,0 +1,5 @@ +--- +title: Fix confirmation modal showing on project integration +merge_request: 48720 +author: +type: fixed diff --git a/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml b/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml new file mode 100644 index 00000000000..85b7f8e6701 --- /dev/null +++ b/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml @@ -0,0 +1,5 @@ +--- +title: Fix spacing between buttons on pipeline header +merge_request: 48660 +author: +type: fixed diff --git a/config/feature_flags/development/add_issues_button.yml b/config/feature_flags/development/add_issues_button.yml new file mode 100644 index 00000000000..cc4727a29c4 --- /dev/null +++ b/config/feature_flags/development/add_issues_button.yml @@ -0,0 +1,8 @@ +--- +name: add_issues_button +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47898 +rollout_issue_url: +milestone: '13.6' +type: development +group: group::project management +default_enabled: false diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8822e57e153..948724bdf76 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -1042,6 +1042,10 @@ production: &base shared: # path: /mnt/gitlab # Default: shared + # Encrypted Settings configuration + encrypted_settings: + # path: /mnt/gitlab/encrypted_settings # Default: shared/encrypted_settings + # Gitaly settings gitaly: # Path to the directory containing Gitaly client executables. diff --git a/config/initializers/01_secret_token.rb b/config/initializers/01_secret_token.rb index 5949f463457..d7e725477eb 100644 --- a/config/initializers/01_secret_token.rb +++ b/config/initializers/01_secret_token.rb @@ -34,6 +34,9 @@ def create_tokens openid_connect_signing_key: generate_new_rsa_private_key } + # encrypted_settings_key_base is optional for now + defaults[:encrypted_settings_key_base] = generate_new_secure_token if ENV['GITLAB_GENERATE_ENCRYPTED_SETTINGS_KEY_BASE'] + missing_secrets = set_missing_keys(defaults) write_secrets_yml(missing_secrets) unless missing_secrets.empty? diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index d0c4cccd874..f2315b2a431 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -3,6 +3,13 @@ require_relative '../object_store_settings' require_relative '../smime_signature_settings' # Default settings +Settings['shared'] ||= Settingslogic.new({}) +Settings.shared['path'] = Settings.absolute(Settings.shared['path'] || "shared") + +Settings['encrypted_settings'] ||= Settingslogic.new({}) +Settings.encrypted_settings['path'] ||= File.join(Settings.shared['path'], "encrypted_settings") +Settings.encrypted_settings['path'] = Settings.absolute(Settings.encrypted_settings['path']) + Settings['ldap'] ||= Settingslogic.new({}) Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['prevent_ldap_sign_in'] = false if Settings.ldap['prevent_ldap_sign_in'].blank? @@ -140,9 +147,6 @@ if Gitlab.ee? && Rails.env.test? && !saml_provider_enabled Settings.omniauth.providers << Settingslogic.new({ 'name' => 'group_saml' }) end -Settings['shared'] ||= Settingslogic.new({}) -Settings.shared['path'] = Settings.absolute(Settings.shared['path'] || "shared") - Settings['issues_tracker'] ||= {} # diff --git a/config/settings.rb b/config/settings.rb index c681fa32491..3369f2a4480 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -152,6 +152,14 @@ class Settings < Settingslogic Gitlab::Application.secrets.db_key_base end + def encrypted(path) + Gitlab::EncryptedConfiguration.new( + content_path: path, + base_key: Gitlab::Application.secrets.encrypted_settings_key_base, + previous_keys: Gitlab::Application.secrets.rotated_encrypted_settings_key_base || [] + ) + end + def load_dynamic_cron_schedules! cron_jobs['gitlab_usage_ping_worker']['cron'] ||= cron_for_usage_ping end diff --git a/doc/development/application_secrets.md b/doc/development/application_secrets.md index f1e01af7702..06c2c8bf8b5 100644 --- a/doc/development/application_secrets.md +++ b/doc/development/application_secrets.md @@ -16,6 +16,7 @@ This page is a development guide for application secrets. | `otp_key_base` | The base key for One Time Passwords, described in [User management](../raketasks/user_management.md#rotate-two-factor-authentication-encryption-key) | |`db_key_base` | The base key to encrypt the data for `attr_encrypted` columns | |`openid_connect_signing_key` | The singing key for OpenID Connect | +| `encrypted_settings_key_base` | The base key to encrypt settings files with | ## Where the secrets are stored diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 1a51e8ff604..ddf19dc9935 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -7,8 +7,7 @@ description: 'Writing styles, markup, formatting, and other standards for GitLab # Documentation Style Guide -This document defines the standards for GitLab's documentation content and -files. +This document defines the standards for GitLab documentation. For broader information about the documentation, see the [Documentation guidelines](../index.md). @@ -241,16 +240,16 @@ to update. Put files for a specific product area into the related folder: -| Directory | What belongs here | -|:----------------------|:----------------------------------------------------------------------------------------------------------------------------------------| -| `doc/user/` | User related documentation. Anything that can be done within the GitLab user interface goes here, including usage of the `/admin` interface. | -| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. The admin settings that can be accessed by using GitLab's interface exist under `doc/user/admin_area/`. | -| `doc/api/` | API related documentation. | +| Directory | What belongs here | +|:----------------------|:------------------| +| `doc/user/` | User related documentation. Anything that can be done within the GitLab user interface goes here, including usage of the `/admin` interface. | +| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. Administrator settings in the GitLab user interface are under `doc/user/admin_area/`. | +| `doc/api/` | API-related documentation. | | `doc/development/` | Documentation related to the development of GitLab, whether contributing code or documentation. Related process and style guides should go here. | -| `doc/legal/` | Legal documents about contributing to GitLab. | -| `doc/install/` | Contains instructions for installing GitLab. | -| `doc/update/` | Contains instructions for updating GitLab. | -| `doc/topics/` | Indexes per topic (`doc/topics/topic_name/index.md`): all resources for that topic. | +| `doc/legal/` | Legal documents about contributing to GitLab. | +| `doc/install/` | Contains instructions for installing GitLab. | +| `doc/update/` | Contains instructions for updating GitLab. | +| `doc/topics/` | Indexes per topic (`doc/topics/topic_name/index.md`): all resources for that topic. | ### Work with directories and files @@ -277,10 +276,10 @@ Refer to the following items when working with directories and files: Every page you would navigate under `/profile` should have its own document, for example, `account.md`, `applications.md`, or `emails.md`. - `doc/user/dashboard/` should contain all dashboard related documentation. - - `doc/user/admin_area/` should contain all admin related documentation - describing what can be achieved by accessing GitLab's admin interface - (_not to be confused with `doc/administration` where server access is - required_). + - `doc/user/admin_area/` should contain all administrator-related + documentation describing what can be achieved by accessing the GitLab + administrator interface (not to be confused with `doc/administration` where + server access is required). - Every category under `/admin/application_settings/` should have its own document located at `doc/user/admin_area/settings/`. For example, the **Visibility and Access Controls** category should have a document @@ -567,9 +566,9 @@ tenses, words, and phrases: - Avoid using the word *currently* when talking about the product or its features. The documentation describes the product as it is, and not as it will be at some indeterminate point in the future. -- Avoid the using the word *scalability* with increasing GitLab's performance - for additional users. Using the words *scale* or *scaling* in other cases is - acceptable, but references to increasing GitLab's performance for additional +- Avoid using the word scalability when talking about increasing GitLab + performance for additional users. The words scale or scaling are sometimes + acceptable, but references to increasing GitLab performance for additional users should direct readers to the GitLab [reference architectures](../../../administration/reference_architectures/index.md) page. @@ -577,8 +576,8 @@ tenses, words, and phrases: direct readers to the GitLab [reference architectures](../../../administration/reference_architectures/index.md) for information about configuring GitLab to have the performance needed for additional users over time. -- Don't use profanity or obscenities. Doing so may negatively affect other - users and contributors, which is contrary to GitLab's value of +- Don't use profanity or obscenities. Doing so may negatively affect other users + and contributors, which is contrary to the GitLab value of [Diversity, Inclusion, and Belonging](https://about.gitlab.com/handbook/values/#diversity-inclusion). - Avoid the use of [racially-insensitive terminology or phrases](https://www.marketplace.org/2020/06/17/tech-companies-update-language-to-avoid-offensive-terms/). For example: - Use *primary* and *secondary* for database and server relationships. @@ -982,9 +981,9 @@ Important: tutorials, presentations, StackOverflow posts, and other sources. - Do not link to `h1` headings. -Note that, with Kramdown, it is possible to add a custom ID to an HTML element -with Markdown markup, but they _do not_ work in GitLab's `/help`. Therefore, -do not use this option until further notice. +Note that with Kramdown, it's possible to add a custom ID to an HTML element +with Markdown markup, but they don't work in `/help`. Because of this, don't use +this option. ## Links @@ -1268,7 +1267,7 @@ request. ## Videos -Adding GitLab's existing YouTube video tutorials to the documentation is highly +Adding GitLab YouTube video tutorials to the documentation is highly encouraged, unless the video is outdated. Videos should not replace documentation, but complement or illustrate it. If content in a video is fundamental to a feature and its key use cases, but this is not adequately @@ -1297,7 +1296,7 @@ You can link any up-to-date video that's useful to the GitLab user. The [GitLab documentation site](https://docs.gitlab.com) supports embedded videos. -You can only embed videos from [GitLab's official YouTube account](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg). +You can embed videos from [the official YouTube account for GitLab](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg) only. For videos from other sources, [link](#link-to-video) them instead. In most cases, it is better to [link to video](#link-to-video) instead, because @@ -1341,9 +1340,9 @@ This is how it renders on the GitLab documentation site: > - The `figure` tag is required for semantic SEO and the `video_container` class is necessary to make sure the video is responsive and displays on different mobile devices. -> - The `
` is a fallback necessary for GitLab's -`/help`, as GitLab's Markdown processor does not support iframes. It's hidden on -the documentation site, but will be displayed on GitLab's `/help`. +> - The `
` is a fallback necessary for +`/help`, because the GitLab Markdown processor doesn't support iframes. It's +hidden on the documentation site, but is displayed by `/help`. ## Code blocks @@ -1659,8 +1658,8 @@ elements: To help users be aware of recent product improvements or additions, we add GitLab version information to our documentation. -The GitLab Technical Writing team determines which versions of GitLab's -documentation to display on this site based on GitLab's +The GitLab Technical Writing team determines which versions of +documentation to display on this site based on the GitLab [Statement of Support](https://about.gitlab.com/support/statement-of-support.html#we-support-the-current-major-version-and-the-two-previous-major-versions). ### View older GitLab documentation versions diff --git a/doc/user/project/img/issue_boards_add_issues_modal_v13_6.png b/doc/user/project/img/issue_boards_add_issues_modal_v13_6.png deleted file mode 100644 index a138efc9c1c45654104baf82476cc83740e1ce29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10189 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 - Feature flag [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47898) in GitLab 13.7. +> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. +> - It's disabled on GitLab.com. +> - It's recommended for production use. +> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-adding-issues-to-the-list). **(CORE ONLY)** You can add issues to a list in a project issue board by clicking the **Add issues** button in the top right corner of the issue board. This opens up a modal @@ -453,7 +459,23 @@ the list by filtering by the following: - Release - Weight -![Bulk adding issues to lists](img/issue_boards_add_issues_modal_v13_6.png) +#### Enable or disable adding issues to the list **(CORE ONLY)** + +Adding issues to the list is deployed behind a feature flag that is **disabled by default**. +[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) +can enable it. + +To enable it: + +```ruby +Feature.enable(:add_issues_button) +``` + +To disable it: + +```ruby +Feature.disable(:add_issues_button) +``` ### Remove an issue from a list diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md index c2cf648abee..7fa5fbb13c8 100644 --- a/doc/user/project/merge_requests/code_quality.md +++ b/doc/user/project/merge_requests/code_quality.md @@ -254,18 +254,18 @@ NOTE: **Note:** Although the Code Climate spec supports more properties, those are ignored by GitLab. -## Code Quality reports **(STARTER)** +## Code Quality reports -Once the Code Quality job has completed: +After the Code Quality job completes: -- The full list of code quality violations generated by a pipeline is shown in the - Code Quality tab of the Pipeline Details page. - Potential changes to code quality are shown directly in the merge request. The Code Quality widget in the merge request compares the reports from the base and head of the branch, then lists any violations that are resolved or created when the branch is merged. - The full JSON report is available as a [downloadable artifact](../../../ci/pipelines/job_artifacts.md#downloading-artifacts) for the `code_quality` job. +- The full list of code quality violations generated by a pipeline is shown in the + Code Quality tab of the Pipeline Details page. **(STARTER)** ### Generating an HTML report diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md index 9b3776c8878..5e43a02403a 100644 --- a/doc/user/project/releases/index.md +++ b/doc/user/project/releases/index.md @@ -79,6 +79,16 @@ To create a new release through the GitLab UI: [release notes](#release-notes-description), or [assets links](#links). 1. Click **Create release**. +### Create release from GitLab CI + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19298) in GitLab 12.7. + +You can [create a release directly from the GitLab CI pipeline](../../../ci/yaml/README.md#release) +by using a `release` node in the job definition. + +The release is created only if the job processes without error. If the Rails API returns an error +during release creation, the release job fails. + ### Schedule a future release > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/38105) in GitLab 12.1. diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb new file mode 100644 index 00000000000..fe49af3ab33 --- /dev/null +++ b/lib/gitlab/encrypted_configuration.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module Gitlab + class EncryptedConfiguration + delegate :[], :fetch, to: :config + delegate_missing_to :options + attr_reader :content_path, :key, :previous_keys + + CIPHER = "aes-256-gcm" + SALT = "GitLabEncryptedConfigSalt" + + class MissingKeyError < RuntimeError + def initialize(msg = "Missing encryption key to encrypt/decrypt file with.") + super + end + end + + class InvalidConfigError < RuntimeError + def initialize(msg = "Content was not a valid yml config file") + super + end + end + + def self.generate_key(base_key) + # Because the salt is static, we want uniqueness to be coming from the base_key + # Error if the base_key is empty or suspiciously short + raise 'Base key too small' if base_key.blank? || base_key.length < 16 + + ActiveSupport::KeyGenerator.new(base_key).generate_key(SALT, ActiveSupport::MessageEncryptor.key_len(CIPHER)) + end + + def initialize(content_path: nil, base_key: nil, previous_keys: []) + @content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path } if content_path + @key = self.class.generate_key(base_key) if base_key + @previous_keys = previous_keys + end + + def active? + content_path&.exist? + end + + def read + if active? + decrypt(content_path.binread) + else + "" + end + end + + def write(contents) + # ensure contents are valid to deserialize before write + deserialize(contents) + + temp_file = Tempfile.new(File.basename(content_path), File.dirname(content_path)) + File.open(temp_file.path, 'wb') do |file| + file.write(encrypt(contents)) + end + FileUtils.mv(temp_file.path, content_path) + ensure + temp_file&.unlink + end + + def config + return @config if @config + + contents = deserialize(read) + + raise InvalidConfigError.new unless contents.is_a?(Hash) + + @config = contents.deep_symbolize_keys + end + + def change(&block) + writing(read, &block) + end + + private + + def writing(contents) + updated_contents = yield contents + + write(updated_contents) if updated_contents != contents + end + + def encrypt(contents) + handle_missing_key! + encryptor.encrypt_and_sign(contents) + end + + def decrypt(contents) + handle_missing_key! + encryptor.decrypt_and_verify(contents) + end + + def encryptor + return @encryptor if @encryptor + + @encryptor = ActiveSupport::MessageEncryptor.new(key, cipher: CIPHER) + + # Allow fallback to previous keys + @previous_keys.each do |key| + @encryptor.rotate(self.class.generate_key(key)) + end + + @encryptor + end + + def options + # Allows top level keys to be referenced using dot syntax + @options ||= ActiveSupport::InheritableOptions.new(config) + end + + def deserialize(contents) + YAML.safe_load(contents, permitted_classes: [Symbol]).presence || {} + end + + def handle_missing_key! + raise MissingKeyError.new if @key.nil? + end + end +end diff --git a/lib/tasks/gitlab/user_management.rake b/lib/tasks/gitlab/user_management.rake index 5bf3b8c806e..f47e549e795 100644 --- a/lib/tasks/gitlab/user_management.rake +++ b/lib/tasks/gitlab/user_management.rake @@ -6,8 +6,8 @@ namespace :gitlab do result = User.where(id: group.direct_and_indirect_users_with_inactive.select(:id)).update_all(projects_limit: 0, can_create_group: false) ids_count = group.direct_and_indirect_users_with_inactive.count - puts "Done".green if result == ids_count - puts "Something went wrong".red if result != ids_count + puts "Done".color(:green) if result == ids_count + puts "Something went wrong".color(:red) if result != ids_count end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9248e9b1918..d93d2dbb8a3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -896,9 +896,6 @@ msgstr "" msgid "< 1 hour" msgstr "" -msgid "<no scopes selected>" -msgstr "" - msgid "'%{data}' at %{location} does not match format: %{format}" msgstr "" @@ -32767,6 +32764,9 @@ msgstr "" msgid "no one can merge" msgstr "" +msgid "no scopes selected" +msgstr "" + msgid "none" msgstr "" diff --git a/package.json b/package.json index e33ef3ec1a3..ded5488b2b2 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.175.0", - "@gitlab/ui": "24.3.0", + "@gitlab/ui": "24.3.1", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-3", "@rails/ujs": "^6.0.3-2", diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb index ed873478fc9..6525ae653c9 100644 --- a/spec/config/settings_spec.rb +++ b/spec/config/settings_spec.rb @@ -134,4 +134,20 @@ RSpec.describe Settings do end end end + + describe '.encrypted' do + before do + allow(Gitlab::Application.secrets).to receive(:encryped_settings_key_base).and_return(SecureRandom.hex(64)) + end + + it 'defaults to using the encrypted_settings_key_base for the key' do + expect(Gitlab::EncryptedConfiguration).to receive(:new).with(hash_including(base_key: Gitlab::Application.secrets.encrypted_settings_key_base)) + Settings.encrypted('tmp/tests/test.enc') + end + + it 'returns empty encrypted config when a key has not been set' do + allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil) + expect(Settings.encrypted('tmp/tests/test.enc').read).to be_empty + end + end end diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb index f941adca233..8d0fa3e023b 100644 --- a/spec/features/boards/add_issues_modal_spec.rb +++ b/spec/features/boards/add_issues_modal_spec.rb @@ -37,6 +37,10 @@ RSpec.describe 'Issue Boards add issue modal', :js do end context 'modal interaction' do + before do + stub_feature_flags(add_issues_button: true) + end + it 'opens modal' do click_button('Add issues') @@ -72,6 +76,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do context 'issues list' do before do + stub_feature_flags(add_issues_button: true) click_button('Add issues') wait_for_requests diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb index 59e6f54da2f..d31913d2dcf 100644 --- a/spec/features/projects/settings/service_desk_setting_spec.rb +++ b/spec/features/projects/settings/service_desk_setting_spec.rb @@ -14,20 +14,57 @@ RSpec.describe 'Service Desk Setting', :js do allow_any_instance_of(Project).to receive(:present).with(current_user: user).and_return(presenter) allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true } allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true } - - visit edit_project_path(project) end it 'shows activation checkbox' do + visit edit_project_path(project) + expect(page).to have_selector("#service-desk-checkbox") end - it 'shows incoming email after activating' do - find("#service-desk-checkbox").click - wait_for_requests - project.reload - expect(project.service_desk_enabled).to be_truthy - expect(project.service_desk_address).to be_present - expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_address) + context 'when service_desk_email is disabled' do + before do + allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(false) + + visit edit_project_path(project) + end + + it 'shows incoming email but not project name suffix after activating' do + find("#service-desk-checkbox").click + + wait_for_requests + + project.reload + expect(project.service_desk_enabled).to be_truthy + expect(project.service_desk_address).to be_present + expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_incoming_address) + expect(page).not_to have_selector('#service-desk-project-suffix') + end + end + + context 'when service_desk_email is enabled' do + before do + allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?) { true } + allow(::Gitlab::ServiceDeskEmail).to receive(:address_for_key) { 'address-suffix@example.com' } + + visit edit_project_path(project) + end + + it 'allows setting of custom address suffix' do + find("#service-desk-checkbox").click + wait_for_requests + + project.reload + expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_incoming_address) + + page.within '#js-service-desk' do + fill_in('service-desk-project-suffix', with: 'foo') + click_button 'Save changes' + end + + wait_for_requests + + expect(find('[data-testid="incoming-email"]').value).to eq('address-suffix@example.com') + end end end diff --git a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap index 7471ef2c1b3..6f28573c808 100644 --- a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap +++ b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap @@ -9,7 +9,7 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ menu-class="dropdown-menu-large" >