Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-06-07 12:23:52 +00:00
parent 66b78786e8
commit 447f884dc0
72 changed files with 675 additions and 80 deletions

View File

@ -220,7 +220,7 @@
{"name":"gitlab-glfm-markdown","version":"0.0.17","platform":"x86_64-darwin","checksum":"50e0f4865ef7c455426c7c058fc10ff9c8366482d48a63d6f6693b38c4a49c1c"},
{"name":"gitlab-glfm-markdown","version":"0.0.17","platform":"x86_64-linux","checksum":"cc877ff8ceb3aa8a331fdb8991592e35897823e0f77ba9e4b2b65082c665089b"},
{"name":"gitlab-labkit","version":"0.36.0","platform":"ruby","checksum":"35f21d1c3870ed0c9b8321e25d0b0b0b5021805a5d0525d1eb0fde6b103af981"},
{"name":"gitlab-license","version":"2.4.0","platform":"ruby","checksum":"fd238fb1e605a6b9250d4eb1744434ffd131f18d50a3be32f613c883f7635e20"},
{"name":"gitlab-license","version":"2.5.0","platform":"ruby","checksum":"4c166c469c2ad17876ca43188a4ccebe3feb0726c4c1770047f8dcef96573f4d"},
{"name":"gitlab-mail_room","version":"0.0.25","platform":"ruby","checksum":"223ce7c3c0797b6015eaa37147884e6ddc7be9a7ee90a424358c96bc18613b1a"},
{"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"},
{"name":"gitlab-net-dns","version":"0.9.2","platform":"ruby","checksum":"f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d"},

View File

@ -712,7 +712,7 @@ GEM
opentracing (~> 0.4)
pg_query (>= 4.2.3, < 6.0)
redis (> 3.0.0, < 6.0.0)
gitlab-license (2.4.0)
gitlab-license (2.5.0)
gitlab-mail_room (0.0.25)
jwt (>= 2.0)
net-imap (>= 0.2.1)

View File

