Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5f85444a43
commit
421200ab0d
|
@ -293,7 +293,7 @@ export default {
|
|||
</div>
|
||||
|
||||
<gl-modal
|
||||
v-if="canRenderPipelineButton"
|
||||
v-if="canRenderPipelineButton || shouldRenderEmptyState"
|
||||
:id="modalId"
|
||||
ref="modal"
|
||||
:modal-id="modalId"
|
||||
|
|
|
@ -64,3 +64,5 @@ export const EXTENSION_PRIORITY_HIGHEST = 200;
|
|||
*/
|
||||
export const SAFE_VIDEO_EXT = ['mp4', 'm4v', 'mov', 'webm', 'ogv'];
|
||||
export const SAFE_AUDIO_EXT = ['mp3', 'oga', 'ogg', 'spx', 'wav'];
|
||||
|
||||
export const DIAGRAM_LANGUAGES = ['plantuml', 'mermaid'];
|
||||
|
|
|
@ -5,6 +5,7 @@ import Bold from './bold';
|
|||
import BulletList from './bullet_list';
|
||||
import Code from './code';
|
||||
import CodeBlockHighlight from './code_block_highlight';
|
||||
import Diagram from './diagram';
|
||||
import FootnoteReference from './footnote_reference';
|
||||
import FootnoteDefinition from './footnote_definition';
|
||||
import Frontmatter from './frontmatter';
|
||||
|
@ -39,6 +40,7 @@ export default Extension.create({
|
|||
BulletList.name,
|
||||
Code.name,
|
||||
CodeBlockHighlight.name,
|
||||
Diagram.name,
|
||||
FootnoteReference.name,
|
||||
FootnoteDefinition.name,
|
||||
Frontmatter.name,
|
||||
|
|
|
@ -126,7 +126,7 @@ const defaultSerializerConfig = {
|
|||
}),
|
||||
[BulletList.name]: preserveUnchanged(renderBulletList),
|
||||
[CodeBlockHighlight.name]: preserveUnchanged(renderCodeBlock),
|
||||
[Diagram.name]: renderCodeBlock,
|
||||
[Diagram.name]: preserveUnchanged(renderCodeBlock),
|
||||
[DescriptionList.name]: renderHTMLNode('dl', true),
|
||||
[DescriptionItem.name]: (state, node, parent, index) => {
|
||||
if (index === 1) state.ensureNewLine();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { render } from '~/lib/gfm';
|
||||
import { isValidAttribute } from '~/lib/dompurify';
|
||||
import { SAFE_AUDIO_EXT, SAFE_VIDEO_EXT } from '../constants';
|
||||
import { SAFE_AUDIO_EXT, SAFE_VIDEO_EXT, DIAGRAM_LANGUAGES } from '../constants';
|
||||
import { createProseMirrorDocFromMdastTree } from './hast_to_prosemirror_converter';
|
||||
|
||||
const ALL_AUDIO_VIDEO_EXT = [...SAFE_AUDIO_EXT, ...SAFE_VIDEO_EXT];
|
||||
|
@ -40,6 +40,12 @@ const extractMediaFileExtension = (url) => {
|
|||
}
|
||||
};
|
||||
|
||||
const isCodeBlock = (hastNode) => hastNode.tagName === 'codeblock';
|
||||
|
||||
const isDiagramCodeBlock = (hastNode) => DIAGRAM_LANGUAGES.includes(hastNode.properties?.language);
|
||||
|
||||
const getCodeBlockAttrs = (hastNode) => ({ language: hastNode.properties.language });
|
||||
|
||||
const factorySpecs = {
|
||||
blockquote: { type: 'block', selector: 'blockquote' },
|
||||
paragraph: { type: 'block', selector: 'p' },
|
||||
|
@ -68,8 +74,13 @@ const factorySpecs = {
|
|||
},
|
||||
codeBlock: {
|
||||
type: 'block',
|
||||
selector: 'codeblock',
|
||||
getAttrs: (hastNode) => ({ ...hastNode.properties }),
|
||||
selector: (hastNode) => isCodeBlock(hastNode) && !isDiagramCodeBlock(hastNode),
|
||||
getAttrs: getCodeBlockAttrs,
|
||||
},
|
||||
diagram: {
|
||||
type: 'block',
|
||||
selector: (hastNode) => isCodeBlock(hastNode) && isDiagramCodeBlock(hastNode),
|
||||
getAttrs: getCodeBlockAttrs,
|
||||
},
|
||||
horizontalRule: {
|
||||
type: 'block',
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: seat_count_alerts
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89204
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362041
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::purchase
|
||||
default_enabled: false
|
|
@ -957,6 +957,33 @@ For example:
|
|||
Pattern matching is case-sensitive by default. Use the `i` flag modifier to make a
|
||||
pattern case-insensitive. For example: `/pattern/i`.
|
||||
|
||||
#### Store the regex pattern in a variable
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35438) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `ci_fix_rules_if_comparison_with_regexp_variable`, disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/359740) and feature flag `ci_fix_rules_if_comparison_with_regexp_variable` removed in GitLab 15.1.
|
||||
|
||||
Variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions.
|
||||
The regular expression must be enclosed in forward slashes (`/`). For example:
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
pattern: '/^ab.*/'
|
||||
|
||||
regex-job1:
|
||||
variables:
|
||||
teststring: 'abcde'
|
||||
script: echo "This job will run, because 'abcde' matches the /^ab.*/ pattern."
|
||||
rules:
|
||||
- if: '$teststring =~ $pattern'
|
||||
|
||||
regex-job2:
|
||||
variables:
|
||||
teststring: 'fghij'
|
||||
script: echo "This job will not run, because 'fghi' does not match the /^ab.*/ pattern."
|
||||
rules:
|
||||
- if: '$teststring =~ $pattern'
|
||||
```
|
||||
|
||||
### Join variable expressions together with `&&` or `||`
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62867) in GitLab 12.0
|
||||
|
|
|
@ -3224,8 +3224,7 @@ job:
|
|||
- Unlike variables in [`script`](../variables/index.md#use-cicd-variables-in-job-scripts)
|
||||
sections, variables in rules expressions are always formatted as `$VARIABLE`.
|
||||
- You can use `rules:if` with `include` to [conditionally include other configuration files](includes.md#use-rules-with-include).
|
||||
- In [GitLab 15.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/35438),
|
||||
variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions.
|
||||
- CI/CD variables on the right side of `=~` and `!~` expressions are [evaluated as regular expressions](../jobs/job_control.md#store-the-regex-pattern-in-a-variable).
|
||||
|
||||
**Related topics**:
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ For guidance not on this page, we defer to these style guides:
|
|||
<!-- vale off -->
|
||||
<!-- markdownlint-disable -->
|
||||
|
||||
## `&`
|
||||
|
||||
Do not use Latin abbreviations. Use **and** instead, unless you are documenting a UI element that uses an `&`.
|
||||
|
||||
## `@mention`
|
||||
|
||||
Try to avoid **`@mention`**. Say **mention** instead, and consider linking to the
|
||||
|
|
|
@ -90,7 +90,10 @@ SILENCE_DEPRECATIONS=1 bin/rspec spec/models/project_spec.rb
|
|||
All new spec files are run in [random order](https://gitlab.com/gitlab-org/gitlab/-/issues/337399)
|
||||
to surface flaky tests that are dependent on test order.
|
||||
|
||||
When randomized the used seed is shown in the spec output below the test suite summary. For example, `Randomized with seed 27443`.
|
||||
When randomized:
|
||||
|
||||
- The string `# order random` is added below the example group description.
|
||||
- The used seed is shown in the spec output below the test suite summary. For example, `Randomized with seed 27443`.
|
||||
|
||||
For a list of spec files which are still run in defined order, see [`rspec_order_todo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/rspec_order_todo.yml).
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
|
@ -33,22 +33,19 @@ you can automatically configure Mattermost slash commands:
|
|||
1. On the left sidebar, select **Settings > Integrations**.
|
||||
1. In **Add an integration**, select **Mattermost slash commands**.
|
||||
1. In **Enable integration**, ensure the **Active** checkbox is selected.
|
||||
1. Select **Add to Mattermost** and **Save changes**.
|
||||
1. Select **Add to Mattermost**, and select **Save changes**.
|
||||
|
||||
## Configure manually
|
||||
|
||||
To manually configure slash commands in Mattermost, you must:
|
||||
|
||||
1. [Enable custom slash commands](#enable-custom-slash-commands) in Mattermost.
|
||||
1. [Get configuration values](#get-configuration-values-from-gitlab) from GitLab.
|
||||
1. [Create a new slash command](#create-a-slash-command) in Mattermost.
|
||||
1. [Provide the Mattermost token](#provide-the-mattermost-token-to-gitlab) to GitLab.
|
||||
1. [Enable custom slash commands in Mattermost](#enable-custom-slash-commands-in-mattermost).
|
||||
(This step is required only for installations from source.)
|
||||
1. [Get configuration values from GitLab](#get-configuration-values-from-gitlab).
|
||||
1. [Create a slash command in Mattermost](#create-a-slash-command-in-mattermost).
|
||||
1. [Provide the Mattermost token to GitLab](#provide-the-mattermost-token-to-gitlab).
|
||||
|
||||
### Enable custom slash commands
|
||||
|
||||
NOTE:
|
||||
Omnibus GitLab installations are preconfigured. This step is required only for
|
||||
installations from source.
|
||||
### Enable custom slash commands in Mattermost
|
||||
|
||||
To enable custom slash commands from the Mattermost administrator console:
|
||||
|
||||
|
@ -59,65 +56,51 @@ To enable custom slash commands from the Mattermost administrator console:
|
|||
- **Enable Custom Slash Commands**
|
||||
- **Enable integrations to override usernames**
|
||||
- **Enable integrations to override profile picture icons**
|
||||
1. Select **Save**, but do not close this browser tab, because you need it in
|
||||
1. Select **Save**, but do not close this browser tab. You need it in
|
||||
a later step.
|
||||
|
||||
### Get configuration values from GitLab
|
||||
|
||||
After you enable custom slash commands in Mattermost, you need configuration
|
||||
information from GitLab. To get this information:
|
||||
To get configuration values from GitLab:
|
||||
|
||||
1. In a different browser tab than your current Mattermost session, sign in to
|
||||
1. In a different browser tab, sign in to
|
||||
GitLab as a user with administrator access.
|
||||
1. On the top bar, select **Menu > Admin**.
|
||||
1. In the left menu, select **Settings > Integrations**, then select
|
||||
**Mattermost slash commands**.
|
||||
1. GitLab displays potential values for Mattermost settings. Copy the **Request URL**
|
||||
as you need it for the next step. All other values are suggestions.
|
||||
1. Do not close this browser tab, because you need it in future steps.
|
||||
1. On the left sidebar, select **Settings > Integrations**.
|
||||
1. Select **Mattermost slash commands**. GitLab displays potential values for Mattermost settings.
|
||||
1. Copy the **Request URL** value. All other values are suggestions.
|
||||
1. Do not close this browser tab. You need it in a later step.
|
||||
|
||||
Next, create a slash command in Mattermost with the values from GitLab.
|
||||
### Create a slash command in Mattermost
|
||||
|
||||
### Create a slash command
|
||||
To create a slash command in Mattermost:
|
||||
|
||||
To create a slash command, you need the values you obtained from GitLab in
|
||||
the previous step:
|
||||
|
||||
1. In the Mattermost tab you left open when you
|
||||
[enabled custom slash commands](#enable-custom-slash-commands), go to your
|
||||
team page.
|
||||
1. [In the Mattermost browser tab](#enable-custom-slash-commands-in-mattermost),
|
||||
go to your team page.
|
||||
1. Select the **{ellipsis_v}** **Settings** icon, and select **Integrations**.
|
||||
1. In the left menu, select **Slash commands**.
|
||||
1. Select **Add Slash Command**:
|
||||
|
||||
![Mattermost add command](img/mattermost_add_slash_command.png)
|
||||
1. On the left sidebar, select **Slash commands**.
|
||||
1. Select **Add Slash Command**.
|
||||
1. Provide a **Display Name** and **Description** for your new command.
|
||||
1. Provide a **Command Trigger Word** according to your application's configuration:
|
||||
1. Provide a **Command Trigger Word** based on your application's configuration:
|
||||
|
||||
- **If you intend to only connect one project to your Mattermost team**: Use
|
||||
- **If you intend to only connect one project to your Mattermost team**, use
|
||||
`/gitlab` for your trigger word.
|
||||
- **If you intend to connect multiple projects**: Use a trigger word that relates
|
||||
- **If you intend to connect multiple projects**, use a trigger word that relates
|
||||
to your project, such as `/project-name` or `/gitlab-project-name`.
|
||||
1. For **Request URL**, provide the value you copied from GitLab when you
|
||||
[viewed configuration values](#get-configuration-values-from-gitlab).
|
||||
1. For all other values, you may use the suggestions from GitLab or use your
|
||||
1. For **Request URL**, [paste the value you copied from GitLab](#get-configuration-values-from-gitlab).
|
||||
1. For all other values, you may use the suggestions from GitLab or your
|
||||
preferred values.
|
||||
1. Copy the **Token** value, as you need it in a later step, and select **Done**.
|
||||
1. Copy the **Token** value, and select **Done**.
|
||||
|
||||
### Provide the Mattermost token to GitLab
|
||||
|
||||
When you create a new slash command in Mattermost, it generates a token you must
|
||||
Creating a slash command in Mattermost generates a token you must
|
||||
provide to GitLab:
|
||||
|
||||
1. In the GitLab browser tab from
|
||||
[getting configuration values from GitLab](#get-configuration-values-from-gitlab),
|
||||
select the **Active** checkbox to enable this configuration.
|
||||
1. In the **Token** field, paste the token you obtained from Mattermost.
|
||||
ensure that the **Active** toggle is enabled.
|
||||
|
||||
![Mattermost copy token to GitLab](img/mattermost_gitlab_token.png)
|
||||
|
||||
1. Select **Save changes** for the changes to take effect.
|
||||
1. [In the GitLab browser tab](#get-configuration-values-from-gitlab),
|
||||
select the **Active** checkbox.
|
||||
1. In the **Token** text box, [paste the token you copied from Mattermost](#create-a-slash-command-in-mattermost).
|
||||
1. Select **Save changes**.
|
||||
|
||||
Your slash command can now communicate with your GitLab project.
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ module Banzai
|
|||
include RequestStoreReferenceCache
|
||||
include OutputSafety
|
||||
|
||||
REFERENCE_TYPE_DATA_ATTRIBUTE = 'data-reference-type='
|
||||
|
||||
class << self
|
||||
# Implement in child class
|
||||
# Example: self.reference_type = :merge_request
|
||||
|
@ -132,13 +134,19 @@ module Banzai
|
|||
def data_attribute(attributes = {})
|
||||
attributes = attributes.reject { |_, v| v.nil? }
|
||||
|
||||
attributes[:reference_type] ||= self.class.reference_type
|
||||
# "data-reference-type=" attribute got moved into a constant because we need
|
||||
# to use it on ReferenceRewriter class to detect if the markdown contains any reference
|
||||
reference_type_attribute = "#{REFERENCE_TYPE_DATA_ATTRIBUTE}#{escape_once(self.class.reference_type)} "
|
||||
|
||||
attributes[:container] ||= 'body'
|
||||
attributes[:placement] ||= 'top'
|
||||
attributes.delete(:original) if context[:no_original_data]
|
||||
|
||||
attributes.map do |key, value|
|
||||
%Q(data-#{key.to_s.dasherize}="#{escape_once(value)}")
|
||||
end.join(' ')
|
||||
end
|
||||
.join(' ')
|
||||
.prepend(reference_type_attribute)
|
||||
end
|
||||
|
||||
def ignore_ancestor_query
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
class TablesSortedByForeignKeys
|
||||
include TSort
|
||||
|
||||
def initialize(connection, tables)
|
||||
@connection = connection
|
||||
@tables = tables
|
||||
end
|
||||
|
||||
def execute
|
||||
strongly_connected_components
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tsort_each_node(&block)
|
||||
tables_dependencies.each_key(&block)
|
||||
end
|
||||
|
||||
def tsort_each_child(node, &block)
|
||||
tables_dependencies[node].each(&block)
|
||||
end
|
||||
|
||||
# it maps the tables to the tables that depend on it
|
||||
def tables_dependencies
|
||||
@tables.to_h do |table_name|
|
||||
[table_name, all_foreign_keys[table_name]&.map(&:from_table).to_a]
|
||||
end
|
||||
end
|
||||
|
||||
def all_foreign_keys
|
||||
@all_foreign_keys ||= @tables.flat_map do |table_name|
|
||||
@connection.foreign_keys(table_name)
|
||||
end.group_by(&:to_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
class TablesTruncate
|
||||
GITLAB_SCHEMAS_TO_IGNORE = %i[gitlab_geo].freeze
|
||||
|
||||
def initialize(database_name:, min_batch_size:, logger: nil, until_table: nil, dry_run: false)
|
||||
@database_name = database_name
|
||||
@min_batch_size = min_batch_size
|
||||
@logger = logger
|
||||
@until_table = until_table
|
||||
@dry_run = dry_run
|
||||
end
|
||||
|
||||
def execute
|
||||
raise "Cannot truncate legacy tables in single-db setup" unless Gitlab::Database.has_config?(:ci)
|
||||
raise "database is not supported" unless %w[main ci].include?(database_name)
|
||||
|
||||
logger&.info "DRY RUN:" if dry_run
|
||||
|
||||
connection = Gitlab::Database.database_base_models[database_name].connection
|
||||
|
||||
schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
|
||||
tables_to_truncate = Gitlab::Database::GitlabSchema.tables_to_schema.reject do |_, schema_name|
|
||||
(GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection)).include?(schema_name)
|
||||
end.keys
|
||||
|
||||
tables_sorted = Gitlab::Database::TablesSortedByForeignKeys.new(connection, tables_to_truncate).execute
|
||||
# Checking if all the tables have the write-lock triggers
|
||||
# to make sure we are deleting the right tables on the right database.
|
||||
tables_sorted.flatten.each do |table_name|
|
||||
query = <<~SQL
|
||||
SELECT COUNT(*) from information_schema.triggers
|
||||
WHERE event_object_table = '#{table_name}'
|
||||
AND trigger_name = 'gitlab_schema_write_trigger_for_#{table_name}'
|
||||
SQL
|
||||
|
||||
if connection.select_value(query) == 0
|
||||
raise "Table '#{table_name}' is not locked for writes. Run the rake task gitlab:db:lock_writes first"
|
||||
end
|
||||
end
|
||||
|
||||
if until_table
|
||||
table_index = tables_sorted.find_index { |tables_group| tables_group.include?(until_table) }
|
||||
raise "The table '#{until_table}' is not within the truncated tables" if table_index.nil?
|
||||
|
||||
tables_sorted = tables_sorted[0..table_index]
|
||||
end
|
||||
|
||||
# min_batch_size is the minimum number of new tables to truncate at each stage.
|
||||
# But in each stage we have also have to truncate the already truncated tables in the previous stages
|
||||
logger&.info "Truncating legacy tables for the database #{database_name}"
|
||||
truncate_tables_in_batches(connection, tables_sorted, min_batch_size)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :database_name, :min_batch_size, :logger, :dry_run, :until_table
|
||||
|
||||
def truncate_tables_in_batches(connection, tables_sorted, min_batch_size)
|
||||
truncated_tables = []
|
||||
|
||||
unless dry_run
|
||||
tables_sorted.flatten.compact.each do |table|
|
||||
sql_statement = "SELECT set_config('lock_writes.#{table}', 'false', false)"
|
||||
logger&.info sql_statement
|
||||
connection.execute(sql_statement)
|
||||
end
|
||||
end
|
||||
|
||||
# We do the truncation in stages to avoid high IO
|
||||
# In each stage, we truncate the new tables along with the already truncated
|
||||
# tables before. That's because PostgreSQL doesn't allow to truncate any table (A)
|
||||
# without truncating any other table (B) that has a Foreign Key pointing to the table (A).
|
||||
# even if table (B) is empty, because it has been already truncated in a previous stage.
|
||||
tables_sorted.in_groups_of(min_batch_size).each do |tables_groups|
|
||||
new_tables_to_truncate = tables_groups.flatten.compact
|
||||
logger&.info "= New tables to truncate: #{new_tables_to_truncate.join(', ')}"
|
||||
truncated_tables.push(*new_tables_to_truncate).tap(&:sort!)
|
||||
sql_statement = "TRUNCATE TABLE #{truncated_tables.join(', ')} RESTRICT"
|
||||
|
||||
logger&.info sql_statement
|
||||
connection.execute(sql_statement) unless dry_run
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -55,7 +55,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def needs_rewrite?
|
||||
strong_memoize(:needs_rewrite) { @text_html.include?('data-reference-type=') }
|
||||
strong_memoize(:needs_rewrite) do
|
||||
reference_type_attribute =
|
||||
Banzai::Filter::References::ReferenceFilter::REFERENCE_TYPE_DATA_ATTRIBUTE
|
||||
|
||||
@text_html.include?(reference_type_attribute)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
namespace :gitlab do
|
||||
namespace :db do
|
||||
namespace :truncate_legacy_tables do
|
||||
desc "GitLab | DB | Truncate CI Tables on Main"
|
||||
task :main, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
|
||||
args.with_defaults(min_batch_size: 5)
|
||||
Gitlab::Database::TablesTruncate.new(
|
||||
database_name: 'main',
|
||||
min_batch_size: args.min_batch_size.to_i,
|
||||
logger: Logger.new($stdout),
|
||||
dry_run: ENV['DRY_RUN'] == 'true',
|
||||
until_table: ENV['UNTIL_TABLE']
|
||||
).execute
|
||||
end
|
||||
|
||||
desc "GitLab | DB | Truncate Main Tables on CI"
|
||||
task :ci, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
|
||||
args.with_defaults(min_batch_size: 5)
|
||||
Gitlab::Database::TablesTruncate.new(
|
||||
database_name: 'ci',
|
||||
min_batch_size: args.min_batch_size.to_i,
|
||||
logger: Logger.new($stdout),
|
||||
dry_run: ENV['DRY_RUN'] == 'true',
|
||||
until_table: ENV['UNTIL_TABLE']
|
||||
).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -39373,6 +39373,9 @@ msgstr ""
|
|||
msgid "The parent epic is confidential and can only contain confidential epics and issues"
|
||||
msgstr ""
|
||||
|
||||
msgid "The parsed YAML is too big"
|
||||
msgstr ""
|
||||
|
||||
msgid "The password for the Jenkins server."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -46,11 +46,11 @@ class StaticAnalysis
|
|||
# around this we will only enable this task on EE installations.
|
||||
TASKS_WITH_DURATIONS_SECONDS = [
|
||||
(Gitlab.ee? ? Task.new(%w[bin/rake gettext:updated_check], 360) : nil),
|
||||
Task.new(%w[yarn run lint:prettier], 160),
|
||||
Task.new(%w[bin/rake gettext:lint], 85),
|
||||
Task.new(%W[scripts/license-check.sh #{project_path}], 20),
|
||||
Task.new(%w[bin/rake lint:static_verification], 35),
|
||||
Task.new(%w[scripts/rubocop-max-files-in-cache-check], 20),
|
||||
Task.new(%w[yarn run lint:prettier], 200),
|
||||
Task.new(%w[bin/rake gettext:lint], 105),
|
||||
Task.new(%W[scripts/license-check.sh #{project_path}], 200),
|
||||
Task.new(%w[bin/rake lint:static_verification], 40),
|
||||
Task.new(%w[scripts/rubocop-max-files-in-cache-check], 25),
|
||||
Task.new(%w[bin/rake config_lint], 10),
|
||||
Task.new(%w[bin/rake gitlab:sidekiq:all_queues_yml:check], 15),
|
||||
(Gitlab.ee? ? Task.new(%w[bin/rake gitlab:sidekiq:sidekiq_queues_yml:check], 11) : nil),
|
||||
|
|
|
@ -773,7 +773,7 @@
|
|||
markdown: |-
|
||||
Hi @gfm_user - thank you for reporting this bug (#1) we hope to fix it in %1.1 as part of !1
|
||||
html: |-
|
||||
<p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-user="1" data-reference-type="user" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-reference-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-reference-type="milestone" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-reference-type="merge_request" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
|
||||
<p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-reference-type="issue" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-reference-type="milestone" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-reference-type="merge_request" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
|
||||
- name: strike
|
||||
markdown: |-
|
||||
~~del~~
|
||||
|
|
|
@ -302,6 +302,33 @@ describe('Pipelines table in Commits and Merge requests', () => {
|
|||
expect(findModal()).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no pipelines were created on a forked merge request', () => {
|
||||
beforeEach(async () => {
|
||||
mock.onGet('endpoint.json').reply(200, []);
|
||||
|
||||
createComponent({
|
||||
projectId: '5',
|
||||
mergeRequestId: 3,
|
||||
canCreatePipelineInTargetProject: true,
|
||||
sourceProjectFullPath: 'test/parent-project',
|
||||
targetProjectFullPath: 'test/fork-project',
|
||||
});
|
||||
|
||||
jest.spyOn(findModal().vm, 'show').mockReturnValue();
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('should show security modal from empty state run pipeline button', () => {
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
expect(findModal().exists()).toBe(true);
|
||||
|
||||
findRunPipelineBtn().trigger('click');
|
||||
|
||||
expect(findModal().vm.show).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unsuccessfull request', () => {
|
||||
|
|
|
@ -4,6 +4,7 @@ import Blockquote from '~/content_editor/extensions/blockquote';
|
|||
import BulletList from '~/content_editor/extensions/bullet_list';
|
||||
import Code from '~/content_editor/extensions/code';
|
||||
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
|
||||
import Diagram from '~/content_editor/extensions/diagram';
|
||||
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
|
||||
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
|
||||
import Frontmatter from '~/content_editor/extensions/frontmatter';
|
||||
|
@ -29,7 +30,7 @@ import TaskItem from '~/content_editor/extensions/task_item';
|
|||
import Video from '~/content_editor/extensions/video';
|
||||
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
|
||||
import markdownSerializer from '~/content_editor/services/markdown_serializer';
|
||||
import { SAFE_VIDEO_EXT, SAFE_AUDIO_EXT } from '~/content_editor/constants';
|
||||
import { SAFE_VIDEO_EXT, SAFE_AUDIO_EXT, DIAGRAM_LANGUAGES } from '~/content_editor/constants';
|
||||
|
||||
import { createTestEditor, createDocBuilder } from './test_utils';
|
||||
|
||||
|
@ -41,6 +42,7 @@ const tiptapEditor = createTestEditor({
|
|||
BulletList,
|
||||
Code,
|
||||
CodeBlockHighlight,
|
||||
Diagram,
|
||||
FootnoteDefinition,
|
||||
FootnoteReference,
|
||||
Frontmatter,
|
||||
|
@ -77,6 +79,7 @@ const {
|
|||
code,
|
||||
codeBlock,
|
||||
div,
|
||||
diagram,
|
||||
footnoteDefinition,
|
||||
footnoteReference,
|
||||
frontmatter,
|
||||
|
@ -108,6 +111,7 @@ const {
|
|||
bulletList: { nodeType: BulletList.name },
|
||||
code: { markType: Code.name },
|
||||
codeBlock: { nodeType: CodeBlockHighlight.name },
|
||||
diagram: { nodeType: Diagram.name },
|
||||
footnoteDefinition: { nodeType: FootnoteDefinition.name },
|
||||
footnoteReference: { nodeType: FootnoteReference.name },
|
||||
frontmatter: { nodeType: Frontmatter.name },
|
||||
|
@ -1280,6 +1284,16 @@ title: 'layout'
|
|||
),
|
||||
};
|
||||
}),
|
||||
...DIAGRAM_LANGUAGES.map((language) => {
|
||||
const markdown = `\`\`\`${language}
|
||||
content
|
||||
\`\`\``;
|
||||
|
||||
return {
|
||||
markdown,
|
||||
expectedDoc: doc(diagram({ ...source(markdown), language }, 'content')),
|
||||
};
|
||||
}),
|
||||
];
|
||||
|
||||
const runOnly = examples.find((example) => example.only === true);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
|
||||
let(:connection) { ApplicationRecord.connection }
|
||||
let(:tables) { %w[_test_gitlab_main_items _test_gitlab_main_references] }
|
||||
|
||||
subject do
|
||||
described_class.new(connection, tables).execute
|
||||
end
|
||||
|
||||
before do
|
||||
statement = <<~SQL
|
||||
CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
|
||||
|
||||
CREATE TABLE _test_gitlab_main_references (
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
item_id BIGINT NOT NULL,
|
||||
CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
|
||||
);
|
||||
SQL
|
||||
connection.execute(statement)
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
it 'returns the tables sorted by the foreign keys dependency' do
|
||||
expect(subject).to eq([['_test_gitlab_main_references'], ['_test_gitlab_main_items']])
|
||||
end
|
||||
|
||||
it 'returns both tables together if they are strongly connected' do
|
||||
statement = <<~SQL
|
||||
ALTER TABLE _test_gitlab_main_items ADD COLUMN reference_id BIGINT
|
||||
REFERENCES _test_gitlab_main_references(id)
|
||||
SQL
|
||||
connection.execute(statement)
|
||||
|
||||
expect(subject).to eq([tables])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,255 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_base,
|
||||
:suppress_gitlab_schemas_validate_connection do
|
||||
include MigrationsHelpers
|
||||
|
||||
let(:logger) { instance_double(Logger) }
|
||||
let(:dry_run) { false }
|
||||
let(:until_table) { nil }
|
||||
let(:min_batch_size) { 1 }
|
||||
let(:main_connection) { ApplicationRecord.connection }
|
||||
let(:ci_connection) { Ci::ApplicationRecord.connection }
|
||||
let(:test_gitlab_main_table) { '_test_gitlab_main_table' }
|
||||
let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' }
|
||||
|
||||
# Main Database
|
||||
let(:main_db_main_item_model) { table("_test_gitlab_main_items", database: "main") }
|
||||
let(:main_db_main_reference_model) { table("_test_gitlab_main_references", database: "main") }
|
||||
let(:main_db_ci_item_model) { table("_test_gitlab_ci_items", database: "main") }
|
||||
let(:main_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "main") }
|
||||
let(:main_db_shared_item_model) { table("_test_gitlab_shared_items", database: "main") }
|
||||
# CI Database
|
||||
let(:ci_db_main_item_model) { table("_test_gitlab_main_items", database: "ci") }
|
||||
let(:ci_db_main_reference_model) { table("_test_gitlab_main_references", database: "ci") }
|
||||
let(:ci_db_ci_item_model) { table("_test_gitlab_ci_items", database: "ci") }
|
||||
let(:ci_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "ci") }
|
||||
let(:ci_db_shared_item_model) { table("_test_gitlab_shared_items", database: "ci") }
|
||||
|
||||
subject(:truncate_legacy_tables) do
|
||||
described_class.new(
|
||||
database_name: database_name,
|
||||
min_batch_size: min_batch_size,
|
||||
logger: logger,
|
||||
dry_run: dry_run,
|
||||
until_table: until_table
|
||||
).execute
|
||||
end
|
||||
|
||||
shared_examples 'truncating legacy tables on a database' do
|
||||
before do
|
||||
skip_if_multiple_databases_not_setup
|
||||
|
||||
# Creating some test tables on the main database
|
||||
main_tables_sql = <<~SQL
|
||||
CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
|
||||
|
||||
CREATE TABLE _test_gitlab_main_references (
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
item_id BIGINT NOT NULL,
|
||||
CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
|
||||
);
|
||||
SQL
|
||||
|
||||
ApplicationRecord.connection.execute(main_tables_sql)
|
||||
Ci::ApplicationRecord.connection.execute(main_tables_sql)
|
||||
|
||||
ci_tables_sql = <<~SQL
|
||||
CREATE TABLE _test_gitlab_ci_items (id serial NOT NULL PRIMARY KEY);
|
||||
|
||||
CREATE TABLE _test_gitlab_ci_references (
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
item_id BIGINT NOT NULL,
|
||||
CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_ci_items(id)
|
||||
);
|
||||
SQL
|
||||
|
||||
ApplicationRecord.connection.execute(ci_tables_sql)
|
||||
Ci::ApplicationRecord.connection.execute(ci_tables_sql)
|
||||
|
||||
internal_tables_sql = <<~SQL
|
||||
CREATE TABLE _test_gitlab_shared_items (id serial NOT NULL PRIMARY KEY);
|
||||
SQL
|
||||
|
||||
ApplicationRecord.connection.execute(internal_tables_sql)
|
||||
Ci::ApplicationRecord.connection.execute(internal_tables_sql)
|
||||
|
||||
# Filling the tables
|
||||
5.times do |i|
|
||||
# Main Database
|
||||
main_db_main_item_model.create!(id: i)
|
||||
main_db_main_reference_model.create!(item_id: i)
|
||||
main_db_ci_item_model.create!(id: i)
|
||||
main_db_ci_reference_model.create!(item_id: i)
|
||||
main_db_shared_item_model.create!(id: i)
|
||||
# CI Database
|
||||
ci_db_main_item_model.create!(id: i)
|
||||
ci_db_main_reference_model.create!(item_id: i)
|
||||
ci_db_ci_item_model.create!(id: i)
|
||||
ci_db_ci_reference_model.create!(item_id: i)
|
||||
ci_db_shared_item_model.create!(id: i)
|
||||
end
|
||||
|
||||
allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
|
||||
{
|
||||
"_test_gitlab_main_items" => :gitlab_main,
|
||||
"_test_gitlab_main_references" => :gitlab_main,
|
||||
"_test_gitlab_ci_items" => :gitlab_ci,
|
||||
"_test_gitlab_ci_references" => :gitlab_ci,
|
||||
"_test_gitlab_shared_items" => :gitlab_shared,
|
||||
"_test_gitlab_geo_items" => :gitlab_geo
|
||||
}
|
||||
)
|
||||
|
||||
allow(logger).to receive(:info).with(any_args)
|
||||
end
|
||||
|
||||
context 'when the truncated tables are not locked for writes' do
|
||||
it 'raises an error that the tables are not locked for writes' do
|
||||
error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/
|
||||
expect { truncate_legacy_tables }.to raise_error(error_message)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the truncated tables are locked for writes' do
|
||||
before do
|
||||
legacy_tables_models.map(&:table_name).each do |table|
|
||||
Gitlab::Database::LockWritesManager.new(
|
||||
table_name: table,
|
||||
connection: connection,
|
||||
database_name: database_name
|
||||
).lock_writes
|
||||
end
|
||||
end
|
||||
|
||||
it 'truncates the legacy tables' do
|
||||
old_counts = legacy_tables_models.map(&:count)
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.to change { legacy_tables_models.map(&:count) }.from(old_counts).to([0] * legacy_tables_models.length)
|
||||
end
|
||||
|
||||
it 'does not affect the other tables' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.not_to change { other_tables_models.map(&:count) }
|
||||
end
|
||||
|
||||
it 'logs the sql statements to the logger' do
|
||||
expect(logger).to receive(:info)
|
||||
.with(/TRUNCATE TABLE #{legacy_tables_models.map(&:table_name).sort.join(', ')} RESTRICT/)
|
||||
truncate_legacy_tables
|
||||
end
|
||||
|
||||
context 'when running in dry_run mode' do
|
||||
let(:dry_run) { true }
|
||||
|
||||
it 'does not truncate the legacy tables if running in dry run mode' do
|
||||
legacy_tables_models = [main_db_ci_reference_model, main_db_ci_reference_model]
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.not_to change { legacy_tables_models.map(&:count) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passing until_table parameter' do
|
||||
context 'with a table that exists' do
|
||||
let(:until_table) { referencing_table_model.table_name }
|
||||
|
||||
it 'only truncates until the table specified' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.to change(referencing_table_model, :count).by(-5)
|
||||
.and change(referenced_table_model, :count).by(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a table that does not exist' do
|
||||
let(:until_table) { 'foobar' }
|
||||
|
||||
it 'raises an error if the specified table does not exist' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.to raise_error(/The table 'foobar' is not within the truncated tables/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with geo configured' do
|
||||
let(:geo_connection) { Gitlab::Database.database_base_models[:geo].connection }
|
||||
|
||||
before do
|
||||
skip unless geo_configured?
|
||||
geo_connection.execute('CREATE TABLE _test_gitlab_geo_items (id serial NOT NULL PRIMARY KEY)')
|
||||
geo_connection.execute('INSERT INTO _test_gitlab_geo_items VALUES(generate_series(1, 50))')
|
||||
end
|
||||
|
||||
it 'does not truncate gitlab_geo tables' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.not_to change { geo_connection.select_value("select count(*) from _test_gitlab_geo_items") }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when truncating gitlab_ci tables on the main database' do
|
||||
let(:connection) { ApplicationRecord.connection }
|
||||
let(:database_name) { "main" }
|
||||
let(:legacy_tables_models) { [main_db_ci_item_model, main_db_ci_reference_model] }
|
||||
let(:referencing_table_model) { main_db_ci_reference_model }
|
||||
let(:referenced_table_model) { main_db_ci_item_model }
|
||||
let(:other_tables_models) do
|
||||
[
|
||||
main_db_main_item_model, main_db_main_reference_model,
|
||||
ci_db_ci_item_model, ci_db_ci_reference_model,
|
||||
ci_db_main_item_model, ci_db_main_reference_model,
|
||||
main_db_shared_item_model, ci_db_shared_item_model
|
||||
]
|
||||
end
|
||||
|
||||
it_behaves_like 'truncating legacy tables on a database'
|
||||
end
|
||||
|
||||
context 'when truncating gitlab_main tables on the ci database' do
|
||||
let(:connection) { Ci::ApplicationRecord.connection }
|
||||
let(:database_name) { "ci" }
|
||||
let(:legacy_tables_models) { [ci_db_main_item_model, ci_db_main_reference_model] }
|
||||
let(:referencing_table_model) { ci_db_main_reference_model }
|
||||
let(:referenced_table_model) { ci_db_main_item_model }
|
||||
let(:other_tables_models) do
|
||||
[
|
||||
main_db_main_item_model, main_db_main_reference_model,
|
||||
ci_db_ci_item_model, ci_db_ci_reference_model,
|
||||
main_db_ci_item_model, main_db_ci_reference_model,
|
||||
main_db_shared_item_model, ci_db_shared_item_model
|
||||
]
|
||||
end
|
||||
|
||||
it_behaves_like 'truncating legacy tables on a database'
|
||||
end
|
||||
|
||||
context 'when running in a single database mode' do
|
||||
before do
|
||||
skip_if_multiple_databases_are_setup
|
||||
end
|
||||
|
||||
it 'raises an error when truncating the main database that it is a single database setup' do
|
||||
expect do
|
||||
described_class.new(database_name: 'main', min_batch_size: min_batch_size).execute
|
||||
end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
|
||||
end
|
||||
|
||||
it 'raises an error when truncating the ci database that it is a single database setup' do
|
||||
expect do
|
||||
described_class.new(database_name: 'ci', min_batch_size: min_batch_size).execute
|
||||
end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
|
||||
end
|
||||
end
|
||||
|
||||
def geo_configured?
|
||||
!!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo')
|
||||
end
|
||||
end
|
|
@ -30,8 +30,27 @@ module Support
|
|||
|
||||
@todo.include?(path)
|
||||
end
|
||||
|
||||
# Adds '# order <ORDER>` below the example group description if the order
|
||||
# has been set to help debugging in case of failure.
|
||||
#
|
||||
# Previously, we've modified metadata[:description] directly but that led
|
||||
# to bugs. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96137
|
||||
module DocumentationFormatterPatch
|
||||
# See https://github.com/rspec/rspec-core/blob/v3.11.0/lib/rspec/core/formatters/documentation_formatter.rb#L24-L29
|
||||
def example_group_started(notification)
|
||||
super
|
||||
|
||||
order = notification.group.metadata[:order]
|
||||
return unless order
|
||||
|
||||
output.puts "#{current_indentation}# order #{order}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec::Core::Formatters::DocumentationFormatter.prepend Support::RspecOrder::DocumentationFormatterPatch
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Useful to find order-dependent specs.
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rake_helper'
|
||||
|
||||
RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablished_active_record_base,
|
||||
:suppress_gitlab_schemas_validate_connection do
|
||||
let(:main_connection) { ApplicationRecord.connection }
|
||||
let(:ci_connection) { Ci::ApplicationRecord.connection }
|
||||
let(:test_gitlab_main_table) { '_test_gitlab_main_table' }
|
||||
let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' }
|
||||
|
||||
before :all do
|
||||
Rake.application.rake_require 'active_record/railties/databases'
|
||||
Rake.application.rake_require 'tasks/seed_fu'
|
||||
Rake.application.rake_require 'tasks/gitlab/db/validate_config'
|
||||
Rake.application.rake_require 'tasks/gitlab/db/truncate_legacy_tables'
|
||||
|
||||
# empty task as env is already loaded
|
||||
Rake::Task.define_task :environment
|
||||
end
|
||||
|
||||
before do
|
||||
skip_if_multiple_databases_not_setup
|
||||
|
||||
# Filling the table on both databases main and ci
|
||||
Gitlab::Database.database_base_models.each_value do |base_model|
|
||||
base_model.connection.execute(<<~SQL)
|
||||
CREATE TABLE #{test_gitlab_main_table} (id integer NOT NULL);
|
||||
INSERT INTO #{test_gitlab_main_table} VALUES(generate_series(1, 50));
|
||||
SQL
|
||||
base_model.connection.execute(<<~SQL)
|
||||
CREATE TABLE #{test_gitlab_ci_table} (id integer NOT NULL);
|
||||
INSERT INTO #{test_gitlab_ci_table} VALUES(generate_series(1, 50));
|
||||
SQL
|
||||
end
|
||||
|
||||
allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
|
||||
{
|
||||
test_gitlab_main_table => :gitlab_main,
|
||||
test_gitlab_ci_table => :gitlab_ci
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
shared_examples 'truncating legacy tables' do
|
||||
before do
|
||||
allow(ENV).to receive(:[]).and_return(nil)
|
||||
end
|
||||
|
||||
context 'when tables are not locked for writes' do
|
||||
it 'raises an error when trying to truncate the tables' do
|
||||
error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/
|
||||
expect { truncate_legacy_tables }.to raise_error(error_message)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tables are locked for writes' do
|
||||
before do
|
||||
# Locking ci table on the main database
|
||||
Gitlab::Database::LockWritesManager.new(
|
||||
table_name: test_gitlab_ci_table,
|
||||
connection: main_connection,
|
||||
database_name: "main"
|
||||
).lock_writes
|
||||
|
||||
# Locking main table on the ci database
|
||||
Gitlab::Database::LockWritesManager.new(
|
||||
table_name: test_gitlab_main_table,
|
||||
connection: ci_connection,
|
||||
database_name: "ci"
|
||||
).lock_writes
|
||||
end
|
||||
|
||||
it 'calls TablesTruncate with the correct parameters and default minimum batch size' do
|
||||
expect(Gitlab::Database::TablesTruncate).to receive(:new).with(
|
||||
database_name: database_name,
|
||||
min_batch_size: 5,
|
||||
logger: anything,
|
||||
dry_run: false,
|
||||
until_table: nil
|
||||
).and_call_original
|
||||
|
||||
truncate_legacy_tables
|
||||
end
|
||||
|
||||
it 'truncates the legacy table' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.to change { connection.select_value("SELECT count(*) from #{legacy_table}") }.from(50).to(0)
|
||||
end
|
||||
|
||||
it 'does not truncate the table that belongs to the connection schema' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.not_to change { connection.select_value("SELECT count(*) from #{active_table}") }
|
||||
end
|
||||
|
||||
context 'when running in dry_run mode' do
|
||||
before do
|
||||
allow(ENV).to receive(:[]).with("DRY_RUN").and_return("true")
|
||||
end
|
||||
|
||||
it 'does not truncate any tables' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.not_to change { connection.select_value("SELECT count(*) from #{legacy_table}") }
|
||||
end
|
||||
|
||||
it 'prints the truncation sql statement to the output' do
|
||||
expect do
|
||||
truncate_legacy_tables
|
||||
end.to output(/TRUNCATE TABLE #{legacy_table} RESTRICT/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passing until_table parameter via environment variable' do
|
||||
before do
|
||||
allow(ENV).to receive(:[]).with("UNTIL_TABLE").and_return(legacy_table)
|
||||
end
|
||||
|
||||
it 'sends the table name to TablesTruncate' do
|
||||
expect(Gitlab::Database::TablesTruncate).to receive(:new).with(
|
||||
database_name: database_name,
|
||||
min_batch_size: 5,
|
||||
logger: anything,
|
||||
dry_run: false,
|
||||
until_table: legacy_table
|
||||
).and_call_original
|
||||
|
||||
truncate_legacy_tables
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when truncating ci tables on the main database' do
|
||||
subject(:truncate_legacy_tables) { run_rake_task('gitlab:db:truncate_legacy_tables:main') }
|
||||
|
||||
let(:connection) { ApplicationRecord.connection }
|
||||
let(:database_name) { 'main' }
|
||||
let(:active_table) { test_gitlab_main_table }
|
||||
let(:legacy_table) { test_gitlab_ci_table }
|
||||
|
||||
it_behaves_like 'truncating legacy tables'
|
||||
end
|
||||
|
||||
context 'when truncating main tables on the ci database' do
|
||||
subject(:truncate_legacy_tables) { run_rake_task('gitlab:db:truncate_legacy_tables:ci') }
|
||||
|
||||
let(:connection) { Ci::ApplicationRecord.connection }
|
||||
let(:database_name) { 'ci' }
|
||||
let(:active_table) { test_gitlab_ci_table }
|
||||
let(:legacy_table) { test_gitlab_main_table }
|
||||
|
||||
it_behaves_like 'truncating legacy tables'
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue