Add latest changes from gitlab-org/gitlab@master
|
|
@ -32,6 +32,7 @@ RSpec/BeEq:
|
|||
- 'ee/spec/elastic/migrate/20240130144625_reindex_epics_to_update_analyzer_spec.rb'
|
||||
- 'ee/spec/elastic/migrate/20240814231502_remove_work_item_access_level_from_work_item_spec.rb'
|
||||
- 'ee/spec/elastic/migrate/20241002103536_reindex_merge_requests_for_title_completion_spec.rb'
|
||||
- 'ee/spec/elastic/migrate/20241017094601_add_embedding_to_work_items_opensearch_spec.rb'
|
||||
- 'ee/spec/features/admin/admin_emails_spec.rb'
|
||||
- 'ee/spec/features/admin/admin_settings_spec.rb'
|
||||
- 'ee/spec/features/admin/users/users_spec.rb'
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ RSpec/ChangeByZero:
|
|||
- 'ee/spec/services/software_license_policies/bulk_create_scan_result_policy_service_spec.rb'
|
||||
- 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
|
||||
- 'ee/spec/services/vulnerabilities/security_finding/create_merge_request_service_spec.rb'
|
||||
- 'ee/spec/services/work_items/legacy_epics/related_epic_links/create_service_spec.rb'
|
||||
- 'ee/spec/support/shared_examples/models/concerns/replicable_model_with_separate_table_shared_examples.rb'
|
||||
- 'ee/spec/workers/observability/alert_query_worker_spec.rb'
|
||||
- 'ee/spec/workers/security/store_security_reports_by_project_worker_spec.rb'
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ RSpec/ContainExactly:
|
|||
- 'spec/lib/gitlab/database/load_balancing/host_list_spec.rb'
|
||||
- 'spec/lib/gitlab/database/loose_foreign_keys_spec.rb'
|
||||
- 'spec/lib/gitlab/database/migrations/instrumentation_spec.rb'
|
||||
- 'spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb'
|
||||
- 'spec/lib/gitlab/database_spec.rb'
|
||||
- 'spec/lib/gitlab/feature_categories_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<script>
|
||||
import ModelEdit from '../components/model_edit.vue';
|
||||
|
||||
export default {
|
||||
name: 'EditMlModel',
|
||||
components: {
|
||||
ModelEdit,
|
||||
},
|
||||
props: {
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
canWriteModelRegistry: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
markdownPreviewPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
modelName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelDescription: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
model() {
|
||||
return {
|
||||
id: this.modelId,
|
||||
name: this.modelName,
|
||||
description: this.modelDescription,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<model-edit
|
||||
v-if="canWriteModelRegistry"
|
||||
:model="model"
|
||||
:project-path="projectPath"
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:model-path="modelPath"
|
||||
:disable-attachments="false"
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -3,5 +3,13 @@ import ShowMlModelVersion from './show_ml_model_version.vue';
|
|||
import NewMlModelVersion from './new_ml_model_version.vue';
|
||||
import IndexMlModels from './index_ml_models.vue';
|
||||
import NewMlModel from './new_ml_model.vue';
|
||||
import EditMlModel from './edit_ml_model.vue';
|
||||
|
||||
export { ShowMlModel, ShowMlModelVersion, IndexMlModels, NewMlModel, NewMlModelVersion };
|
||||
export {
|
||||
EditMlModel,
|
||||
IndexMlModels,
|
||||
NewMlModel,
|
||||
NewMlModelVersion,
|
||||
ShowMlModel,
|
||||
ShowMlModelVersion,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
|||
import DeleteDisclosureDropdownItem from '../components/delete_disclosure_dropdown_item.vue';
|
||||
import LoadOrErrorOrShow from '../components/load_or_error_or_show.vue';
|
||||
import DeleteModel from '../components/functional/delete_model.vue';
|
||||
import ModelEdit from '../components/model_edit.vue';
|
||||
|
||||
const ROUTE_DETAILS = 'details';
|
||||
const ROUTE_VERSIONS = 'versions';
|
||||
|
|
@ -53,7 +52,6 @@ export default {
|
|||
MetadataItem,
|
||||
LoadOrErrorOrShow,
|
||||
DeleteModel,
|
||||
ModelEdit,
|
||||
},
|
||||
router: new VueRouter({
|
||||
routes,
|
||||
|
|
@ -66,6 +64,7 @@ export default {
|
|||
maxAllowedFileSize: this.maxAllowedFileSize,
|
||||
latestVersion: this.latestVersion,
|
||||
markdownPreviewPath: this.markdownPreviewPath,
|
||||
editModelPath: this.editModelPath,
|
||||
createModelVersionPath: this.createModelVersionPath,
|
||||
modelGid: this.modelGid,
|
||||
};
|
||||
|
|
@ -87,6 +86,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
editModelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createModelVersionPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -179,6 +182,7 @@ export default {
|
|||
},
|
||||
i18n: {
|
||||
createModelVersionLinkTitle: s__('MlModelRegistry|Create model version'),
|
||||
editModelButtonLabel: s__('MlModelRegistry|Edit model'),
|
||||
},
|
||||
modelVersionEntity: MODEL_ENTITIES.modelVersion,
|
||||
ROUTE_DETAILS,
|
||||
|
|
@ -196,7 +200,13 @@ export default {
|
|||
</template>
|
||||
|
||||
<template #right-actions>
|
||||
<model-edit v-if="canWriteModelRegistry" :model="model" />
|
||||
<gl-button
|
||||
v-if="canWriteModelRegistry"
|
||||
data-testid="edit-model-button"
|
||||
variant="confirm"
|
||||
:href="editModelPath"
|
||||
>{{ $options.i18n.editModelButtonLabel }}</gl-button
|
||||
>
|
||||
<gl-button
|
||||
v-if="canWriteModelRegistry"
|
||||
data-testid="model-version-create-button"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
<script>
|
||||
import {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlForm,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
|
|
@ -16,7 +8,6 @@ import { helpPagePath } from '~/helpers/help_page_helper';
|
|||
import { noSpacesRegex } from '~/lib/utils/regexp';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import editModelMutation from '../graphql/mutations/edit_model.mutation.graphql';
|
||||
import { MODEL_EDIT_MODAL_ID } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'ModelEdit',
|
||||
|
|
@ -24,15 +15,10 @@ export default {
|
|||
MarkdownEditor,
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlModal,
|
||||
GlForm,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
},
|
||||
directives: {
|
||||
GlModal: GlModalDirective,
|
||||
},
|
||||
inject: ['projectPath', 'maxAllowedFileSize', 'markdownPreviewPath'],
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
|
|
@ -43,6 +29,18 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
markdownPreviewPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -60,23 +58,12 @@ export default {
|
|||
modelNameIsValid() {
|
||||
return this.model.name && noSpacesRegex.test(this.model.name);
|
||||
},
|
||||
submitButtonDisabled() {
|
||||
return !this.modelNameIsValid;
|
||||
},
|
||||
actionPrimary() {
|
||||
return {
|
||||
text: s__('MlModelRegistry|Save changes'),
|
||||
attributes: { variant: 'confirm', disabled: this.submitButtonDisabled },
|
||||
};
|
||||
},
|
||||
modelNameDescription() {
|
||||
return !this.model.name || this.modelNameIsValid ? this.$options.modal.nameDescription : '';
|
||||
return !this.model.name || this.modelNameIsValid ? this.$options.i18n.nameDescription : '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async edit($event) {
|
||||
$event.preventDefault();
|
||||
|
||||
async edit() {
|
||||
this.errorMessage = '';
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
|
|
@ -100,12 +87,6 @@ export default {
|
|||
this.errorMessage = error;
|
||||
}
|
||||
},
|
||||
resetModal() {
|
||||
this.name = null;
|
||||
this.modelData = null;
|
||||
this.description = null;
|
||||
this.errorMessage = null;
|
||||
},
|
||||
hideAlert() {
|
||||
this.errorMessage = null;
|
||||
},
|
||||
|
|
@ -115,23 +96,18 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
i18n: {},
|
||||
descriptionFormFieldProps: {
|
||||
placeholder: s__('MlModelRegistry|Enter a model description'),
|
||||
id: 'model-description',
|
||||
name: 'model-description',
|
||||
},
|
||||
modal: {
|
||||
id: MODEL_EDIT_MODAL_ID,
|
||||
actionSecondary: {
|
||||
text: __('Cancel'),
|
||||
attributes: { variant: 'default' },
|
||||
},
|
||||
i18n: {
|
||||
actionSecondaryText: __('Cancel'),
|
||||
actionPrimaryText: s__('MlModelRegistry|Save changes'),
|
||||
nameDescriptionLabel: s__('MlModelRegistry|Must be unique. May not contain spaces.'),
|
||||
nameDescription: s__('MlModelRegistry|Example: my-model'),
|
||||
nameInvalid: s__('MlModelRegistry|May not contain spaces.'),
|
||||
nameDescriptionPlaceholder: s__('MlModelRegistry|Enter a model description'),
|
||||
editButtonLabel: s__('MlModelRegistry|Edit model'),
|
||||
title: s__('MlModelRegistry|Edit model'),
|
||||
modelName: s__('MlModelRegistry|Model name'),
|
||||
modelDescription: __('Model description'),
|
||||
|
|
@ -142,67 +118,58 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button v-gl-modal="$options.modal.id">{{ $options.modal.editButtonLabel }}</gl-button>
|
||||
<gl-modal
|
||||
:modal-id="$options.modal.id"
|
||||
:title="$options.modal.title"
|
||||
:action-primary="actionPrimary"
|
||||
:action-secondary="$options.modal.actionSecondary"
|
||||
size="lg"
|
||||
@primary="edit"
|
||||
@secondary="resetModal"
|
||||
>
|
||||
<gl-form>
|
||||
<gl-form-group
|
||||
:label="$options.modal.modelName"
|
||||
:label-description="$options.modal.nameDescriptionLabel"
|
||||
label-for="nameId"
|
||||
data-testid="nameGroupId"
|
||||
:state="modelNameIsValid"
|
||||
:invalid-feedback="$options.modal.nameInvalid"
|
||||
:description="modelNameDescription"
|
||||
>
|
||||
<gl-form-input
|
||||
id="nameId"
|
||||
:value="model.name"
|
||||
data-testid="nameId"
|
||||
type="text"
|
||||
:disabled="true"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
:label="$options.modal.modelDescription"
|
||||
data-testid="descriptionGroupId"
|
||||
label-for="descriptionId"
|
||||
optional
|
||||
:optional-text="$options.modal.optionalText"
|
||||
class="common-note-form gfm-form js-main-target-form new-note gl-grow"
|
||||
>
|
||||
<markdown-editor
|
||||
ref="markdownEditor"
|
||||
data-testid="descriptionId"
|
||||
:value="model.description"
|
||||
enable-autocomplete
|
||||
:autocomplete-data-sources="autocompleteDataSources"
|
||||
:enable-content-editor="true"
|
||||
:form-field-props="$options.descriptionFormFieldProps"
|
||||
:render-markdown-path="markdownPreviewPath"
|
||||
:markdown-docs-path="markdownDocPath"
|
||||
:disable-attachments="disableAttachments"
|
||||
:placeholder="$options.modal.nameDescriptionPlaceholder"
|
||||
:restricted-tool-bar-items="markdownEditorRestrictedToolBarItems"
|
||||
@input="setDescription"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</gl-form>
|
||||
|
||||
<gl-alert
|
||||
v-if="errorMessage"
|
||||
data-testid="modalEditAlert"
|
||||
variant="danger"
|
||||
@dismiss="hideAlert"
|
||||
>{{ errorMessage }}
|
||||
</gl-alert>
|
||||
</gl-modal>
|
||||
<h2>{{ $options.i18n.title }}</h2>
|
||||
<gl-form>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.modelName"
|
||||
:label-description="$options.i18n.nameDescriptionLabel"
|
||||
label-for="nameId"
|
||||
data-testid="nameGroupId"
|
||||
:state="modelNameIsValid"
|
||||
:invalid-feedback="$options.i18n.nameInvalid"
|
||||
:description="modelNameDescription"
|
||||
>
|
||||
<gl-form-input
|
||||
id="nameId"
|
||||
:value="model.name"
|
||||
data-testid="nameId"
|
||||
type="text"
|
||||
:disabled="true"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.modelDescription"
|
||||
data-testid="descriptionGroupId"
|
||||
label-for="descriptionId"
|
||||
optional
|
||||
:optional-text="$options.i18n.optionalText"
|
||||
class="common-note-form gfm-form js-main-target-form new-note gl-grow"
|
||||
>
|
||||
<markdown-editor
|
||||
ref="markdownEditor"
|
||||
data-testid="descriptionId"
|
||||
:value="model.description"
|
||||
enable-autocomplete
|
||||
:autocomplete-data-sources="autocompleteDataSources"
|
||||
:enable-content-editor="true"
|
||||
:form-field-props="$options.descriptionFormFieldProps"
|
||||
:render-markdown-path="markdownPreviewPath"
|
||||
:markdown-docs-path="markdownDocPath"
|
||||
:disable-attachments="disableAttachments"
|
||||
:placeholder="$options.i18n.nameDescriptionPlaceholder"
|
||||
:restricted-tool-bar-items="markdownEditorRestrictedToolBarItems"
|
||||
@input="setDescription"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</gl-form>
|
||||
<gl-alert v-if="errorMessage" data-testid="edit-alert" variant="danger" @dismiss="hideAlert"
|
||||
>{{ errorMessage }}
|
||||
</gl-alert>
|
||||
<gl-button data-testid="secondary-button" variant="default" :href="modelPath"
|
||||
>{{ $options.i18n.actionSecondaryText }}
|
||||
</gl-button>
|
||||
<gl-button data-testid="primary-button" variant="confirm" @click="edit"
|
||||
>{{ $options.i18n.actionPrimaryText }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ export const MODEL_ENTITIES = {
|
|||
};
|
||||
|
||||
export const MLFLOW_USAGE_MODAL_ID = 'model-registry-mlflow-usage-modal';
|
||||
export const MODEL_EDIT_MODAL_ID = 'edit-model-modal';
|
||||
export const MODEL_VERSION_EDIT_MODAL_ID = 'edit-model-version-modal';
|
||||
|
||||
export const emptyArtifactFile = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import { initSimpleApp } from '~/helpers/init_simple_app_helper';
|
||||
import { EditMlModel } from '~/ml/model_registry/apps';
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
initSimpleApp('#js-mount-edit-ml-model', EditMlModel, { withApolloProvider: true });
|
||||
|
|
@ -4,6 +4,7 @@ import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merg
|
|||
import autoMergeEnabledQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql';
|
||||
import { createAlert } from '~/alert';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { __ } from '~/locale';
|
||||
import { AUTO_MERGE_STRATEGIES } from '../../constants';
|
||||
import eventHub from '../../event_hub';
|
||||
|
|
@ -16,6 +17,7 @@ export default {
|
|||
apollo: {
|
||||
state: {
|
||||
query: autoMergeEnabledQuery,
|
||||
fetchPolicy: fetchPolicies.NETWORK_ONLY,
|
||||
variables() {
|
||||
return this.mergeRequestQueryVariables;
|
||||
},
|
||||
|
|
@ -41,14 +43,14 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
state: {},
|
||||
state: null,
|
||||
isCancellingAutoMerge: false,
|
||||
isRemovingSourceBranch: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return this.$apollo.queries.state.loading && Object.keys(this.state).length === 0;
|
||||
return this.$apollo.queries.state.loading || !this.state;
|
||||
},
|
||||
stateRemoveSourceBranch() {
|
||||
if (!this.state.mergeRequest.shouldRemoveSourceBranch) return false;
|
||||
|
|
|
|||
|
|
@ -8,3 +8,7 @@ $code-flow-tabs-height: 51px;
|
|||
.code-flow-content {
|
||||
height: calc(100vh - #{$performance-bar-height} - #{$top-bar-height} - var(--broadcast-message-height, 0px) - #{$detail-page-header-height} - #{$vulnerability-page-title-height} - #{$content-wrapper-padding} - #{$code-flow-tabs-height});
|
||||
}
|
||||
|
||||
.code-flow-content-modal {
|
||||
height: var(--modal-content-height);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class Projects::BuildArtifactsController < Projects::ApplicationController
|
||||
include ExtractsPath
|
||||
include RendersBlob
|
||||
|
||||
before_action :authorize_read_build!
|
||||
before_action :extract_ref_name_and_path
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ module Projects
|
|||
module Ml
|
||||
class ModelsController < ::Projects::ApplicationController
|
||||
before_action :authorize_read_model_registry!
|
||||
before_action :authorize_write_model_registry!, only: [:destroy, :new]
|
||||
before_action :set_model, only: [:show, :destroy]
|
||||
before_action :authorize_write_model_registry!, only: [:destroy, :new, :edit]
|
||||
before_action :set_model, only: [:show, :destroy, :edit]
|
||||
feature_category :mlops
|
||||
|
||||
MAX_MODELS_PER_PAGE = 20
|
||||
|
|
@ -16,6 +16,8 @@ module Projects
|
|||
|
||||
def show; end
|
||||
|
||||
def edit; end
|
||||
|
||||
def destroy
|
||||
@model.destroy!
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ module Projects
|
|||
data = {
|
||||
projectPath: project.full_path,
|
||||
index_models_path: project_ml_models_path(project),
|
||||
edit_model_path: edit_project_ml_model_path(project, model.id),
|
||||
create_model_version_path: new_project_ml_model_version_path(project, model_model_id: model.id),
|
||||
can_write_model_registry: can_write_model_registry?(user, project),
|
||||
mlflow_tracking_url: mlflow_tracking_url(project),
|
||||
|
|
@ -48,6 +49,22 @@ module Projects
|
|||
to_json(data)
|
||||
end
|
||||
|
||||
def edit_ml_model_data(model, user)
|
||||
project = model.project
|
||||
|
||||
data = {
|
||||
projectPath: project.full_path,
|
||||
can_write_model_registry: can_write_model_registry?(user, project),
|
||||
markdown_preview_path: preview_markdown_path(project),
|
||||
model_path: project_ml_model_path(project, model),
|
||||
model_id: model.id,
|
||||
model_name: model.name,
|
||||
model_description: model.description.to_s
|
||||
}
|
||||
|
||||
to_json(data)
|
||||
end
|
||||
|
||||
def new_ml_model_version_data(model, user)
|
||||
project = model.project
|
||||
|
||||
|
|
|
|||
|
|
@ -99,12 +99,7 @@ module Ci
|
|||
end
|
||||
|
||||
def assign_resource_from_resource_group(processable)
|
||||
if Feature.enabled?(:assign_resource_worker_deduplicate_until_executing, processable.project) &&
|
||||
Feature.disabled?(:assign_resource_worker_deduplicate_until_executing_override, processable.project)
|
||||
Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2.perform_async(processable.resource_group_id)
|
||||
else
|
||||
Ci::ResourceGroups::AssignResourceFromResourceGroupWorker.perform_async(processable.resource_group_id)
|
||||
end
|
||||
Ci::ResourceGroups::AssignResourceFromResourceGroupWorker.perform_async(processable.resource_group_id)
|
||||
end
|
||||
|
||||
def self.select_with_aggregated_needs(project)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
- add_to_breadcrumbs s_('ModelRegistry|Model registry'), project_ml_models_path(@model.project)
|
||||
- breadcrumb_title @model.name
|
||||
- page_title @model.name
|
||||
|
||||
#js-mount-edit-ml-model{ data: { view_model: edit_ml_model_data(@model, @current_user) } }
|
||||
|
|
@ -8,6 +8,14 @@ module Ci
|
|||
# whereas this AssignResourceFromResourceGroupWorkerV2 has a strategy of `until_executing`.
|
||||
# We are also using the same data_consistency (always) as the old AssignResourceFromResourceGroupWorker
|
||||
# since this needs to process the latest/real-time data.
|
||||
#
|
||||
# 2024-10-17 Update:
|
||||
# This worker is no longer needed and is not being called anywhere in the code base.
|
||||
# This will no-op when `perform` is called.
|
||||
# See: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168483
|
||||
#
|
||||
# TODO: this worker should be completely removed in the future.
|
||||
# Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/499658
|
||||
class AssignResourceFromResourceGroupWorkerV2
|
||||
include ApplicationWorker
|
||||
|
||||
|
|
@ -19,20 +27,10 @@ module Ci
|
|||
queue_namespace :pipeline_processing
|
||||
feature_category :continuous_delivery
|
||||
|
||||
# This worker is idempotent that it produces the same result
|
||||
# as long as the same resource group id is passed as an argument.
|
||||
# We often run into a race condition with an `until_executed` strategy,
|
||||
# so we are using an `until_executing` strategy here.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/436988 for details.
|
||||
idempotent!
|
||||
deduplicate :until_executing, if_deduplicated: :reschedule_once, including_scheduled: true
|
||||
|
||||
def perform(resource_group_id)
|
||||
::Ci::ResourceGroup.find_by_id(resource_group_id).try do |resource_group|
|
||||
Ci::ResourceGroups::AssignResourceFromResourceGroupService.new(resource_group.project, nil)
|
||||
.execute(resource_group)
|
||||
end
|
||||
end
|
||||
def perform(resource_group_id); end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: assign_resource_worker_deduplicate_until_executing
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/436988
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/152303
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460793
|
||||
milestone: '17.1'
|
||||
group: group::environments
|
||||
type: beta
|
||||
default_enabled: true
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: assign_resource_worker_deduplicate_until_executing_override
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/436988
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/160564/
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/460793
|
||||
milestone: '17.3'
|
||||
group: group::environments
|
||||
type: beta
|
||||
default_enabled: false
|
||||
|
|
@ -479,7 +479,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
namespace :ml do
|
||||
resources :experiments, only: [:index, :show, :destroy], controller: 'experiments', param: :iid
|
||||
resources :candidates, only: [:show, :destroy], controller: 'candidates', param: :iid
|
||||
resources :models, only: [:index, :show, :destroy, :new], controller: 'models', param: :model_id do
|
||||
resources :models, only: [:index, :show, :edit, :destroy, :new], controller: 'models', param: :model_id do
|
||||
resources :versions, only: [:new], controller: 'model_versions'
|
||||
resources :versions, only: [:show], controller: 'model_versions', param: :model_version_id
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,4 +11,3 @@ description: SSH keys used by users or for deployments.
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9ba1224867665844b117fa037e1465bb706b3685
|
||||
milestone: "<6.0"
|
||||
gitlab_schema: gitlab_main_clusterwide
|
||||
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/463929
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,60 @@ blobs start being deleted is anything permanent done.
|
|||
You can run garbage collection in the background without the need to schedule it or require read-only mode,
|
||||
if you migrate to the [metadata database](container_registry_metadata_database.md).
|
||||
|
||||
## Scaling by component
|
||||
|
||||
This section outlines the potential performance bottlenecks as registry traffic increases by component.
|
||||
Each subsection is roughly ordered by recommendations that benefit from smaller to larger registry workloads.
|
||||
The registry is not included in the [reference architectures](../reference_architectures/index.md),
|
||||
and there are no scaling guides which target number of seats or requests per second.
|
||||
|
||||
### Database
|
||||
|
||||
1. **Move to a separate database**: As database load increases, scale vertically by moving the registry metadata database
|
||||
to a separate physical database. A separate database can increase the amount of resources available
|
||||
to the registry database while isolating the traffic produced by the registry.
|
||||
1. **Move to a HA PostgreSQL third-party solution**: Similar to [Praefect](../reference_architectures/5k_users.md#praefect-ha-postgresql-third-party-solution),
|
||||
moving to a reputable provider or solution enables HA and is suitable for multi-node registry deployments.
|
||||
You must pick a provider that supports native Postgres partitioning, triggers, and functions,
|
||||
as the registry makes heavy use of these.
|
||||
|
||||
### Registry server
|
||||
|
||||
1. **Move to a separate node**: A [separate node](#configure-gitlab-and-registry-to-run-on-separate-nodes-linux-package-installations)
|
||||
is one way to scale vertically to increase the resources available to the container registry server process.
|
||||
1. **Run multiple registry nodes behind a load balancer**: While the registry can handle
|
||||
a high amount of traffic with a single large node, the registry is generally intended to
|
||||
scale horizontally with multiple deployments. Configuring multiple smaller nodes
|
||||
also enables techniques such as autoscaling.
|
||||
|
||||
### Redis Cache
|
||||
|
||||
Enabling the [Redis](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/configuration.md?ref_type=heads#redis)
|
||||
cache improves performance, but also enables features such as renaming repositories.
|
||||
|
||||
1. **Redis Server**: A single Redis instance is supported and is the simplest way
|
||||
to access the benefits of the Redis caching.
|
||||
1. **Redis Sentinel**: Redis Sentinel is also supported and enables the cache to be HA.
|
||||
1. **Redis Cluster**: Redis Cluster can also be used for further scaling as deployments grow.
|
||||
|
||||
### Storage
|
||||
|
||||
1. **Local file system**: A local file system is the default and is relatively performant,
|
||||
but not suitable for multi-node deployments or a large amount of registry data.
|
||||
1. **Object storage**: [Use object storage](#use-object-storage) to enable the practical storage
|
||||
of a larger amount of registry data. Object storage is also suitable for multi-node registry deployments.
|
||||
|
||||
### Online garbage collection
|
||||
|
||||
1. **Adjust defaults**: If online garbage collection is not reliably clearing the [review queues](container_registry_metadata_database.md#queue-monitoring),
|
||||
you can adjust the `interval` settings in the `manifests` and `blobs` sections under the
|
||||
[`gc`](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/configuration.md?ref_type=heads#gc)
|
||||
configuration section. The default is `5s`, and these can be configured with milliseconds as well,
|
||||
for example `500ms`.
|
||||
1. **Scale horizontally with the registry server**: If you are scaling the registry application horizontally
|
||||
with multi-node deployments, online garbage collection automatically scales without
|
||||
the need for configuration changes.
|
||||
|
||||
## Configure GitLab and Registry to run on separate nodes (Linux package installations)
|
||||
|
||||
By default, package assumes that both services are running on the same node.
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ can be prevented in some circumstances.
|
|||
> - Job retries for rollback deployments checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/410427) in GitLab 16.3.
|
||||
|
||||
You might need to quickly roll back to a stable, outdated deployment.
|
||||
By default, pipeline job retries for [deployment rollback](index.md#environment-rollback) are enabled.
|
||||
By default, pipeline job retries for [deployment rollback](index.md#deployment-rollback) are enabled.
|
||||
|
||||
To disable pipeline retries, clear the **Allow job retries for rollback deployments** checkbox. You should disable pipeline retries in sensitive projects.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ For example, the following features are available by setting up tracking:
|
|||
|
||||
NOTE:
|
||||
Some of the features are not available because GitLab can't authorize and leverage those external deployments, including
|
||||
[Protected Environments](protected_environments.md), [Deployment Approvals](deployment_approvals.md), [Deployment safety](deployment_safety.md), and [Environment rollback](index.md#environment-rollback).
|
||||
[Protected Environments](protected_environments.md), [Deployment Approvals](deployment_approvals.md), [Deployment safety](deployment_safety.md), and [Deployment rollback](index.md#deployment-rollback).
|
||||
|
||||
## How to set up deployment tracking
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ DETAILS:
|
|||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
Environments describe where code is deployed.
|
||||
Environments connect GitLab to your infrastructure. An environment:
|
||||
|
||||
- Can monitor and deploy to its target infrastructure.
|
||||
- Has its own variables.
|
||||
- Can be long-lived or ephemeral, depending on its use case.
|
||||
|
||||
In addition, access to an environment can be controlled.
|
||||
|
||||
Each time [GitLab CI/CD](../index.md) deploys a version of code to an environment,
|
||||
a deployment is created.
|
||||
|
|
@ -47,52 +53,182 @@ There are a few ways to view a list of environments for a given project:
|
|||
|
||||
Deployments show up in this list only after a deployment job has created them.
|
||||
|
||||
## Search environments
|
||||
### Environment URL
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10754) in GitLab 15.5.
|
||||
> - [Searching environments within a folder](https://gitlab.com/gitlab-org/gitlab/-/issues/373850) was introduced in GitLab 15.7 with [Feature flag `enable_environments_search_within_folder`](https://gitlab.com/gitlab-org/gitlab/-/issues/382108). Enabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/382108) in GitLab 17.4. Feature flag `enable_environments_search_within_folder` removed.
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/337417) to persist arbitrary URLs in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `soft_validation_on_external_url`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/337417) in GitLab 15.3. [Feature flag `soft_validation_on_external_url`](https://gitlab.com/gitlab-org/gitlab/-/issues/367206) removed.
|
||||
|
||||
To search environments by name:
|
||||
The [environment URL](../yaml/index.md#environmenturl) is displayed in a few
|
||||
places in GitLab:
|
||||
|
||||
- In a merge request as a link:
|
||||

|
||||
- In the Environments view as a button:
|
||||

|
||||
- In the Deployments view as a button:
|
||||

|
||||
|
||||
You can see this information in a merge request if:
|
||||
|
||||
- The merge request is eventually merged to the default branch (usually `main`).
|
||||
- That branch also deploys to an environment (for example, `staging` or `production`).
|
||||
|
||||
For example:
|
||||
|
||||

|
||||
|
||||
#### Go from source files to public pages
|
||||
|
||||
With GitLab [Route Maps](../review_apps/index.md#route-maps), you can go directly
|
||||
from source files to public pages in the environment set for review apps.
|
||||
|
||||
## Working with deployments
|
||||
|
||||
When you deploy a version of your code to an environment, you create a deployment.
|
||||
There is usually only one active deployment per environment.
|
||||
|
||||
After a deployment is created, you can roll it out to users.
|
||||
|
||||
### Configure manual deployments
|
||||
|
||||
You can create a job that requires someone to manually start the deployment.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to production server"
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
when: manual
|
||||
```
|
||||
|
||||
The `when: manual` action:
|
||||
|
||||
- Exposes the **Run** (**{play}**) button for the job in the GitLab UI, with the text **Can be manually deployed to <environment>**.
|
||||
- Means the `deploy_prod` job must be triggered manually.
|
||||
|
||||
You can find **Run** (**{play}**) in the pipelines, environments, deployments, and jobs views.
|
||||
|
||||
### Track newly included merge requests per deployment
|
||||
|
||||
GitLab can track newly included merge requests per deployment.
|
||||
When a deployment succeeds, the system calculates commit-diffs between the latest deployment and the previous deployment.
|
||||
You can fetch tracking information with the [Deployment API](../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment)
|
||||
or view it at a post-merge pipeline in [merge request pages](../../user/project/merge_requests/index.md).
|
||||
|
||||
To enable tracking configure your environment so either:
|
||||
|
||||
- The [environment name](../yaml/index.md#environmentname) doesn't use folders with `/` (long-lived or top-level environments).
|
||||
- The [environment tier](#deployment-tier-of-environments) is either `production` or `staging`.
|
||||
|
||||
Here are some example configurations using the [`environment` keyword](../yaml/index.md#environment) in `.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
# Trackable
|
||||
environment: production
|
||||
environment: production/aws
|
||||
environment: development
|
||||
|
||||
# Non Trackable
|
||||
environment: review/$CI_COMMIT_REF_SLUG
|
||||
environment: testing/aws
|
||||
```
|
||||
|
||||
Configuration changes apply only to new deployments. Existing deployment records do not have merge requests linked or unlinked from them.
|
||||
|
||||
### Check out deployments locally
|
||||
|
||||
A reference in the Git repository is saved for each deployment, so
|
||||
knowing the state of your current environments is only a `git fetch` away.
|
||||
|
||||
In your Git configuration, append the `[remote "<your-remote>"]` block with an extra
|
||||
fetch line:
|
||||
|
||||
```plaintext
|
||||
fetch = +refs/environments/*:refs/remotes/origin/environments/*
|
||||
```
|
||||
|
||||
### Archive old deployments
|
||||
|
||||
When a new deployment happens in your project,
|
||||
GitLab creates [a special Git-ref to the deployment](#check-out-deployments-locally).
|
||||
Since these Git-refs are populated from the remote GitLab repository,
|
||||
you could find that some Git operations, such as `git-fetch` and `git-pull`,
|
||||
become slower as the number of deployments in your project increases.
|
||||
|
||||
To maintain the efficiency of your Git operations, GitLab keeps
|
||||
only recent deployment refs (up to 50,000) and deletes the rest of the old deployment refs.
|
||||
Archived deployments are still available, in the UI or by using the API, for auditing purposes.
|
||||
Also, you can still fetch the deployed commit from the repository
|
||||
with specifying the commit SHA (for example, `git checkout <deployment-sha>`), even after archive.
|
||||
|
||||
NOTE:
|
||||
GitLab preserves all commits as [`keep-around` refs](../../user/project/repository/repository_size.md#reduce-repository-size)
|
||||
so that deployed commits are not garbage collected, even if it's not referenced by the deployment refs.
|
||||
|
||||
### Deployment rollback
|
||||
|
||||
When you roll back a deployment on a specific commit,
|
||||
a _new_ deployment is created. This deployment has its own unique job ID.
|
||||
It points to the commit you're rolling back to.
|
||||
|
||||
For the rollback to succeed, the deployment process must be defined in
|
||||
the job's `script`.
|
||||
|
||||
Only the [deployment jobs](../jobs/index.md#deployment-jobs) are run.
|
||||
In cases where a previous job generates artifacts that must be regenerated
|
||||
on deploy, you must manually run the necessary jobs from the pipelines page.
|
||||
For example, if you use Terraform and your `plan` and `apply` commands are separated
|
||||
into multiple jobs, you must manually run the jobs to deploy or roll back.
|
||||
|
||||
#### Retry or roll back a deployment
|
||||
|
||||
If there is a problem with a deployment, you can retry it or roll it back.
|
||||
|
||||
To retry or roll back a deployment:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Operate > Environments**.
|
||||
1. In the search bar, enter your search term.
|
||||
- The length of your **search term should be 3 or more characters**.
|
||||
- Matching applies from the beginning of the environment name.
|
||||
- For example, `devel` matches the environment name `development`, but `elop` does not.
|
||||
- For environments with a folder name format, matching applies after the base folder name.
|
||||
- For example when the name is `review/test-app`, search term `test` matches `review/test-app`.
|
||||
- Also searching with the folder name prefixed like `review/test` matches `review/test-app`.
|
||||
1. Select the environment.
|
||||
1. To the right of the deployment name:
|
||||
- To retry a deployment, select **Re-deploy to environment**.
|
||||
- To roll back to a deployment, next to a previously successful deployment, select **Rollback environment**.
|
||||
|
||||
## CI/CD variables
|
||||
NOTE:
|
||||
If you have [prevented outdated deployment jobs](deployment_safety.md#prevent-outdated-deployment-jobs) in your project,
|
||||
the rollback buttons might be hidden or disabled.
|
||||
In this case, see [job retries for rollback deployments](deployment_safety.md#job-retries-for-rollback-deployments).
|
||||
|
||||
To customize your environments and deployments, you can use any of the
|
||||
[predefined CI/CD variables](../../ci/variables/predefined_variables.md),
|
||||
and define custom CI/CD variables.
|
||||
## Working with environments
|
||||
|
||||
## Environment states
|
||||
Environments describe where code is deployed.
|
||||
|
||||
An environment state indicates whether an environment's [stop job](../../ci/yaml/index.md#environmenton_stop) has run.
|
||||
There are three states:
|
||||
Each environment has one of three states, depending on whether its [stop job](../../ci/yaml/index.md#environmenton_stop) has run:
|
||||
|
||||
- `available`: The environment exists. There might be a deployment.
|
||||
- `stopping`: The _on stop job_ has started. This state does not apply when there is no on stop job defined.
|
||||
- `stopped`: Either the _on stop job_ has run, or a user manually stopped the job.
|
||||
|
||||
## Types of environments
|
||||
### Types of environments
|
||||
|
||||
An environment is either static or dynamic:
|
||||
An environment is either static or dynamic.
|
||||
|
||||
- Static environment
|
||||
- Usually reused by successive deployments.
|
||||
- Has a static name - for example, `staging` or `production`.
|
||||
- Created manually or as part of a CI/CD pipeline.
|
||||
- Dynamic environment
|
||||
- Usually created in a CI/CD pipeline and used by only a single deployment, then either stopped or
|
||||
deleted.
|
||||
- Has a dynamic name, usually based on the value of a CI/CD variable.
|
||||
- A feature of [review apps](../review_apps/index.md).
|
||||
Static environments:
|
||||
|
||||
- Are usually reused by successive deployments.
|
||||
- Have static names. For example, `staging` or `production`.
|
||||
- Are created manually or as part of a CI/CD pipeline.
|
||||
|
||||
Dynamic environments:
|
||||
|
||||
- Are usually created in a CI/CD pipeline and are used by only a single deployment, then either stopped or deleted.
|
||||
- Have dynamic names, usually based on the value of a CI/CD variable.
|
||||
- Are a feature of [review apps](../review_apps/index.md).
|
||||
|
||||
### Create a static environment
|
||||
|
||||
|
|
@ -251,20 +387,7 @@ For Windows runners, you should use the PowerShell `Add-Content` command to writ
|
|||
Add-Content -Path deploy.env -Value "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL"
|
||||
```
|
||||
|
||||
### Rename an environment
|
||||
|
||||
> - Renaming an environment by using the API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 15.9.
|
||||
> - Renaming an environment with the API [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 16.0.
|
||||
|
||||
You cannot rename an environment.
|
||||
|
||||
To achieve the same result as renaming an environment:
|
||||
|
||||
1. [Stop the existing environment](#stop-an-environment-by-using-the-ui).
|
||||
1. [Delete the existing environment](#delete-an-environment).
|
||||
1. [Create a new environment](#create-a-static-environment) with the desired name.
|
||||
|
||||
## Deployment tier of environments
|
||||
### Deployment tier of environments
|
||||
|
||||
Sometimes, instead of using an [industry standard](https://en.wikipedia.org/wiki/Deployment_environment)
|
||||
environment name, like `production`, you might want to use a code name, like `customer-portal`.
|
||||
|
|
@ -288,130 +411,122 @@ By default, GitLab assumes a tier based on [the environment name](../yaml/index.
|
|||
You cannot set an environment tier using the UI.
|
||||
Instead, you can use the [`deployment_tier` keyword](../yaml/index.md#environmentdeployment_tier) to specify a tier.
|
||||
|
||||
## Configure manual deployments
|
||||
#### Rename an environment
|
||||
|
||||
You can create a job that requires someone to manually start the deployment.
|
||||
For example:
|
||||
> - Renaming an environment by using the API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 15.9.
|
||||
> - Renaming an environment with the API [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 16.0.
|
||||
|
||||
```yaml
|
||||
deploy_prod:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy to production server"
|
||||
environment:
|
||||
name: production
|
||||
url: https://example.com
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
when: manual
|
||||
```
|
||||
You cannot rename an environment.
|
||||
|
||||
The `when: manual` action:
|
||||
To achieve the same result as renaming an environment:
|
||||
|
||||
- Exposes the **Run** (**{play}**) button for the job in the GitLab UI, with the text **Can be manually deployed to <environment>**.
|
||||
- Means the `deploy_prod` job must be triggered manually.
|
||||
1. [Stop the existing environment](#stop-an-environment-by-using-the-ui).
|
||||
1. [Delete the existing environment](#delete-an-environment).
|
||||
1. [Create a new environment](#create-a-static-environment) with the desired name.
|
||||
|
||||
You can find **Run** (**{play}**) in the pipelines, environments, deployments, and jobs views.
|
||||
### CI/CD variables
|
||||
|
||||
## Track newly included merge requests per deployment
|
||||
To customize your environments and deployments, you can use any of the
|
||||
[predefined CI/CD variables](../../ci/variables/predefined_variables.md),
|
||||
and define custom CI/CD variables.
|
||||
|
||||
GitLab can track newly included merge requests per deployment.
|
||||
When a deployment succeeds, the system calculates commit-diffs between the latest deployment and the previous deployment.
|
||||
You can fetch tracking information with the [Deployment API](../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment)
|
||||
or view it at a post-merge pipeline in [merge request pages](../../user/project/merge_requests/index.md).
|
||||
#### Limit the environment scope of a CI/CD variable
|
||||
|
||||
To enable tracking configure your environment so either:
|
||||
By default, all [CI/CD variables](../variables/index.md) are available to all jobs in a pipeline.
|
||||
If a test tool in a job becomes compromised, the tool could attempt to retrieve all
|
||||
CI/CD variables available to the job. To help mitigate this kind of supply chain attack,
|
||||
you should limit the environment scope of sensitive variables to only the jobs that require them.
|
||||
|
||||
- The [environment name](../yaml/index.md#environmentname) doesn't use folders with `/` (long-lived or top-level environments).
|
||||
- The [environment tier](#deployment-tier-of-environments) is either `production` or `staging`.
|
||||
Limit the environment scope of a CI/CD variable by defining which environments it
|
||||
can be available for. The default environment scope is the `*` wildcard, so any job
|
||||
can access the variable.
|
||||
|
||||
Here are some example configurations using the [`environment` keyword](../yaml/index.md#environment) in `.gitlab-ci.yml`:
|
||||
You can use specific matching to select a particular environment. For example, set
|
||||
the variable's environment scope to `production` to only allow jobs with an [environment](../yaml/index.md#environment)
|
||||
of `production` to access the variable.
|
||||
|
||||
```yaml
|
||||
# Trackable
|
||||
environment: production
|
||||
environment: production/aws
|
||||
environment: development
|
||||
You can also use wildcard matching (`*`) to select a particular environment group,
|
||||
like all [review apps](../review_apps/index.md) with `review/*`.
|
||||
|
||||
# Non Trackable
|
||||
environment: review/$CI_COMMIT_REF_SLUG
|
||||
environment: testing/aws
|
||||
```
|
||||
For example, with these four environments:
|
||||
|
||||
Configuration changes apply only to new deployments. Existing deployment records do not have merge requests linked or unlinked from them.
|
||||
- `production`
|
||||
- `staging`
|
||||
- `review/feature-1`
|
||||
- `review/feature-2`
|
||||
|
||||
## Working with environments
|
||||
These environment scopes match as follows:
|
||||
|
||||
Once environments are configured, GitLab provides many features for working with them,
|
||||
as documented below.
|
||||
| ↓ Scope / Environment → | `production` | `staging` | `review/feature-1` | `review/feature-2` |
|
||||
|:------------------------|:-------------|:----------|:-------------------|:-------------------|
|
||||
| `*` | Match | Match | Match | Match |
|
||||
| `production` | Match | | | |
|
||||
| `staging` | | Match | | |
|
||||
| `review/*` | | | Match | Match |
|
||||
| `review/feature-1` | | | Match | |
|
||||
|
||||
### Environment rollback
|
||||
You should not use environment-scoped variables with [`rules`](../yaml/index.md#rules)
|
||||
or [`include`](../yaml/index.md#include). The variables might not be defined when
|
||||
GitLab validates the pipeline configuration at pipeline creation.
|
||||
|
||||
When you roll back a deployment on a specific commit,
|
||||
a _new_ deployment is created. This deployment has its own unique job ID.
|
||||
It points to the commit you're rolling back to.
|
||||
### Search environments
|
||||
|
||||
For the rollback to succeed, the deployment process must be defined in
|
||||
the job's `script`.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10754) in GitLab 15.5.
|
||||
> - [Searching environments within a folder](https://gitlab.com/gitlab-org/gitlab/-/issues/373850) was introduced in GitLab 15.7 with [Feature flag `enable_environments_search_within_folder`](https://gitlab.com/gitlab-org/gitlab/-/issues/382108). Enabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/382108) in GitLab 17.4. Feature flag `enable_environments_search_within_folder` removed.
|
||||
|
||||
Only the [deployment jobs](../jobs/index.md#deployment-jobs) are run.
|
||||
In cases where a previous job generates artifacts that must be regenerated
|
||||
on deploy, you must manually run the necessary jobs from the pipelines page.
|
||||
For example, if you use Terraform and your `plan` and `apply` commands are separated
|
||||
into multiple jobs, you must manually run the jobs to deploy or roll back.
|
||||
|
||||
#### Retry or roll back a deployment
|
||||
|
||||
If there is a problem with a deployment, you can retry it or roll it back.
|
||||
|
||||
To retry or roll back a deployment:
|
||||
To search environments by name:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Operate > Environments**.
|
||||
1. Select the environment.
|
||||
1. To the right of the deployment name:
|
||||
- To retry a deployment, select **Re-deploy to environment**.
|
||||
- To roll back to a deployment, next to a previously successful deployment, select **Rollback environment**.
|
||||
1. In the search bar, enter your search term.
|
||||
- The length of your **search term should be 3 or more characters**.
|
||||
- Matching applies from the beginning of the environment name.
|
||||
- For example, `devel` matches the environment name `development`, but `elop` does not.
|
||||
- For environments with a folder name format, matching applies after the base folder name.
|
||||
- For example when the name is `review/test-app`, search term `test` matches `review/test-app`.
|
||||
- Also searching with the folder name prefixed like `review/test` matches `review/test-app`.
|
||||
|
||||
NOTE:
|
||||
If you have [prevented outdated deployment jobs](deployment_safety.md#prevent-outdated-deployment-jobs) in your project,
|
||||
the rollback buttons might be hidden or disabled.
|
||||
In this case, see [job retries for rollback deployments](deployment_safety.md#job-retries-for-rollback-deployments).
|
||||
### Group similar environments
|
||||
|
||||
### Environment URL
|
||||
You can group environments into collapsible sections in the UI.
|
||||
|
||||
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/337417) to persist arbitrary URLs in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `soft_validation_on_external_url`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/337417) in GitLab 15.3. [Feature flag `soft_validation_on_external_url`](https://gitlab.com/gitlab-org/gitlab/-/issues/367206) removed.
|
||||
For example, if all of your environments start with the name `review`,
|
||||
then in the UI, the environments are grouped under that heading:
|
||||
|
||||
The [environment URL](../yaml/index.md#environmenturl) is displayed in a few
|
||||
places in GitLab:
|
||||

|
||||
|
||||
- In a merge request as a link:
|
||||

|
||||
- In the Environments view as a button:
|
||||

|
||||
- In the Deployments view as a button:
|
||||

|
||||
The following example shows how to start your environment names with `review`.
|
||||
The `$CI_COMMIT_REF_SLUG` variable is populated with the branch name at runtime:
|
||||
|
||||
You can see this information in a merge request if:
|
||||
|
||||
- The merge request is eventually merged to the default branch (usually `main`).
|
||||
- That branch also deploys to an environment (for example, `staging` or `production`).
|
||||
|
||||
For example:
|
||||
|
||||

|
||||
|
||||
#### Go from source files to public pages
|
||||
|
||||
With GitLab [Route Maps](../review_apps/index.md#route-maps), you can go directly
|
||||
from source files to public pages in the environment set for review apps.
|
||||
```yaml
|
||||
deploy_review:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy a review app"
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
```
|
||||
|
||||
### Stopping an environment
|
||||
|
||||
Stopping an environment means its deployments are not accessible on the target server. You must stop
|
||||
an environment before it can be deleted.
|
||||
|
||||
#### Stop an environment by using the UI
|
||||
|
||||
NOTE:
|
||||
To trigger an `on_stop` action and manually stop an environment from the
|
||||
Environments view, the stop and deploy jobs must be in the same
|
||||
[`resource_group`](../yaml/index.md#resource_group).
|
||||
|
||||
To stop an environment in the GitLab UI:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Operate > Environments**.
|
||||
1. Next to the environment you want to stop, select **Stop**.
|
||||
1. On the confirmation dialog, select **Stop environment**.
|
||||
|
||||
#### Stop an environment when a branch is deleted
|
||||
|
||||
You can configure environments to stop when a branch is deleted.
|
||||
|
|
@ -481,58 +596,6 @@ stop_review:
|
|||
when: manual
|
||||
```
|
||||
|
||||
#### Run a pipeline job when environment is stopped
|
||||
|
||||
> - Feature flag `environment_stop_actions_include_all_finished_deployments` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435128) in GitLab 16.9. Disabled by default.
|
||||
> - Feature flag `environment_stop_actions_include_all_finished_deployments` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150932) in GitLab 17.0.
|
||||
|
||||
You can define a stop job for the environment with an [`on_stop` action](../yaml/index.md#environmenton_stop) in the environment's deploy job.
|
||||
|
||||
The stop jobs of finished deployments in the latest finished pipeline are run when an environment is stopped. A deployment or pipeline is _finished_ if it has the successful, canceled, or failed status.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Both the deploy and stop jobs must have the same rules or only/except configuration.
|
||||
- The stop job must have the following keywords defined:
|
||||
- `when`, defined at either:
|
||||
- [The job level](../yaml/index.md#when).
|
||||
- [In a rules clause](../yaml/index.md#rules). If you use `rules` and `when: manual`, you should
|
||||
also set [`allow_failure: true`](../yaml/index.md#allow_failure) so the pipeline can complete
|
||||
even if the job doesn't run.
|
||||
- `environment:name`
|
||||
- `environment:action`
|
||||
|
||||
In the following example:
|
||||
|
||||
- A `review_app` job calls a `stop_review_app` job after the first job is finished.
|
||||
- The `stop_review_app` is triggered based on what is defined under `when`. In this
|
||||
case, it is set to `manual`, so it needs a
|
||||
[manual action](../jobs/job_control.md#create-a-job-that-must-be-run-manually)
|
||||
from the GitLab UI to run.
|
||||
- The `GIT_STRATEGY` is set to `none`. If the `stop_review_app` job is
|
||||
[automatically triggered](../environments/index.md#stopping-an-environment),
|
||||
the runner doesn't try to check out the code after the branch is deleted.
|
||||
|
||||
```yaml
|
||||
review_app:
|
||||
stage: deploy
|
||||
script: make deploy-app
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
url: https://$CI_ENVIRONMENT_SLUG.example.com
|
||||
on_stop: stop_review_app
|
||||
|
||||
stop_review_app:
|
||||
stage: deploy
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script: make delete-app
|
||||
when: manual
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
action: stop
|
||||
```
|
||||
|
||||
#### Stop an environment after a certain time period
|
||||
|
||||
You can set an environment to stop automatically after a certain time period.
|
||||
|
|
@ -606,29 +669,79 @@ To override an environment's expiration in the `.gitlab-ci.yml`:
|
|||
The `auto_stop_in` setting is overridden and the environment remains active until it's stopped
|
||||
manually.
|
||||
|
||||
#### Stop an environment without running the `on_stop` action
|
||||
#### Clean up stale environments
|
||||
|
||||
There may be times when you want to stop an environment without running the defined
|
||||
[`on_stop`](../yaml/index.md#environmenton_stop) action. For example, you want to delete many
|
||||
environments without using [compute quota](../pipelines/compute_minutes.md).
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108616) in GitLab 15.8 [with a flag](../../administration/feature_flags.md) named `stop_stale_environments`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112098) in GitLab 15.10. Feature flag `stop_stale_environments` removed.
|
||||
|
||||
To stop an environment without running the defined `on_stop` action, execute the
|
||||
[Stop an environment API](../../api/environments.md#stop-an-environment) with the parameter
|
||||
`force=true`.
|
||||
Clean up stale environments when you want to stop old environments in a project.
|
||||
|
||||
#### Stop an environment by using the UI
|
||||
Prerequisites:
|
||||
|
||||
NOTE:
|
||||
To trigger an `on_stop` action and manually stop an environment from the
|
||||
Environments view, the stop and deploy jobs must be in the same
|
||||
[`resource_group`](../yaml/index.md#resource_group).
|
||||
- You must have at least the Maintainer role.
|
||||
|
||||
To stop an environment in the GitLab UI:
|
||||
To clean up stale environments:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Operate > Environments**.
|
||||
1. Next to the environment you want to stop, select **Stop**.
|
||||
1. On the confirmation dialog, select **Stop environment**.
|
||||
1. Select **Clean up environments**.
|
||||
1. Select the date to use for determining which environments to consider stale.
|
||||
1. Select **Clean up**.
|
||||
|
||||
Active environments that haven't been updated after the specified date are stopped.
|
||||
Protected environments are ignored and not stopped.
|
||||
|
||||
#### Run a pipeline job when environment is stopped
|
||||
|
||||
> - Feature flag `environment_stop_actions_include_all_finished_deployments` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435128) in GitLab 16.9. Disabled by default.
|
||||
> - Feature flag `environment_stop_actions_include_all_finished_deployments` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150932) in GitLab 17.0.
|
||||
|
||||
You can define a stop job for the environment with an [`on_stop` action](../yaml/index.md#environmenton_stop) in the environment's deploy job.
|
||||
|
||||
The stop jobs of finished deployments in the latest finished pipeline are run when an environment is stopped. A deployment or pipeline is _finished_ if it has the successful, canceled, or failed status.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Both the deploy and stop jobs must have the same rules or only/except configuration.
|
||||
- The stop job must have the following keywords defined:
|
||||
- `when`, defined at either:
|
||||
- [The job level](../yaml/index.md#when).
|
||||
- [In a rules clause](../yaml/index.md#rules). If you use `rules` and `when: manual`, you should
|
||||
also set [`allow_failure: true`](../yaml/index.md#allow_failure) so the pipeline can complete
|
||||
even if the job doesn't run.
|
||||
- `environment:name`
|
||||
- `environment:action`
|
||||
|
||||
In the following example:
|
||||
|
||||
- A `review_app` job calls a `stop_review_app` job after the first job is finished.
|
||||
- The `stop_review_app` is triggered based on what is defined under `when`. In this
|
||||
case, it is set to `manual`, so it needs a
|
||||
[manual action](../jobs/job_control.md#create-a-job-that-must-be-run-manually)
|
||||
from the GitLab UI to run.
|
||||
- The `GIT_STRATEGY` is set to `none`. If the `stop_review_app` job is
|
||||
[automatically triggered](../environments/index.md#stopping-an-environment),
|
||||
the runner doesn't try to check out the code after the branch is deleted.
|
||||
|
||||
```yaml
|
||||
review_app:
|
||||
stage: deploy
|
||||
script: make deploy-app
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
url: https://$CI_ENVIRONMENT_SLUG.example.com
|
||||
on_stop: stop_review_app
|
||||
|
||||
stop_review_app:
|
||||
stage: deploy
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script: make delete-app
|
||||
when: manual
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
action: stop
|
||||
```
|
||||
|
||||
#### Multiple stop actions for an environment
|
||||
|
||||
|
|
@ -682,7 +795,17 @@ teardown-cloud-b:
|
|||
when: manual
|
||||
```
|
||||
|
||||
### Delete an environment
|
||||
#### Stop an environment without running the `on_stop` action
|
||||
|
||||
There may be times when you want to stop an environment without running the defined
|
||||
[`on_stop`](../yaml/index.md#environmenton_stop) action. For example, you want to delete many
|
||||
environments without using [compute quota](../pipelines/compute_minutes.md).
|
||||
|
||||
To stop an environment without running the defined `on_stop` action, execute the
|
||||
[Stop an environment API](../../api/environments.md#stop-an-environment) with the parameter
|
||||
`force=true`.
|
||||
|
||||
#### Delete an environment
|
||||
|
||||
Delete an environment when you want to remove it and all its deployments.
|
||||
|
||||
|
|
@ -699,28 +822,6 @@ To delete an environment:
|
|||
1. Next to the environment you want to delete, select **Delete environment**.
|
||||
1. On the confirmation dialog, select **Delete environment**.
|
||||
|
||||
### Clean up stale environments
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108616) in GitLab 15.8 [with a flag](../../administration/feature_flags.md) named `stop_stale_environments`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112098) in GitLab 15.10. Feature flag `stop_stale_environments` removed.
|
||||
|
||||
Clean up stale environments when you want to stop old environments in a project.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role.
|
||||
|
||||
To clean up stale environments:
|
||||
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Operate > Environments**.
|
||||
1. Select **Clean up environments**.
|
||||
1. Select the date to use for determining which environments to consider stale.
|
||||
1. Select **Clean up**.
|
||||
|
||||
Active environments that haven't been updated after the specified date are stopped.
|
||||
Protected environments are ignored and not stopped.
|
||||
|
||||
### Access an environment for preparation or verification purposes
|
||||
|
||||
You can define a job that accesses an environment for various purposes, such as verification or preparation. This
|
||||
|
|
@ -742,27 +843,6 @@ build:
|
|||
This gives you access to environment-scoped variables, and can be used to protect builds from unauthorized access. Also,
|
||||
it's effective to avoid the [prevent outdated deployment jobs](deployment_safety.md#prevent-outdated-deployment-jobs) feature.
|
||||
|
||||
### Group similar environments
|
||||
|
||||
You can group environments into collapsible sections in the UI.
|
||||
|
||||
For example, if all of your environments start with the name `review`,
|
||||
then in the UI, the environments are grouped under that heading:
|
||||
|
||||

|
||||
|
||||
The following example shows how to start your environment names with `review`.
|
||||
The `$CI_COMMIT_REF_SLUG` variable is populated with the branch name at runtime:
|
||||
|
||||
```yaml
|
||||
deploy_review:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploy a review app"
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_SLUG
|
||||
```
|
||||
|
||||
### Environment incident management
|
||||
|
||||
Production environments can go down unexpectedly, including for reasons outside
|
||||
|
|
@ -828,7 +908,39 @@ GitLab Auto Rollback is turned off by default. To turn it on:
|
|||
1. Select the checkbox for **Enable automatic rollbacks**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
### Web terminals (deprecated)
|
||||
### Environment permissions
|
||||
|
||||
Depending on your role, you can interact with environments in public
|
||||
and private projects.
|
||||
|
||||
#### View environments
|
||||
|
||||
- In public projects, anyone can view a list of environments, including non-members.
|
||||
- In private projects, you must have at least the Reporter role to view a list of environments.
|
||||
|
||||
#### Create and update environments
|
||||
|
||||
- You must have at least the Developer role to create a new environment, or update an existing unprotected environment.
|
||||
- If an existing environment is protected and you don't have access to it, you cannot update the environment.
|
||||
|
||||
#### Stop and delete environments
|
||||
|
||||
- You must have at least the Developer role to stop or delete an unprotected environment.
|
||||
- If an environment is protected and you don't have access to it, you cannot stop or delete the environment.
|
||||
|
||||
#### Run deployment jobs in protected environments
|
||||
|
||||
If you can push or merge to the protected branch:
|
||||
|
||||
- You must have at least the Reporter role.
|
||||
|
||||
If you can't push to the protected branch:
|
||||
|
||||
- You must be a part of a group with the Reporter role.
|
||||
|
||||
See [Deployment-only access to protected environments](protected_environments.md#deployment-only-access-to-protected-environments).
|
||||
|
||||
## Web terminals (deprecated)
|
||||
|
||||
WARNING:
|
||||
This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) in GitLab 14.5.
|
||||
|
|
@ -868,107 +980,6 @@ by your deployment so you can:
|
|||
You can open multiple terminals to the same environment. They each get their own shell
|
||||
session and even a multiplexer like `screen` or `tmux`.
|
||||
|
||||
### Check out deployments locally
|
||||
|
||||
A reference in the Git repository is saved for each deployment, so
|
||||
knowing the state of your current environments is only a `git fetch` away.
|
||||
|
||||
In your Git configuration, append the `[remote "<your-remote>"]` block with an extra
|
||||
fetch line:
|
||||
|
||||
```plaintext
|
||||
fetch = +refs/environments/*:refs/remotes/origin/environments/*
|
||||
```
|
||||
|
||||
### Archive Old Deployments
|
||||
|
||||
When a new deployment happens in your project,
|
||||
GitLab creates [a special Git-ref to the deployment](#check-out-deployments-locally).
|
||||
Since these Git-refs are populated from the remote GitLab repository,
|
||||
you could find that some Git operations, such as `git-fetch` and `git-pull`,
|
||||
become slower as the number of deployments in your project increases.
|
||||
|
||||
To maintain the efficiency of your Git operations, GitLab keeps
|
||||
only recent deployment refs (up to 50,000) and deletes the rest of the old deployment refs.
|
||||
Archived deployments are still available, in the UI or by using the API, for auditing purposes.
|
||||
Also, you can still fetch the deployed commit from the repository
|
||||
with specifying the commit SHA (for example, `git checkout <deployment-sha>`), even after archive.
|
||||
|
||||
NOTE:
|
||||
GitLab preserves all commits as [`keep-around` refs](../../user/project/repository/repository_size.md#reduce-repository-size)
|
||||
so that deployed commits are not garbage collected, even if it's not referenced by the deployment refs.
|
||||
|
||||
### Limit the environment scope of a CI/CD variable
|
||||
|
||||
By default, all [CI/CD variables](../variables/index.md) are available to all jobs in a pipeline.
|
||||
If a test tool in a job becomes compromised, the tool could attempt to retrieve all
|
||||
CI/CD variables available to the job. To help mitigate this kind of supply chain attack,
|
||||
you should limit the environment scope of sensitive variables to only the jobs that require them.
|
||||
|
||||
Limit the environment scope of a CI/CD variable by defining which environments it
|
||||
can be available for. The default environment scope is the `*` wildcard, so any job
|
||||
can access the variable.
|
||||
|
||||
You can use specific matching to select a particular environment. For example, set
|
||||
the variable's environment scope to `production` to only allow jobs with an [environment](../yaml/index.md#environment)
|
||||
of `production` to access the variable.
|
||||
|
||||
You can also use wildcard matching (`*`) to select a particular environment group,
|
||||
like all [review apps](../review_apps/index.md) with `review/*`.
|
||||
|
||||
For example, with these four environments:
|
||||
|
||||
- `production`
|
||||
- `staging`
|
||||
- `review/feature-1`
|
||||
- `review/feature-2`
|
||||
|
||||
These environment scopes match as follows:
|
||||
|
||||
| ↓ Scope / Environment → | `production` | `staging` | `review/feature-1` | `review/feature-2` |
|
||||
|:------------------------|:-------------|:----------|:-------------------|:-------------------|
|
||||
| `*` | Match | Match | Match | Match |
|
||||
| `production` | Match | | | |
|
||||
| `staging` | | Match | | |
|
||||
| `review/*` | | | Match | Match |
|
||||
| `review/feature-1` | | | Match | |
|
||||
|
||||
You should not use environment-scoped variables with [`rules`](../yaml/index.md#rules)
|
||||
or [`include`](../yaml/index.md#include). The variables might not be defined when
|
||||
GitLab validates the pipeline configuration at pipeline creation.
|
||||
|
||||
## Environment permissions
|
||||
|
||||
Depending on your role, you can interact with environments in public
|
||||
and private projects.
|
||||
|
||||
### View environments
|
||||
|
||||
- In public projects, anyone can view a list of environments, including non-members.
|
||||
- In private projects, you must have at least the Reporter role to view a list of environments.
|
||||
|
||||
### Create and update environments
|
||||
|
||||
- You must have at least the Developer role to create a new environment, or update an existing unprotected environment.
|
||||
- If an existing environment is protected and you don't have access to it, you cannot update the environment.
|
||||
|
||||
### Stop and delete environments
|
||||
|
||||
- You must have at least the Developer role to stop or delete an unprotected environment.
|
||||
- If an environment is protected and you don't have access to it, you cannot stop or delete the environment.
|
||||
|
||||
### Run deployment jobs in protected environments
|
||||
|
||||
If you can push or merge to the protected branch:
|
||||
|
||||
- You must have at least the Reporter role.
|
||||
|
||||
If you can't push to the protected branch:
|
||||
|
||||
- You must be a part of a group with the Reporter role.
|
||||
|
||||
See [Deployment-only access to protected environments](protected_environments.md#deployment-only-access-to-protected-environments).
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Dashboard for Kubernetes](kubernetes_dashboard.md)
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
|
@ -77,7 +77,7 @@ can find the reason:
|
|||
|
||||
In each place, if you hover over the failed job you can see the reason it failed.
|
||||
|
||||

|
||||

|
||||
|
||||
You can also see the reason it failed on the Job detail page.
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
|
@ -39,7 +39,7 @@ using the [shell executor](https://docs.gitlab.com/runner/executors/shell.html).
|
|||
If using Jenkins' built-in shell execution option to directly call `mvn` commands
|
||||
from the shell on the agent, the configuration might look like:
|
||||
|
||||

|
||||

|
||||
|
||||
### Freestyle with Maven task plugin
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ If using the Maven plugin in Jenkins to declare and execute any specific goals
|
|||
in the [Maven build lifecycle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html),
|
||||
the configuration might look like:
|
||||
|
||||

|
||||

|
||||
|
||||
This plugin requires Maven to be installed on the Jenkins agent, and uses a script wrapper
|
||||
for calling Maven commands.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
|
@ -23,7 +23,7 @@ Review apps:
|
|||
- Are fully integrated with the [GitLab DevOps LifeCycle](https://about.gitlab.com/stages-devops-lifecycle/).
|
||||
- Allow you to deploy your changes wherever you want.
|
||||
|
||||

|
||||

|
||||
|
||||
In the previous example:
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ After you have the route mapping set up, it takes effect in the following locati
|
|||
- The list shows the first 5 matched items from the route map, but you can filter them if more
|
||||
than 5 are available.
|
||||
|
||||

|
||||

|
||||
|
||||
- In the diff for a comparison or commit, by selecting **View** (**{external-link}**) next to the file.
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
|
@ -55,7 +55,7 @@ Hosted runners for GitLab.com are configured as such:
|
|||
|
||||
The following graphic shows the architecture diagram of hosted runners for GitLab.com
|
||||
|
||||

|
||||

|
||||
|
||||
For more information on how runners are authenticating and executing the job payload, see [Runner Execution Flow](https://docs.gitlab.com/runner#runner-execution-flow).
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ For more information on how runners are authenticating and executing the job pay
|
|||
In addition to isolating runners on the network, each ephemeral runner VM only serves a single job and is deleted straight after the job execution.
|
||||
In the following example, three jobs are executed in a project's pipeline. Each of these jobs runs in a dedicated ephemeral VM.
|
||||
|
||||

|
||||

|
||||
|
||||
The build job ran on `runner-ns46nmmj-project-43717858`, test job on `f131a6a2runner-new2m-od-project-43717858` and deploy job on `runner-tmand5m-project-43717858`.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ The runner fleet dashboard shows:
|
|||
- Compute minutes used by instance runners
|
||||
- Job queue times (available only with [ClickHouse](#enable-more-ci-analytics-features-with-clickhouse))
|
||||
|
||||

|
||||

|
||||
|
||||
## View the runner fleet dashboard
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ DETAILS:
|
|||
|
||||
Users with at least the Maintainer role for a group can use the runner fleet dashboard to assess the health of group runners.
|
||||
|
||||

|
||||

|
||||
|
||||
## Dashboard metrics
|
||||
|
||||
|
|
|
|||
|
|
@ -673,7 +673,7 @@ project.
|
|||
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
|
||||
1. Select the runner name and find the **IP Address** row.
|
||||
|
||||

|
||||

|
||||
|
||||
## Enable use of runner registration tokens in projects and groups
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ How to enable:
|
|||
export GITLAB_SIMULATE_SAAS=0
|
||||
export GITLAB_LICENSE_MODE=test
|
||||
export CUSTOMER_PORTAL_URL=https://customers.staging.gitlab.com
|
||||
export OVERRIDE_OBSERVABILITY_URL=https://observe.staging.gitlab.com
|
||||
export OVERRIDE_OBSERVABILITY_QUERY_URL=https://observe.staging.gitlab.com
|
||||
export OVERRIDE_OBSERVABILITY_INGEST_URL=https://observe.staging.gitlab.com
|
||||
```
|
||||
|
||||
On a non-GDK/GCK instance, you can set the variables using `gitlab_rails['env']` in the `gitlab.rb` file:
|
||||
|
|
@ -46,7 +47,8 @@ How to enable:
|
|||
gitlab_rails['env'] = {
|
||||
'GITLAB_LICENSE_MODE' => 'test',
|
||||
'CUSTOMER_PORTAL_URL' => 'https://customers.staging.gitlab.com',
|
||||
'OVERRIDE_OBSERVABILITY_URL' => 'https://observe.staging.gitlab.com'
|
||||
'OVERRIDE_OBSERVABILITY_QUERY_URL' => 'https://observe.staging.gitlab.com',
|
||||
'OVERRIDE_OBSERVABILITY_INGEST_URL' => 'https://observe.staging.gitlab.com'
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -94,10 +94,11 @@ See the section above for situations that might require adjustment to the comman
|
|||
- [Google Chrome](https://www.google.com/chrome/)
|
||||
- [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/)
|
||||
- [Git](https://git-scm.com/download/win)
|
||||
- [Ruby](https://rubyinstaller.org/downloads/). Please refer to the [`.ruby-version` file](../../../../../.ruby-version) for the exact version of Ruby to install.
|
||||
- [Ruby](https://rubyinstaller.org/downloads/). Refer to the [`.ruby-version` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.ruby-version)
|
||||
for the exact version of Ruby to install.
|
||||
|
||||
NOTE:
|
||||
Please be aware that [Docker Desktop must be set to use Linux containers](https://learn.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux#run-your-first-linux-container).
|
||||
Be aware that [Docker Desktop must be set to use Linux containers](https://learn.microsoft.com/en-us/virtualization/windowscontainers/quick-start/quick-start-windows-10-linux#run-your-first-linux-container).
|
||||
|
||||
1. Use the following command to start an instance that you can visit at `http://127.0.0.1`. You might need to grant admin rights if asked:
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ DETAILS:
|
|||
**Tier:** Premium, Ultimate
|
||||
**Offering:** GitLab.com
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/473774) in GitLab 17.6.
|
||||
|
||||
You can set up deployment gating to bring change requests from GitLab to Jira Service Management for approval.
|
||||
With deployment gating, any GitLab deployments to your selected environments are automatically sent
|
||||
to Jira Service Management and are only deployed if they're approved.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
|
@ -25,19 +25,21 @@ the minimum key length for each technology:
|
|||
|
||||
1. On the left sidebar, at the bottom, select **Admin**.
|
||||
1. Select **Settings > General** .
|
||||
1. Expand **Visibility and access controls**:
|
||||
|
||||

|
||||
1. Expand **Visibility and access controls** and set your desired values for each key type:
|
||||
- **RSA SSH keys**.
|
||||
- **DSA SSH keys**.
|
||||
- **ECDSA SSH keys**.
|
||||
- **ED25519 SSH keys**.
|
||||
- **ECDSA_SK SSH keys**.
|
||||
- **ED25519_SK SSH keys**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
If a restriction is imposed on any key type, users cannot upload new SSH keys that don't meet the
|
||||
requirement. Any existing keys that don't meet it are disabled but not removed and users cannot
|
||||
pull or push code using them.
|
||||
|
||||
An icon is visible to the user of a restricted key in the SSH keys section of their profile:
|
||||
|
||||

|
||||
|
||||
Hovering over this icon tells you why the key is restricted.
|
||||
If you have a restricted key, a warning icon (**{warning-icon}**) is visible to you in the **SSH keys** section of your profile.
|
||||
To learn why that key is restricted, hover over the icon.
|
||||
|
||||
## Default settings
|
||||
|
||||
|
|
|
|||
|
|
@ -315,10 +315,12 @@ use the information in the failure error logs or the database:
|
|||
|
||||
When dealing with multiple arguments, such as `[["id"],["id_convert_to_bigint"]]`, escape the
|
||||
comma between each argument with a backslash <code>\</code> to prevent an invalid character error.
|
||||
The command should be:
|
||||
Every comma in the `job_arguments` parameter value must be escaped with a backslash.
|
||||
|
||||
For example:
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[["id"]\, ["id_convert_to_bigint"]]']
|
||||
sudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,ci_builds,id,'[["id"\, "stage_id"]\,["id_convert_to_bigint"\,"stage_id_convert_to_bigint"]]']
|
||||
```
|
||||
|
||||
::EndTabs
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ For authentication CI/CD variables, see [Authentication](authentication.md).
|
|||
| `DAST_SCOPE_IGNORE_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are accessed, not attacked, and not reported against. |
|
||||
| `DAST_TARGET_CHECK_SKIP` | boolean | `true` | Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`. |
|
||||
| `DAST_TARGET_CHECK_TIMEOUT` | number | `60` | Time limit in seconds to wait for target availability. Default: `60s`. |
|
||||
| `DAST_TARGET_PATHS_FILE` | string | `/builds/project/urls.txt` | Limit the paths scanned to a provided list. Set to a file path containing a list of URL paths relative to `DAST_TARGET_URL`. The file must be plain text with one path per line. |
|
||||
| `DAST_TARGET_PATHS` | string | `/page1.html,/category1/page3.html` | Limit the paths scanned to a provided list. Set to a comma-separated list of URL paths relative to `DAST_TARGET_URL`. |
|
||||
| `DAST_TARGET_PATHS_FILE` | string | `/builds/project/urls.txt` | Ensures that the provided paths are always scanned. Set to a file path containing a list of URL paths relative to `DAST_TARGET_URL`. The file must be plain text with one path per line. |
|
||||
| `DAST_TARGET_PATHS` | string | `/page1.html,/category1/page3.html` | Ensures that the provided paths are always scanned. Set to a comma-separated list of URL paths relative to `DAST_TARGET_URL`. |
|
||||
| `DAST_TARGET_URL` | URL | `https://site.com` | The URL of the website to scan. |
|
||||
| `DAST_USE_CACHE` | boolean | `true` | Set to `false` to disable caching. Default: `true`. **Note:** Disabling cache can cause OOM events or DAST job timeouts. |
|
||||
| `SECURE_ANALYZERS_PREFIX` | URL | `registry.organization.com` | Set the Docker registry base address from which to download the analyzer. |
|
||||
|
|
|
|||
|
|
@ -90,8 +90,6 @@ For user contributions to be mapped, each user must complete the following befor
|
|||
|
||||
1. Connect your Bitbucket account in [GitLab profile service sign-in](https://gitlab.com/-/profile/account).
|
||||
|
||||
1. [Set your public email](../../profile/index.md#set-your-public-email).
|
||||
|
||||
## Import your Bitbucket repositories
|
||||
|
||||
> - Ability to re-import projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23905) in GitLab 15.9.
|
||||
|
|
|
|||
|
|
@ -605,36 +605,140 @@ glab api --method GET projects/$GL_PROJECT_ID/pipelines | jq --compact-output '.
|
|||
In the following example that uses a Bash script:
|
||||
|
||||
- `jq` and the GitLab CLI are installed and authorized.
|
||||
- The exported environment variable `GL_PROJECT_ID`.
|
||||
- The exported environment variable `GL_PROJECT_ID`. Defaults to the GitLab predefined variable `CI_PROJECT_ID`.
|
||||
- The exported environment variable `CI_SERVER_HOST` that points to the GitLab instance URL.
|
||||
|
||||
::Tabs
|
||||
|
||||
:::TabTitle Using the API with glab
|
||||
|
||||
The full script `get_cicd_pipelines_compare_age_threshold_example.sh` is located in the [GitLab API with Linux Shell](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-linux-shell) project.
|
||||
|
||||
```shell
|
||||
#/bin/bash
|
||||
#!/bin/bash
|
||||
|
||||
CREATED_AT_ARR=$(glab api --method GET projects/$GL_PROJECT_ID/pipelines | jq --compact-output '.[]' | jq --compact-output '.created_at' | jq --raw-output @sh)
|
||||
# Required programs:
|
||||
# - GitLab CLI (glab): https://docs.gitlab.com/ee/editor_extensions/gitlab_cli/index.html
|
||||
# - jq: https://jqlang.github.io/jq/
|
||||
|
||||
for row in ${CREATED_AT_ARR[@]}
|
||||
do
|
||||
stripped=$(echo $row | xargs echo)
|
||||
#echo $stripped #DEBUG
|
||||
# Required variables:
|
||||
# - PAT: Project Access Token with API scope and Owner role, or Personal Access Token with API scope
|
||||
# - GL_PROJECT_ID: ID of the project where pipelines must be cleaned
|
||||
# - AGE_THRESHOLD (optional): Maximum age in days of pipelines to keep (default: 90)
|
||||
|
||||
CREATED_AT_TS=$(date -d "$stripped" +%s)
|
||||
NOW=$(date +%s)
|
||||
set -euo pipefail
|
||||
|
||||
AGE=$(($NOW-$CREATED_AT_TS))
|
||||
AGE_THRESHOLD=$((90*24*60*60)) # 90 days
|
||||
# Constants
|
||||
DEFAULT_AGE_THRESHOLD=90
|
||||
SECONDS_PER_DAY=$((24 * 60 * 60))
|
||||
|
||||
if [ $AGE -gt $AGE_THRESHOLD ];
|
||||
then
|
||||
echo "Pipeline age $AGE older than threshold $AGE_THRESHOLD, should be deleted."
|
||||
# TODO call glab to delete the pipeline. Needs an ID collected from the glab call above.
|
||||
# Functions
|
||||
log_info() {
|
||||
echo "[INFO] $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo "[ERROR] $1" >&2
|
||||
}
|
||||
|
||||
delete_pipeline() {
|
||||
local project_id=$1
|
||||
local pipeline_id=$2
|
||||
if glab api --method DELETE "projects/$project_id/pipelines/$pipeline_id"; then
|
||||
log_info "Deleted pipeline ID $pipeline_id"
|
||||
else
|
||||
echo "Pipeline age $AGE not older than threshold $AGE_THRESHOLD. Ignore."
|
||||
log_error "Failed to delete pipeline ID $pipeline_id"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Main script
|
||||
main() {
|
||||
# Authenticate
|
||||
if ! glab auth login --hostname "$CI_SERVER_HOST" --token "$PAT"; then
|
||||
log_error "Authentication failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set variables
|
||||
AGE_THRESHOLD=${AGE_THRESHOLD:-$DEFAULT_AGE_THRESHOLD}
|
||||
AGE_THRESHOLD_IN_SECONDS=$((AGE_THRESHOLD * SECONDS_PER_DAY))
|
||||
GL_PROJECT_ID=${GL_PROJECT_ID:-$CI_PROJECT_ID}
|
||||
|
||||
# Fetch pipelines
|
||||
PIPELINES=$(glab api --method GET "projects/$GL_PROJECT_ID/pipelines")
|
||||
if [ -z "$PIPELINES" ]; then
|
||||
log_error "Failed to fetch pipelines or no pipelines found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Process pipelines
|
||||
echo "$PIPELINES" | jq -r '.[] | [.id, .created_at] | @tsv' | while IFS=$'\t' read -r id created_at; do
|
||||
CREATED_AT_TS=$(date -d "$created_at" +%s)
|
||||
NOW=$(date +%s)
|
||||
AGE=$((NOW - CREATED_AT_TS))
|
||||
|
||||
if [ "$AGE" -gt "$AGE_THRESHOLD_IN_SECONDS" ]; then
|
||||
log_info "Pipeline ID $id created at $created_at is older than threshold $AGE_THRESHOLD days, deleting..."
|
||||
delete_pipeline "$GL_PROJECT_ID" "$id"
|
||||
else
|
||||
log_info "Pipeline ID $id created at $created_at is not older than threshold $AGE_THRESHOLD days. Ignoring."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
main
|
||||
```
|
||||
|
||||
:::TabTitle Using the glab CLI
|
||||
|
||||
The full script `cleanup-old-pipelines.sh` is located in the [GitLab API with Linux Shell](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-linux-shell) project.
|
||||
|
||||
```shell
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Required environment variables:
|
||||
# PAT: Project Access Token with API scope and Owner role, or Personal Access Token with API scope.
|
||||
# Optional environment variables:
|
||||
# AGE_THRESHOLD: Maximum age (in days) of pipelines to keep. Default: 90 days.
|
||||
# REPO: Repository to clean up. If not set, the current repository will be used.
|
||||
# CI_SERVER_HOST: GitLab server hostname.
|
||||
|
||||
# Function to display error message and exit
|
||||
error_exit() {
|
||||
echo "Error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Validate required environment variables
|
||||
[[ -z "${PAT:-}" ]] && error_exit "PAT (Project Access Token or Personal Access Token) is not set."
|
||||
[[ -z "${CI_SERVER_HOST:-}" ]] && error_exit "CI_SERVER_HOST is not set."
|
||||
|
||||
# Set and validate AGE_THRESHOLD
|
||||
AGE_THRESHOLD=${AGE_THRESHOLD:-90}
|
||||
[[ ! "$AGE_THRESHOLD" =~ ^[0-9]+$ ]] && error_exit "AGE_THRESHOLD must be a positive integer."
|
||||
|
||||
AGE_THRESHOLD_IN_HOURS=$((AGE_THRESHOLD * 24))
|
||||
|
||||
echo "Deleting pipelines older than $AGE_THRESHOLD days"
|
||||
|
||||
# Authenticate with GitLab
|
||||
glab auth login --hostname "$CI_SERVER_HOST" --token "$PAT" || error_exit "Authentication failed"
|
||||
|
||||
# Delete old pipelines
|
||||
delete_cmd="glab ci delete --older-than ${AGE_THRESHOLD_IN_HOURS}h"
|
||||
if [[ -n "${REPO:-}" ]]; then
|
||||
delete_cmd+=" --repo $REPO"
|
||||
fi
|
||||
|
||||
$delete_cmd || error_exit "Pipeline deletion failed"
|
||||
|
||||
echo "Pipeline cleanup completed."
|
||||
```
|
||||
|
||||
:::TabTitle Using the API with Python
|
||||
|
||||
You can also use the [`python-gitlab` API library](https://python-gitlab.readthedocs.io/en/stable/gl_objects/pipelines_and_jobs.html#project-pipelines) and
|
||||
the `created_at` attribute to implement a similar algorithm that compares the job artifact age:
|
||||
|
||||
|
|
@ -656,6 +760,8 @@ the `created_at` attribute to implement a similar algorithm that compares the jo
|
|||
pipeline_obj.delete()
|
||||
```
|
||||
|
||||
::EndTabs
|
||||
|
||||
Automatic deletion of old pipelines is proposed in [issue 338480](https://gitlab.com/gitlab-org/gitlab/-/issues/338480).
|
||||
|
||||
### List expiry settings for job artifacts
|
||||
|
|
|
|||
|
|
@ -169,6 +169,13 @@ To install the Helm chart for the proxy:
|
|||
https://gitlab.com/api/v4/projects/gitlab-org%2fworkspaces%2fgitlab-workspaces-proxy/packages/helm/devel
|
||||
```
|
||||
|
||||
For Helm chart 0.1.13 and earlier, use the following command:
|
||||
|
||||
```shell
|
||||
helm repo add gitlab-workspaces-proxy \
|
||||
https://gitlab.com/api/v4/projects/gitlab-org%2fremote-development%2fgitlab-workspaces-proxy/packages/helm/devel
|
||||
```
|
||||
|
||||
1. Modify the `ingress.className` parameter if you're using a different Ingress class:
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -7,7 +7,16 @@ module Gitlab
|
|||
# Returns the GitLab Observability URL
|
||||
#
|
||||
def observability_url
|
||||
return ENV['OVERRIDE_OBSERVABILITY_URL'] if ENV['OVERRIDE_OBSERVABILITY_URL']
|
||||
return ENV['OVERRIDE_OBSERVABILITY_QUERY_URL'] if ENV['OVERRIDE_OBSERVABILITY_QUERY_URL']
|
||||
# TODO Make observability URL configurable https://gitlab.com/gitlab-org/opstrace/opstrace-ui/-/issues/80
|
||||
# Dev, test and staging instances can all point to `observe.staging.gitlab.com` by default
|
||||
return 'https://observe.staging.gitlab.com' if Gitlab.staging? || Gitlab.dev_or_test_env?
|
||||
|
||||
'https://observe.gitlab.com'
|
||||
end
|
||||
|
||||
def observability_ingest_url
|
||||
return ENV['OVERRIDE_OBSERVABILITY_INGEST_URL'] if ENV['OVERRIDE_OBSERVABILITY_INGEST_URL']
|
||||
# TODO Make observability URL configurable https://gitlab.com/gitlab-org/opstrace/opstrace-ui/-/issues/80
|
||||
# Dev, test and staging instances can all point to `observe.staging.gitlab.com` by default
|
||||
return 'https://observe.staging.gitlab.com' if Gitlab.staging? || Gitlab.dev_or_test_env?
|
||||
|
|
|
|||
|
|
@ -18,16 +18,26 @@ module QA
|
|||
Env.namespace_name || "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}-#{SecureRandom.hex(8)}"
|
||||
end
|
||||
|
||||
# Predefined top level group name
|
||||
# Top level group name
|
||||
#
|
||||
# @return [String]
|
||||
def sandbox_name
|
||||
@sandbox_name ||= Runtime::Env.sandbox_name ||
|
||||
if !QA::Runtime::Env.running_on_dot_com? && QA::Runtime::Env.run_in_parallel?
|
||||
"gitlab-qa-sandbox-group-#{SecureRandom.hex(4)}-#{Time.now.wday + 1}"
|
||||
else
|
||||
"gitlab-qa-sandbox-group-#{Time.now.wday + 1}"
|
||||
end
|
||||
return "gitlab-qa-sandbox-group-#{Time.now.wday + 1}" if live_env?
|
||||
|
||||
"qa-sandbox-#{SecureRandom.hex(6)}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Test is running on live environment with limitations for top level group creation
|
||||
#
|
||||
# @return [Boolean]
|
||||
def live_env?
|
||||
return @live_env unless @live_env.nil?
|
||||
|
||||
# Memoize the result of this check so every call doesn't parse gitlab address and check hostname
|
||||
# There is no case to change gitlab address in the middle of test process so it should be safe to do
|
||||
@live_env = Runtime::Env.running_on_dot_com? || Runtime::Env.running_on_release?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ RSpec.describe QA::Runtime::Namespace do
|
|||
|
||||
before(:context) do
|
||||
described_class.instance_variable_set(:@time, nil)
|
||||
described_class.instance_variable_set(:@sandbox_name, nil)
|
||||
end
|
||||
|
||||
describe '.group_name' do
|
||||
|
|
@ -17,12 +16,37 @@ RSpec.describe QA::Runtime::Namespace do
|
|||
end
|
||||
|
||||
describe '.sandbox_name' do
|
||||
let(:dot_com) { false }
|
||||
let(:release) { false }
|
||||
|
||||
before do
|
||||
allow(QA::Runtime::Scenario).to receive(:gitlab_address).and_return("http://gitlab.test")
|
||||
described_class.instance_variable_set(:@live_env, nil)
|
||||
allow(QA::Runtime::Env).to receive_messages(
|
||||
running_on_dot_com?: dot_com,
|
||||
running_on_release?: release
|
||||
)
|
||||
end
|
||||
|
||||
it "returns day specific sandbox name" do
|
||||
expect(described_class.sandbox_name).to match(%r{gitlab-qa-sandbox-group-#{time.wday + 1}})
|
||||
context "when running on .com environment" do
|
||||
let(:dot_com) { true }
|
||||
|
||||
it "returns day specific sandbox name" do
|
||||
expect(described_class.sandbox_name).to match(%r{gitlab-qa-sandbox-group-#{time.wday + 1}})
|
||||
end
|
||||
end
|
||||
|
||||
context "when running on release environment" do
|
||||
let(:release) { true }
|
||||
|
||||
it "returns day specific sandbox name" do
|
||||
expect(described_class.sandbox_name).to match(%r{gitlab-qa-sandbox-group-#{time.wday + 1}})
|
||||
end
|
||||
end
|
||||
|
||||
context "when running on ephemeral environment" do
|
||||
it "returns random sandbox name" do
|
||||
expect(described_class.sandbox_name).to match(/qa-sandbox-[a-f0-9]{12}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import ModelEdit from '~/ml/model_registry/components/model_edit.vue';
|
||||
import EditMlModel from '~/ml/model_registry/apps/edit_ml_model.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
describe('ml/model_registry/apps/edit_ml_model.vue', () => {
|
||||
let wrapper;
|
||||
|
||||
const mountComponent = (canWriteModelRegistry) => {
|
||||
wrapper = shallowMountExtended(EditMlModel, {
|
||||
propsData: {
|
||||
projectPath: 'project/path',
|
||||
canWriteModelRegistry,
|
||||
markdownPreviewPath: 'markdown/preview/path',
|
||||
modelPath: 'model/path',
|
||||
modelId: 1,
|
||||
modelName: 'GPT0',
|
||||
modelDescription: 'No desc',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findModelEdit = () => wrapper.findComponent(ModelEdit);
|
||||
|
||||
it('when user has no permission does not render the model edit component', () => {
|
||||
mountComponent(false);
|
||||
|
||||
expect(findModelEdit().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('when user has permission renders the model edit component', () => {
|
||||
mountComponent(true);
|
||||
|
||||
expect(findModelEdit().props()).toEqual({
|
||||
projectPath: 'project/path',
|
||||
disableAttachments: false,
|
||||
model: { id: 1, name: 'GPT0', description: 'No desc' },
|
||||
modelPath: 'model/path',
|
||||
markdownPreviewPath: 'markdown/preview/path',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -10,7 +10,6 @@ import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
|||
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import ModelDetail from '~/ml/model_registry/components/model_detail.vue';
|
||||
import ModelEdit from '~/ml/model_registry/components/model_edit.vue';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
|
|
@ -87,6 +86,7 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
modelName: 'MyModel',
|
||||
projectPath: 'project/path',
|
||||
indexModelsPath: 'index/path',
|
||||
editModelPath: 'edit/modal/path',
|
||||
mlflowTrackingUrl: 'path/to/tracking',
|
||||
canWriteModelRegistry,
|
||||
maxAllowedFileSize: 99999,
|
||||
|
|
@ -114,7 +114,7 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
const findDeleteModel = () => wrapper.findComponent(DeleteModel);
|
||||
const findModelVersionCreateButton = () => wrapper.findByTestId('model-version-create-button');
|
||||
const findLoadOrErrorOrShow = () => wrapper.findComponent(LoadOrErrorOrShow);
|
||||
const findModelEdit = () => wrapper.findComponent(ModelEdit);
|
||||
const findModelEditButton = () => wrapper.findByTestId('edit-model-button');
|
||||
|
||||
describe('Title', () => {
|
||||
beforeEach(() => createWrapper());
|
||||
|
|
@ -166,18 +166,20 @@ describe('ml/model_registry/apps/show_ml_model', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ModelEdit', () => {
|
||||
describe('Model edit button', () => {
|
||||
beforeEach(() => createWrapper());
|
||||
|
||||
it('displays model edit button', () => {
|
||||
expect(findModelEdit().props('model')).toEqual(model);
|
||||
expect(findModelEdit().props('disableAttachments')).toBe(false);
|
||||
expect(findModelEditButton().props()).toMatchObject({
|
||||
variant: 'confirm',
|
||||
category: 'primary',
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user has no permission to write model registry', () => {
|
||||
it('does not display model edit button', () => {
|
||||
createWrapper({ canWriteModelRegistry: false });
|
||||
expect(findModelEdit().exists()).toBe(false);
|
||||
expect(findModelEditButton().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
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 ModelEdit from '~/ml/model_registry/components/model_edit.vue';
|
||||
|
||||
import editModelMutation from '~/ml/model_registry/graphql/mutations/edit_model.mutation.graphql';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
import { createMockDirective } from 'helpers/vue_mock_directive';
|
||||
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
|
||||
import { editModelResponses, model } from '../graphql_mock_data';
|
||||
|
||||
|
|
@ -41,28 +38,25 @@ describe('ModelEdit', () => {
|
|||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
||||
wrapper = shallowMountExtended(ModelEdit, {
|
||||
propsData: { model: modelProp, disableAttachments: true },
|
||||
provide: {
|
||||
propsData: {
|
||||
model: modelProp,
|
||||
disableAttachments: true,
|
||||
projectPath: 'some/project',
|
||||
maxAllowedFileSize: 99999,
|
||||
markdownPreviewPath: '/markdown-preview',
|
||||
modelId: 'gid://gitlab/Ml::Model/1',
|
||||
},
|
||||
directives: {
|
||||
GlModal: createMockDirective('gl-modal'),
|
||||
modelPath: 'model/path',
|
||||
},
|
||||
apolloProvider,
|
||||
});
|
||||
};
|
||||
|
||||
const findEditModal = () => wrapper.findComponent(ModelEdit);
|
||||
const findPrimaryButton = () => wrapper.findByTestId('primary-button');
|
||||
const findSecondaryButton = () => wrapper.findByTestId('secondary-button');
|
||||
const findModelName = () => wrapper.findByTestId('nameId');
|
||||
const findModelDescription = () => wrapper.findByTestId('descriptionId');
|
||||
const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
|
||||
const findGlModal = () => wrapper.findComponent(GlModal);
|
||||
const findGlAlert = () => wrapper.findByTestId('modalEditAlert');
|
||||
const findGlAlert = () => wrapper.findByTestId('edit-alert');
|
||||
const submitForm = async () => {
|
||||
findGlModal().vm.$emit('primary', new Event('primary'));
|
||||
findPrimaryButton().vm.$emit('click');
|
||||
await waitForPromises();
|
||||
};
|
||||
|
||||
|
|
@ -71,10 +65,6 @@ describe('ModelEdit', () => {
|
|||
createWrapper();
|
||||
});
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(findEditModal().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('shows disabled model name input', () => {
|
||||
expect(findModelName().attributes('disabled')).toBe('true');
|
||||
});
|
||||
|
|
@ -87,17 +77,16 @@ describe('ModelEdit', () => {
|
|||
expect(findModelDescription().attributes('value')).toBe(model.description);
|
||||
});
|
||||
|
||||
it('renders the edit button in the modal', () => {
|
||||
expect(findGlModal().props('actionPrimary')).toEqual({
|
||||
attributes: { variant: 'confirm', disabled: false },
|
||||
text: 'Save changes',
|
||||
it('renders the edit button', () => {
|
||||
expect(findPrimaryButton().props()).toMatchObject({
|
||||
variant: 'confirm',
|
||||
disabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the cancel button in the modal', () => {
|
||||
expect(findGlModal().props('actionSecondary')).toEqual({
|
||||
text: 'Cancel',
|
||||
attributes: { variant: 'default' },
|
||||
it('renders the cancel button', () => {
|
||||
expect(findSecondaryButton().props()).toMatchObject({
|
||||
variant: 'default',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
|
|||
is_expected.to eq({
|
||||
'projectPath' => project.full_path,
|
||||
'indexModelsPath' => "/#{project.full_path}/-/ml/models",
|
||||
'editModelPath' => "/#{project.full_path}/-/ml/models/#{model.id}/edit",
|
||||
'createModelVersionPath' => "/#{project.full_path}/-/ml/models/#{model.id}/versions/new",
|
||||
'canWriteModelRegistry' => true,
|
||||
'maxAllowedFileSize' => 10737418240,
|
||||
|
|
@ -109,6 +110,42 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#edit_ml_model_data' do
|
||||
let_it_be(:model) do
|
||||
build_stubbed(:ml_models, :with_latest_version_and_package, project: project, name: 'cool_model',
|
||||
description: 'desc')
|
||||
end
|
||||
|
||||
subject(:parsed) { Gitlab::Json.parse(helper.edit_ml_model_data(model, user)) }
|
||||
|
||||
it 'generates the correct data' do
|
||||
stub_member_access_level(project, owner: user)
|
||||
|
||||
is_expected.to eq({
|
||||
'projectPath' => project.full_path,
|
||||
'canWriteModelRegistry' => true,
|
||||
'markdownPreviewPath' => "/#{project.full_path}/-/preview_markdown",
|
||||
'modelPath' => "/#{project.full_path}/-/ml/models/#{model.id}",
|
||||
'modelId' => model.id,
|
||||
'modelName' => 'cool_model',
|
||||
'modelDescription' => 'desc'
|
||||
})
|
||||
end
|
||||
|
||||
context 'when user does not have write access to model registry' do
|
||||
before do
|
||||
allow(Ability).to receive(:allowed?).and_call_original
|
||||
allow(Ability).to receive(:allowed?)
|
||||
.with(user, :write_model_registry, project)
|
||||
.and_return(false)
|
||||
end
|
||||
|
||||
it 'canWriteModelRegistry is false' do
|
||||
expect(parsed['canWriteModelRegistry']).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_ml_model_version_data' do
|
||||
let_it_be(:model) do
|
||||
build_stubbed(:ml_models, :with_latest_version_and_package, project: project, id: 1)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,52 @@ RSpec.describe Gitlab::Observability, feature_category: :observability do
|
|||
let(:observe_url) { 'https://example.net' }
|
||||
|
||||
before do
|
||||
stub_env('OVERRIDE_OBSERVABILITY_URL', observe_url)
|
||||
stub_env('OVERRIDE_OBSERVABILITY_QUERY_URL', observe_url)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(observe_url) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.observability_ingest_url' do
|
||||
let(:gitlab_url) { 'https://example.com' }
|
||||
|
||||
subject { described_class.observability_ingest_url }
|
||||
|
||||
before do
|
||||
stub_rails_env('production')
|
||||
stub_config_setting(url: gitlab_url)
|
||||
end
|
||||
|
||||
it { is_expected.to eq('https://observe.gitlab.com') }
|
||||
|
||||
context 'when in dev environment' do
|
||||
before do
|
||||
stub_rails_env('development')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('https://observe.staging.gitlab.com') }
|
||||
end
|
||||
|
||||
context 'when in test environment' do
|
||||
before do
|
||||
stub_rails_env('test')
|
||||
end
|
||||
|
||||
it { is_expected.to eq('https://observe.staging.gitlab.com') }
|
||||
end
|
||||
|
||||
context 'when on staging.gitlab.com' do
|
||||
let(:gitlab_url) { Gitlab::Saas.staging_com_url }
|
||||
|
||||
it { is_expected.to eq('https://observe.staging.gitlab.com') }
|
||||
end
|
||||
|
||||
context 'when overriden via ENV' do
|
||||
let(:observe_url) { 'https://example.net' }
|
||||
|
||||
before do
|
||||
stub_env('OVERRIDE_OBSERVABILITY_INGEST_URL', observe_url)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(observe_url) }
|
||||
|
|
|
|||
|
|
@ -2997,7 +2997,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
|
|||
before do
|
||||
allow(build).to receive(:with_resource_group?) { true }
|
||||
allow(Ci::ResourceGroups::AssignResourceFromResourceGroupWorker).to receive(:perform_async)
|
||||
allow(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).to receive(:perform_async)
|
||||
|
||||
build.enqueue
|
||||
end
|
||||
|
|
|
|||
|
|
@ -466,21 +466,6 @@ RSpec.describe Ci::Processable, feature_category: :continuous_integration do
|
|||
expect(build.waiting_for_resource_at).not_to be_nil
|
||||
end
|
||||
|
||||
context 'when `assign_resource_worker_deduplicate_until_executing` FF is enabled and the override is disabled' do
|
||||
before do
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing: true)
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing_override: false)
|
||||
end
|
||||
|
||||
it 'is waiting for resource when build is enqueued' do
|
||||
expect(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).to receive(:perform_async).with(resource_group.id)
|
||||
|
||||
expect { build.enqueue! }.to change { build.status }.from('created').to('waiting_for_resource')
|
||||
|
||||
expect(build.waiting_for_resource_at).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build is waiting for resource' do
|
||||
before do
|
||||
build.update_column(:status, 'waiting_for_resource')
|
||||
|
|
@ -505,28 +490,6 @@ RSpec.describe Ci::Processable, feature_category: :continuous_integration do
|
|||
build.success!
|
||||
end
|
||||
|
||||
context 'when `assign_resource_worker_deduplicate_until_executing` FF is enabled and the override is disabled' do
|
||||
before do
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing: true)
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing_override: false)
|
||||
end
|
||||
|
||||
it 'releases a resource when build finished' do
|
||||
expect(build.resource_group).to receive(:release_resource_from).with(build).and_return(true).and_call_original
|
||||
expect(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).to receive(:perform_async).with(build.resource_group_id)
|
||||
|
||||
build.enqueue_waiting_for_resource!
|
||||
build.success!
|
||||
end
|
||||
|
||||
it 're-checks the resource group even if the processable does not retain a resource' do
|
||||
expect(build.resource_group).to receive(:release_resource_from).with(build).and_return(false).and_call_original
|
||||
expect(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).to receive(:perform_async).with(build.resource_group_id)
|
||||
|
||||
build.success!
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build has prerequisites' do
|
||||
before do
|
||||
allow(build).to receive(:any_unmet_prerequisites?) { true }
|
||||
|
|
|
|||
|
|
@ -6,11 +6,35 @@ RSpec.describe Projects::BuildArtifactsController, feature_category: :job_artifa
|
|||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:ci_build) { create(:ci_build, :artifacts, project: project) }
|
||||
|
||||
describe '#download' do
|
||||
it 'redirects' do
|
||||
get download_project_build_artifacts_path(project, ci_build, query_parameter: 1)
|
||||
|
||||
expect(response).to redirect_to download_project_job_artifacts_path(project, ci_build, query_parameter: 1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#browse' do
|
||||
it 'redirects' do
|
||||
get browse_project_build_artifacts_path(project, ci_build, ref_name_and_path: 'test')
|
||||
get browse_project_build_artifacts_path(project, ci_build, path: 'test')
|
||||
|
||||
expect(response).to redirect_to browse_project_job_artifacts_path(project, ci_build)
|
||||
expect(response).to redirect_to browse_project_job_artifacts_path(project, ci_build, path: 'test')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#file' do
|
||||
it 'redirects' do
|
||||
get file_project_build_artifacts_path(project, ci_build, path: 'test')
|
||||
|
||||
expect(response).to redirect_to file_project_job_artifacts_path(project, ci_build, path: 'test')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#raw' do
|
||||
it 'redirects' do
|
||||
get raw_project_build_artifacts_path(project, ci_build, path: 'test')
|
||||
|
||||
expect(response).to redirect_to raw_project_job_artifacts_path(project, ci_build, path: 'test')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
|
|||
end
|
||||
|
||||
context 'when model project does not match project id' do
|
||||
let(:request_project) { create(:project) }
|
||||
let_it_be(:request_project) { create(:project) }
|
||||
|
||||
it { is_expected.to have_gitlab_http_status(:not_found) }
|
||||
end
|
||||
|
|
@ -106,6 +106,48 @@ RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'edit' do
|
||||
let(:model_id) { model1.id }
|
||||
let(:request_project) { model1.project }
|
||||
|
||||
subject(:edit_request) do
|
||||
edit_model
|
||||
response
|
||||
end
|
||||
|
||||
before do
|
||||
edit_request
|
||||
end
|
||||
|
||||
it 'renders the template' do
|
||||
is_expected.to render_template('projects/ml/models/edit')
|
||||
end
|
||||
|
||||
it 'fetches the correct model' do
|
||||
edit_request
|
||||
|
||||
expect(assigns(:model)).to eq(model1)
|
||||
end
|
||||
|
||||
context 'when model id does not exist' do
|
||||
let(:model_id) { non_existing_record_id }
|
||||
|
||||
it { is_expected.to have_gitlab_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when model project does not match project id' do
|
||||
let(:request_project) { create(:project) }
|
||||
|
||||
it { is_expected.to have_gitlab_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when user does not have access' do
|
||||
let(:write_model_registry) { false }
|
||||
|
||||
it { is_expected.to have_gitlab_http_status(:not_found) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'destroy' do
|
||||
let(:model_for_deletion) do
|
||||
create(:ml_models, project: project)
|
||||
|
|
@ -156,4 +198,8 @@ RSpec.describe Projects::Ml::ModelsController, feature_category: :mlops do
|
|||
def new_model
|
||||
get new_project_ml_model_path(project)
|
||||
end
|
||||
|
||||
def edit_model
|
||||
get edit_project_ml_model_path(request_project, model_id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService, featu
|
|||
|
||||
before do
|
||||
allow(Ci::ResourceGroups::AssignResourceFromResourceGroupWorker).to receive(:perform_in)
|
||||
allow(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).to receive(:perform_in)
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
|
|
@ -217,19 +216,6 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService, featu
|
|||
subject
|
||||
end
|
||||
|
||||
context 'when `assign_resource_worker_deduplicate_until_executing` FF is enabled and override is disabled' do
|
||||
before do
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing: true)
|
||||
stub_feature_flags(assign_resource_worker_deduplicate_until_executing_override: false)
|
||||
end
|
||||
|
||||
it 'does not re-spawn the old worker for assigning a resource' do
|
||||
expect(Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2).not_to receive(:perform_in)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a stale build assigned to a resource' do
|
||||
before do
|
||||
other_build.doom!
|
||||
|
|
|
|||
|
|
@ -28,14 +28,8 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupWorkerV2, feat
|
|||
end
|
||||
|
||||
context 'when resource group exists' do
|
||||
it 'executes AssignResourceFromResourceGroupService' do
|
||||
expect_next_instance_of(
|
||||
Ci::ResourceGroups::AssignResourceFromResourceGroupService,
|
||||
resource_group.project,
|
||||
nil
|
||||
) do |service|
|
||||
expect(service).to receive(:execute).with(resource_group)
|
||||
end
|
||||
it 'does not execute AssignResourceFromResourceGroupService' do
|
||||
expect(Ci::ResourceGroups::AssignResourceFromResourceGroupService).not_to receive(:new)
|
||||
|
||||
perform
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ require (
|
|||
golang.org/x/net v0.29.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.34.2
|
||||
google.golang.org/protobuf v1.35.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
|
|
@ -957,8 +957,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 h1:DkD0plWEVUB8v/Ru6kRBW30Hy/fRNBC8hPdcExuBZMc=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0/go.mod h1:wRKMf/tRASHwH/UOfPQ3IQmVFhTz2/1a1/mpXoIjF54=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
|||