Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-17 09:10:02 +00:00
parent 04ebfaf17c
commit 0fd2296553
54 changed files with 1050 additions and 24190 deletions

View File

@ -1,6 +1,8 @@
<script>
import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
import defaultAvatarUrl from 'images/no_avatar.png';
import { __, sprintf } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import allVersionsMixin from '../../mixins/all_versions';
import { findVersionId } from '../../utils/design_management_utils';
@ -9,6 +11,7 @@ export default {
GlDropdown,
GlDropdownItem,
GlSprintf,
TimeAgo,
},
mixins: [allVersionsMixin],
computed: {
@ -58,6 +61,9 @@ export default {
}
return __('Version %{versionNumber}');
},
getAvatarUrl(version) {
return version?.author?.avatarUrl || defaultAvatarUrl;
},
},
};
</script>
@ -68,14 +74,28 @@ export default {
v-for="(version, index) in allVersions"
:key="version.id"
:is-check-item="true"
:is-check-centered="true"
:is-checked="findVersionId(version.id) === currentVersionId"
:avatar-url="getAvatarUrl(version)"
@click="routeToVersion(version.id)"
>
<gl-sprintf :message="versionText(version.id)">
<template #versionNumber>
{{ allVersions.length - index }}
</template>
</gl-sprintf>
<strong>
<gl-sprintf :message="versionText(version.id)">
<template #versionNumber>
{{ allVersions.length - index }}
</template>
</gl-sprintf>
</strong>
<div v-if="version.author" class="gl-text-gray-600 gl-mt-1">
<div>{{ version.author.name }}</div>
<time-ago
v-if="version.createdAt"
class="text-1"
:time="version.createdAt"
tooltip-placement="bottom"
/>
</div>
</gl-dropdown-item>
</gl-dropdown>
</template>

View File

@ -11,7 +11,7 @@ import {
GlSprintf,
GlSafeHtmlDirective as SafeHtml,
GlTable,
GlTooltip,
GlFormCheckbox,
} from '@gitlab/ui';
import { s__, __, n__ } from '~/locale';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
@ -40,8 +40,8 @@ export default {
GlLink,
GlLoadingIcon,
GlSearchBoxByClick,
GlFormCheckbox,
GlSprintf,
GlTooltip,
GlTable,
ImportStatus,
ImportTargetCell,
@ -71,6 +71,7 @@ export default {
filter: '',
page: 1,
perPage: DEFAULT_PAGE_SIZE,
selectedGroups: [],
};
},
@ -85,11 +86,20 @@ export default {
},
fields: [
{
key: 'selected',
label: '',
// eslint-disable-next-line @gitlab/require-i18n-strings
thClass: `${DEFAULT_TH_CLASSES} gl-w-3 gl-pr-3!`,
// eslint-disable-next-line @gitlab/require-i18n-strings
tdClass: `${DEFAULT_TD_CLASSES} gl-pr-3!`,
},
{
key: 'web_url',
label: s__('BulkImport|From source group'),
thClass: `${DEFAULT_TH_CLASSES} import-jobs-from-col`,
tdClass: DEFAULT_TD_CLASSES,
thClass: `${DEFAULT_TH_CLASSES} gl-pl-0! import-jobs-from-col`,
// eslint-disable-next-line @gitlab/require-i18n-strings
tdClass: `${DEFAULT_TD_CLASSES} gl-pl-0!`,
},
{
key: 'import_target',
@ -117,16 +127,16 @@ export default {
return this.bulkImportSourceGroups?.nodes ?? [];
},
hasGroupsWithValidationError() {
return this.groups.some((g) => g.validation_errors.length);
hasSelectedGroups() {
return this.selectedGroups.length > 0;
},
hasAllAvailableGroupsSelected() {
return this.selectedGroups.length === this.availableGroupsForImport.length;
},
availableGroupsForImport() {
return this.groups.filter((g) => g.progress.status === STATUSES.NONE);
},
isImportAllButtonDisabled() {
return this.hasGroupsWithValidationError || this.availableGroupsForImport.length === 0;
return this.groups.filter((g) => g.progress.status === STATUSES.NONE && !this.isInvalid(g));
},
humanizedTotal() {
@ -156,7 +166,7 @@ export default {
total: 0,
};
const start = (page - 1) * perPage + 1;
const end = start + (this.bulkImportSourceGroups.nodes?.length ?? 0) - 1;
const end = start + this.groups.length - 1;
return { start, end, total };
},
@ -166,6 +176,17 @@ export default {
filter() {
this.page = 1;
},
groups() {
const table = this.getTableRef();
this.groups.forEach((g, idx) => {
if (this.selectedGroups.includes(g)) {
this.$nextTick(() => {
table.selectRow(idx);
});
}
});
this.selectedGroups = [];
},
},
methods: {
@ -210,13 +231,33 @@ export default {
});
},
importAllGroups() {
this.importGroups(this.availableGroupsForImport.map((g) => g.id));
importSelectedGroups() {
this.importGroups(this.selectedGroups.map((g) => g.id));
},
setPageSize(size) {
this.perPage = size;
},
getTableRef() {
// Acquire reference to BTable to manipulate selection
// issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531
// refs are not reactive, so do not use computed here
return this.$refs.table?.$children[0];
},
preventSelectingAlreadyImportedGroups(updatedSelection) {
if (updatedSelection) {
this.selectedGroups = updatedSelection;
}
const table = this.getTableRef();
this.groups.forEach((group, idx) => {
if (table.isRowSelected(idx) && (this.isAlreadyImported(group) || this.isInvalid(group))) {
table.unselectRow(idx);
}
});
},
},
gitlabLogo: window.gon.gitlab_logo,
@ -231,28 +272,6 @@ export default {
>
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
{{ s__('BulkImport|Import groups from GitLab') }}
<div ref="importAllButtonWrapper" class="gl-ml-auto">
<gl-button
v-if="!$apollo.loading && hasGroups"
:disabled="isImportAllButtonDisabled"
variant="confirm"
@click="importAllGroups"
>
<gl-sprintf :message="s__('BulkImport|Import %{groups}')">
<template #groups>
{{ groupsCount(availableGroupsForImport.length) }}
</template>
</gl-sprintf>
</gl-button>
</div>
<gl-tooltip v-if="isImportAllButtonDisabled" :target="() => $refs.importAllButtonWrapper">
<template v-if="hasGroupsWithValidationError">
{{ s__('BulkImport|One or more groups has validation errors') }}
</template>
<template v-else>
{{ s__('BulkImport|No groups on this page are available for import') }}
</template>
</gl-tooltip>
</h1>
<div
class="gl-py-5 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex"
@ -298,19 +317,58 @@ export default {
:description="s__('Check your source instance permissions.')"
/>
<template v-else>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-p-4 gl-display-flex gl-align-items-center"
>
<gl-sprintf :message="__('%{count} selected')">
<template #count>
{{ selectedGroups.length }}
</template>
</gl-sprintf>
<gl-button
category="primary"
variant="confirm"
class="gl-ml-4"
:disabled="!hasSelectedGroups"
@click="importSelectedGroups"
>{{ s__('BulkImport|Import selected') }}</gl-button
>
</div>
<gl-table
ref="table"
class="gl-w-full"
data-qa-selector="import_table"
tbody-tr-class="gl-border-gray-200 gl-border-0 gl-border-b-1 gl-border-solid"
:tbody-tr-attr="qaRowAttributes"
:items="bulkImportSourceGroups.nodes"
:items="groups"
:fields="$options.fields"
selectable
select-mode="multi"
selected-variant="primary"
@row-selected="preventSelectingAlreadyImportedGroups"
>
<template #head(selected)="{ selectAllRows, clearSelected }">
<gl-form-checkbox
:key="`checkbox-${selectedGroups.length}`"
class="gl-h-7 gl-pt-3"
:checked="hasSelectedGroups"
:indeterminate="hasSelectedGroups && !hasAllAvailableGroupsSelected"
@change="hasAllAvailableGroupsSelected ? clearSelected() : selectAllRows()"
/>
</template>
<template #cell(selected)="{ rowSelected, selectRow, unselectRow, item: group }">
<gl-form-checkbox
class="gl-h-7 gl-pt-3"
:checked="rowSelected"
:disabled="isAlreadyImported(group) || isInvalid(group)"
@change="rowSelected ? unselectRow() : selectRow()"
/>
</template>
<template #cell(web_url)="{ value: web_url, item: { full_path } }">
<gl-link
:href="web_url"
target="_blank"
class="gl-display-flex gl-align-items-center gl-h-7"
class="gl-display-inline-flex gl-align-items-center gl-h-7"
>
{{ full_path }} <gl-icon name="external-link" />
</gl-link>
@ -330,7 +388,7 @@ export default {
/>
</template>
<template #cell(progress)="{ value: { status } }">
<import-status :status="status" class="gl-mt-2" />
<import-status :status="status" class="gl-line-height-32" />
</template>
<template #cell(actions)="{ item: group }">
<gl-button

View File

@ -87,7 +87,7 @@ export default {
<template>
<gl-link
v-if="isFinished"
class="gl-display-flex gl-align-items-center gl-h-7"
class="gl-display-inline-flex gl-align-items-center gl-h-7"
:href="absolutePath"
>
{{ fullPath }}

View File

@ -68,7 +68,11 @@ export default {
this.$emit('set-option', option || null);
},
isSelected(option) {
return this.selected && this.selected.title === option.title;
return (
this.selected &&
((option.name && this.selected.name === option.name) ||
(option.title && this.selected.title === option.title))
);
},
showDropdown() {
this.$refs.dropdown.show();
@ -79,6 +83,13 @@ export default {
setSearchTerm(search) {
this.$emit('set-search', search);
},
avatarUrl(option) {
return option.avatar_url || option.avatarUrl || null;
},
secondaryText(option) {
// TODO: this has some knowledge of the context where the component is used. We could later rework it.
return option.username || null;
},
},
i18n: {
noMatchingResults: __('No matching results'),
@ -121,7 +132,9 @@ export default {
:is-check-item="true"
@click="selectOption(option)"
>
{{ option.title }}
<slot name="preset-item" :item="option">
{{ option.title }}
</slot>
</gl-dropdown-item>
<gl-dropdown-divider />
</template>
@ -131,10 +144,14 @@ export default {
:is-checked="isSelected(option)"
:is-check-centered="true"
:is-check-item="true"
:avatar-url="avatarUrl(option)"
:secondary-text="secondaryText(option)"
data-testid="unselected-option"
@click="selectOption(option)"
>
{{ option.title }}
<slot name="item" :item="option">
{{ option.title }}
</slot>
</gl-dropdown-item>
<gl-dropdown-item v-if="noOptionsFound" class="gl-pl-6!">
{{ $options.i18n.noMatchingResults }}

View File

@ -12,17 +12,22 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
def publish(_result = nil)
super
publish_to_client if should_track? # publish the experiment data to the client
publish_to_database if @record # publish the experiment context to the database
publish_to_client
publish_to_database
end
def publish_to_client
return unless should_track?
Gon.push({ experiment: { name => signature } }, true)
rescue NoMethodError
# means we're not in the request cycle, and can't add to Gon. Log a warning maybe?
end
def publish_to_database
return unless @record
return unless should_track?
# if the context contains a namespace, group, project, user, or actor
value = context.value
subject = value[:namespace] || value[:group] || value[:project] || value[:user] || value[:actor]

View File

@ -32,6 +32,13 @@ query getDesignList($fullPath: ID!, $iid: String!, $atVersion: ID) {
__typename
id
sha
createdAt
author {
__typename
id
name
avatarUrl
}
}
}
}

View File

@ -9,7 +9,7 @@
- if current_user.can_create_project?
.page-title-controls
= link_to _("New project"), new_project_path, class: "gl-button btn btn-confirm"
= link_to _("New project"), new_project_path, class: "gl-button btn btn-confirm", data: { qa_selector: 'new_project_button' }
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= sprite_icon('chevron-lg-left', size: 12)

View File

@ -118,6 +118,8 @@ production: &base
# ca_certs_file: /home/git/gitlab/.gitlab_smime_ca_certs
# Email server smtp settings are in config/initializers/smtp_settings.rb.sample
# File location to read encrypted SMTP secrets from
# email_smtp_secret_file: /mnt/gitlab/smtp.yaml.enc # Default: shared/encrypted_settings/smtp.yaml.enc
# default_can_create_group: false # default: true
# username_changing_enabled: false # default: true - User can change their username/namespace

View File

@ -178,6 +178,7 @@ Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'G
Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}"
Settings.gitlab['email_subject_suffix'] ||= ENV['GITLAB_EMAIL_SUBJECT_SUFFIX'] || ""
Settings.gitlab['email_smime'] = SmimeSignatureSettings.parse(Settings.gitlab['email_smime'])
Settings.gitlab['email_smtp_secret_file'] = Settings.absolute(Settings.gitlab['email_smtp_secret_file'] || File.join(Settings.encrypted_settings['path'], "smtp.yaml.enc"))
Settings.gitlab['base_url'] ||= Settings.__send__(:build_base_gitlab_url)
Settings.gitlab['url'] ||= Settings.__send__(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git'

View File

@ -9,6 +9,7 @@
if Rails.env.production?
Rails.application.config.action_mailer.delivery_method = :smtp
secrets = Gitlab::Email::SmtpConfig.secrets
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
@ -16,6 +17,10 @@ if Rails.env.production?
port: 465,
user_name: "smtp",
password: "123456",
## If you are using encrypted smtp credentials then you should instead use the secrets user_name/password
## See: https://docs.gitlab.com/ee/administration/raketasks/smtp.html#secrets
# user_name: secrets.username,
# password: secrets.password,
domain: "gitlab.company.com",
authentication: :login,
enable_starttls_auto: true,
@ -31,6 +36,7 @@ end
#
# if Rails.env.production?
# Rails.application.config.action_mailer.delivery_method = :smtp_pool
# secrets = Gitlab::Email::SmtpConfig.secrets
#
# ActionMailer::Base.delivery_method = :smtp_pool
# ActionMailer::Base.smtp_pool_settings = {
@ -40,6 +46,10 @@ end
# port: 465,
# user_name: "smtp",
# password: "123456",
# ## If you are using encrypted smtp credentials then you should instead use the secrets user_name/password
# ## See: https://docs.gitlab.com/ee/administration/raketasks/smtp.html#secrets
# # user_name: secrets.username,
# # password: secrets.password,
# domain: "gitlab.company.com",
# authentication: :login,
# enable_starttls_auto: true,

View File

@ -0,0 +1,20 @@
---
key_path: settings.smtp_encrypted_secrets_enabled
description: Is encrypted SMTP secrets configured?
product_section: enablement
product_stage: enablement
product_group: distribution
value_type: boolean
status: implemented
milestone: "14.2"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67802
time_frame: none
data_source: system
data_category: Optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -10,14 +10,6 @@ Please check the ~"product intelligence" [guide](https://docs.gitlab.com/ee/deve
MSG
UPDATE_DICTIONARY_MESSAGE = <<~MSG
When adding, changing, or updating metrics, please update the [Metrics Dictionary](https://docs.gitlab.com/ee/development/usage_ping/dictionary.html)
```shell
bundle exec rake gitlab:usage_data:generate_metrics_dictionary
```
MSG
# exit if not matching files
matching_changed_files = product_intelligence.matching_changed_files
return unless matching_changed_files.any?

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class ResetJobTokenScopeEnabledAgain < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
remove_column :project_ci_cd_settings, :job_token_scope_enabled
add_column :project_ci_cd_settings, :job_token_scope_enabled, :boolean, default: false, null: false
end
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
24c49a12b6624c8e215e8a0c16b1bc9acc1875e68d3727fc3904b9e2eee1d319

View File

@ -12,6 +12,7 @@ type: reference
GitLab can read settings for certain features from encrypted settings files. The supported features are:
- [LDAP `user_bn` and `password`](auth/ldap/index.md#using-encrypted-credentials)
- [SMTP `user_name` and `password`](raketasks/smtp.md#secrets)
In order to enable the encrypted configuration settings, a new base key needs to be generated for
`encrypted_settings_key_base`. The secret can be generated in the following ways:

View File

@ -0,0 +1,103 @@
---
stage: Enablement
group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# SMTP Rake tasks **(FREE SELF)**
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67802) in GitLab 14.2.
The following are SMTP-related Rake tasks.
## Secrets
GitLab can use SMTP configuration secrets to read from an encrypted file. The following Rake tasks are provided for updating the contents of the encrypted file.
### Show secret
Show the contents of the current SMTP secrets.
**Omnibus Installation**
```shell
sudo gitlab-rake gitlab:smtp:secret:show
```
**Source Installation**
```shell
bundle exec rake gitlab:smtp:secret:show RAILS_ENV=production
```
**Example output:**
```plaintext
password: '123'
user_name: 'gitlab-inst'
```
### Edit secret
Opens the secret contents in your editor, and writes the resulting content to the encrypted secret file when you exit.
**Omnibus Installation**
```shell
sudo gitlab-rake gitlab:smtp:secret:edit EDITOR=vim
```
**Source Installation**
```shell
bundle exec rake gitlab:smtp:secret:edit RAILS_ENV=production EDITOR=vim
```
### Write raw secret
Write new secret content by providing it on STDIN.
**Omnibus Installation**
```shell
echo -e "password: '123'" | sudo gitlab-rake gitlab:smtp:secret:write
```
**Source Installation**
```shell
echo -e "password: '123'" | bundle exec rake gitlab:smtp:secret:write RAILS_ENV=production
```
### Secrets examples
**Editor example**
The write task can be used in cases where the edit command does not work with your editor:
```shell
# Write the existing secret to a plaintext file
sudo gitlab-rake gitlab:smtp:secret:show > smtp.yaml
# Edit the smtp file in your editor
...
# Re-encrypt the file
cat smtp.yaml | sudo gitlab-rake gitlab:smtp:secret:write
# Remove the plaintext file
rm smtp.yaml
```
**KMS integration example**
It can also be used as a receiving application for content encrypted with a KMS:
```shell
gcloud kms decrypt --key my-key --keyring my-test-kms --plaintext-file=- --ciphertext-file=my-file --location=us-west1 | sudo gitlab-rake gitlab:smtp:secret:write
```
**Google Cloud secret integration example**
It can also be used as a receiving application for secrets out of Google Cloud:
```shell
gcloud secrets versions access latest --secret="my-test-secret" > $1 | sudo gitlab-rake gitlab:smtp:secret:write
```

View File

@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
<!---
This documentation is auto generated by a script.
Please do not edit this file directly, check generate_metrics_dictionary task on lib/tasks/gitlab/usage_data.rake.
Please do not edit this file directly, check generate_event_dictionary task on lib/tasks/gitlab/snowplow.rake.
--->
<!-- vale gitlab.Spelling = NO -->

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,7 @@ The following Rake tasks are available for use with GitLab:
| [Project import/export](../administration/raketasks/project_import_export.md) | Prepare for [project exports and imports](../user/project/settings/import_export.md). |
| [Sample Prometheus data](generate_sample_prometheus_data.md) | Generate sample Prometheus data. |
| [Sidekiq job migration](sidekiq_job_migration.md) | Migrate Sidekiq jobs scheduled for future dates to a new queue. |
| [SMTP maintenance](../administration/raketasks/smtp.md) | SMTP-related tasks. |
| [SPDX license list import](spdx.md) | Import a local copy of the [SPDX license list](https://spdx.org/licenses/) for matching [License Compliance policies](../user/compliance/license_compliance/index.md). |
| [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. |
| [Uploads migrate](../administration/raketasks/uploads/migrate.md) | Migrate uploads between local storage and object storage. |

View File

@ -14,6 +14,11 @@ enable you to store the state file in a remote, shared store. GitLab uses the
to securely store the state files in local storage (the default) or
[the remote store of your choice](../../administration/terraform_state.md).
WARNING:
Using local storage (the default) on clustered deployments of GitLab will result in
a split state across nodes, making subsequent executions of Terraform inconsistent.
You are highly advised to use a remote storage in that case.
The GitLab managed Terraform state backend can store your Terraform state easily and
securely, and spares you from setting up additional remote resources like
Amazon S3 or Google Cloud Storage. Its features include:

View File

@ -41,31 +41,31 @@ module Gitlab
def select_all(arel, name = nil, binds = [], preparable: nil)
if arel.respond_to?(:locked) && arel.locked
# SELECT ... FOR UPDATE queries should be sent to the primary.
write_using_load_balancer(:select_all, [arel, name, binds],
write_using_load_balancer(:select_all, arel, name, binds,
sticky: true)
else
read_using_load_balancer(:select_all, [arel, name, binds])
read_using_load_balancer(:select_all, arel, name, binds)
end
end
NON_STICKY_READS.each do |name|
define_method(name) do |*args, &block|
read_using_load_balancer(name, args, &block)
define_method(name) do |*args, **kwargs, &block|
read_using_load_balancer(name, *args, **kwargs, &block)
end
end
STICKY_WRITES.each do |name|
define_method(name) do |*args, &block|
write_using_load_balancer(name, args, sticky: true, &block)
define_method(name) do |*args, **kwargs, &block|
write_using_load_balancer(name, *args, sticky: true, **kwargs, &block)
end
end
def transaction(*args, &block)
def transaction(*args, **kwargs, &block)
if current_session.fallback_to_replicas_for_ambiguous_queries?
track_read_only_transaction!
read_using_load_balancer(:transaction, args, &block)
read_using_load_balancer(:transaction, *args, **kwargs, &block)
else
write_using_load_balancer(:transaction, args, sticky: true, &block)
write_using_load_balancer(:transaction, *args, sticky: true, **kwargs, &block)
end
ensure
@ -73,26 +73,26 @@ module Gitlab
end
# Delegates all unknown messages to a read-write connection.
def method_missing(name, *args, &block)
def method_missing(...)
if current_session.fallback_to_replicas_for_ambiguous_queries?
read_using_load_balancer(name, args, &block)
read_using_load_balancer(...)
else
write_using_load_balancer(name, args, &block)
write_using_load_balancer(...)
end
end
# Performs a read using the load balancer.
#
# name - The name of the method to call on a connection object.
def read_using_load_balancer(name, args, &block)
def read_using_load_balancer(...)
if current_session.use_primary? &&
!current_session.use_replicas_for_read_queries?
@load_balancer.read_write do |connection|
connection.send(name, *args, &block)
connection.send(...)
end
else
@load_balancer.read do |connection|
connection.send(name, *args, &block)
connection.send(...)
end
end
end
@ -102,7 +102,7 @@ module Gitlab
# name - The name of the method to call on a connection object.
# sticky - If set to true the session will stick to the master after
# the write.
def write_using_load_balancer(name, args, sticky: false, &block)
def write_using_load_balancer(name, *args, sticky: false, **kwargs, &block)
if read_only_transaction?
raise WriteInsideReadOnlyTransactionError, 'A write query is performed inside a read-only transaction'
end
@ -113,7 +113,7 @@ module Gitlab
# secondary instead of on a primary (when necessary).
current_session.write! if sticky
connection.send(name, *args, &block)
connection.send(name, *args, **kwargs, &block)
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Gitlab
module Email
class SmtpConfig
def self.encrypted_secrets
Settings.encrypted(Gitlab.config.gitlab.email_smtp_secret_file)
end
def self.secrets
self.new
end
def initialize
@secrets ||= self.class.encrypted_secrets.config
rescue StandardError => e
Gitlab::AppLogger.error "SMTP encrypted secrets are invalid: #{e.inspect}"
end
def username
@secrets&.fetch(:user_name, nil)&.chomp
end
def password
@secrets&.fetch(:password, nil)&.chomp
end
end
end
end

View File

@ -0,0 +1,105 @@
# frozen_string_literal: true
# rubocop:disable Rails/Output
module Gitlab
class EncryptedCommandBase
DISPLAY_NAME = "Base"
EDIT_COMMAND_NAME = "base"
class << self
def encrypted_secrets
raise NotImplementedError
end
def write(contents)
encrypted = encrypted_secrets
return unless validate_config(encrypted)
validate_contents(contents)
encrypted.write(contents)
puts "File encrypted and saved."
rescue Interrupt
warn "Aborted changing file: nothing saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
warn "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
end
def edit
encrypted = encrypted_secrets
return unless validate_config(encrypted)
if ENV["EDITOR"].blank?
warn 'No $EDITOR specified to open file. Please provide one when running the command:'
warn "gitlab-rake #{self::EDIT_COMMAND_NAME} EDITOR=vim"
return
end
temp_file = Tempfile.new(File.basename(encrypted.content_path), File.dirname(encrypted.content_path))
contents_changed = false
encrypted.change do |contents|
contents = encrypted_file_template unless File.exist?(encrypted.content_path)
File.write(temp_file.path, contents)
system(ENV['EDITOR'], temp_file.path)
changes = File.read(temp_file.path)
contents_changed = contents != changes
validate_contents(changes)
changes
end
puts "Contents were unchanged." unless contents_changed
puts "File encrypted and saved."
rescue Interrupt
warn "Aborted changing file: nothing saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
warn "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
ensure
temp_file&.unlink
end
def show
encrypted = encrypted_secrets
return unless validate_config(encrypted)
puts encrypted.read.presence || "File '#{encrypted.content_path}' does not exist. Use `gitlab-rake #{self::EDIT_COMMAND_NAME}` to change that."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
warn "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
end
def validate_config(encrypted)
dir_path = File.dirname(encrypted.content_path)
unless File.exist?(dir_path)
warn "Directory #{dir_path} does not exist. Create the directory and try again."
return false
end
if encrypted.key.nil?
warn "Missing encryption key encrypted_settings_key_base."
return false
end
true
end
def validate_contents(contents)
begin
config = YAML.safe_load(contents, permitted_classes: [Symbol])
error_contents = "Did not include any key-value pairs" unless config.is_a?(Hash)
rescue Psych::Exception => e
error_contents = e.message
end
puts "WARNING: Content was not a valid #{self::DISPLAY_NAME} secret yml file. #{error_contents}" if error_contents
contents
end
def encrypted_file_template
raise NotImplementedError
end
end
end
end
# rubocop:enable Rails/Output

View File

@ -2,93 +2,13 @@
# rubocop:disable Rails/Output
module Gitlab
class EncryptedLdapCommand
class EncryptedLdapCommand < EncryptedCommandBase
DISPLAY_NAME = "LDAP"
EDIT_COMMAND_NAME = "gitlab:ldap:secret:edit"
class << self
def write(contents)
encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
return unless validate_config(encrypted)
validate_contents(contents)
encrypted.write(contents)
puts "File encrypted and saved."
rescue Interrupt
puts "Aborted changing file: nothing saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
end
def edit
encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
return unless validate_config(encrypted)
if ENV["EDITOR"].blank?
puts 'No $EDITOR specified to open file. Please provide one when running the command:'
puts 'gitlab-rake gitlab:ldap:secret:edit EDITOR=vim'
return
end
temp_file = Tempfile.new(File.basename(encrypted.content_path), File.dirname(encrypted.content_path))
contents_changed = false
encrypted.change do |contents|
contents = encrypted_file_template unless File.exist?(encrypted.content_path)
File.write(temp_file.path, contents)
system(ENV['EDITOR'], temp_file.path)
changes = File.read(temp_file.path)
contents_changed = contents != changes
validate_contents(changes)
changes
end
puts "Contents were unchanged." unless contents_changed
puts "File encrypted and saved."
rescue Interrupt
puts "Aborted changing file: nothing saved."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
ensure
temp_file&.unlink
end
def show
encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
return unless validate_config(encrypted)
puts encrypted.read.presence || "File '#{encrypted.content_path}' does not exist. Use `gitlab-rake gitlab:ldap:secret:edit` to change that."
rescue ActiveSupport::MessageEncryptor::InvalidMessage
puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
end
private
def validate_config(encrypted)
dir_path = File.dirname(encrypted.content_path)
unless File.exist?(dir_path)
puts "Directory #{dir_path} does not exist. Create the directory and try again."
return false
end
if encrypted.key.nil?
puts "Missing encryption key encrypted_settings_key_base."
return false
end
true
end
def validate_contents(contents)
begin
config = YAML.safe_load(contents, permitted_classes: [Symbol])
error_contents = "Did not include any key-value pairs" unless config.is_a?(Hash)
rescue Psych::Exception => e
error_contents = e.message
end
puts "WARNING: Content was not a valid LDAP secret yml file. #{error_contents}" if error_contents
contents
def encrypted_secrets
Gitlab::Auth::Ldap::Config.encrypted_secrets
end
def encrypted_file_template

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
# rubocop:disable Rails/Output
module Gitlab
class EncryptedSmtpCommand < EncryptedCommandBase
DISPLAY_NAME = "SMTP"
EDIT_COMMAND_NAME = "gitlab:smtp:secret:edit"
class << self
def encrypted_secrets
Gitlab::Email::SmtpConfig.encrypted_secrets
end
def encrypted_file_template
<<~YAML
# password: '123'
# user_name: 'gitlab-inst'
YAML
end
end
end
end
# rubocop:enable Rails/Output

View File

@ -16,7 +16,7 @@ module Gitlab
<!---
This documentation is auto generated by a script.
Please do not edit this file directly, check generate_metrics_dictionary task on lib/tasks/gitlab/usage_data.rake.
Please do not edit this file directly, check generate_event_dictionary task on lib/tasks/gitlab/snowplow.rake.
--->
<!-- vale gitlab.Spelling = NO -->

View File

@ -1,64 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Docs
# Helper with functions to be used by HAML templates
module Helper
def auto_generated_comment
<<-MARKDOWN.strip_heredoc
---
stage: Growth
group: Product Intelligence
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
<!---
This documentation is auto generated by a script.
Please do not edit this file directly, check generate_metrics_dictionary task on lib/tasks/gitlab/usage_data.rake.
--->
MARKDOWN
end
def render_name(name)
"### `#{name}`"
end
def render_description(object)
return 'Missing description' unless object[:description].present?
object[:description]
end
def render_object_schema(object)
"[Object JSON schema](#{object.json_schema_path})"
end
def render_yaml_link(yaml_path)
"[YAML definition](#{yaml_path})"
end
def render_status(object)
"Status: #{format(:status, object[:status])}"
end
def render_owner(object)
"Group: `#{object[:product_group]}`"
end
def render_tiers(object)
"Tiers:#{format(:tier, object[:tier])}"
end
def render_data_category(object)
"Data Category: `#{object[:data_category]}`"
end
def format(key, value)
Gitlab::Usage::Docs::ValueFormatter.format(key, value)
end
end
end
end
end

View File

@ -1,32 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Docs
class Renderer
include Gitlab::Usage::Docs::Helper
DICTIONARY_PATH = Rails.root.join('doc', 'development', 'service_ping')
TEMPLATE_PATH = Rails.root.join('lib', 'gitlab', 'usage', 'docs', 'templates', 'default.md.haml')
def initialize(metrics_definitions)
@layout = Haml::Engine.new(File.read(TEMPLATE_PATH))
@metrics_definitions = metrics_definitions.sort
end
def contents
# Render and remove an extra trailing new line
@contents ||= @layout.render(self, metrics_definitions: @metrics_definitions).sub!(/\n(?=\Z)/, '')
end
def write
filename = DICTIONARY_PATH.join('dictionary.md').to_s
FileUtils.mkdir_p(DICTIONARY_PATH)
File.write(filename, contents)
filename
end
end
end
end
end

View File

@ -1,48 +0,0 @@
= auto_generated_comment
:plain
# Metrics Dictionary
This file is autogenerated, please do not edit directly.
To generate these files from the GitLab repository, run:
```shell
bundle exec rake gitlab:usage_data:generate_metrics_dictionary
```
The Metrics Dictionary is based on the following metrics definition YAML files:
- [`config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/config/metrics)
- [`ee/config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/config/metrics)
Each table includes a `milestone`, which corresponds to the GitLab version when the metric
was released.
<!-- vale off -->
<!-- Docs linting disabled after this line. -->
<!-- See https://docs.gitlab.com/ee/development/documentation/testing.html#disable-vale-tests -->
## Metrics Definitions
\
- metrics_definitions.each do |name, object|
= render_name(name)
\
= render_description(object.attributes)
- if object.has_json_schema?
\
= render_object_schema(object)
\
= render_yaml_link(object.yaml_path)
\
= render_owner(object.attributes)
- if object.attributes[:data_category].present?
\
= render_data_category(object.attributes)
\
= render_status(object.attributes)
\
= render_tiers(object.attributes)
\

View File

@ -1,28 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Usage
module Docs
class ValueFormatter
def self.format(key, value)
return '' unless value.present?
case key
when :key_path
"**`#{value}`**"
when :data_source
value.to_s.capitalize
when :product_group, :product_category, :status
"`#{value}`"
when :introduced_by_url
"[Introduced by](#{value})"
when :distribution, :tier
Array(value).map { |tier| " `#{tier}`" }.join(',')
else
value
end
end
end
end
end
end

View File

@ -256,6 +256,7 @@ module Gitlab
{
settings: {
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
smtp_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Email::SmtpConfig.encrypted_secrets.active? },
operating_system: alt_usage_data(fallback: nil) { operating_system },
gitaly_apdex: alt_usage_data { gitaly_apdex },
collected_data_categories: add_metric('CollectedDataCategoriesMetric', time_frame: 'none')

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
namespace :gitlab do
namespace :smtp do
namespace :secret do
desc 'GitLab | SMTP | Secret | Write SMTP secrets'
task write: [:environment] do
content = $stdin.tty? ? $stdin.gets : $stdin.read
Gitlab::EncryptedSmtpCommand.write(content)
end
desc 'GitLab | SMTP | Secret | Edit SMTP secrets'
task edit: [:environment] do
Gitlab::EncryptedSmtpCommand.edit
end
desc 'GitLab | SMTP | Secret | Show SMTP secrets'
task show: [:environment] do
Gitlab::EncryptedSmtpCommand.show
end
end
end
end

View File

@ -522,6 +522,9 @@ msgstr[1] ""
msgid "%{count} related %{pluralized_subject}: %{links}"
msgstr ""
msgid "%{count} selected"
msgstr ""
msgid "%{count} total weight"
msgstr ""
@ -5422,9 +5425,6 @@ msgstr ""
msgid "BoardScope|Milestone"
msgstr ""
msgid "BoardScope|No matching results"
msgstr ""
msgid "BoardScope|No milestone"
msgstr ""
@ -5813,30 +5813,24 @@ msgstr ""
msgid "BulkImport|From source group"
msgstr ""
msgid "BulkImport|Import %{groups}"
msgstr ""
msgid "BulkImport|Import failed: Destination cannot be a subgroup of the source group. Change the destination and try again."
msgstr ""
msgid "BulkImport|Import groups from GitLab"
msgstr ""
msgid "BulkImport|Import selected"
msgstr ""
msgid "BulkImport|Importing the group failed"
msgstr ""
msgid "BulkImport|Name already exists."
msgstr ""
msgid "BulkImport|No groups on this page are available for import"
msgstr ""
msgid "BulkImport|No parent"
msgstr ""
msgid "BulkImport|One or more groups has validation errors"
msgstr ""
msgid "BulkImport|Showing %{start}-%{end} of %{total}"
msgstr ""

View File

@ -13,6 +13,10 @@ module QA
element :user_role_content
end
view 'app/views/dashboard/_projects_head.html.haml' do
element :new_project_button
end
def has_project_with_access_role?(project_name, access_role)
within_element(:project_content, text: project_name) do
has_element?(:user_role_content, text: access_role)
@ -25,6 +29,10 @@ module QA
find_link(text: name).click
end
def click_new_project_button
click_element(:new_project_button, Page::Project::New)
end
def self.path
'/'
end

View File

@ -20,7 +20,7 @@ module QA
:name,
:add_name_uuid,
:description,
:standalone,
:personal_namespace,
:runners_token,
:visibility,
:template_name,
@ -52,7 +52,7 @@ module QA
def initialize
@add_name_uuid = true
@standalone = false
@personal_namespace = false
@description = 'My awesome project'
@initialize_with_readme = false
@auto_devops_enabled = false
@ -70,7 +70,9 @@ module QA
def fabricate!
return if @import
unless @standalone
if @personal_namespace
Page::Dashboard::Projects.perform(&:click_new_project_button)
else
group.visit!
Page::Group::Show.perform(&:go_to_new_project)
end
@ -85,13 +87,15 @@ module QA
Page::Project::New.perform(&:click_blank_project_link)
Page::Project::New.perform do |new_page|
new_page.choose_test_namespace
new_page.choose_test_namespace unless @personal_namespace
new_page.choose_name(@name)
new_page.add_description(@description)
new_page.set_visibility(@visibility)
new_page.disable_initialize_with_readme unless @initialize_with_readme
new_page.create_new_project
end
@id = Page::Project::Show.perform(&:project_id)
end
def fabricate_via_api!
@ -219,7 +223,7 @@ module QA
auto_devops_enabled: @auto_devops_enabled
}
unless @standalone
unless @personal_namespace
post_body[:namespace_id] = group.id
post_body[:path] = name
end

View File

@ -47,7 +47,7 @@ module QA
def create_project(user, api_client, project_name)
project = Resource::Project.fabricate_via_api! do |project|
project.standalone = true
project.personal_namespace = true
project.add_name_uuid = false
project.name = project_name
project.path_with_namespace = "#{user.username}/#{project_name}"

View File

@ -2,24 +2,52 @@
module QA
RSpec.describe 'Manage', :smoke do
describe 'Project creation' do
it 'user creates a new project',
testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1857' do
describe 'Project' do
shared_examples 'successful project creation' do
it 'creates a new project' do
Page::Project::Show.perform do |project|
expect(project).to have_content(project_name)
expect(project).to have_content(
/Project \S?#{project_name}\S+ was successfully created/
)
expect(project).to have_content('create awesome project test')
expect(project).to have_content('The repository for this project is empty')
end
end
end
before do
Flow::Login.sign_in
project
end
created_project = Resource::Project.fabricate_via_browser_ui! do |project|
project.name = 'awesome-project'
project.description = 'create awesome project test'
context 'in group', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1857' do
let(:project_name) { "project-in-group-#{SecureRandom.hex(8)}" }
let(:project) do
Resource::Project.fabricate_via_browser_ui! do |project|
project.name = project_name
project.description = 'create awesome project test'
end
end
Page::Project::Show.perform do |project|
expect(project).to have_content(created_project.name)
expect(project).to have_content(
/Project \S?awesome-project\S+ was successfully created/
)
expect(project).to have_content('create awesome project test')
expect(project).to have_content('The repository for this project is empty')
it_behaves_like 'successful project creation'
end
context 'in personal namespace', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1888' do
let(:project_name) { "project-in-personal-namespace-#{SecureRandom.hex(8)}" }
let(:project) do
Resource::Project.fabricate_via_browser_ui! do |project|
project.name = project_name
project.description = 'create awesome project test'
project.personal_namespace = true
end
end
it_behaves_like 'successful project creation'
end
after do
project.remove_via_api!
end
end
end

View File

@ -57,37 +57,44 @@ RSpec.describe ApplicationExperiment, :experiment do
end
describe "#publish" do
it "doesn't track or publish to the client or database if we can't track", :snowplow do
allow(subject).to receive(:should_track?).and_return(false)
let(:should_track) { true }
expect(subject).not_to receive(:publish_to_client)
expect(subject).not_to receive(:publish_to_database)
subject.publish
expect_no_snowplow_event
before do
allow(subject).to receive(:should_track?).and_return(should_track)
end
it "tracks the assignment" do
expect(subject).to receive(:track).with(:assignment)
it "tracks the assignment", :snowplow do
subject.publish
expect_snowplow_event(
category: 'namespaced/stub',
action: 'assignment',
context: [{ schema: anything, data: anything }]
)
end
it "publishes the to the client" do
it "publishes to the client" do
expect(subject).to receive(:publish_to_client)
subject.publish
end
it "publishes to the database if we've opted for that" do
subject.record!
expect(subject).to receive(:publish_to_database)
subject.publish
end
context 'when we should not track' do
let(:should_track) { false }
it 'does not track an event to Snowplow', :snowplow do
subject.publish
expect_no_snowplow_event
end
end
describe "#publish_to_client" do
it "adds the data into Gon" do
signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' }
@ -101,50 +108,79 @@ RSpec.describe ApplicationExperiment, :experiment do
expect { subject.publish_to_client }.not_to raise_error
end
context 'when we should not track' do
let(:should_track) { false }
it 'returns early' do
expect(Gon).not_to receive(:push)
subject.publish_to_client
end
end
end
describe "#publish_to_database" do
using RSpec::Parameterized::TableSyntax
let(:context) { { context_key => context_value }}
before do
subject.record!
end
context "when there's a usable subject" do
where(:context_key, :context_value, :object_type) do
:namespace | build(:namespace) | :namespace
:group | build(:namespace) | :namespace
:project | build(:project) | :project
:user | build(:user) | :user
:actor | build(:user) | :user
describe '#publish_to_database' do
shared_examples 'does not record to the database' do
it 'does not create an experiment record' do
expect { subject.publish_to_database }.not_to change(Experiment, :count)
end
with_them do
it "creates an experiment and experiment subject record" do
expect { subject.publish_to_database }.to change(Experiment, :count).by(1)
expect(Experiment.last.name).to eq('namespaced/stub')
expect(ExperimentSubject.last.send(object_type)).to eq(context[context_key])
end
it 'does not create an experiment subject record' do
expect { subject.publish_to_database }.not_to change(ExperimentSubject, :count)
end
end
context "when there's not a usable subject" do
where(:context_key, :context_value) do
:namespace | nil
:foo | :bar
context 'when we explicitly request to record' do
using RSpec::Parameterized::TableSyntax
before do
subject.record!
end
with_them do
it "doesn't create an experiment record" do
expect { subject.publish_to_database }.not_to change(Experiment, :count)
context 'when there is a usable subject' do
let(:context) { { context_key => context_value } }
where(:context_key, :context_value, :object_type) do
:namespace | build(:namespace) | :namespace
:group | build(:namespace) | :namespace
:project | build(:project) | :project
:user | build(:user) | :user
:actor | build(:user) | :user
end
it "doesn't create an experiment subject record" do
expect { subject.publish_to_database }.not_to change(ExperimentSubject, :count)
with_them do
it 'creates an experiment and experiment subject record' do
expect { subject.publish_to_database }.to change(Experiment, :count).by(1)
expect(Experiment.last.name).to eq('namespaced/stub')
expect(ExperimentSubject.last.send(object_type)).to eq(context[context_key])
end
end
end
context 'when there is not a usable subject' do
let(:context) { { context_key => context_value } }
where(:context_key, :context_value) do
:namespace | nil
:foo | :bar
end
with_them do
include_examples 'does not record to the database'
end
end
context 'but we should not track' do
let(:should_track) { false }
include_examples 'does not record to the database'
end
end
context 'when we have not explicitly requested to record' do
include_examples 'does not record to the database'
end
end
end

View File

@ -17,13 +17,31 @@ exports[`Design management design version dropdown component renders design vers
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckcentered="true"
ischecked="true"
ischeckitem="true"
secondarytext=""
>
Version
2
(latest)
<strong>
Version
2
(latest)
</strong>
<div
class="gl-text-gray-600 gl-mt-1"
>
<div>
Adminstrator
</div>
<time-ago-stub
class="text-1"
cssclass=""
time="2021-08-09T06:05:00Z"
tooltipplacement="bottom"
/>
</div>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
@ -31,12 +49,30 @@ exports[`Design management design version dropdown component renders design vers
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckcentered="true"
ischeckitem="true"
secondarytext=""
>
Version
1
<strong>
Version
1
</strong>
<div
class="gl-text-gray-600 gl-mt-1"
>
<div>
Adminstrator
</div>
<time-ago-stub
class="text-1"
cssclass=""
time="2021-08-09T06:05:00Z"
tooltipplacement="bottom"
/>
</div>
</gl-dropdown-item-stub>
</gl-dropdown-stub>
`;
@ -58,13 +94,31 @@ exports[`Design management design version dropdown component renders design vers
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckcentered="true"
ischecked="true"
ischeckitem="true"
secondarytext=""
>
Version
2
(latest)
<strong>
Version
2
(latest)
</strong>
<div
class="gl-text-gray-600 gl-mt-1"
>
<div>
Adminstrator
</div>
<time-ago-stub
class="text-1"
cssclass=""
time="2021-08-09T06:05:00Z"
tooltipplacement="bottom"
/>
</div>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
@ -72,12 +126,30 @@ exports[`Design management design version dropdown component renders design vers
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckcentered="true"
ischeckitem="true"
secondarytext=""
>
Version
1
<strong>
Version
1
</strong>
<div
class="gl-text-gray-600 gl-mt-1"
>
<div>
Adminstrator
</div>
<time-ago-stub
class="text-1"
cssclass=""
time="2021-08-09T06:05:00Z"
tooltipplacement="bottom"
/>
</div>
</gl-dropdown-item-stub>
</gl-dropdown-stub>
`;

View File

@ -1,9 +1,10 @@
import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DesignVersionDropdown from '~/design_management/components/upload/design_version_dropdown.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import mockAllVersions from './mock_data/all_versions';
const LATEST_VERSION_ID = 3;
const LATEST_VERSION_ID = 1;
const PREVIOUS_VERSION_ID = 2;
const designRouteFactory = (versionId) => ({
@ -110,5 +111,13 @@ describe('Design management design version dropdown component', () => {
expect(wrapper.findAll(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
});
});
it('should render TimeAgo', async () => {
createComponent();
await wrapper.vm.$nextTick();
expect(wrapper.findAllComponents(TimeAgo)).toHaveLength(wrapper.vm.allVersions.length);
});
});
});

View File

@ -1,10 +1,20 @@
export default [
{
id: 'gid://gitlab/DesignManagement::Version/3',
sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55',
id: 'gid://gitlab/DesignManagement::Version/1',
sha: 'b389071a06c153509e11da1f582005b316667001',
createdAt: '2021-08-09T06:05:00Z',
author: {
id: 'gid://gitlab/User/1',
name: 'Adminstrator',
},
},
{
id: 'gid://gitlab/DesignManagement::Version/2',
sha: '5b063fef0cd7213b312db65b30e24f057df21b20',
sha: 'b389071a06c153509e11da1f582005b316667021',
createdAt: '2021-08-09T06:05:00Z',
author: {
id: 'gid://gitlab/User/1',
name: 'Adminstrator',
},
},
];

View File

@ -2,5 +2,19 @@ export default [
{
id: 'gid://gitlab/DesignManagement::Version/1',
sha: 'b389071a06c153509e11da1f582005b316667001',
createdAt: '2021-08-09T06:05:00Z',
author: {
id: 'gid://gitlab/User/1',
name: 'Adminstrator',
},
},
{
id: 'gid://gitlab/DesignManagement::Version/1',
sha: 'b389071a06c153509e11da1f582005b316667021',
createdAt: '2021-08-09T06:05:00Z',
author: {
id: 'gid://gitlab/User/1',
name: 'Adminstrator',
},
},
];

View File

@ -5,8 +5,10 @@ import {
GlSearchBoxByClick,
GlDropdown,
GlDropdownItem,
GlTable,
} from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import stubChildren from 'helpers/stub_children';
@ -40,10 +42,15 @@ describe('import table', () => {
];
const FAKE_PAGE_INFO = { page: 1, perPage: 20, total: 40, totalPages: 2 };
const findImportAllButton = () => wrapper.find('h1').find(GlButton);
const findImportSelectedButton = () =>
wrapper.findAllComponents(GlButton).wrappers.find((w) => w.text() === 'Import selected');
const findPaginationDropdown = () => wrapper.findComponent(GlDropdown);
const findPaginationDropdownText = () => findPaginationDropdown().find({ ref: 'text' }).text();
// TODO: remove this ugly approach when
// issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531
const findTable = () => wrapper.vm.getTableRef();
const createComponent = ({ bulkImportSourceGroups }) => {
apolloProvider = createMockApollo([], {
Query: {
@ -294,16 +301,8 @@ describe('import table', () => {
});
});
describe('import all button', () => {
it('does not exists when no groups available', () => {
createComponent({
bulkImportSourceGroups: () => new Promise(() => {}),
});
expect(findImportAllButton().exists()).toBe(false);
});
it('exists when groups are available for import', async () => {
describe('bulk operations', () => {
it('import selected button is disabled when no groups selected', async () => {
createComponent({
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
@ -312,10 +311,66 @@ describe('import table', () => {
});
await waitForPromises();
expect(findImportAllButton().exists()).toBe(true);
expect(findImportSelectedButton().props().disabled).toBe(true);
});
it('counts only not-imported groups', async () => {
it('import selected button is enabled when groups were selected for import', async () => {
createComponent({
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
}),
});
await waitForPromises();
wrapper.find(GlTable).vm.$emit('row-selected', [FAKE_GROUPS[0]]);
await nextTick();
expect(findImportSelectedButton().props().disabled).toBe(false);
});
it('does not allow selecting already started groups', async () => {
const NEW_GROUPS = [generateFakeEntry({ id: 1, status: STATUSES.FINISHED })];
createComponent({
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
}),
});
await waitForPromises();
findTable().selectRow(0);
await nextTick();
expect(findImportSelectedButton().props().disabled).toBe(true);
});
it('does not allow selecting groups with validation errors', async () => {
const NEW_GROUPS = [
generateFakeEntry({
id: 2,
status: STATUSES.NONE,
validation_errors: [{ field: 'new_name', message: 'FAKE_VALIDATION_ERROR' }],
}),
];
createComponent({
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
}),
});
await waitForPromises();
// TODO: remove this ugly approach when
// issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531
findTable().selectRow(0);
await nextTick();
expect(findImportSelectedButton().props().disabled).toBe(true);
});
it('invokes importGroups mutation when import selected button is clicked', async () => {
const NEW_GROUPS = [
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
generateFakeEntry({ id: 2, status: STATUSES.NONE }),
@ -328,31 +383,19 @@ describe('import table', () => {
pageInfo: FAKE_PAGE_INFO,
}),
});
jest.spyOn(apolloProvider.defaultClient, 'mutate');
await waitForPromises();
expect(findImportAllButton().text()).toMatchInterpolatedText('Import 2 groups');
});
findTable().selectRow(0);
findTable().selectRow(1);
await nextTick();
it('disables button when any group has validation errors', async () => {
const NEW_GROUPS = [
generateFakeEntry({ id: 1, status: STATUSES.NONE }),
generateFakeEntry({
id: 2,
status: STATUSES.NONE,
validation_errors: [{ field: 'new_name', message: 'test validation error' }],
}),
generateFakeEntry({ id: 3, status: STATUSES.FINISHED }),
];
findImportSelectedButton().vm.$emit('click');
createComponent({
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
}),
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
mutation: importGroupsMutation,
variables: { sourceGroupIds: [NEW_GROUPS[0].id, NEW_GROUPS[1].id] },
});
await waitForPromises();
expect(findImportAllButton().props().disabled).toBe(true);
});
});
});

View File

@ -13,7 +13,6 @@ describe('DropdownWidget component', () => {
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(DropdownWidget, {
propsData: {
...props,
options: [
{
id: '1',
@ -24,6 +23,7 @@ describe('DropdownWidget component', () => {
title: 'Option 2',
},
],
...props,
},
stubs: {
GlDropdown,
@ -76,4 +76,22 @@ describe('DropdownWidget component', () => {
expect(wrapper.emitted('set-option')).toEqual([[wrapper.props().options[1]]]);
});
});
describe('when options are users', () => {
const mockUser = {
id: 1,
name: 'User name',
username: 'username',
avatarUrl: 'foo/bar',
};
beforeEach(() => {
createComponent({ props: { options: [mockUser] } });
});
it('passes user related props to dropdown item', () => {
expect(findDropdownItems().at(0).props('avatarUrl')).toBe(mockUser.avatarUrl);
expect(findDropdownItems().at(0).props('secondaryText')).toBe(mockUser.username);
});
});
});

View File

@ -7,7 +7,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
describe '#select' do
it 'performs a read' do
expect(proxy).to receive(:read_using_load_balancer).with(:select, ['foo'])
expect(proxy).to receive(:read_using_load_balancer).with(:select, 'foo')
proxy.select('foo')
end
@ -26,7 +26,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
arel = double(:arel)
expect(proxy).to receive(:read_using_load_balancer)
.with(:select_all, [arel, 'foo', []])
.with(:select_all, arel, 'foo', [])
proxy.select_all(arel, 'foo')
end
@ -37,7 +37,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
arel = double(:arel, locked: true)
expect(proxy).to receive(:write_using_load_balancer)
.with(:select_all, [arel, 'foo', []], sticky: true)
.with(:select_all, arel, 'foo', [], sticky: true)
proxy.select_all(arel, 'foo')
end
@ -48,7 +48,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
describe "#{name}" do
it 'runs the query on the replica' do
expect(proxy).to receive(:read_using_load_balancer)
.with(name, ['foo'])
.with(name, 'foo')
proxy.send(name, 'foo')
end
@ -59,7 +59,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
describe "#{name}" do
it 'runs the query on the primary and sticks to it' do
expect(proxy).to receive(:write_using_load_balancer)
.with(name, ['foo'], sticky: true)
.with(name, 'foo', sticky: true)
proxy.send(name, 'foo')
end
@ -187,7 +187,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
describe '#method_missing' do
it 'runs the query on the primary without sticking to it' do
expect(proxy).to receive(:write_using_load_balancer)
.with(:foo, ['foo'])
.with(:foo, 'foo')
proxy.foo('foo')
end
@ -197,7 +197,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(proxy).to receive(:write_using_load_balancer).and_call_original
expect { proxy.case_sensitive_comparison(:table, :attribute, :column, { value: :value, format: :format }) }
expect { proxy.case_sensitive_comparison(:table, :attribute, :column, value: :value, format: :format) }
.not_to raise_error
end
@ -212,7 +212,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
end
it 'runs the query on the replica' do
expect(proxy).to receive(:read_using_load_balancer).with(:foo, ['foo'])
expect(proxy).to receive(:read_using_load_balancer).with(:foo, 'foo')
proxy.foo('foo')
end
@ -222,7 +222,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(proxy).to receive(:read_using_load_balancer).and_call_original
expect { proxy.case_sensitive_comparison(:table, :attribute, :column, { value: :value, format: :format }) }
expect { proxy.case_sensitive_comparison(:table, :attribute, :column, value: :value, format: :format) }
.not_to raise_error
end
end
@ -245,7 +245,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
proxy.read_using_load_balancer(:foo, ['foo'])
proxy.read_using_load_balancer(:foo, 'foo')
end
end
@ -257,7 +257,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
proxy.read_using_load_balancer(:foo, ['foo'])
proxy.read_using_load_balancer(:foo, 'foo')
end
end
@ -269,7 +269,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(connection).to receive(:foo).with('foo')
expect(proxy.load_balancer).to receive(:read).and_yield(connection)
proxy.read_using_load_balancer(:foo, ['foo'])
proxy.read_using_load_balancer(:foo, 'foo')
end
end
@ -283,7 +283,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(proxy.load_balancer).to receive(:read_write)
.and_yield(connection)
proxy.read_using_load_balancer(:foo, ['foo'])
proxy.read_using_load_balancer(:foo, 'foo')
end
end
end
@ -302,7 +302,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(connection).to receive(:foo).with('foo')
expect(session).not_to receive(:write!)
proxy.write_using_load_balancer(:foo, ['foo'])
proxy.write_using_load_balancer(:foo, 'foo')
end
it 'sticks to the primary when sticking is enabled' do
@ -310,7 +310,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ConnectionProxy do
expect(connection).to receive(:foo).with('foo')
expect(session).to receive(:write!)
proxy.write_using_load_balancer(:foo, ['foo'], sticky: true)
proxy.write_using_load_balancer(:foo, 'foo', sticky: true)
end
end
end

View File

@ -1,79 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Docs::Helper do
subject(:helper) { klass.new }
let_it_be(:klass) do
Class.new do
include Gitlab::Usage::Docs::Helper
end
end
let(:metric_definition) do
{
data_category: 'standard',
name: 'test_metric',
description: description,
product_group: 'group::product intelligence',
status: 'data_available',
tier: %w(free premium)
}
end
let(:description) { 'Metric description' }
describe '#render_name' do
it { expect(helper.render_name(metric_definition[:name])).to eq('### `test_metric`') }
end
describe '#render_description' do
context 'without description' do
let(:description) { nil }
it { expect(helper.render_description(metric_definition)).to eq('Missing description') }
end
context 'without description' do
it { expect(helper.render_description(metric_definition)).to eq('Metric description') }
end
end
describe '#render_yaml_link' do
let(:yaml_link) { 'config/metrics/license/test_metric.yml' }
let(:expected) { "[YAML definition](#{yaml_link})" }
it { expect(helper.render_yaml_link(yaml_link)).to eq(expected) }
end
describe '#render_status' do
let(:expected) { "Status: `data_available`" }
it { expect(helper.render_status(metric_definition)).to eq(expected) }
end
describe '#render_owner' do
let(:expected) { "Group: `group::product intelligence`" }
it { expect(helper.render_owner(metric_definition)).to eq(expected) }
end
describe '#render_tiers' do
let(:expected) { "Tiers: `free`, `premium`" }
it { expect(helper.render_tiers(metric_definition)).to eq(expected) }
end
describe '#render_data_category' do
let(:expected) { 'Data Category: `standard`' }
it { expect(helper.render_data_category(metric_definition)).to eq(expected) }
end
describe '#render_owner' do
let(:expected) { "Group: `group::product intelligence`" }
it { expect(helper.render_owner(metric_definition)).to eq(expected) }
end
end

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
CODE_REGEX = %r{<code>(.*)</code>}.freeze
RSpec.describe Gitlab::Usage::Docs::Renderer do
describe 'contents' do
let(:dictionary_path) { Gitlab::Usage::Docs::Renderer::DICTIONARY_PATH }
let(:items) { Gitlab::Usage::MetricDefinition.definitions.first(10).to_h }
it 'generates dictionary for given items' do
generated_dictionary = described_class.new(items).contents
generated_dictionary_keys = RDoc::Markdown
.parse(generated_dictionary)
.table_of_contents
.select { |metric_doc| metric_doc.level == 3 }
.map { |item| item.text.match(CODE_REGEX)&.captures&.first }
expect(generated_dictionary_keys).to match_array(items.keys)
end
end
end

View File

@ -1,26 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Docs::ValueFormatter do
describe '.format' do
using RSpec::Parameterized::TableSyntax
where(:key, :value, :expected_value) do
:product_group | 'growth::product intelligence' | '`growth::product intelligence`'
:data_source | 'redis' | 'Redis'
:data_source | 'ruby' | 'Ruby'
:introduced_by_url | 'http://test.com' | '[Introduced by](http://test.com)'
:tier | %w(gold premium) | ' `gold`, `premium`'
:distribution | %w(ce ee) | ' `ce`, `ee`'
:key_path | 'key.path' | '**`key.path`**'
:milestone | '13.4' | '13.4'
:status | 'data_available' | '`data_available`'
end
with_them do
subject { described_class.format(key, value) }
it { is_expected.to eq(expected_value) }
end
end
end

View File

@ -1067,8 +1067,9 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.system_usage_data_settings }
it 'gathers settings usage data', :aggregate_failures do
it 'gathers encrypted secrets usage data', :aggregate_failures do
expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
expect(subject[:settings][:smtp_encrypted_secrets_enabled]).to eq(Gitlab::Email::SmtpConfig.encrypted_secrets.active?)
end
it 'populates operating system information' do

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ResetJobTokenScopeEnabledAgain do
let(:settings) { table(:project_ci_cd_settings) }
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)}
let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)}
before do
settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true)
settings.create!(id: 2, project_id: project_2.id, job_token_scope_enabled: false)
end
it 'migrates job_token_scope_enabled to be always false' do
expect { migrate! }
.to change { settings.where(job_token_scope_enabled: false).count }
.from(1).to(2)
end
end

View File

@ -38,13 +38,13 @@ RSpec.describe 'gitlab:ldap:secret rake tasks' do
it 'displays error when key does not exist' do
Settings.encrypted(ldap_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when key is changed' do
Settings.encrypted(ldap_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stderr
end
it 'outputs the unencrypted content when present' do
@ -64,18 +64,18 @@ RSpec.describe 'gitlab:ldap:secret rake tasks' do
it 'displays error when key does not exist' do
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when key is changed' do
Settings.encrypted(ldap_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stderr
end
it 'displays error when write directory does not exist' do
FileUtils.rm_rf(Rails.root.join('tmp/tests/ldapenc'))
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Directory .* does not exist./).to_stdout
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Directory .* does not exist./).to_stderr
end
it 'shows a warning when content is invalid' do
@ -87,7 +87,7 @@ RSpec.describe 'gitlab:ldap:secret rake tasks' do
it 'displays error when $EDITOR is not set' do
stub_env('EDITOR', nil)
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/No \$EDITOR specified to open file. Please provide one when running the command/).to_stdout
expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/No \$EDITOR specified to open file. Please provide one when running the command/).to_stderr
end
end
@ -106,12 +106,12 @@ RSpec.describe 'gitlab:ldap:secret rake tasks' do
it 'displays error when key does not exist' do
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when write directory does not exist' do
FileUtils.rm_rf('tmp/tests/ldapenc/')
expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Directory .* does not exist./).to_stdout
expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Directory .* does not exist./).to_stderr
end
it 'shows a warning when content is invalid' do

View File

@ -0,0 +1,112 @@
# frozen_string_literal: true
require 'rake_helper'
RSpec.describe 'gitlab:smtp:secret rake tasks' do
let(:smtp_secret_file) { 'tmp/tests/smtpenc/smtp_secret.yaml.enc' }
before do
Rake.application.rake_require 'tasks/gitlab/smtp'
stub_env('EDITOR', 'cat')
stub_warn_user_is_not_gitlab
FileUtils.mkdir_p('tmp/tests/smtpenc/')
allow(Gitlab.config.gitlab).to receive(:email_smtp_secret_file).and_return(smtp_secret_file)
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
end
after do
FileUtils.rm_rf(Rails.root.join('tmp/tests/smtpenc'))
end
describe ':show' do
it 'displays error when file does not exist' do
expect { run_rake_task('gitlab:smtp:secret:show') }.to output(/File .* does not exist. Use `gitlab-rake gitlab:smtp:secret:edit` to change that./).to_stdout
end
it 'displays error when key does not exist' do
Settings.encrypted(smtp_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:smtp:secret:show') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when key is changed' do
Settings.encrypted(smtp_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
expect { run_rake_task('gitlab:smtp:secret:show') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stderr
end
it 'outputs the unencrypted content when present' do
encrypted = Settings.encrypted(smtp_secret_file)
encrypted.write('somevalue')
expect { run_rake_task('gitlab:smtp:secret:show') }.to output(/somevalue/).to_stdout
end
end
describe 'edit' do
it 'creates encrypted file' do
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/File encrypted and saved./).to_stdout
expect(File.exist?(smtp_secret_file)).to be true
value = Settings.encrypted(smtp_secret_file)
expect(value.read).to match(/password: '123'/)
end
it 'displays error when key does not exist' do
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when key is changed' do
Settings.encrypted(smtp_secret_file).write('somevalue')
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stderr
end
it 'displays error when write directory does not exist' do
FileUtils.rm_rf(Rails.root.join('tmp/tests/smtpenc'))
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/Directory .* does not exist./).to_stderr
end
it 'shows a warning when content is invalid' do
Settings.encrypted(smtp_secret_file).write('somevalue')
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/WARNING: Content was not a valid SMTP secret yml file/).to_stdout
value = Settings.encrypted(smtp_secret_file)
expect(value.read).to match(/somevalue/)
end
it 'displays error when $EDITOR is not set' do
stub_env('EDITOR', nil)
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/No \$EDITOR specified to open file. Please provide one when running the command/).to_stderr
end
end
describe 'write' do
before do
allow($stdin).to receive(:tty?).and_return(false)
allow($stdin).to receive(:read).and_return('username: foo')
end
it 'creates encrypted file from stdin' do
expect { run_rake_task('gitlab:smtp:secret:write') }.to output(/File encrypted and saved./).to_stdout
expect(File.exist?(smtp_secret_file)).to be true
value = Settings.encrypted(smtp_secret_file)
expect(value.read).to match(/username: foo/)
end
it 'displays error when key does not exist' do
allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
expect { run_rake_task('gitlab:smtp:secret:write') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stderr
end
it 'displays error when write directory does not exist' do
FileUtils.rm_rf('tmp/tests/smtpenc/')
expect { run_rake_task('gitlab:smtp:secret:write') }.to output(/Directory .* does not exist./).to_stderr
end
it 'shows a warning when content is invalid' do
Settings.encrypted(smtp_secret_file).write('somevalue')
expect { run_rake_task('gitlab:smtp:secret:edit') }.to output(/WARNING: Content was not a valid SMTP secret yml file/).to_stdout
value = Settings.encrypted(smtp_secret_file)
expect(value.read).to match(/somevalue/)
end
end
end

View File

@ -177,7 +177,6 @@ RSpec.describe Tooling::Danger::ProjectHelper do
'ee/config/metrics/counts_7d/20210216174919_g_analytics_issues_weekly.yml' | [:product_intelligence]
'lib/gitlab/usage_data_counters/aggregated_metrics/common.yml' | [:product_intelligence]
'lib/gitlab/usage_data_counters/hll_redis_counter.rb' | [:backend, :product_intelligence]
'doc/development/usage_ping/dictionary.md' | [:docs, :product_intelligence]
'lib/gitlab/tracking.rb' | [:backend, :product_intelligence]
'spec/lib/gitlab/tracking_spec.rb' | [:backend, :product_intelligence]
'app/helpers/tracking_helper.rb' | [:backend, :product_intelligence]

View File

@ -39,7 +39,6 @@ module Tooling
%r{\A(ee/)?config/feature_flags/} => :feature_flag,
%r{\Adoc/development/usage_ping/dictionary\.md\z} => [:docs, :product_intelligence],
%r{\Adoc/.*(\.(md|png|gif|jpg|yml))\z} => :docs,
%r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
%r{\Adata/whats_new/} => :docs,