Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-10-22 09:22:04 +00:00
parent a3a4afd892
commit 4f27fd0e82
69 changed files with 1070 additions and 657 deletions

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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>

View File

@ -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,
};

View File

@ -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"

View File

@ -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>

View File

@ -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 = {

View File

@ -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 });

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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!

View File

@ -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

View File

@ -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)

View File

@ -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) } }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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:
![Environment URL in merge request](../img/environments_mr_review_app_v11_10.png)
- In the Environments view as a button:
![Open live environment from environments view](img/environments_open_live_environment_v14_8.png)
- In the Deployments view as a button:
![Environment URL in deployments](../img/deployments_view_v11_10.png)
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:
![Environment URLs in merge request](../img/environments_link_url_mr_v10_1.png)
#### 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 &lt;environment&gt;**.
- 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 &lt;environment&gt;**.
- 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:
![Environment groups](img/environments_dynamic_groups_v13_10.png)
- In a merge request as a link:
![Environment URL in merge request](../img/environments_mr_review_app_v11_10.png)
- In the Environments view as a button:
![Open live environment from environments view](img/environments_open_live_environment_v14_8.png)
- In the Deployments view as a button:
![Environment URL in deployments](../img/deployments_view_v11_10.png)
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:
![Environment URLs in merge request](../img/environments_link_url_mr_v10_1.png)
#### 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:
![Environment groups](img/environments_dynamic_groups_v13_10.png)
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)

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -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.
![Pipeline detail](img/job_failure_reason.png)
![Pipeline detail](img/job_failure_reason_v10_7.png)
You can also see the reason it failed on the Job detail page.

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -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 shell](img/maven-freestyle-shell.png)
![freestyle shell](img/maven-freestyle-shell_v16_4.png)
### 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:
![freestyle plugin](img/maven-freestyle-plugin.png)
![freestyle plugin](img/maven-freestyle-plugin_v16_4.png)
This plugin requires Maven to be installed on the Jenkins agent, and uses a script wrapper
for calling Maven commands.

View File

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -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.
![review apps workflow](img/continuous-delivery-review-apps.svg)
![review apps workflow](img/continuous-delivery-review-apps_v11_4.svg)
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.
![View app file list in merge request widget](img/view_on_mr_widget.png)
![View app file list in merge request widget](img/view_on_mr_widget_v11_5.png)
- In the diff for a comparison or commit, by selecting **View** (**{external-link}**) next to the file.

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -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
![Hosted runners for GitLab.com architecture](img/gitlab-hosted_runners_architecture.png)
![Hosted runners for GitLab.com architecture](img/gitlab-hosted_runners_architecture_v17_0.png)
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.
![Job isolation](img/build_isolation.png)
![Job isolation](img/build_isolation_v16_1.png)
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`.

View File

@ -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))
![Runner fleet dashboard](img/runner_fleet_dashboard.png)
![Runner fleet dashboard](img/runner_fleet_dashboard_v17_1.png)
## View the runner fleet dashboard

View File

@ -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.
![Runner fleet dashboard for groups](img/runner_fleet_dashboard_groups.png)
![Runner fleet dashboard for groups](img/runner_fleet_dashboard_groups_v17_1.png)
## Dashboard metrics

View File

@ -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.
![Project runner IP address](img/project_runner_ip_address.png)
![Project runner IP address](img/project_runner_ip_address_v10_7.png)
## Enable use of runner registration tokens in projects and groups

View File

@ -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'
}
```

View File

@ -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:

View File

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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**:
![SSH keys restriction **Admin** area settings](img/ssh_keys_restrictions_settings.png)
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:
![Restricted SSH key icon](img/ssh_keys_restricted_key_icon.png)
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

View File

@ -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>&#92;</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

View File

@ -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. |

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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',
});
});
});

View File

@ -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);
});
});
});

View File

@ -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',
});
});

View File

@ -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)

View File

@ -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) }

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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!

View File

@ -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

View File

@ -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 (

View File

@ -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=