summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_table.vue28
-rw-r--r--app/assets/javascripts/cycle_analytics/constants.js1
-rw-r--r--app/assets/javascripts/jobs/components/job_container_item.vue3
-rw-r--r--app/assets/stylesheets/page_bundles/build.scss6
-rw-r--r--app/models/container_registry/event.rb1
-rw-r--r--app/models/group.rb6
-rw-r--r--app/models/namespace.rb9
-rw-r--r--app/models/pool_repository.rb4
-rw-r--r--app/models/project.rb10
-rw-r--r--config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml8
-rw-r--r--db/migrate/20220531100920_add_license_usage_data_exported_to_application_settings.rb9
-rw-r--r--db/schema_migrations/202205311009201
-rw-r--r--db/structure.sql1
-rw-r--r--doc/development/migration_style_guide.md38
-rw-r--r--doc/user/analytics/value_stream_analytics.md3
-rw-r--r--doc/user/group/value_stream_analytics/index.md3
-rw-r--r--doc/user/project/deploy_tokens/index.md9
-rw-r--r--doc/user/project/merge_requests/test_coverage_visualization.md24
-rw-r--r--lib/gitlab/database/migration_helpers.rb14
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/frontend/cycle_analytics/stage_table_spec.js23
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb16
-rw-r--r--spec/models/ci/build_spec.rb25
-rw-r--r--spec/models/container_registry/event_spec.rb6
-rw-r--r--spec/models/group_spec.rb38
-rw-r--r--spec/models/namespace_spec.rb25
-rw-r--r--spec/models/pool_repository_spec.rb20
-rw-r--r--spec/models/project_spec.rb37
-rw-r--r--spec/requests/projects/environments_controller_spec.rb15
-rw-r--r--spec/support/shared_examples/controllers/environments_controller_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/requests/projects/environments_controller_spec_shared_examples.rb18
32 files changed, 351 insertions, 75 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index beb05183b55..83f706a8903 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-7c2fcde23bd4a962409897adbbb71da11c6db99a
+a31bd1be25d0ff03efaa7f756321ea9440122b24
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
index d460f764c00..85a40b89b77 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
@@ -13,6 +13,7 @@ import { __ } from '~/locale';
import Tracking from '~/tracking';
import {
NOT_ENOUGH_DATA_ERROR,
+ FIELD_KEY_TITLE,
PAGINATION_SORT_FIELD_END_EVENT,
PAGINATION_SORT_FIELD_DURATION,
PAGINATION_SORT_DIRECTION_ASC,
@@ -22,7 +23,8 @@ import TotalTime from './total_time.vue';
const DEFAULT_WORKFLOW_TITLE_PROPERTIES = {
thClass: 'gl-w-half',
- key: PAGINATION_SORT_FIELD_END_EVENT,
+ key: FIELD_KEY_TITLE,
+ sortable: false,
};
const WORKFLOW_COLUMN_TITLES = {
@@ -132,14 +134,16 @@ export default {
return [
this.workflowTitle,
{
+ key: PAGINATION_SORT_FIELD_END_EVENT,
+ label: __('Last event'),
+ sortable: this.sortable,
+ },
+ {
key: PAGINATION_SORT_FIELD_DURATION,
- label: __('Time'),
- thClass: 'gl-w-half',
+ label: __('Duration'),
+ sortable: this.sortable,
},
- ].map((field) => ({
- ...field,
- sortable: this.sortable,
- }));
+ ];
},
prevPage() {
return Math.max(this.pagination.page - 1, 0);
@@ -201,7 +205,7 @@ export default {
:empty-text="emptyStateMessage"
@sort-changed="onSort"
>
- <template v-if="stageCount" #head(end_event)="data">
+ <template v-if="stageCount" #head(title)="data">
<span>{{ data.label }}</span
><gl-badge class="gl-ml-2" size="sm"
><formatted-stage-count :stage-count="stageCount"
@@ -210,7 +214,10 @@ export default {
<template #head(duration)="data">
<span data-testid="vsa-stage-header-duration">{{ data.label }}</span>
</template>
- <template #cell(end_event)="{ item }">
+ <template #head(end_event)="data">
+ <span data-testid="vsa-stage-header-last-event">{{ data.label }}</span>
+ </template>
+ <template #cell(title)="{ item }">
<div data-testid="vsa-stage-event">
<div v-if="item.id" data-testid="vsa-stage-content">
<p class="gl-m-0">
@@ -282,6 +289,9 @@ export default {
<template #cell(duration)="{ item }">
<total-time :time="item.totalTime" data-testid="vsa-stage-event-time" />
</template>
+ <template #cell(end_event)="{ item }">
+ <span data-testid="vsa-stage-last-event">{{ item.endEventTimestamp }}</span>
+ </template>
</gl-table>
<gl-pagination
v-if="pagination && !isLoading && !isEmptyStage"
diff --git a/app/assets/javascripts/cycle_analytics/constants.js b/app/assets/javascripts/cycle_analytics/constants.js
index f0b2bd9dc5b..2758d686fb1 100644
--- a/app/assets/javascripts/cycle_analytics/constants.js
+++ b/app/assets/javascripts/cycle_analytics/constants.js
@@ -22,6 +22,7 @@ export const PAGINATION_SORT_FIELD_END_EVENT = 'end_event';
export const PAGINATION_SORT_FIELD_DURATION = 'duration';
export const PAGINATION_SORT_DIRECTION_DESC = 'desc';
export const PAGINATION_SORT_DIRECTION_ASC = 'asc';
+export const FIELD_KEY_TITLE = 'title';
export const I18N_VSA_ERROR_STAGES = __(
'There was an error fetching value stream analytics stages.',
diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job_container_item.vue
index d0594d1ad27..097ab3b4cf6 100644
--- a/app/assets/javascripts/jobs/components/job_container_item.vue
+++ b/app/assets/javascripts/jobs/components/job_container_item.vue
@@ -64,9 +64,10 @@ export default {
v-if="isActive"
name="arrow-right"
class="icon-arrow-right gl-absolute gl-display-block"
+ :size="14"
/>
- <ci-icon :status="job.status" />
+ <ci-icon :status="job.status" class="gl-mr-2" :size="14" />
<span class="gl-text-truncate gl-w-full">{{ jobName }}</span>
diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss
index 9213754d419..d40c03b7fd1 100644
--- a/app/assets/stylesheets/page_bundles/build.scss
+++ b/app/assets/stylesheets/page_bundles/build.scss
@@ -170,12 +170,6 @@
width: 289px;
overflow: auto;
- svg {
- margin-right: 3px;
- height: 14px;
- width: 14px;
- }
-
a {
padding: $gl-padding 10px $gl-padding 40px;
width: 270px;
diff --git a/app/models/container_registry/event.rb b/app/models/container_registry/event.rb
index 5409bdf5af4..b9fa61b4edf 100644
--- a/app/models/container_registry/event.rb
+++ b/app/models/container_registry/event.rb
@@ -78,6 +78,7 @@ module ContainerRegistry
return unless project
return unless Feature.enabled?(:container_registry_project_statistics, project)
+ Rails.cache.delete(project.root_ancestor.container_repositories_size_cache_key)
ProjectCacheWorker.perform_async(project.id, [], [:container_registry_size])
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 86f4b14cb6c..5369e873d17 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -863,6 +863,12 @@ class Group < Namespace
end
end
+ def gitlab_deploy_token
+ strong_memoize(:gitlab_deploy_token) do
+ deploy_tokens.gitlab_deploy_token
+ end
+ end
+
private
def feature_flag_enabled_for_self_or_ancestor?(feature_flag)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index ec4b9786945..7ecf97ce4c2 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -427,14 +427,21 @@ class Namespace < ApplicationRecord
aggregation_schedule.present?
end
+ def container_repositories_size_cache_key
+ "namespaces:#{id}:container_repositories_size"
+ end
+
def container_repositories_size
strong_memoize(:container_repositories_size) do
next unless Gitlab.com?
+ next unless root?
next unless ContainerRegistry::GitlabApiClient.supports_gitlab_api?
next 0 if all_container_repositories.empty?
next unless all_container_repositories.all_migrated?
- ContainerRegistry::GitlabApiClient.deduplicated_size(full_path)
+ Rails.cache.fetch(container_repositories_size_cache_key, expires_in: 7.days) do
+ ContainerRegistry::GitlabApiClient.deduplicated_size(full_path)
+ end
end
end
diff --git a/app/models/pool_repository.rb b/app/models/pool_repository.rb
index 78cddaa1302..3461104ae35 100644
--- a/app/models/pool_repository.rb
+++ b/app/models/pool_repository.rb
@@ -81,7 +81,9 @@ class PoolRepository < ApplicationRecord
object_pool.link(repository.raw)
end
- def mark_obsolete_if_last(repository)
+ def unlink_repository(repository)
+ repository.disconnect_alternates
+
if member_projects.where.not(id: repository.project.id).exists?
true
else
diff --git a/app/models/project.rb b/app/models/project.rb
index a215ff2a878..0f6a65be230 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2509,7 +2509,13 @@ class Project < ApplicationRecord
end
def gitlab_deploy_token
- @gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token
+ strong_memoize(:gitlab_deploy_token) do
+ if Feature.enabled?(:ci_variable_for_group_gitlab_deploy_token, self)
+ deploy_tokens.gitlab_deploy_token || group&.gitlab_deploy_token
+ else
+ deploy_tokens.gitlab_deploy_token
+ end
+ end
end
def any_lfs_file_locks?
@@ -2566,7 +2572,7 @@ class Project < ApplicationRecord
end
def leave_pool_repository
- pool_repository&.mark_obsolete_if_last(repository) && update_column(:pool_repository_id, nil)
+ pool_repository&.unlink_repository(repository) && update_column(:pool_repository_id, nil)
end
def link_pool_repository
diff --git a/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml b/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml
new file mode 100644
index 00000000000..6474e8aa85e
--- /dev/null
+++ b/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml
@@ -0,0 +1,8 @@
+---
+name: ci_variable_for_group_gitlab_deploy_token
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88696
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363621
+milestone: '15.1'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/db/migrate/20220531100920_add_license_usage_data_exported_to_application_settings.rb b/db/migrate/20220531100920_add_license_usage_data_exported_to_application_settings.rb
new file mode 100644
index 00000000000..825697d5387
--- /dev/null
+++ b/db/migrate/20220531100920_add_license_usage_data_exported_to_application_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddLicenseUsageDataExportedToApplicationSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :application_settings, :license_usage_data_exported, :boolean, default: false, null: false
+ end
+end
diff --git a/db/schema_migrations/20220531100920 b/db/schema_migrations/20220531100920
new file mode 100644
index 00000000000..8010729a4a1
--- /dev/null
+++ b/db/schema_migrations/20220531100920
@@ -0,0 +1 @@
+55c13dd2cf8db2ca54d3fb1bd09d459e90a90e01b3c1f7ad950e4b618df241af \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 1183e7d329a..9eff36609e7 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -11319,6 +11319,7 @@ CREATE TABLE application_settings (
jira_connect_application_key text,
globally_allowed_ips text DEFAULT ''::text NOT NULL,
container_registry_pre_import_tags_rate numeric(6,2) DEFAULT 0.5 NOT NULL,
+ license_usage_data_exported boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 2359f0fc2d1..c9b59ba66b5 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -898,6 +898,44 @@ def down
end
```
+## Dropping a sequence
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88387) in GitLab 15.1.
+
+Dropping a sequence is uncommon, but you can use the `drop_sequence` method provided by the database team.
+
+Under the hood, it works like this:
+
+Remove a sequence:
+
+- Remove the default value if the sequence is actually used.
+- Execute `DROP SEQUENCE`.
+
+Re-add a sequence:
+
+- Create the sequence, with the possibility of specifying the current value.
+- Change the default value of the column.
+
+A Rails migration example:
+
+```ruby
+class DropSequenceTest < Gitlab::Database::Migration[2.0]
+ def up
+ drop_sequence(:ci_pipelines_config, :pipeline_id, :ci_pipelines_config_pipeline_id_seq)
+ end
+
+ def down
+ default_value = Ci::Pipeline.maximum(:id) + 10_000
+
+ add_sequence(:ci_pipelines_config, :pipeline_id, :ci_pipelines_config_pipeline_id_seq, default_value)
+ end
+end
+```
+
+NOTE:
+`add_sequence` should be avoided for columns with foreign keys.
+Adding sequence to these columns is **only allowed** in the down method (restore previous schema state).
+
## Integer column type
By default, an integer column can hold up to a 4-byte (32-bit) number. That is
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index a428db75a28..afefbee7ca0 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -43,8 +43,7 @@ To view value stream analytics for your project:
- In the **From** field, select a start date.
- In the **To** field, select an end date.
1. Optional. Sort results by ascending or descending:
- - To sort by most recent or oldest workflow item, select the **Merge requests** or **Issues**
- header. The header name differs based on the stage you select.
+ - To sort by most recent or oldest workflow item, select the **Last event** header.
- To sort by most or least amount of time spent in each stage, select the **Time** header.
The table shows a list of related workflow items for the selected stage. Based on the stage you choose, this can be:
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
index 8908052f4d7..c2b729ebcc2 100644
--- a/doc/user/group/value_stream_analytics/index.md
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -50,8 +50,7 @@ To view value stream analytics for your group:
- In the **To** field, select an end date. The charts and list show workflow items created
during the date range.
1. Optional. Sort results by ascending or descending:
- - To sort by most recent or oldest workflow item, select the **Merge requests** or **Issues**
- header. The header name differs based on the stage you select.
+ - To sort by most recent or oldest workflow item, select the **Last event** header.
- To sort by most or least amount of time spent in each stage, select the **Time** header.
A badge next to the workflow items table header shows the number of workflow items that
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 64c18ab6f3b..0854b95275a 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -190,6 +190,8 @@ To pull images from the Dependency Proxy, you must:
### GitLab deploy token
+> Support for `gitlab-deploy-token` at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214014) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `ci_variable_for_group_gitlab_deploy_token`. Disabled by default.
+
There's a special case when it comes to deploy tokens. If a user creates one
named `gitlab-deploy-token`, the username and token of the deploy token is
automatically exposed to the CI/CD jobs as CI/CD variables: `CI_DEPLOY_USER`
@@ -203,9 +205,10 @@ docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
```
NOTE:
-The special handling for the `gitlab-deploy-token` deploy token is not
-implemented for group deploy tokens. To make the group-level deploy token available for
-CI/CD jobs, the `CI_DEPLOY_USER` and `CI_DEPLOY_PASSWORD` variables should be set under **Settings** to the name and token of the group deploy token respectively.
+In GitLab 15.0 and earlier, the special handling for the `gitlab-deploy-token` deploy token
+does not work for group deploy tokens. To make the group-level deploy token available
+for CI/CD jobs, the `CI_DEPLOY_USER` and `CI_DEPLOY_PASSWORD` CI/CD variables must be
+set in **Settings > CI/CD > Variables** to the name and token of the group deploy token.
## Troubleshooting
diff --git a/doc/user/project/merge_requests/test_coverage_visualization.md b/doc/user/project/merge_requests/test_coverage_visualization.md
index 87c08ce5d62..20c16ed64b2 100644
--- a/doc/user/project/merge_requests/test_coverage_visualization.md
+++ b/doc/user/project/merge_requests/test_coverage_visualization.md
@@ -394,3 +394,27 @@ run tests:
coverage_format: cobertura
path: coverage/coverage.xml
```
+
+## Troubleshooting
+
+### Test coverage visualization not displayed
+
+If the test coverage visualization is not displayed in the diff view, you can check
+the coverage report itself and verify that:
+
+- The file you are viewing in the diff view is mentioned in the coverage report.
+- The `source` and `filename` nodes in the report follows the [expected structure](#automatic-class-path-correction)
+ to match the files in your repository.
+
+Report artifacts are not downloadable by default. If you want the report to be downloadable
+from the job details page, add your coverage report to the artifact `paths`:
+
+```yaml
+artifacts:
+ paths:
+ - coverage/cobertura-coverage.xml
+ reports:
+ coverage_report:
+ coverage_format: cobertura
+ path: coverage/cobertura-coverage.xml
+```
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 2bdb839875b..4bb1d71ce18 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -1499,6 +1499,20 @@ into similar problems in the future (e.g. when new tables are created).
SQL
end
+ def drop_sequence(table_name, column_name, sequence_name)
+ execute <<~SQL
+ ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} DROP DEFAULT;
+ DROP SEQUENCE IF EXISTS #{quote_table_name(sequence_name)}
+ SQL
+ end
+
+ def add_sequence(table_name, column_name, sequence_name, start_value)
+ execute <<~SQL
+ CREATE SEQUENCE #{quote_table_name(sequence_name)} START #{start_value};
+ ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT nextval(#{quote(sequence_name)})
+ SQL
+ end
+
private
def create_temporary_columns_and_triggers(table, columns, primary_key: :id, data_type: :bigint)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ce47193b965..1344728ec96 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -22362,6 +22362,9 @@ msgstr ""
msgid "Last edited by %{link_start}%{avatar} %{name}%{link_end}"
msgstr ""
+msgid "Last event"
+msgstr ""
+
msgid "Last item before this page loaded in your browser:"
msgstr ""
@@ -33858,9 +33861,6 @@ msgstr ""
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr ""
-msgid "SecurityOrchestration|Group level policy"
-msgstr ""
-
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""
diff --git a/spec/frontend/cycle_analytics/stage_table_spec.js b/spec/frontend/cycle_analytics/stage_table_spec.js
index 0d15d67866d..473e1d5b664 100644
--- a/spec/frontend/cycle_analytics/stage_table_spec.js
+++ b/spec/frontend/cycle_analytics/stage_table_spec.js
@@ -27,6 +27,7 @@ const findTableHeadColumns = () => findTableHead().findAll('th');
const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
const findStageEventLink = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-link');
const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
+const findStageLastEvent = () => wrapper.findByTestId('vsa-stage-last-event');
const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
function createComponent(props = {}, shallow = false) {
@@ -128,6 +129,10 @@ describe('StageTable', () => {
expect(findStageTime().text()).toBe(createdAt);
});
+ it('will render the end event', () => {
+ expect(findStageLastEvent().text()).toBe(firstIssueEvent.endEventTimestamp);
+ });
+
it('will render the author', () => {
expect(wrapper.findByTestId('vsa-stage-event-author').text()).toContain(
firstIssueEvent.author.name,
@@ -303,10 +308,20 @@ describe('StageTable', () => {
wrapper.destroy();
});
- it('can sort the table by each column', () => {
- findTableHeadColumns().wrappers.forEach((w) => {
- expect(w.attributes('aria-sort')).toBe('none');
- });
+ it('can sort the end event or duration', () => {
+ findTableHeadColumns()
+ .wrappers.slice(1)
+ .forEach((w) => {
+ expect(w.attributes('aria-sort')).toBe('none');
+ });
+ });
+
+ it('cannot be sorted by title', () => {
+ findTableHeadColumns()
+ .wrappers.slice(0, 1)
+ .forEach((w) => {
+ expect(w.attributes('aria-sort')).toBeUndefined();
+ });
});
it('clicking a table column will send tracking information', () => {
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 1964fecbadd..e09016b2b2b 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -3281,4 +3281,20 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
model.rename_constraint(:test_table, :fk_old_name, :fk_new_name)
end
end
+
+ describe '#drop_sequence' do
+ it "executes the statement to drop the sequence" do
+ expect(model).to receive(:execute).with /ALTER TABLE "test_table" ALTER COLUMN "test_column" DROP DEFAULT;\nDROP SEQUENCE IF EXISTS "test_table_id_seq"/
+
+ model.drop_sequence(:test_table, :test_column, :test_table_id_seq)
+ end
+ end
+
+ describe '#add_sequence' do
+ it "executes the statement to add the sequence" do
+ expect(model).to receive(:execute).with "CREATE SEQUENCE \"test_table_id_seq\" START 1;\nALTER TABLE \"test_table\" ALTER COLUMN \"test_column\" SET DEFAULT nextval(\'test_table_id_seq\')\n"
+
+ model.add_sequence(:test_table, :test_column, :test_table_id_seq, 1)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 2aac4b5a3c8..9beeb9e8737 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -3538,7 +3538,7 @@ RSpec.describe Ci::Build do
]
end
- context 'when gitlab-deploy-token exists' do
+ context 'when gitlab-deploy-token exists for project' do
before do
project.deploy_tokens << deploy_token
end
@@ -3548,11 +3548,32 @@ RSpec.describe Ci::Build do
end
end
- context 'when gitlab-deploy-token does not exist' do
+ context 'when gitlab-deploy-token does not exist for project' do
it 'does not include deploy token variables' do
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER'}).to be_nil
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD'}).to be_nil
end
+
+ context 'when gitlab-deploy-token exists for group' do
+ before do
+ group.deploy_tokens << deploy_token
+ end
+
+ it 'includes deploy token variables' do
+ is_expected.to include(*deploy_token_variables)
+ end
+
+ context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
+ before do
+ stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
+ end
+
+ it 'does not include deploy token variables' do
+ expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER'}).to be_nil
+ expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD'}).to be_nil
+ end
+ end
+ end
end
end
diff --git a/spec/models/container_registry/event_spec.rb b/spec/models/container_registry/event_spec.rb
index 6b544c95cc8..13028c0e1da 100644
--- a/spec/models/container_registry/event_spec.rb
+++ b/spec/models/container_registry/event_spec.rb
@@ -46,6 +46,12 @@ RSpec.describe ContainerRegistry::Event do
handle!
end
+ it 'clears the cache for the namespace container repositories size' do
+ expect(Rails.cache).to receive(:delete).with(group.container_repositories_size_cache_key)
+
+ handle!
+ end
+
shared_examples 'event without project statistics update' do
it 'does not queue a project statistics update' do
expect(ProjectCacheWorker).not_to receive(:perform_async)
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index e7c830ce5e7..ab92606e6fc 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -3396,4 +3396,42 @@ RSpec.describe Group do
end
end
end
+
+ describe '#gitlab_deploy_token' do
+ subject(:gitlab_deploy_token) { group.gitlab_deploy_token }
+
+ context 'when there is a gitlab deploy token associated' do
+ let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, groups: [group]) }
+
+ it { is_expected.to eq(deploy_token) }
+ end
+
+ context 'when there is no a gitlab deploy token associated' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a gitlab deploy token associated but is has been revoked' do
+ let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, :revoked, groups: [group]) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a gitlab deploy token associated but it is expired' do
+ let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, :expired, groups: [group]) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a deploy token associated with a different name' do
+ let!(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a gitlab deploy token associated to a different group' do
+ let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, groups: [create(:group)]) }
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 6c1ceb335f7..eab82de0b01 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -583,7 +583,13 @@ RSpec.describe Namespace do
end
end
- describe '#container_repositories_size' do
+ describe '#container_repositories_size_cache_key' do
+ it 'returns the correct cache key' do
+ expect(namespace.container_repositories_size_cache_key).to eq "namespaces:#{namespace.id}:container_repositories_size"
+ end
+ end
+
+ describe '#container_repositories_size', :clean_gitlab_redis_cache do
let(:project_namespace) { create(:namespace) }
subject { project_namespace.container_repositories_size }
@@ -611,12 +617,29 @@ RSpec.describe Namespace do
end
it { is_expected.to eq(expected_result) }
+
+ it 'caches the result when all migrated' do
+ if all_migrated
+ expect(Rails.cache)
+ .to receive(:fetch)
+ .with(project_namespace.container_repositories_size_cache_key, expires_in: 7.days)
+
+ subject
+ end
+ end
end
end
context 'not on gitlab.com' do
it { is_expected.to eq(nil) }
end
+
+ context 'for a sub-group' do
+ let(:parent_namespace) { create(:group) }
+ let(:project_namespace) { create(:group, parent: parent_namespace) }
+
+ it { is_expected.to eq(nil) }
+ end
end
describe '#all_container_repositories' do
diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb
index 92b3e41cd18..447b7b2e0a2 100644
--- a/spec/models/pool_repository_spec.rb
+++ b/spec/models/pool_repository_spec.rb
@@ -24,23 +24,35 @@ RSpec.describe PoolRepository do
end
end
- describe '#mark_obsolete_if_last' do
+ describe '#unlink_repository' do
let(:pool) { create(:pool_repository, :ready) }
+ let(:repository_path) { File.join(TestEnv.repos_path, pool.source_project.repository.relative_path) }
+ let(:alternates_file) { File.join(repository_path, 'objects', 'info', 'alternates') }
+
+ before do
+ pool.link_repository(pool.source_project.repository)
+ end
context 'when the last member leaves' do
it 'schedules pool removal' do
expect(::ObjectPool::DestroyWorker).to receive(:perform_async).with(pool.id).and_call_original
- pool.mark_obsolete_if_last(pool.source_project.repository)
+ pool.unlink_repository(pool.source_project.repository)
+
+ expect(File).not_to exist(alternates_file)
end
end
context 'when the second member leaves' do
it 'does not schedule pool removal' do
- create(:project, :repository, pool_repository: pool)
+ other_project = create(:project, :repository, pool_repository: pool)
+ pool.link_repository(other_project.repository)
+
expect(::ObjectPool::DestroyWorker).not_to receive(:perform_async).with(pool.id)
- pool.mark_obsolete_if_last(pool.source_project.repository)
+ pool.unlink_repository(pool.source_project.repository)
+
+ expect(File).not_to exist(alternates_file)
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 44d4b99bc36..21978321a0f 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -6220,7 +6220,7 @@ RSpec.describe Project, factory_default: :keep do
describe '#gitlab_deploy_token' do
let(:project) { create(:project) }
- subject { project.gitlab_deploy_token }
+ subject(:gitlab_deploy_token) { project.gitlab_deploy_token }
context 'when there is a gitlab deploy token associated' do
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
@@ -6252,10 +6252,43 @@ RSpec.describe Project, factory_default: :keep do
context 'when there is a deploy token associated to a different project' do
let(:project_2) { create(:project) }
- let!(:deploy_token) { create(:deploy_token, projects: [project_2]) }
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project_2]) }
it { is_expected.to be_nil }
end
+
+ context 'when the project group has a gitlab deploy token associated' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
+
+ it { is_expected.to eq(deploy_token) }
+
+ context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
+ before do
+ stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ context 'when the project and its group has a gitlab deploy token associated' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let!(:project_deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
+ let!(:group_deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
+
+ it { is_expected.to eq(project_deploy_token) }
+
+ context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
+ before do
+ stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
+ end
+
+ it { is_expected.to eq(project_deploy_token) }
+ end
+ end
end
context 'with uploads' do
diff --git a/spec/requests/projects/environments_controller_spec.rb b/spec/requests/projects/environments_controller_spec.rb
index 1fd29f474ef..5cdf507abef 100644
--- a/spec/requests/projects/environments_controller_spec.rb
+++ b/spec/requests/projects/environments_controller_spec.rb
@@ -14,20 +14,7 @@ RSpec.describe Projects::EnvironmentsController do
sign_in(project.owner)
end
- it 'avoids N+1 queries', :use_sql_query_cache do
- create_deployment_with_associations(commit_depth: 19)
-
- control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
- get project_environment_path(project, environment), params: environment_params
- end
-
- 18.downto(0).each { |n| create_deployment_with_associations(commit_depth: n) }
-
- # N+1s exist for loading commit emails and users
- expect do
- get project_environment_path(project, environment), params: environment_params
- end.not_to exceed_all_query_limit(control).with_threshold(9)
- end
+ include_examples 'avoids N+1 queries on environment detail page'
end
def environment_params(opts = {})
diff --git a/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb b/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb
index a79b94209f3..c6e880635aa 100644
--- a/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/environments_controller_shared_examples.rb
@@ -65,20 +65,3 @@ RSpec.shared_examples 'failed response for #cancel_auto_stop' do
end
end
end
-
-RSpec.shared_examples 'avoids N+1 queries on environment detail page' do
- render_views
-
- before do
- create_deployment_with_associations(sequence: 0)
- end
-
- it 'avoids N+1 queries' do
- control = ActiveRecord::QueryRecorder.new { get :show, params: environment_params }
-
- create_deployment_with_associations(sequence: 1)
- create_deployment_with_associations(sequence: 2)
-
- expect { get :show, params: environment_params }.not_to exceed_query_limit(control.count).with_threshold(34)
- end
-end
diff --git a/spec/support/shared_examples/requests/projects/environments_controller_spec_shared_examples.rb b/spec/support/shared_examples/requests/projects/environments_controller_spec_shared_examples.rb
new file mode 100644
index 00000000000..31218b104bd
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/environments_controller_spec_shared_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'avoids N+1 queries on environment detail page' do
+ it 'avoids N+1 queries', :use_sql_query_cache do
+ create_deployment_with_associations(commit_depth: 19)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ get project_environment_path(project, environment), params: environment_params
+ end
+
+ 18.downto(0).each { |n| create_deployment_with_associations(commit_depth: n) }
+
+ # N+1s exist for loading commit emails and users
+ expect do
+ get project_environment_path(project, environment), params: environment_params
+ end.not_to exceed_all_query_limit(control).with_threshold(9)
+ end
+end