@ -637,6 +637,7 @@ export default {
gitlabLogo: window.gon.gitlab_logo,
PAGE_SIZES,
permissionsHelpPath: helpPagePath('user/permissions', { anchor: 'group-members-permissions' }),
betaFeatureHelpPath: helpPagePath('policy/experiment-beta-support', { anchor: 'beta-features' }),
popoverOptions: { title: __('What is listed here?') },
i18n,
LOCAL_STORAGE_KEY: 'gl-bulk-imports-status-page-size-v1',
@ -802,6 +803,23 @@ export default {
data-testid="import-projects-warning"
/>
</span>
<span class="gl-ml-3">
<gl-icon name="information-o" :size="12" class="gl-text-blue-600" />
<gl-sprintf
:message="
s__(
'BulkImport|Importing projects is a %{docsLinkStart}Beta%{docsLinkEnd} feature.',
)
"
>
<template #docsLink="{ content }"
><gl-link :href="$options.betaFeatureHelpPath" target="_blank">{{
content
}}</gl-link></template
>
</gl-sprintf>
</span>
</div>
<gl-table
ref="table"

View File

@ -30,6 +30,7 @@ export default {
return {
mlflowTrackingUrl: this.mlflowTrackingUrl,
projectPath: this.projectPath,
maxAllowedFileSize: this.maxAllowedFileSize,
};
},
props: {
@ -47,6 +48,10 @@ export default {
required: false,
default: '',
},
maxAllowedFileSize: {
type: Number,
required: true,
},
},
apollo: {
models: {

View File

@ -69,6 +69,7 @@ export default {
mlflowTrackingUrl: this.mlflowTrackingUrl,
projectPath: this.projectPath,
canWriteModelRegistry: this.canWriteModelRegistry,
maxAllowedFileSize: this.maxAllowedFileSize,
};
},
props: {
@ -96,6 +97,10 @@ export default {
type: String,
required: true,
},
maxAllowedFileSize: {
type: Number,
required: true,
},
},
apollo: {
model: {

View File

@ -26,6 +26,7 @@ export default {
canWriteModelRegistry: this.canWriteModelRegistry,
importPath: this.importPath,
versionName: this.versionName,
maxAllowedFileSize: this.maxAllowedFileSize,
};
},
props: {
@ -61,6 +62,10 @@ export default {
type: String,
required: true,
},
maxAllowedFileSize: {
type: Number,
required: true,
},
},
apollo: {
modelWithModelVersion: {

View File

@ -25,6 +25,7 @@ export default {
GlInputGroupText,
UploadDropzone,
},
inject: ['maxAllowedFileSize'],
props: {
path: {
type: String,
@ -65,7 +66,12 @@ export default {
},
submitRequest(importPath) {
this.loading = true;
uploadModel({ importPath, file: this.file, subfolder: this.subfolder })
uploadModel({
importPath,
file: this.file,
subfolder: this.subfolder,
maxAllowedFileSize: this.maxAllowedFileSize,
})
.then(() => {
this.resetFile();
this.alert = { message: this.$options.i18n.successfulUpload, variant: 'success' };

View File

@ -28,7 +28,7 @@ export default {
GlFormTextarea,
ImportArtifactZone: () => import('./import_artifact_zone.vue'),
},
inject: ['projectPath'],
inject: ['projectPath', 'maxAllowedFileSize'],
props: {
createModelVisible: {
type: Boolean,
@ -106,6 +106,7 @@ export default {
importPath,
file: this.selectedFile.file,
subfolder: this.selectedFile.subfolder,
maxAllowedFileSize: this.maxAllowedFileSize,
});
const { showPath } = this.versionData.mlModelVersionCreate.modelVersion._links;
@ -118,7 +119,6 @@ export default {
} catch (error) {
Sentry.captureException(error);
this.errorMessage = error;
this.selectedFile = emptyArtifactFile;
this.showModal();
}
},
@ -258,9 +258,13 @@ export default {
</gl-form-group>
</gl-form>
<gl-alert v-if="errorMessage" variant="danger" @dismiss="hideAlert">{{
errorMessage
}}</gl-alert>
<gl-alert
v-if="errorMessage"
data-testid="modal-create-alert"
variant="danger"
@dismiss="hideAlert"
>{{ errorMessage }}</gl-alert
>
</gl-modal>
</div>
</template>

View File

@ -27,7 +27,7 @@ export default {
GlFormTextarea,
ImportArtifactZone: () => import('./import_artifact_zone.vue'),
},
inject: ['projectPath'],
inject: ['projectPath', 'maxAllowedFileSize'],
props: {
modelGid: {
type: String,
@ -78,6 +78,7 @@ export default {
importPath,
file: this.selectedFile.file,
subfolder: this.selectedFile.subfolder,
maxAllowedFileSize: this.maxAllowedFileSize,
});
const { showPath } = this.versionData.mlModelVersionCreate.modelVersion._links;
visitUrl(showPath);
@ -85,7 +86,6 @@ export default {
} catch (error) {
Sentry.captureException(error);
this.errorMessage = error;
this.selectedFile = emptyArtifactFile;
this.showModal();
}
},

View File

@ -1,11 +1,30 @@
import axios from '~/lib/utils/axios_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { contentTypeMultipartFormData } from '~/lib/utils/headers';
import { joinPaths } from '~/lib/utils/url_utility';
import { s__, sprintf } from '~/locale';
export const uploadModel = ({ importPath, file, subfolder }) => {
export const uploadModel = ({ importPath, file, subfolder, maxAllowedFileSize }) => {
if (!file) {
return Promise.resolve();
}
if (!maxAllowedFileSize) {
return Promise.resolve(s__('Mlmodelregistry|Provide the max allowed file size'));
}
if (file.size > maxAllowedFileSize) {
const errorMessage = sprintf(
s__(
'MlModelRegistry|File "%{name}" is %{size}. It is larger than max allowed size of %{maxAllowedFileSize}',
),
{
name: file.name,
size: numberToHumanSize(file.size),
maxAllowedFileSize: numberToHumanSize(maxAllowedFileSize),
},
);
return Promise.reject(new Error(errorMessage));
}
const formData = new FormData();
const importUrl = joinPaths(importPath, subfolder, encodeURIComponent(file.name));

View File

@ -44,7 +44,7 @@ export default {
import(/* webpackChunkName: 'organization_switcher' */ './organization_switcher.vue'),
},
i18n: {
issues: __('Issues'),
issues: __('Assigned issues'),
mergeRequests: __('Merge requests'),
searchKbdHelp: sprintf(
s__('GlobalSearch|Type %{kbdOpen}/%{kbdClose} to search'),

View File

@ -36,7 +36,7 @@ module PreferencesHelper
followed_user_activity: _("Followed Users' Activity"),
groups: _("Your Groups"),
todos: _("Your To-Do List"),
issues: _("Assigned Issues"),
issues: _("Assigned issues"),
merge_requests: _("Assigned merge requests"),
operations: _("Operations Dashboard")
}.with_indifferent_access.freeze

View File

@ -10,7 +10,8 @@ module Projects
projectPath: project.full_path,
create_model_path: new_project_ml_model_path(project),
can_write_model_registry: can_write_model_registry?(user, project),
mlflow_tracking_url: mlflow_tracking_url(project)
mlflow_tracking_url: mlflow_tracking_url(project),
max_allowed_file_size: max_allowed_file_size(project)
}
to_json(data)
@ -25,7 +26,8 @@ module Projects
can_write_model_registry: can_write_model_registry?(user, project),
mlflow_tracking_url: mlflow_tracking_url(project),
model_id: model.id,
model_name: model.name
model_name: model.name,
max_allowed_file_size: max_allowed_file_size(project)
}
to_json(data)
@ -42,7 +44,8 @@ module Projects
version_name: model_version.version,
can_write_model_registry: can_write_model_registry?(user, project),
import_path: model_version_artifact_import_path(project.id, model_version.id),
model_path: project_ml_model_path(project, model_version.model)
model_path: project_ml_model_path(project, model_version.model),
max_allowed_file_size: max_allowed_file_size(project)
}
to_json(data)
@ -62,6 +65,10 @@ module Projects
user&.can?(:write_model_registry, project)
end
def max_allowed_file_size(project)
project.actual_limits.ml_model_max_file_size
end
def to_json(data)
Gitlab::Json.generate(data.deep_transform_keys { |k| k.to_s.camelize(:lower) })
end

View File

@ -1,4 +1,4 @@
- page_title _("Issues")
- page_title _("Assigned issues")
- @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
- add_page_specific_style 'page_bundles/issuable_list'
- add_page_specific_style 'page_bundles/dashboard'
@ -9,7 +9,7 @@
= render_if_exists 'shared/dashboard/saml_reauth_notice',
groups_requiring_saml_reauth: user_groups_requiring_reauth
= render ::Layouts::PageHeadingComponent.new(_('Issues')) do |c|
= render ::Layouts::PageHeadingComponent.new(_('Assigned issues')) do |c|
- c.with_actions do
= render 'shared/new_project_item_vue_select'

View File

@ -0,0 +1,9 @@
---
migration_job_name: BackfillProjectRelationExportsProjectId
description: Backfills sharding key `project_relation_exports.project_id` from `project_export_jobs`.
feature_category: importers
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155429
milestone: '17.1'
queued_migration_version: 20240605113250
finalize_after: '2024-07-22'
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,9 @@
---
migration_job_name: BackfillTerraformStateVersionsProjectId
description: Backfills sharding key `terraform_state_versions.project_id` from `terraform_states`.
feature_category: infrastructure_as_code
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155438
milestone: '17.1'
queued_migration_version: 20240605132810
finalize_after: '2024-07-22'
finalized_by: # version of the migration that finalized this BBM

View File

@ -19,3 +19,4 @@ desired_sharding_key:
table: project_export_jobs
sharding_key: project_id
belongs_to: project_export_job
desired_sharding_key_migration_job_name: BackfillProjectRelationExportsProjectId

View File

@ -4,7 +4,8 @@ classes:
- Terraform::StateVersion
feature_categories:
- infrastructure_as_code
description: Represents a Terraform state file at a point in time, with a corresponding file stored in object storage
description: Represents a Terraform state file at a point in time, with a corresponding
file stored in object storage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35211
milestone: '13.4'
gitlab_schema: gitlab_main_cell
@ -19,3 +20,4 @@ desired_sharding_key:
table: terraform_states
sharding_key: project_id
belongs_to: terraform_state
desired_sharding_key_migration_job_name: BackfillTerraformStateVersionsProjectId

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddProjectIdToProjectRelationExports < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :project_relation_exports, :project_id, :bigint
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddProjectIdToTerraformStateVersions < Gitlab::Database::Migration[2.2]
milestone '17.1'
def change
add_column :terraform_state_versions, :project_id, :bigint
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class IndexProjectRelationExportsOnProjectId < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
INDEX_NAME = 'index_project_relation_exports_on_project_id'
def up
add_concurrent_index :project_relation_exports, :project_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :project_relation_exports, INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddProjectRelationExportsProjectIdFk < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :project_relation_exports, :projects, column: :project_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :project_relation_exports, column: :project_id
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class AddProjectRelationExportsProjectIdTrigger < Gitlab::Database::Migration[2.2]
milestone '17.1'
def up
install_sharding_key_assignment_trigger(
table: :project_relation_exports,
sharding_key: :project_id,
parent_table: :project_export_jobs,
parent_sharding_key: :project_id,
foreign_key: :project_export_job_id
)
end
def down
remove_sharding_key_assignment_trigger(
table: :project_relation_exports,
sharding_key: :project_id,
parent_table: :project_export_jobs,
parent_sharding_key: :project_id,
foreign_key: :project_export_job_id
)
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class QueueBackfillProjectRelationExportsProjectId < Gitlab::Database::Migration[2.2]
milestone '17.1'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillProjectRelationExportsProjectId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:project_relation_exports,
:id,
:project_id,
:project_export_jobs,
:project_id,
:project_export_job_id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(
MIGRATION,
:project_relation_exports,
:id,
[
:project_id,
:project_export_jobs,
:project_id,
:project_export_job_id
]
)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class IndexTerraformStateVersionsOnProjectId < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
INDEX_NAME = 'index_terraform_state_versions_on_project_id'
def up
add_concurrent_index :terraform_state_versions, :project_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :terraform_state_versions, INDEX_NAME
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddTerraformStateVersionsProjectIdFk < Gitlab::Database::Migration[2.2]
milestone '17.1'
disable_ddl_transaction!
def up
add_concurrent_foreign_key :terraform_state_versions, :projects, column: :project_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :terraform_state_versions, column: :project_id
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class AddTerraformStateVersionsProjectIdTrigger < Gitlab::Database::Migration[2.2]
milestone '17.1'
def up
install_sharding_key_assignment_trigger(
table: :terraform_state_versions,
sharding_key: :project_id,
parent_table: :terraform_states,
parent_sharding_key: :project_id,
foreign_key: :terraform_state_id
)
end
def down
remove_sharding_key_assignment_trigger(
table: :terraform_state_versions,
sharding_key: :project_id,
parent_table: :terraform_states,
parent_sharding_key: :project_id,
foreign_key: :terraform_state_id
)
end
end

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class QueueBackfillTerraformStateVersionsProjectId < Gitlab::Database::Migration[2.2]
milestone '17.1'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillTerraformStateVersionsProjectId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
SUB_BATCH_SIZE = 100
def up
queue_batched_background_migration(
MIGRATION,
:terraform_state_versions,
:id,
:project_id,
:terraform_states,
:project_id,
:terraform_state_id,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(
MIGRATION,
:terraform_state_versions,
:id,
[
:project_id,
:terraform_states,
:project_id,
:terraform_state_id
]
)
end
end

View File

@ -0,0 +1 @@
0633b0f78ffa5b7550bb6c08df4d1992d137e703fad7be7dfbeb8f1947043b28

View File

@ -0,0 +1 @@
e437a04b396cc520e4efdfba802a6fa019ebe42ef56556f6456c96c957017dfc

View File

@ -0,0 +1 @@
01772232031f3546e17b11e863114eb97908f657d9525136c5810cc1254ae14c

View File

@ -0,0 +1 @@
cbbdfc5caaa00e2fc0b458a6a8e555ed033f066d85f7a363726fdb66ff4aad8f

View File

@ -0,0 +1 @@
333432f11538c10e77ac8f73bf0b364c3230999ff718d7e25b7e0df33c372e56

View File

@ -0,0 +1 @@
48616de4dab655eb5a01da5f7b8149c3d5ca66c64cdccea86468a3b6a9e83237

View File

@ -0,0 +1 @@
63ab2d580707d4a7095ccdfcbd636b701708b5b415284112084446b21ee35807

View File

@ -0,0 +1 @@
8e168eb75e5ab533b51be54c9df72249fc166f1d0fc3f1f396c87058c4116501

View File

@ -0,0 +1 @@
0e2504f0165fbeb417a7a5ff3650f0cb9aa151ff9f72e5c773659e4c3f4075c5

View File

@ -0,0 +1 @@
c4eaba0dc0a28d090b3d79f49752ad929d0ed89ca4cfb422c6f15087ba81e31a

View File

@ -1153,6 +1153,22 @@ RETURN NEW;
END
$$;
CREATE FUNCTION trigger_b2612138515d() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."project_id" IS NULL THEN
SELECT "project_id"
INTO NEW."project_id"
FROM "project_export_jobs"
WHERE "project_export_jobs"."id" = NEW."project_export_job_id";
END IF;
RETURN NEW;
END
$$;
CREATE FUNCTION trigger_b4520c29ea74() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -1233,6 +1249,22 @@ RETURN NEW;
END
$$;
CREATE FUNCTION trigger_d4487a75bd44() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW."project_id" IS NULL THEN
SELECT "project_id"
INTO NEW."project_id"
FROM "terraform_states"
WHERE "terraform_states"."id" = NEW."terraform_state_id";
END IF;
RETURN NEW;
END
$$;
CREATE FUNCTION trigger_dbdd61a66a91() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -15146,6 +15178,7 @@ CREATE TABLE project_relation_exports (
relation text NOT NULL,
jid text,
export_error text,
project_id bigint,
CONSTRAINT check_15e644d856 CHECK ((char_length(jid) <= 255)),
CONSTRAINT check_4b5880b795 CHECK ((char_length(relation) <= 255)),
CONSTRAINT check_dbd1cf73d0 CHECK ((char_length(export_error) <= 300))
@ -17367,6 +17400,7 @@ CREATE TABLE terraform_state_versions (
ci_build_id bigint,
verification_started_at timestamp with time zone,
verification_state smallint DEFAULT 0 NOT NULL,
project_id bigint,
CONSTRAINT check_0824bb7bbd CHECK ((char_length(file) <= 255)),
CONSTRAINT tf_state_versions_verification_failure_text_limit CHECK ((char_length(verification_failure) <= 255))
);
@ -27609,6 +27643,8 @@ CREATE INDEX index_project_pages_metadata_on_project_id_and_deployed_is_true ON
CREATE INDEX index_project_relation_export_upload_id ON project_relation_export_uploads USING btree (project_relation_export_id);
CREATE INDEX index_project_relation_exports_on_project_id ON project_relation_exports USING btree (project_id);
CREATE UNIQUE INDEX index_project_repositories_on_disk_path ON project_repositories USING btree (disk_path);
CREATE UNIQUE INDEX index_project_repositories_on_project_id ON project_repositories USING btree (project_id);
@ -28241,6 +28277,8 @@ CREATE INDEX index_terraform_state_versions_on_ci_build_id ON terraform_state_ve
CREATE INDEX index_terraform_state_versions_on_created_by_user_id ON terraform_state_versions USING btree (created_by_user_id);
CREATE INDEX index_terraform_state_versions_on_project_id ON terraform_state_versions USING btree (project_id);
CREATE UNIQUE INDEX index_terraform_state_versions_on_state_id_and_version ON terraform_state_versions USING btree (terraform_state_id, version);
CREATE INDEX index_terraform_state_versions_on_verification_state ON terraform_state_versions USING btree (verification_state);
@ -30663,6 +30701,8 @@ CREATE TRIGGER trigger_a1bc7c70cbdf BEFORE INSERT OR UPDATE ON vulnerability_use
CREATE TRIGGER trigger_a253cb3cacdf BEFORE INSERT OR UPDATE ON dora_daily_metrics FOR EACH ROW EXECUTE FUNCTION trigger_a253cb3cacdf();
CREATE TRIGGER trigger_b2612138515d BEFORE INSERT OR UPDATE ON project_relation_exports FOR EACH ROW EXECUTE FUNCTION trigger_b2612138515d();
CREATE TRIGGER trigger_b4520c29ea74 BEFORE INSERT OR UPDATE ON approval_merge_request_rule_sources FOR EACH ROW EXECUTE FUNCTION trigger_b4520c29ea74();
CREATE TRIGGER trigger_c17a166692a2 BEFORE INSERT OR UPDATE ON audit_events_streaming_headers FOR EACH ROW EXECUTE FUNCTION trigger_c17a166692a2();
@ -30675,6 +30715,8 @@ CREATE TRIGGER trigger_c9090feed334 BEFORE INSERT OR UPDATE ON boards_epic_lists
CREATE TRIGGER trigger_catalog_resource_sync_event_on_project_update AFTER UPDATE ON projects FOR EACH ROW WHEN ((((old.name)::text IS DISTINCT FROM (new.name)::text) OR (old.description IS DISTINCT FROM new.description) OR (old.visibility_level IS DISTINCT FROM new.visibility_level))) EXECUTE FUNCTION insert_catalog_resource_sync_event();
CREATE TRIGGER trigger_d4487a75bd44 BEFORE INSERT OR UPDATE ON terraform_state_versions FOR EACH ROW EXECUTE FUNCTION trigger_d4487a75bd44();
CREATE TRIGGER trigger_dbdd61a66a91 BEFORE INSERT OR UPDATE ON agent_activity_events FOR EACH ROW EXECUTE FUNCTION trigger_dbdd61a66a91();
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
@ -30859,6 +30901,9 @@ ALTER TABLE ONLY scan_result_policy_violations
ALTER TABLE ONLY incident_management_timeline_events
ADD CONSTRAINT fk_1800597ef9 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY terraform_state_versions
ADD CONSTRAINT fk_180cde327a FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_features
ADD CONSTRAINT fk_18513d9b92 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -31312,6 +31357,9 @@ ALTER TABLE ONLY users
ALTER TABLE ONLY analytics_devops_adoption_snapshots
ADD CONSTRAINT fk_78c9eac821 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_relation_exports
ADD CONSTRAINT fk_7a4d3d5c0f FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY lists
ADD CONSTRAINT fk_7a5553d60f FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE;

View File

@ -295,7 +295,7 @@ gitlab_rails['omniauth_providers'] = [
name: "azure_oauth2",
label: "Azure OIDC", # optional label for login button, defaults to "Openid Connect"
args: {
name: "azure_activedirectory_v2",
name: "azure_oauth2",
strategy_class: "OmniAuth::Strategies::OpenIDConnect",
scope: ["openid", "profile", "email"],
response_type: "code",
@ -319,7 +319,7 @@ gitlab_rails['omniauth_providers'] = [
```ruby
gitlab_rails['omniauth_providers'] = [
{
name: "azure_oauth2",
name: "azure_activedirectory_v2",
label: "Azure OIDC", # optional label for login button, defaults to "Openid Connect"
args: {
name: "azure_activedirectory_v2",

View File

@ -39,12 +39,18 @@ To enable the export of
## Enable migration of groups and projects by direct transfer
DETAILS:
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383268) in GitLab 15.8.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/461326) in GitLab 17.1.
WARNING:
In GitLab 16.1 and earlier, you should **not** use direct transfer with [scheduled scan execution policies](../../user/application_security/policies/scan-execution-policies.md). If using direct transfer, first upgrade to GitLab 16.2 and ensure security policy bots are enabled in the projects you are enforcing.
WARNING:
This feature is in [beta](../../policy/experiment-beta-support.md#beta) and subject to change without notice.
This feature is not ready for production use.
Migration of groups and projects by direct transfer is disabled by default.
To enable migration of groups and projects by direct transfer:

View File

@ -15,6 +15,10 @@ DETAILS:
With the group migration by direct transfer API, you can start and view the progress of migrations initiated with
[group migration by direct transfer](../user/group/import/index.md).
WARNING:
Migrating projects with this API is in [beta](../policy/experiment-beta-support.md#beta). This feature is not
ready for production use.
## Prerequisites
For information on prerequisites for group migration by direct transfer API, see
@ -27,7 +31,7 @@ prerequisites for [migrating groups by direct transfer](../user/group/import/dir
Use this endpoint to start a new group or project migration. Specify:
- `entities[group_entity]` to migrate a group.
- `entities[project_entity]` to migrate a project.
- `entities[project_entity]` to migrate a project. (**Status:** Beta)
```plaintext
POST /bulk_imports

View File

@ -906,7 +906,7 @@ Returns [`[CiRunnerUsage!]`](#cirunnerusage).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryrunnerusagefromdate"></a>`fromDate` | [`Date`](#date) | Start of the requested date frame. Defaults to the start of the previous calendar month. |
| <a id="queryrunnerusagefullpath"></a>`fullPath` | [`ID`](#id) | Filter jobs by the full path of the group or project they belong to. For example, `gitlab-org` or `gitlab-org/gitlab`. Available only to admins, group maintainers (when a group is specified), or project maintainers (when a project is specified). Limited to runners from 5000 child projects. |
| <a id="queryrunnerusagefullpath"></a>`fullPath` | [`ID`](#id) | Filter jobs by the full path of the group or project they belong to. For example, `gitlab-org` or `gitlab-org/gitlab`. Available only to administrators and users with the Maintainer role for the group (when a group is specified), or project (when a project is specified). Limited to runners from 5000 child projects. |
| <a id="queryrunnerusagerunnertype"></a>`runnerType` | [`CiRunnerType`](#cirunnertype) | Filter runners by the type. |
| <a id="queryrunnerusagerunnerslimit"></a>`runnersLimit` | [`Int`](#int) | Maximum number of runners to return. Other runners will be aggregated to a `runner: null` entry. Defaults to 5 if unspecified. Maximum of 500. |
| <a id="queryrunnerusagetodate"></a>`toDate` | [`Date`](#date) | End of the requested date frame. Defaults to the end of the previous calendar month. |
@ -926,7 +926,7 @@ Returns [`[CiRunnerUsageByProject!]`](#cirunnerusagebyproject).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryrunnerusagebyprojectfromdate"></a>`fromDate` | [`Date`](#date) | Start of the requested date frame. Defaults to the start of the previous calendar month. |
| <a id="queryrunnerusagebyprojectfullpath"></a>`fullPath` | [`ID`](#id) | Filter jobs based on the full path of the group or project they belong to. For example, `gitlab-org` or `gitlab-org/gitlab`. Available only to admins, group maintainers (when a group is specified), or project maintainers (when a project is specified). Limited to runners from 5000 child projects. |
| <a id="queryrunnerusagebyprojectfullpath"></a>`fullPath` | [`ID`](#id) | Filter jobs based on the full path of the group or project they belong to. For example, `gitlab-org` or `gitlab-org/gitlab`. Available only to administrators and users with the Maintainer role for the group (when a group is specified), or project (when a project is specified). Limited to runners from 5000 child projects. |
| <a id="queryrunnerusagebyprojectprojectslimit"></a>`projectsLimit` | [`Int`](#int) | Maximum number of projects to return. Other projects will be aggregated to a `project: null` entry. Defaults to 5 if unspecified. Maximum of 500. |
| <a id="queryrunnerusagebyprojectrunnertype"></a>`runnerType` | [`CiRunnerType`](#cirunnertype) | Filter jobs by the type of runner that executed them. |
| <a id="queryrunnerusagebyprojecttodate"></a>`toDate` | [`Date`](#date) | End of the requested date frame. Defaults to the end of the previous calendar month. |
@ -8064,6 +8064,7 @@ Input type: `RunnersExportUsageInput`
| ---- | ---- | ----------- |
| <a id="mutationrunnersexportusageclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnersexportusagefromdate"></a>`fromDate` | [`ISO8601Date`](#iso8601date) | UTC start date of the period to report on. Defaults to the start of last full month. |
| <a id="mutationrunnersexportusagefullpath"></a>`fullPath` | [`ID`](#id) | Filter jobs by the full path of the group or project they belong to. For example, `gitlab-org` or `gitlab-org/gitlab`. Available only to administrators and users with the Maintainer role for the group (when a group is specified), or project (when a project is specified). Limited to runners from 5000 child projects. |
| <a id="mutationrunnersexportusagemaxprojectcount"></a>`maxProjectCount` | [`Int`](#int) | Maximum number of projects to return. All other runner usage will be attributed to an `<Other projects>` entry. Defaults to 1000 projects. |
| <a id="mutationrunnersexportusagerunnertype"></a>`runnerType` | [`CiRunnerType`](#cirunnertype) | Scope of the runners to include in the report. |
| <a id="mutationrunnersexportusagetodate"></a>`toDate` | [`ISO8601Date`](#iso8601date) | UTC end date of the period to report on. " \ "Defaults to the end of the month specified by `fromDate`. |

View File

@ -106,7 +106,7 @@ Before starting the flow, generate the `STATE`, the `CODE_VERIFIER` and the `COD
- The `CODE_VERIFIER` is a random string, between 43 and 128 characters in length,
which use the characters `A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, and `~`.
- The `CODE_CHALLENGE` is an URL-safe base64-encoded string of the SHA256 hash of the
`CODE_VERIFIER`
`CODE_VERIFIER`.
- The SHA256 hash must be in binary format before encoding.
- In Ruby, you can set that up with `Base64.urlsafe_encode64(Digest::SHA256.digest(CODE_VERIFIER), padding: false)`.
- For reference, a `CODE_VERIFIER` string of `ks02i3jdikdo2k0dkfodf3m39rjfjsdk0wk349rj3jrhf` when hashed

View File

@ -18,10 +18,12 @@ We strive to run GitLab using the latest Rails releases to benefit from performa
1. Check the [Upgrading Ruby on Rails](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html) guide and prepare the application for the upcoming changes.
1. Update the `rails` gem version in `Gemfile`.
1. Run `bundle update rails`.
1. Run the update task `rake rails:update`.
1. Run `bundle update --conservative rails`.
1. For major and minor version updates, run `bin/rails app:update` and check if any of the suggested changes should be applied.
1. Update the `activesupport` version in `qa/Gemfile`.
1. Run `bundle update --conservative activesupport` in the `qa` folder.
1. Update the `activerecord_version` version in `vendor/gems/attr_encrypted/attr_encrypted.gemspec`.
1. Run `bundle update --conservative activerecord` in the `vendor/gems/attr_encrypted` folder.
1. Resolve any Bundler conflicts.
1. Ensure that `@rails/ujs` and `@rails/actioncable` npm packages match the new rails version in [`package.json`](https://gitlab.com/gitlab-org/gitlab/blob/master/package.json).
1. Run `yarn patch-package @rails/ujs` after updating this to ensure our local patch file version matches.

View File

@ -114,6 +114,10 @@ role.
1. The **Status** column shows the import status of each group. If you leave the page open, it updates in real-time.
1. After a group has been imported, select its GitLab path to open its GitLab URL.
WARNING:
Importing groups with projects is in [beta](../../../policy/experiment-beta-support.md#beta). This feature is not
ready for production use.
## Group import history
> - **Partially completed** status [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/394727) in GitLab 16.7.

View File

@ -43,13 +43,19 @@ Migrating groups by direct transfer copies the groups from one place to another.
- The subgroup of any existing top-level group.
- Another GitLab instance, including GitLab.com.
- In the [API](../../../api/bulk_imports.md), copy top-level groups and subgroups to these locations.
- Copy groups with projects or without projects.
- Copy groups with projects (in [beta](../../../policy/experiment-beta-support.md#beta) and not ready for production
use) or without projects. Copying projects with groups is available:
- On GitLab.com by default.
Not all group and project resources are copied. See list of copied resources below:
- [Migrated group items](migrated_items.md#migrated-group-items).
- [Migrated project items](migrated_items.md#migrated-project-items).
WARNING:
Importing groups with projects is in [beta](../../../policy/experiment-beta-support.md#beta). This feature is not
ready for production use.
We invite you to leave your feedback about migrating by direct transfer in
[the feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/284495).

View File

@ -460,7 +460,7 @@ To view all issues assigned to you:
Or:
- To use a [keyboard shortcut](../../shortcuts.md), press <kbd>Shift</kbd> + <kbd>i</kbd>.
- On the left sidebar, at the top, select **Issues** (**{issues}**).
- On the left sidebar, at the top, select **Assigned issues** (**{issues}**).
## Filter the list of issues

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
class BackfillProjectRelationExportsProjectId < BackfillDesiredShardingKeyJob
operation_name :backfill_project_relation_exports_project_id
feature_category :importers
end
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
class BackfillTerraformStateVersionsProjectId < BackfillDesiredShardingKeyJob
operation_name :backfill_terraform_state_versions_project_id
feature_category :infrastructure_as_code
end
end
end

View File

@ -7151,7 +7151,7 @@ msgstr ""
msgid "Assigned %{reviewer_users_sentence} as %{reviewer_text}."
msgstr ""
msgid "Assigned Issues"
msgid "Assigned issues"
msgstr ""
msgid "Assigned merge requests"
@ -9671,6 +9671,9 @@ msgstr ""
msgid "BulkImport|Import without projects"
msgstr ""
msgid "BulkImport|Importing projects is a %{docsLinkStart}Beta%{docsLinkEnd} feature."
msgstr ""
msgid "BulkImport|Importing the group failed."
msgstr ""
@ -33297,6 +33300,9 @@ msgstr ""
msgid "MlModelRegistry|Failed to load model with error: %{message}"
msgstr ""
msgid "MlModelRegistry|File \"%{name}\" is %{size}. It is larger than max allowed size of %{maxAllowedFileSize}"
msgstr ""
msgid "MlModelRegistry|For example 1.0.0"
msgstr ""
@ -33422,6 +33428,9 @@ msgid_plural "MlModelRegistry|· %d versions"
msgstr[0] ""
msgstr[1] ""
msgid "Mlmodelregistry|Provide the max allowed file size"
msgstr ""
msgid "Mock an external CI integration."
msgstr ""
@ -60846,6 +60855,15 @@ msgstr ""
msgid "Your CI runner usage CSV export containing the top %{exported_objects} has been added to this email as an attachment."
msgstr ""
msgid "Your CI runner usage CSV export containing the top %{exported_objects} in the \"%{full_path}\" group has been added to this email as an attachment."
msgstr ""
msgid "Your CI runner usage CSV export for the \"%{full_path}\" project has been added to this email as an attachment."
msgstr ""
msgid "Your CI runner usage CSV export of the top %{exported_objects} has been added to this email as an attachment."
msgstr ""
msgid "Your CI/CD configuration syntax is invalid. Select the Validate tab for more details."
msgstr ""
@ -60858,9 +60876,6 @@ msgstr ""
msgid "Your CSV export of %{exported_objects} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
msgid "Your CSV export of the top %{exported_objects} has been added to this email as an attachment."
msgstr ""
msgid "Your CSV export request has succeeded. The result will be emailed to %{email}."
msgstr ""

View File

@ -55,7 +55,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching,
expect(dashboard_count).to have_content(count)
within_testid('super-sidebar') do
expect(page).to have_link("Issues #{count}")
expect(page).to have_link("Assigned issues #{count}")
end
end

View File

@ -16,7 +16,7 @@ RSpec.describe 'Dashboard shortcuts', :js, feature_category: :shared do
it 'navigate to tabs' do
find('body').send_keys([:shift, 'I'])
check_page_title('Issues')
check_page_title('Assigned issues')
find('body').send_keys([:shift, 'M'])

View File

@ -21,6 +21,7 @@ Vue.use(VueApollo);
const defaultProps = {
projectPath: 'path/to/project',
canWriteModelRegistry: false,
maxAllowedFileSize: 99999,
};
describe('ml/model_registry/apps/index_ml_models', () => {

View File

@ -89,6 +89,7 @@ describe('ml/model_registry/apps/show_ml_model', () => {
indexModelsPath: 'index/path',
mlflowTrackingUrl: 'path/to/tracking',
canWriteModelRegistry,
maxAllowedFileSize: 99999,
},
stubs: { GlTab, DeleteModel, LoadOrErrorOrShow },
});

View File

@ -60,6 +60,7 @@ describe('ml/model_registry/apps/show_model_version.vue', () => {
canWriteModelRegistry: true,
importPath: 'path/to/import',
modelPath: 'path/to/model',
maxAllowedFileSize: 99999,
},
apolloProvider,
stubs: {

View File

@ -13,6 +13,8 @@ jest.mock('~/ml/model_registry/services/upload_model', () => ({
describe('ImportArtifactZone', () => {
let wrapper;
const provide = { maxAllowedFileSize: 99999 };
const file = { name: 'file.txt', size: 1024 };
const initialProps = {
path: 'some/path',
@ -32,6 +34,7 @@ describe('ImportArtifactZone', () => {
propsData: {
...initialProps,
},
provide,
});
});
@ -71,6 +74,7 @@ describe('ImportArtifactZone', () => {
},
importPath: 'some/path',
subfolder: '',
maxAllowedFileSize: 99999,
});
});
@ -96,6 +100,7 @@ describe('ImportArtifactZone', () => {
propsData: {
...initialProps,
},
provide,
stubs: {
GlFormInputGroup,
},
@ -128,6 +133,7 @@ describe('ImportArtifactZone', () => {
},
importPath: 'some/path',
subfolder: 'action',
maxAllowedFileSize: 99999,
});
});
});
@ -138,6 +144,7 @@ describe('ImportArtifactZone', () => {
propsData: {
...initialProps,
},
provide,
});
});
@ -169,6 +176,7 @@ describe('ImportArtifactZone', () => {
...initialProps,
submitOnSelect: false,
},
provide,
});
});
@ -188,6 +196,7 @@ describe('ImportArtifactZone', () => {
...initialProps,
path: null,
},
provide,
});
});

View File

@ -1,11 +1,12 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlAlert, GlModal } from '@gitlab/ui';
import { GlModal } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { visitUrl } from '~/lib/utils/url_utility';
import ModelCreate from '~/ml/model_registry/components/model_create.vue';
import ImportArtifactZone from '~/ml/model_registry/components/import_artifact_zone.vue';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
import { uploadModel } from '~/ml/model_registry/services/upload_model';
import createModelMutation from '~/ml/model_registry/graphql/mutations/create_model.mutation.graphql';
import createModelVersionMutation from '~/ml/model_registry/graphql/mutations/create_model_version.mutation.graphql';
@ -29,6 +30,8 @@ describe('ModelCreate', () => {
let wrapper;
let apolloProvider;
const file = { name: 'file.txt', size: 1024 };
beforeEach(() => {
jest.spyOn(Sentry, 'captureException').mockImplementation();
});
@ -54,8 +57,12 @@ describe('ModelCreate', () => {
},
provide: {
projectPath: 'some/project',
maxAllowedFileSize: 99999,
},
apolloProvider,
stubs: {
ImportArtifactZone,
},
});
};
@ -65,8 +72,9 @@ describe('ModelCreate', () => {
const findDescriptionInput = () => wrapper.findByTestId('descriptionId');
const findVersionDescriptionInput = () => wrapper.findByTestId('versionDescriptionId');
const findImportArtifactZone = () => wrapper.findComponent(ImportArtifactZone);
const zone = () => wrapper.findComponent(UploadDropzone);
const findGlModal = () => wrapper.findComponent(GlModal);
const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlAlert = () => wrapper.findByTestId('modal-create-alert');
const submitForm = async () => {
findGlModal().vm.$emit('primary', new Event('primary'));
await waitForPromises();
@ -181,6 +189,8 @@ describe('ModelCreate', () => {
findVersionInput().vm.$emit('input', '1.0.0');
findDescriptionInput().vm.$emit('input', 'My model description');
findVersionDescriptionInput().vm.$emit('input', 'My version description');
await Vue.nextTick();
zone().vm.$emit('change', file);
jest.spyOn(apolloProvider.defaultClient, 'mutate');
await submitForm();
@ -215,9 +225,10 @@ describe('ModelCreate', () => {
it('Uploads a file mutation upon confirm', () => {
expect(uploadModel).toHaveBeenCalledWith({
file: null,
file,
importPath: '/api/v4/projects/1/packages/ml_models/1/files/',
subfolder: '',
maxAllowedFileSize: 99999,
});
});
@ -256,6 +267,8 @@ describe('ModelCreate', () => {
findNameInput().vm.$emit('input', 'gpt-alice-1');
findVersionInput().vm.$emit('input', '1.0.0');
findVersionDescriptionInput().vm.$emit('input', 'My version description');
await Vue.nextTick();
zone().vm.$emit('change', file);
await submitForm();
});
@ -304,6 +317,8 @@ describe('ModelCreate', () => {
findVersionInput().vm.$emit('input', '1.0.0');
findDescriptionInput().vm.$emit('input', 'My model description');
findVersionDescriptionInput().vm.$emit('input', 'My version description');
await Vue.nextTick();
zone().vm.$emit('change', file);
uploadModel.mockRejectedValueOnce('Artifact import error.');
await submitForm();
});
@ -313,6 +328,16 @@ describe('ModelCreate', () => {
await submitForm(); // retry submit
expect(visitUrl).toHaveBeenCalledWith('/some/project/-/ml/models/1/versions/1');
});
it('Uploads a file mutation upon confirm', async () => {
await submitForm(); // retry submit
expect(uploadModel).toHaveBeenCalledWith({
file,
importPath: '/api/v4/projects/1/packages/ml_models/1/files/',
subfolder: '',
maxAllowedFileSize: 99999,
});
});
});
describe('Failed flow without version', () => {

View File

@ -11,6 +11,7 @@ let wrapper;
const createWrapper = (modelProp = model) => {
wrapper = shallowMountExtended(ModelDetail, {
propsData: { model: modelProp },
provide: { maxAllowedFileSize: 99999 },
stubs: { GlTab },
});
};

View File

@ -6,6 +6,7 @@ import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { visitUrl } from '~/lib/utils/url_utility';
import ModelVersionCreate from '~/ml/model_registry/components/model_version_create.vue';
import ImportArtifactZone from '~/ml/model_registry/components/import_artifact_zone.vue';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
import { uploadModel } from '~/ml/model_registry/services/upload_model';
import createModelVersionMutation from '~/ml/model_registry/graphql/mutations/create_model_version.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
@ -28,6 +29,8 @@ describe('ModelVersionCreate', () => {
let wrapper;
let apolloProvider;
const file = { name: 'file.txt', size: 1024 };
beforeEach(() => {
jest.spyOn(Sentry, 'captureException').mockImplementation();
});
@ -45,11 +48,15 @@ describe('ModelVersionCreate', () => {
wrapper = shallowMountExtended(ModelVersionCreate, {
provide: {
projectPath: 'some/project',
maxAllowedFileSize: 99999,
},
propsData: {
modelGid: 'gid://gitlab/Ml::Model/1',
},
apolloProvider,
stubs: {
UploadDropzone,
},
});
};
@ -57,6 +64,7 @@ describe('ModelVersionCreate', () => {
const findVersionInput = () => wrapper.findByTestId('versionId');
const findDescriptionInput = () => wrapper.findByTestId('descriptionId');
const findImportArtifactZone = () => wrapper.findComponent(ImportArtifactZone);
const zone = () => wrapper.findComponent(UploadDropzone);
const findGlModal = () => wrapper.findComponent(GlModal);
const findGlAlert = () => wrapper.findComponent(GlAlert);
const submitForm = async () => {
@ -128,6 +136,7 @@ describe('ModelVersionCreate', () => {
createWrapper();
findVersionInput().vm.$emit('input', '1.0.0');
findDescriptionInput().vm.$emit('input', 'My model version description');
zone().vm.$emit('change', file);
jest.spyOn(apolloProvider.defaultClient, 'mutate');
await submitForm();
@ -149,11 +158,13 @@ describe('ModelVersionCreate', () => {
it('Uploads a file mutation upon confirm', () => {
expect(uploadModel).toHaveBeenCalledWith({
file: null,
file,
importPath: '/api/v4/projects/1/packages/ml_models/1/files/',
subfolder: '',
maxAllowedFileSize: 99999,
});
});
it('Visits the model versions page upon successful create mutation', async () => {
createWrapper();
@ -194,18 +205,29 @@ describe('ModelVersionCreate', () => {
describe('Failed flow with file upload retried', () => {
beforeEach(async () => {
createWrapper();
findVersionInput().vm.$emit('input', '1.0.0');
zone().vm.$emit('change', file);
uploadModel.mockRejectedValueOnce('Artifact import error.');
await submitForm();
});
it('Visits the model versions page upon successful create mutation', async () => {
expect(findGlAlert().text()).toBe('Artifact import error.');
await submitForm();
expect(visitUrl).toHaveBeenCalledWith('/some/project/-/ml/models/1/versions/1');
});
it('Uploads the model upon retry', async () => {
await submitForm();
expect(uploadModel).toHaveBeenCalledWith({
file,
importPath: '/api/v4/projects/1/packages/ml_models/1/files/',
subfolder: '',
maxAllowedFileSize: 99999,
});
});
});
});
});

View File

@ -35,6 +35,7 @@ const createWrapper = (modelVersion = modelVersionWithCandidate, props = {}, pro
projectPath: 'path/to/project',
canWriteModelRegistry: true,
importPath: 'path/to/import',
maxAllowedFileSize: 99999,
...provide,
},
});

View File

@ -4,6 +4,8 @@ import { uploadModel } from '~/ml/model_registry/services/upload_model';
describe('uploadModel', () => {
const importPath = 'some/path';
const file = { name: 'file.txt', size: 1024 };
const largeFile = { name: 'file.txt', size: 2024 };
const maxAllowedFileSize = 2000;
let axiosMock;
beforeEach(() => {
@ -18,7 +20,7 @@ describe('uploadModel', () => {
it('should upload a file to the specified import path', async () => {
const filePath = `${importPath}/${encodeURIComponent(file.name)}`;
await uploadModel({ importPath, file });
await uploadModel({ importPath, file, maxAllowedFileSize });
expect(axiosMock).toHaveBeenCalledTimes(1);
expect(axiosMock).toHaveBeenCalledWith(filePath, expect.any(FormData), {
@ -32,7 +34,7 @@ describe('uploadModel', () => {
const subfolder = 'action';
const filePath = `${importPath}/action/${encodeURIComponent(file.name)}`;
await uploadModel({ importPath, file, subfolder });
await uploadModel({ importPath, file, subfolder, maxAllowedFileSize });
expect(axiosMock).toHaveBeenCalledTimes(1);
expect(axiosMock).toHaveBeenCalledWith(filePath, expect.any(FormData), {
@ -47,4 +49,12 @@ describe('uploadModel', () => {
expect(axiosMock).not.toHaveBeenCalled();
});
it('should raise an error for large files', async () => {
await expect(
uploadModel({ importPath: 'some/path', file: largeFile, maxAllowedFileSize }),
).rejects.toThrow(
new Error('File "file.txt" is 1.98 KiB. It is larger than max allowed size of 1.95 KiB'),
);
});
});

View File

@ -106,7 +106,7 @@ describe('UserBar component', () => {
const isuesCounter = findIssuesCounter();
expect(isuesCounter.props('count')).toBe(userCounts.assigned_issues);
expect(isuesCounter.props('href')).toBe(mockSidebarData.issues_dashboard_path);
expect(isuesCounter.props('label')).toBe(__('Issues'));
expect(isuesCounter.props('label')).toBe(__('Assigned issues'));
expect(isuesCounter.attributes('data-track-action')).toBe('click_link');
expect(isuesCounter.attributes('data-track-label')).toBe('issues_link');
expect(isuesCounter.attributes('data-track-property')).toBe('nav_core_menu');

View File

@ -36,7 +36,7 @@ RSpec.describe PreferencesHelper do
{ text: "Followed Users' Activity", value: 'followed_user_activity' },
{ text: "Your Groups", value: 'groups' },
{ text: "Your To-Do List", value: 'todos' },
{ text: "Assigned Issues", value: 'issues' },
{ text: "Assigned issues", value: 'issues' },
{ text: "Assigned merge requests", value: 'merge_requests' }
]
end

View File

@ -17,6 +17,7 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
'projectPath' => project.full_path,
'createModelPath' => "/#{project.full_path}/-/ml/models/new",
'canWriteModelRegistry' => true,
'maxAllowedFileSize' => 10737418240,
'mlflowTrackingUrl' => "http://localhost/api/v4/projects/#{project.id}/ml/mlflow/"
})
end
@ -47,6 +48,7 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
'projectPath' => project.full_path,
'indexModelsPath' => "/#{project.full_path}/-/ml/models",
'canWriteModelRegistry' => true,
'maxAllowedFileSize' => 10737418240,
'mlflowTrackingUrl' => "http://localhost/api/v4/projects/#{project.id}/ml/mlflow/",
'modelId' => model.id,
'modelName' => 'cool_model'
@ -86,6 +88,7 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
"modelName" => model_version.name,
"versionName" => model_version.version,
"canWriteModelRegistry" => true,
'maxAllowedFileSize' => 10737418240,
"importPath" => "/api/v4/projects/#{project.id}/packages/ml_models/#{model_version.id}/files/",
"modelPath" => "/#{project.full_path}/-/ml/models/1"
})

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillProjectRelationExportsProjectId,
feature_category: :importers,
schema: 20240605113246 do
include_examples 'desired sharding key backfill job' do
let(:batch_table) { :project_relation_exports }
let(:backfill_column) { :project_id }
let(:backfill_via_table) { :project_export_jobs }
let(:backfill_via_column) { :project_id }
let(:backfill_via_foreign_key) { :project_export_job_id }
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillTerraformStateVersionsProjectId,
feature_category: :infrastructure_as_code,
schema: 20240605132806 do
include_examples 'desired sharding key backfill job' do
let(:batch_table) { :terraform_state_versions }
let(:backfill_column) { :project_id }
let(:backfill_via_table) { :terraform_states }
let(:backfill_via_column) { :project_id }
let(:backfill_via_foreign_key) { :terraform_state_id }
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillProjectRelationExportsProjectId, feature_category: :importers do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).to have_scheduled_batched_migration(
table_name: :project_relation_exports,
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE,
gitlab_schema: :gitlab_main_cell,
job_arguments: [
:project_id,
:project_export_jobs,
:project_id,
:project_export_job_id
]
)
}
end
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe QueueBackfillTerraformStateVersionsProjectId, feature_category: :infrastructure_as_code do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).to have_scheduled_batched_migration(
table_name: :terraform_state_versions,
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE,
gitlab_schema: :gitlab_main_cell,
job_arguments: [
:project_id,
:terraform_states,
:project_id,
:terraform_state_id
]
)
}
end
end
end

View File

@ -48,9 +48,22 @@ require_relative '../config/initializers/0_inject_enterprise_edition_module'
require_relative '../config/initializers_before_autoloader/000_inflections'
require_relative '../config/initializers_before_autoloader/004_zeitwerk'
Rails.autoloaders.each do |autoloader|
autoloader.push_dir('lib')
autoloader.push_dir('ee/lib') if Gitlab.ee?
autoloader.push_dir('jh/lib') if Gitlab.jh?
autoloader.setup
# We wrap this bit of logic in a module to avoid polluting the global namespace with a local variable and methods
module AutoloadersSetup
def self.dir_already_autoloaded?(autoloaded_dirs, dir)
autoloaded_dirs.any? { File.expand_path(dir, __dir__) }
end
def self.setup_autoloaders
autoloaded_dirs = [] # NOTE: can't use Rails.autoloaders.each_with_object, it doesn't work, so we need a local var.
Rails.autoloaders.each do |autoloader|
autoloader.push_dir('lib') unless dir_already_autoloaded?(autoloaded_dirs, "../lib")
autoloader.push_dir('ee/lib') if Gitlab.ee? && !dir_already_autoloaded?(autoloaded_dirs, "../ee/lib")
autoloader.push_dir('jh/lib') if Gitlab.jh? && !dir_already_autoloaded?(autoloaded_dirs, "../jh/lib")
autoloader.setup
autoloaded_dirs += autoloader.dirs
end
end
end
AutoloadersSetup.setup_autoloaders

View File

@ -735,13 +735,13 @@ RSpec.shared_examples 'work items time tracking' do
click_button '3d'
expect(page).to have_css 'h2', text: 'Time tracking report'
expect(page).to have_text '1d Sidney Jones1 First summary'
expect(page).to have_text '2d Sidney Jones1 Second summary'
expect(page).to have_text "1d #{user.name} First summary"
expect(page).to have_text "2d #{user.name} Second summary"
click_button 'Delete time spent', match: :first
expect(page).to have_text '1d Sidney Jones1 First summary'
expect(page).not_to have_text '2d Sidney Jones1 Second summary'
expect(page).to have_text "1d #{user.name} First summary"
expect(page).not_to have_text "2d #{user.name} Second summary"
click_button 'Close'

View File

@ -1,27 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIEjjCCAvagAwIBAgIQC2au+A/aGQ2Z21O0wVoEwjANBgkqhkiG9w0BAQsFADCB
pTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMT0wOwYDVQQLDDRpZ29y
ZHJvemRvdkBJZ29ycy1NYWNCb29rLVByby0yLmxvY2FsIChJZ29yIERyb3pkb3Yp
MUQwQgYDVQQDDDtta2NlcnQgaWdvcmRyb3pkb3ZASWdvcnMtTWFjQm9vay1Qcm8t
Mi5sb2NhbCAoSWdvciBEcm96ZG92KTAeFw0yMjAzMDcwNDMxMjRaFw0yNDA2MDcw
NDMxMjRaMGgxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0
ZTE9MDsGA1UECww0aWdvcmRyb3pkb3ZASWdvcnMtTWFjQm9vay1Qcm8tMi5sb2Nh
bCAoSWdvciBEcm96ZG92KTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMJ8ofGdcnenVRtNGViF4oxPv+CCFA6D2nfsjkJG8kmO6WW7VlbhJYxCMAuyFF1F
b2UI2rrTFL8Aeq1KxeQzdrb3cpCquVH/UQ00G4ply28XVPRdbIyLQvOThMEeLL6v
6gb4edL5oZmo/vWhdQxv0NGt282PAEt+bjnbdl28on8WVzmsw/m0nZ2BVWke+oUM
krfsbyFaZj7aW8w0dNeK25ANy/Ldx55ENRDquphwYHDnpFOQpkHo5nPuoms5j2Sf
GW3u3hgeFhRrFjqDstU3OKdA4AdHntDjl0gHm35w1m8PXiql/3EpkEMMx5ixQAqM
cMZ7VVzy0HIjqsjdJZpzjx8CAwEAAaN2MHQwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
JQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFKTVZ2JsYLGJOP+UX0AwGO/81Kab
MCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAN
BgkqhkiG9w0BAQsFAAOCAYEAkGntoogSlhukGqTNbTXN9T/gXLtx9afWlgcBEafF
MYQoJ1DOwXoYCQkMsxE0xWUyLDTpvjzfKkkyQwWzTwcYqRHOKafKYVSvENU5oaDY
c2nk32SfkcF6bqJ50uBlFMEvKFExU1U+YSJhuEH/iqT9sSd52uwmnB0TJhSOc3J/
1ZapKM2G71ezi8OyizwlwDJAwQ37CqrYS2slVO6Cy8zJ1l/ZsZ+kxRb+ME0LREI0
J/rFTo9A6iyuXeBQ2jiRUrC6pmmbUQbVSjROx4RSmWoI/58/VnuZBY9P62OAOgUv
pukfAbh3SUjN5++m4Py7WjP/y+L2ILPOFtxTY+CQPWQ5Hbff8iMB4NNfutdU1wSS
CzXT1zWbU12kXod80wkMqWvNb3yU5spqXV6WYhOHiDIyqpPIqp5/i93Ck3Hd6/BQ
DYlNOQsVHdSjWzNw9UubjpatiFqMK4hvJZE0haoLlmfDeZeqWk9oAuuCibLJGPg4
TQri+lKgi0e76ynUr1zP1xUR
MIIDfDCCAmSgAwIBAgIUa/KdQW7BbWxtDFUBbRpFxEvR2wUwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MDcwOTI2MjlaFw0zMjA4
MjQwOTI2MjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDCfKHxnXJ3p1UbTRlYheKMT7/gghQOg9p37I5CRvJJ
jullu1ZW4SWMQjALshRdRW9lCNq60xS/AHqtSsXkM3a293KQqrlR/1ENNBuKZctv
F1T0XWyMi0Lzk4TBHiy+r+oG+HnS+aGZqP71oXUMb9DRrdvNjwBLfm4523ZdvKJ/
Flc5rMP5tJ2dgVVpHvqFDJK37G8hWmY+2lvMNHTXituQDcvy3ceeRDUQ6rqYcGBw
56RTkKZB6OZz7qJrOY9knxlt7t4YHhYUaxY6g7LVNzinQOAHR57Q45dIB5t+cNZv
D14qpf9xKZBDDMeYsUAKjHDGe1Vc8tByI6rI3SWac48fAgMBAAGjZDBiMB0GA1Ud
DgQWBBQBEdAtdWtE9gHf3Dq1I02pE2gQGDAfBgNVHSMEGDAWgBQBEdAtdWtE9gHf
3Dq1I02pE2gQGDAPBgNVHRMBAf8EBTADAQH/MA8GA1UdEQQIMAaHBH8AAAEwDQYJ
KoZIhvcNAQELBQADggEBAGJeDycH50Olfcrm+nr8eOSMTf4Ehq2QRDZabFClp2sy
HHGfqqbCtrBFpshI+T04H10ncUEM6GQR/iQ4+dWR3LppZWc4bQAIE4145n4jsEP1
xwYgiSGRwx3Blzb6RlkTwHmaJpEw9mOSHMkt/0syicvgrPb/dEPrO26IhBhY3Me3
H1tpGYQQW6fXx8nJNHrVmzlp9Be0N71wNBocD2nnmaHf8+m9CF7kQ28zXrawC5lz
JRafQi4fxUQC01cGoJmva1wa9xr4LItVp6PcOowohh3x5mmtySYxYi9DVgHJRmci
6ANK7CUbCaAydlv4EX3sz3YMEe35+e1n3I19wkHWqBA=
-----END CERTIFICATE-----