diff options
17 files changed, 208 insertions, 37 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue index ca78f194a82..107af6b4705 100644 --- a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue +++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue @@ -31,6 +31,14 @@ export default { required: false, default: '', }, + hasUnsavedChanges: { + type: Boolean, + required: true, + }, + isNewCiConfigFile: { + type: Boolean, + required: true, + }, isSaving: { type: Boolean, required: false, @@ -50,11 +58,14 @@ export default { }; }, computed: { + isCommitFormFilledOut() { + return this.message && this.targetBranch; + }, isCurrentBranchTarget() { return this.targetBranch === this.currentBranch; }, - submitDisabled() { - return !(this.message && this.targetBranch); + isSubmitDisabled() { + return !this.isCommitFormFilledOut || (!this.hasUnsavedChanges && !this.isNewCiConfigFile); }, }, watch: { @@ -143,7 +154,7 @@ export default { category="primary" variant="confirm" data-qa-selector="commit_changes_button" - :disabled="submitDisabled" + :disabled="isSubmitDisabled" :loading="isSaving" > {{ $options.i18n.commitChanges }} diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue index 8ff1aea020f..4ef598d6ff3 100644 --- a/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue +++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_section.vue @@ -37,6 +37,10 @@ export default { required: false, default: '', }, + hasUnsavedChanges: { + type: Boolean, + required: true, + }, isNewCiConfigFile: { type: Boolean, required: false, @@ -151,6 +155,8 @@ export default { <commit-form :current-branch="currentBranch" :default-message="defaultCommitMessage" + :has-unsaved-changes="hasUnsavedChanges" + :is-new-ci-config-file="isNewCiConfigFile" :is-saving="isSaving" :scroll-to-commit-form="scrollToCommitForm" v-on="$listeners" diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue index bb759477e1e..631dd8a2c00 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue @@ -131,6 +131,7 @@ export default { :ref="$options.commitSectionRef" :ci-file-content="ciFileContent" :commit-sha="commitSha" + :has-unsaved-changes="hasUnsavedChanges" :is-new-ci-config-file="isNewCiConfigFile" :scroll-to-commit-form="scrollToCommitForm" @scrolled-to-commit-form="setScrollToCommitForm(false)" diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md index f570c9b559f..792afc6c3d7 100644 --- a/doc/administration/integration/terminal.md +++ b/doc/administration/integration/terminal.md @@ -11,6 +11,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w WARNING: This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) in GitLab 14.5. +- Read more about the non-deprecated [Web Terminals accessible through the Web IDE](../../user/project/web_ide/index.md). +- Read more about the non-deprecated [Web Terminals accessible from a running CI job](../../ci/interactive_web_terminal/index.md). + +--- + With the introduction of the [Kubernetes integration](../../user/infrastructure/clusters/index.md), GitLab can store and use credentials for a Kubernetes cluster. GitLab uses these credentials to provide access to diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md index 9a44198d908..4dd187cba8b 100644 --- a/doc/development/database/loose_foreign_keys.md +++ b/doc/development/database/loose_foreign_keys.md @@ -50,6 +50,107 @@ we can: NOTE: For this procedure to work, we must register which tables to clean up asynchronously. +## The `scripts/decomposition/generate-loose-foreign-key` + +We built an automation tool to aid migration of foreign keys into loose foreign keys as part of +decomposition effort. It presents existing keys and allows chosen foreign keys to be automatically +converted into loose foreign keys. This ensures consistency between foreign key and loose foreign +key definitions, and ensures that they are properly tested. + +WARNING: +We strongly advise you to use the automation script for swapping any foreign key to a loose foreign key. + +The tool ensures that all aspects of swapping a foreign key are covered. This includes: + +- Creating a migration to remove a foreign key. +- Updating `db/structure.sql` with the new migration. +- Updating `lib/gitlab/database/gitlab_loose_foreign_keys.yml` to add the new loose foreign key. +- Creating or updating a model's specs to ensure that the loose foreign key is properly supported. +- Creating a new branch, commit, push, and creating a merge request on GitLab.com. +- Creating a merge request template with all the necessary details to validate the safety of the foreign key removal. + +The tool is located at `scripts/decomposition/generate-loose-foreign-key`: + +```shell +$ scripts/decomposition/generate-loose-foreign-key -h + +Usage: scripts/decomposition/generate-loose-foreign-key [options] <filters...> + -c, --cross-schema Show only cross-schema foreign keys + -n, --dry-run Do not execute any commands (dry run) + -b, --[no-]branch Create or not a new branch + -r, --[no-]rspec Create or not a rspecs automatically + -m, --milestone MILESTONE Specify custom milestone (current: 14.8) + -h, --help Prints this help +``` + +For the migration of cross-schema foreign keys, we use the `-c` modifier to show the foreign keys +yet to migrate: + +```shell +$ scripts/decomposition/generate-loose-foreign-key -c +Re-creating current test database +Dropped database 'gitlabhq_test_ee' +Dropped database 'gitlabhq_geo_test_ee' +Created database 'gitlabhq_test_ee' +Created database 'gitlabhq_geo_test_ee' + +Showing cross-schema foreign keys (20): + ID | HAS_LFK | FROM | TO | COLUMN | ON_DELETE + 0 | N | ci_builds | projects | project_id | cascade + 1 | N | ci_job_artifacts | projects | project_id | cascade + 2 | N | ci_pipelines | projects | project_id | cascade + 3 | Y | ci_pipelines | merge_requests | merge_request_id | cascade + 4 | N | external_pull_requests | projects | project_id | cascade + 5 | N | ci_sources_pipelines | projects | project_id | cascade + 6 | N | ci_stages | projects | project_id | cascade + 7 | N | ci_pipeline_schedules | projects | project_id | cascade + 8 | N | ci_runner_projects | projects | project_id | cascade + 9 | Y | dast_site_profiles_pipelines | ci_pipelines | ci_pipeline_id | cascade + 10 | Y | vulnerability_feedback | ci_pipelines | pipeline_id | nullify + 11 | N | ci_variables | projects | project_id | cascade + 12 | N | ci_refs | projects | project_id | cascade + 13 | N | ci_builds_metadata | projects | project_id | cascade + 14 | N | ci_subscriptions_projects | projects | downstream_project_id | cascade + 15 | N | ci_subscriptions_projects | projects | upstream_project_id | cascade + 16 | N | ci_sources_projects | projects | source_project_id | cascade + 17 | N | ci_job_token_project_scope_links | projects | source_project_id | cascade + 18 | N | ci_job_token_project_scope_links | projects | target_project_id | cascade + 19 | N | ci_project_monthly_usages | projects | project_id | cascade + +To match FK write one or many filters to match against FROM/TO/COLUMN: +- scripts/decomposition/generate-loose-foreign-key <filter(s)...> +- scripts/decomposition/generate-loose-foreign-key ci_job_artifacts project_id +- scripts/decomposition/generate-loose-foreign-key dast_site_profiles_pipelines +``` + +The command accepts a list of filters to match from, to, or column for the purpose of the foreign key generation. +For example, run this to swap all foreign keys for `ci_job_token_project_scope_links` for the +decomposed database: + +```shell +scripts/decomposition/generate-loose-foreign-key -c ci_job_token_project_scope_links +``` + +To swap only the `source_project_id` of `ci_job_token_project_scope_links` for the decomposed database, run: + +```shell +scripts/decomposition/generate-loose-foreign-key -c ci_job_token_project_scope_links source_project_id +``` + +To swap all the foreign keys (all having `_id` appended), but not create a new branch (only commit +the changes) and not create rspecs, run: + +```shell +scripts/decomposition/generate-loose-foreign-key -c --no-branch --no-rspec _id +``` + +To swap all foreign keys referencing `projects`, but not create a new branch (only commit the +changes), run: + +```shell +scripts/decomposition/generate-loose-foreign-key -c --no-branch projects +``` + ## Example migration and configuration ### Configure the loose foreign key diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md index 9338d9b8603..ab17e462c6a 100644 --- a/doc/user/clusters/management_project_template.md +++ b/doc/user/clusters/management_project_template.md @@ -4,7 +4,7 @@ group: Configure 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 --- -# Sample GitLab CI/CD project for Kubernetes **(FREE)** +# Manage cluster applications **(FREE)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318) in GitLab 12.10 with Helmfile support via Helm v2. > - Helm v2 support was [dropped](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63577) in GitLab 14.0. Use Helm v3 instead. diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md index 6942b9da393..f0ee9a8da56 100644 --- a/doc/user/group/value_stream_analytics/index.md +++ b/doc/user/group/value_stream_analytics/index.md @@ -276,9 +276,6 @@ To create a value stream: > - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55572) in GitLab 13.10. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294190) in GitLab 13.11. -WARNING: -This feature might not be available to you. Check the **version history** note above for details. - You can create value streams with stages, starting with a default or a blank template. You can add stages as desired. diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index bb6ee720d74..4b0aecbad68 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -358,25 +358,25 @@ Rather than attempting to push all changes at once, this workaround: ### Manually execute export steps Exports sometimes fail without giving enough information to troubleshoot. In these cases, it can be -helpful to [execute the export process manually within rails](https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/uncategorized/project-export.md#export-a-project-via-rails-console). +helpful to [open a rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session) +and loop through [all the defined exporters](https://gitlab.com/gitlab-org/gitlab/-/blob/b67a5b5a12498d57cd877023b7385f7251e57de8/app/services/projects/import_export/export_service.rb#L65). Execute each line individually, rather than pasting the entire block at once, so you can see any errors each command returns. ```shell +# User needs to have permission to export u = User.find_by_username('someuser') p = Project.find_by_full_path('some/project') e = Projects::ImportExport::ExportService.new(p,u) e.send(:version_saver).send(:save) -e.send(:avatar_saver).send(:save) -e.send(:project_tree_saver).send(:save) -e.send(:uploads_saver).send(:save) -e.send(:wiki_repo_saver).send(:save) -e.send(:lfs_saver).send(:save) -e.send(:snippets_repo_saver).send(:save) -e.send(:design_repo_saver).send(:save) +e.send(:repo_saver).send(:save) +## continue using `e.send(:exporter_name).send(:save)` going through the list of exporters +# The following line should show you the export_path similar to /var/opt/gitlab/gitlab-rails/shared/tmp/gitlab_exports/@hashed/49/94/4994.... s = Gitlab::ImportExport::Saver.new(exportable: p, shared:p.import_export_shared) + +# To try and upload use: s.send(:compress_and_save) s.send(:save_upload) ``` diff --git a/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb b/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb index 3aa54f1778d..8d2f5e4e49b 100644 --- a/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb @@ -3,8 +3,6 @@ module QA RSpec.describe 'Package', :orchestrated, only: { pipeline: :main } do describe 'Self-managed Container Registry' do - using RSpec::Parameterized::TableSyntax - let(:project) do Resource::Project.fabricate_via_api! do |project| project.name = 'project-with-registry' diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb index 0219b827c83..9ef5b8c84fa 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb @@ -3,7 +3,6 @@ module QA RSpec.describe 'Package', :orchestrated, :packages, :object_storage do describe 'Maven group level endpoint' do - using RSpec::Parameterized::TableSyntax include Runtime::Fixtures include_context 'packages registry qa scenario' diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb index 9c0ebea554d..d79f65764d4 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb @@ -3,8 +3,6 @@ module QA RSpec.describe 'Package', :orchestrated, :packages, :object_storage do describe 'Maven project level endpoint' do - using RSpec::Parameterized::TableSyntax - let(:group_id) { 'com.gitlab.qa' } let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" } let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') } @@ -112,10 +110,24 @@ module QA package_project.remove_via_api! end - where(:authentication_token_type, :maven_header_name) do - :personal_access_token | 'Private-Token' - :ci_job_token | 'Job-Token' - :project_deploy_token | 'Deploy-Token' + where do + { + 'using a personal access token' => { + authentication_token_type: :personal_access_token, + maven_header_name: 'Private-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354347' + }, + 'using a project deploy token' => { + authentication_token_type: :project_deploy_token, + maven_header_name: 'Deploy-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354348' + }, + 'using a ci job token' => { + authentication_token_type: :ci_job_token, + maven_header_name: 'Job-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354349' + } + } end with_them do @@ -154,7 +166,7 @@ module QA } end - it "pushes and pulls a maven package via maven using #{params[:authentication_token_type]}" do + it 'pushes and pulls a maven package via maven', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = package_project diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb index 67bb489c7b3..4cac055634e 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb @@ -3,7 +3,6 @@ module QA RSpec.describe 'Package', :orchestrated, :packages, :object_storage do describe 'NuGet project level endpoint' do - using RSpec::Parameterized::TableSyntax let(:project) do Resource::Project.fabricate_via_api! do |project| project.name = 'nuget-package-project' @@ -54,10 +53,24 @@ module QA project.remove_via_api! end - where(:authentication_token_type, :token_name) do - :personal_access_token | 'Personal Access Token' - :ci_job_token | 'CI Job Token' - :project_deploy_token | 'Deploy Token' + where do + { + 'using a personal access token' => { + authentication_token_type: :personal_access_token, + maven_header_name: 'Private-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354351' + }, + 'using a project deploy token' => { + authentication_token_type: :project_deploy_token, + maven_header_name: 'Deploy-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354352' + }, + 'using a ci job token' => { + authentication_token_type: :ci_job_token, + maven_header_name: 'Job-Token', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354353' + } + } end with_them do @@ -83,7 +96,7 @@ module QA end end - it "publishes a nuget package and installs using a #{params[:token_name]}" do + it 'publishes a nuget package and installs', testcase: params[:testcase] do Flow::Login.sign_in Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb index daf5ac61d73..7f35881cb21 100644 --- a/spec/features/projects/ci/editor_spec.rb +++ b/spec/features/projects/ci/editor_spec.rb @@ -55,6 +55,10 @@ RSpec.describe 'Pipeline Editor', :js do it 'displays new branch as selected after commiting on a new branch' do find('#target-branch-field').set('new_branch', clear: :backspace) + page.within('#source-editor-') do + find('textarea').send_keys '123' + end + click_button 'Commit changes' page.within('[data-testid="branch-selector"]') do diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js index 7244a179820..59bd71b0e60 100644 --- a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js +++ b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js @@ -17,6 +17,8 @@ describe('Pipeline Editor | Commit Form', () => { propsData: { defaultMessage: mockCommitMessage, currentBranch: mockDefaultBranch, + hasUnsavedChanges: true, + isNewCiConfigFile: false, ...props, }, @@ -82,6 +84,27 @@ describe('Pipeline Editor | Commit Form', () => { }); }); + describe('submit button', () => { + it.each` + hasUnsavedChanges | isNewCiConfigFile | isDisabled | btnState + ${false} | ${false} | ${true} | ${'disabled'} + ${true} | ${false} | ${false} | ${'enabled'} + ${true} | ${true} | ${false} | ${'enabled'} + ${false} | ${true} | ${false} | ${'enabled'} + `( + 'is $btnState when hasUnsavedChanges:$hasUnsavedChanges and isNewCiConfigfile:$isNewCiConfigFile', + ({ hasUnsavedChanges, isNewCiConfigFile, isDisabled }) => { + createComponent({ props: { hasUnsavedChanges, isNewCiConfigFile } }); + + if (isDisabled) { + expect(findSubmitBtn().attributes('disabled')).toBe('true'); + } else { + expect(findSubmitBtn().attributes('disabled')).toBeUndefined(); + } + }, + ); + }); + describe('when user inputs values', () => { const anotherMessage = 'Another commit message'; const anotherBranch = 'my-branch'; diff --git a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js index b54feea6ff7..33c76309951 100644 --- a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js +++ b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js @@ -51,6 +51,7 @@ describe('Pipeline Editor | Commit section', () => { const defaultProps = { ciFileContent: mockCiYml, commitSha: mockCommitSha, + hasUnsavedChanges: true, isNewCiConfigFile: false, }; diff --git a/spec/frontend/security_configuration/components/training_provider_list_spec.js b/spec/frontend/security_configuration/components/training_provider_list_spec.js index 688986af25b..30dd47e6b09 100644 --- a/spec/frontend/security_configuration/components/training_provider_list_spec.js +++ b/spec/frontend/security_configuration/components/training_provider_list_spec.js @@ -25,7 +25,7 @@ import { updateSecurityTrainingProvidersResponse, updateSecurityTrainingProvidersErrorResponse, testProjectPath, - textProviderIds, + testProviderIds, } from '../mock_data'; Vue.use(VueApollo); @@ -71,7 +71,7 @@ describe('TrainingProviderList component', () => { const findLoader = () => wrapper.findComponent(GlSkeletonLoader); const findErrorAlert = () => wrapper.findComponent(GlAlert); - const toggleFirstProvider = () => findFirstToggle().vm.$emit('change', textProviderIds[0]); + const toggleFirstProvider = () => findFirstToggle().vm.$emit('change', testProviderIds[0]); afterEach(() => { wrapper.destroy(); @@ -166,7 +166,7 @@ describe('TrainingProviderList component', () => { mutation: configureSecurityTrainingProvidersMutation, variables: { input: { - providerId: textProviderIds[0], + providerId: testProviderIds[0], isEnabled: true, isPrimary: false, projectPath: testProjectPath, diff --git a/spec/frontend/security_configuration/mock_data.js b/spec/frontend/security_configuration/mock_data.js index 06722921223..c3807a20f42 100644 --- a/spec/frontend/security_configuration/mock_data.js +++ b/spec/frontend/security_configuration/mock_data.js @@ -1,10 +1,10 @@ export const testProjectPath = 'foo/bar'; -export const textProviderIds = [101, 102]; +export const testProviderIds = [101, 102]; export const securityTrainingProviders = [ { - id: textProviderIds[0], + id: testProviderIds[0], name: 'Vendor Name 1', description: 'Interactive developer security education', url: 'https://www.example.org/security/training', @@ -12,7 +12,7 @@ export const securityTrainingProviders = [ isPrimary: false, }, { - id: textProviderIds[1], + id: testProviderIds[1], name: 'Vendor Name 2', description: 'Security training with guide and learning pathways.', url: 'https://www.vendornametwo.com/', |