Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
551b3bfd7e
commit
eab843a2f5
|
|
@ -20,6 +20,9 @@ export default {
|
|||
duration() {
|
||||
return timeIntervalInWords(this.job.duration);
|
||||
},
|
||||
durationTitle() {
|
||||
return this.job.finished_at ? __('Duration') : __('Elapsed time');
|
||||
},
|
||||
erasedAt() {
|
||||
return this.timeFormatted(this.job.erased_at);
|
||||
},
|
||||
|
|
@ -76,7 +79,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div v-if="shouldRenderBlock">
|
||||
<detail-row v-if="job.duration" :value="duration" title="Duration" />
|
||||
<detail-row v-if="job.duration" :value="duration" :title="durationTitle" />
|
||||
<detail-row
|
||||
v-if="job.finished_at"
|
||||
:value="finishedAt"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,14 @@
|
|||
import { GlToggle, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
|
||||
import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
|
||||
import updateDependencyProxySettings from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_settings.mutation.graphql';
|
||||
import updateDependencyProxyImageTtlGroupPolicy from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_image_ttl_group_policy.mutation.graphql';
|
||||
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
|
||||
import { updateGroupDependencyProxySettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
import {
|
||||
updateGroupDependencyProxySettingsOptimisticResponse,
|
||||
updateDependencyProxyImageTtlGroupPolicyOptimisticResponse,
|
||||
} from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
|
||||
import {
|
||||
DEPENDENCY_PROXY_HEADER,
|
||||
|
|
@ -19,14 +24,20 @@ export default {
|
|||
GlSprintf,
|
||||
GlLink,
|
||||
SettingsBlock,
|
||||
SettingsTitles,
|
||||
},
|
||||
i18n: {
|
||||
DEPENDENCY_PROXY_HEADER,
|
||||
DEPENDENCY_PROXY_SETTINGS_DESCRIPTION,
|
||||
label: s__('DependencyProxy|Enable Dependency Proxy'),
|
||||
enabledProxyLabel: s__('DependencyProxy|Enable Dependency Proxy'),
|
||||
enabledProxyHelpText: s__(
|
||||
'DependencyProxy|To see the image prefix and what is in the cache, visit the %{linkStart}Dependency Proxy%{linkEnd}',
|
||||
),
|
||||
storageSettingsTitle: s__('DependencyProxy|Storage settings'),
|
||||
ttlPolicyEnabledLabel: s__('DependencyProxy|Clear the Dependency Proxy cache automatically'),
|
||||
ttlPolicyEnabledHelpText: s__(
|
||||
'DependencyProxy|When enabled, images older than 90 days will be removed from the cache.',
|
||||
),
|
||||
},
|
||||
links: {
|
||||
DEPENDENCY_PROXY_DOCS_PATH,
|
||||
|
|
@ -37,6 +48,10 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
dependencyProxyImageTtlPolicy: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
|
@ -52,29 +67,35 @@ export default {
|
|||
this.updateSettings({ enabled });
|
||||
},
|
||||
},
|
||||
ttlEnabled: {
|
||||
get() {
|
||||
return this.dependencyProxyImageTtlPolicy.enabled;
|
||||
},
|
||||
set(enabled) {
|
||||
const payload = {
|
||||
enabled,
|
||||
ttl: 90, // hardocded TTL for the MVC version
|
||||
};
|
||||
this.updateDependencyProxyImageTtlGroupPolicy(payload);
|
||||
},
|
||||
},
|
||||
helpText() {
|
||||
return this.enabled ? this.$options.i18n.enabledProxyHelpText : '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updateSettings(payload) {
|
||||
mutationVariables(payload) {
|
||||
return {
|
||||
input: {
|
||||
groupPath: this.groupPath,
|
||||
...payload,
|
||||
},
|
||||
};
|
||||
},
|
||||
async executeMutation(config, resource) {
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: updateDependencyProxySettings,
|
||||
variables: {
|
||||
input: {
|
||||
groupPath: this.groupPath,
|
||||
...payload,
|
||||
},
|
||||
},
|
||||
update: updateGroupPackageSettings(this.groupPath),
|
||||
optimisticResponse: updateGroupDependencyProxySettingsOptimisticResponse({
|
||||
...this.dependencyProxySettings,
|
||||
...payload,
|
||||
}),
|
||||
});
|
||||
|
||||
if (data.updateDependencyProxySettings?.errors?.length > 0) {
|
||||
const { data } = await this.$apollo.mutate(config);
|
||||
if (data[resource]?.errors.length > 0) {
|
||||
throw new Error();
|
||||
} else {
|
||||
this.$emit('success');
|
||||
|
|
@ -83,6 +104,32 @@ export default {
|
|||
this.$emit('error');
|
||||
}
|
||||
},
|
||||
async updateSettings(payload) {
|
||||
const apolloConfig = {
|
||||
mutation: updateDependencyProxySettings,
|
||||
variables: this.mutationVariables(payload),
|
||||
update: updateGroupPackageSettings(this.groupPath),
|
||||
optimisticResponse: updateGroupDependencyProxySettingsOptimisticResponse({
|
||||
...this.dependencyProxySettings,
|
||||
...payload,
|
||||
}),
|
||||
};
|
||||
|
||||
this.executeMutation(apolloConfig, 'updateDependencyProxySettings');
|
||||
},
|
||||
async updateDependencyProxyImageTtlGroupPolicy(payload) {
|
||||
const apolloConfig = {
|
||||
mutation: updateDependencyProxyImageTtlGroupPolicy,
|
||||
variables: this.mutationVariables(payload),
|
||||
update: updateGroupPackageSettings(this.groupPath),
|
||||
optimisticResponse: updateDependencyProxyImageTtlGroupPolicyOptimisticResponse({
|
||||
...this.dependencyProxyImageTtlPolicy,
|
||||
...payload,
|
||||
}),
|
||||
};
|
||||
|
||||
this.executeMutation(apolloConfig, 'updateDependencyProxyImageTtlGroupPolicy');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -111,7 +158,7 @@ export default {
|
|||
<gl-toggle
|
||||
v-model="enabled"
|
||||
:disabled="isLoading"
|
||||
:label="$options.i18n.label"
|
||||
:label="$options.i18n.enabledProxyLabel"
|
||||
:help="helpText"
|
||||
data-qa-selector="dependency_proxy_setting_toggle"
|
||||
data-testid="dependency-proxy-setting-toggle"
|
||||
|
|
@ -128,6 +175,15 @@ export default {
|
|||
</span>
|
||||
</template>
|
||||
</gl-toggle>
|
||||
|
||||
<settings-titles :title="$options.i18n.storageSettingsTitle" class="gl-my-6" />
|
||||
<gl-toggle
|
||||
v-model="ttlEnabled"
|
||||
:disabled="isLoading"
|
||||
:label="$options.i18n.ttlPolicyEnabledLabel"
|
||||
:help="$options.i18n.ttlPolicyEnabledHelpText"
|
||||
data-testid="dependency-proxy-ttl-policies-toggle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</settings-block>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ export default {
|
|||
dependencyProxySettings() {
|
||||
return this.group?.dependencyProxySetting || {};
|
||||
},
|
||||
dependencyProxyImageTtlPolicy() {
|
||||
return this.group?.dependencyProxyImageTtlPolicy || {};
|
||||
},
|
||||
isLoading() {
|
||||
return this.$apollo.queries.group.loading;
|
||||
},
|
||||
|
|
@ -82,6 +85,7 @@ export default {
|
|||
<dependency-proxy-settings
|
||||
v-if="dependencyProxyAvailable"
|
||||
:dependency-proxy-settings="dependencyProxySettings"
|
||||
:dependency-proxy-image-ttl-policy="dependencyProxyImageTtlPolicy"
|
||||
:is-loading="isLoading"
|
||||
@success="handleSuccess"
|
||||
@error="handleError"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ export default {
|
|||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -16,10 +17,10 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<h5 class="gl-border-b-solid gl-border-b-1 gl-border-gray-200">
|
||||
<h5 class="gl-border-b-solid gl-border-b-1 gl-border-gray-200 gl-pb-3">
|
||||
{{ title }}
|
||||
</h5>
|
||||
<p>{{ subTitle }}</p>
|
||||
<p v-if="subTitle">{{ subTitle }}</p>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
mutation updateDependencyProxyImageTtlGroupPolicy(
|
||||
$input: UpdateDependencyProxyImageTtlGroupPolicyInput!
|
||||
) {
|
||||
updateDependencyProxyImageTtlGroupPolicy(input: $input) {
|
||||
dependencyProxyImageTtlPolicy {
|
||||
enabled
|
||||
ttl
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,10 @@ query getGroupPackagesSettings($fullPath: ID!) {
|
|||
dependencyProxySetting {
|
||||
enabled
|
||||
}
|
||||
dependencyProxyImageTtlPolicy {
|
||||
ttl
|
||||
enabled
|
||||
}
|
||||
packageSettings {
|
||||
mavenDuplicatesAllowed
|
||||
mavenDuplicateExceptionRegex
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ export const updateGroupPackageSettings = (fullPath) => (client, { data: updated
|
|||
...updatedData.updateDependencyProxySettings.dependencyProxySetting,
|
||||
};
|
||||
}
|
||||
if (updatedData.updateDependencyProxyImageTtlGroupPolicy) {
|
||||
draftState.group.dependencyProxyImageTtlPolicy = {
|
||||
...updatedData.updateDependencyProxyImageTtlGroupPolicy.dependencyProxyImageTtlPolicy,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
client.writeQuery({
|
||||
|
|
|
|||
|
|
@ -21,3 +21,15 @@ export const updateGroupDependencyProxySettingsOptimisticResponse = (changes) =>
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const updateDependencyProxyImageTtlGroupPolicyOptimisticResponse = (changes) => ({
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
__typename: 'Mutation',
|
||||
updateDependencyProxyImageTtlGroupPolicy: {
|
||||
__typename: 'UpdateDependencyProxyImageTtlGroupPolicyPayload',
|
||||
errors: [],
|
||||
dependencyProxyImageTtlPolicy: {
|
||||
...changes,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
|
|||
Digest::MD5.hexdigest(ingredients.join('|'))
|
||||
end
|
||||
|
||||
def nest_experiment(other)
|
||||
instance_exec(:nested, { label: other.name }, &Configuration.tracking_behavior)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def feature_flag_name
|
||||
|
|
|
|||
|
|
@ -70,15 +70,13 @@
|
|||
.card
|
||||
.card-header
|
||||
= _('Projects')
|
||||
%span.badge.badge-pill
|
||||
#{@group.projects.count}
|
||||
= gl_badge_tag @group.projects.count
|
||||
%ul.content-list
|
||||
- @projects.each do |project|
|
||||
%li
|
||||
%strong
|
||||
= link_to project.full_name, [:admin, project]
|
||||
%span.badge.badge-pill
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
= gl_badge_tag storage_counter(project.statistics.storage_size)
|
||||
%span.float-right.light
|
||||
%span.monospace= project.full_path + '.git'
|
||||
- unless @projects.size < Kaminari.config.default_per_page
|
||||
|
|
@ -90,15 +88,13 @@
|
|||
.card
|
||||
.card-header
|
||||
= _('Projects shared with %{group_name}') % { group_name: @group.name }
|
||||
%span.badge.badge-pill
|
||||
#{shared_projects.size}
|
||||
= gl_badge_tag shared_projects.size
|
||||
%ul.content-list
|
||||
- shared_projects.each do |project|
|
||||
%li
|
||||
%strong
|
||||
= link_to project.full_name, [:admin, project]
|
||||
%span.badge.badge-pill
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
= gl_badge_tag storage_counter(project.statistics.storage_size)
|
||||
%span.float-right.light
|
||||
%span.monospace= project.full_path + '.git'
|
||||
|
||||
|
|
@ -126,7 +122,7 @@
|
|||
.card
|
||||
.card-header
|
||||
= html_escape(_("%{group_name} group members")) % { group_name: "<strong>#{html_escape(@group.name)}</strong>".html_safe }
|
||||
%span.badge.badge-pill= @group.users_count
|
||||
= gl_badge_tag @group.users_count
|
||||
= render 'shared/members/manage_access_button', path: group_group_members_path(@group)
|
||||
%ul.content-list.group-users-list.content-list.members-list
|
||||
= render partial: 'shared/members/member',
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@
|
|||
= s_('AdminProjects|Delete')
|
||||
|
||||
.stats
|
||||
%span.badge.badge-pill
|
||||
= storage_counter(project.statistics&.storage_size)
|
||||
= gl_badge_tag storage_counter(project.statistics&.storage_size)
|
||||
= render_if_exists 'admin/projects/archived', project: project
|
||||
.title
|
||||
= link_to(admin_project_path(project)) do
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
= f.check_box :auto_devops_enabled, class: 'form-check-input', checked: group.auto_devops_enabled?
|
||||
= f.label :auto_devops_enabled, class: 'form-check-label' do
|
||||
%strong= s_('GroupSettings|Default to Auto DevOps pipeline for all projects within this group')
|
||||
%span.badge.badge-info#auto-devops-badge= badge_for_auto_devops_scope(group)
|
||||
= gl_badge_tag badge_for_auto_devops_scope(group), variant: :info
|
||||
.form-text.text-muted
|
||||
= s_('GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found.')
|
||||
= link_to _('Learn more.'), help_page_path('topics/autodevops/index.md'), target: '_blank'
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ If the scanner report is small, less than 35 lines, then feel free to [inline th
|
|||
|
||||
#### Test Diffs
|
||||
|
||||
The [go-cmp]<https://github.com/google/go-cmp> package should be used when comparing large structs in tests. It makes it possible to output a specific diff where the two structs differ, rather than seeing the whole of both structs printed out in the test logs. Here is a small example:
|
||||
The [go-cmp](https://github.com/google/go-cmp) package should be used when comparing large structs in tests. It makes it possible to output a specific diff where the two structs differ, rather than seeing the whole of both structs printed out in the test logs. Here is a small example:
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
|
|
|||
|
|
@ -496,12 +496,12 @@ Tests that are tagged with `:mobile` can be run against specified mobile devices
|
|||
|
||||
Running directly against an environment like staging is not recommended because Sauce Labs test logs expose credentials. Therefore, it is best practice and the default to use a tunnel.
|
||||
|
||||
Tunnel installation instructions are here [https://docs.saucelabs.com/secure-connections/sauce-connect/installation]. To start the tunnel, after following the installation above, copy the run command in Sauce Labs > Tunnels (must be logged in to Sauce Labs with the credentials found in 1Password) and run in terminal.
|
||||
For tunnel installation instructions, read [Sauce Connect Proxy Installation](https://docs.saucelabs.com/secure-connections/sauce-connect/installation). To start the tunnel, after following the installation above, copy the run command in Sauce Labs > Tunnels (must be logged in to Sauce Labs with the credentials found in 1Password) and run in terminal.
|
||||
|
||||
NOTE:
|
||||
It is highly recommended to use `GITLAB_QA_ACCESS_TOKEN` to speed up tests and reduce flakiness.
|
||||
|
||||
`QA_REMOTE_MOBILE_DEVICE_NAME` can be any device name listed in [https://saucelabs.com/platform/supported-browsers-devices] under Emulators/simulators and the latest versions of Android or iOS. `QA_BROWSER` must be set to `safari` for iOS devices and `chrome` for Android devices.
|
||||
`QA_REMOTE_MOBILE_DEVICE_NAME` can be any device name listed in [Supported browsers and devices](https://saucelabs.com/platform/supported-browsers-devices) under Emulators/simulators and the latest versions of Android or iOS. `QA_BROWSER` must be set to `safari` for iOS devices and `chrome` for Android devices.
|
||||
|
||||
1. To test against a local instance with a tunnel running, in `gitlab/qa` run:
|
||||
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ The following variables allow configuration of global dependency scanning settin
|
|||
| `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs to trust. The bundle of certificates provided here is also used by other tools during the scanning process, such as `git`, `yarn`, or `npm`. See [Using a custom SSL CA certificate authority](#using-a-custom-ssl-ca-certificate-authority) for more details. |
|
||||
| `DS_EXCLUDED_ANALYZERS` | Specify the analyzers (by name) to exclude from Dependency Scanning. For more information, see [Dependency Scanning Analyzers](analyzers.md). |
|
||||
| `DS_DEFAULT_ANALYZERS` | ([**DEPRECATED - use `DS_EXCLUDED_ANALYZERS` instead**](https://gitlab.com/gitlab-org/gitlab/-/issues/287691)) Override the names of the official default images. For more information, see [Dependency Scanning Analyzers](analyzers.md). |
|
||||
| `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"`. |
|
||||
| `DS_EXCLUDED_PATHS` | Exclude files and directories from the scan based on the paths. A comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"`. |
|
||||
| `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). |
|
||||
| `SECURE_LOG_LEVEL` | Set the minimum logging level. Messages of this logging level or higher are output. From highest to lowest severity, the logging levels are: `fatal`, `error`, `warn`, `info`, `debug`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1. Default: `info`. |
|
||||
|
||||
|
|
|
|||
|
|
@ -313,9 +313,10 @@ rule in the defined policy are met.
|
|||
|
||||
| Field | Type | Possible values | Description |
|
||||
|-------|------|-----------------|-------------|
|
||||
| `scan` | `string` | `dast`, `secret_detection`, `sast` | The action's type. |
|
||||
| `scan` | `string` | `dast`, `secret_detection`, `sast`, `container_scanning`, `cluster_image_scanning` | The action's type. |
|
||||
| `site_profile` | `string` | Name of the selected [DAST site profile](../dast/index.md#site-profile). | The DAST site profile to execute the DAST scan. This field should only be set if `scan` type is `dast`. |
|
||||
| `scanner_profile` | `string` or `null` | Name of the selected [DAST scanner profile](../dast/index.md#scanner-profile). | The DAST scanner profile to execute the DAST scan. This field should only be set if `scan` type is `dast`.|
|
||||
| `variables` | `object` | | Set of variables applied and enforced for the selected scan. The object's key is the variable name with a value provided as a string. |
|
||||
|
||||
Note the following:
|
||||
|
||||
|
|
@ -379,6 +380,9 @@ scan_execution_policy:
|
|||
- main
|
||||
actions:
|
||||
- scan: secret_detection
|
||||
- scan: sast
|
||||
variables:
|
||||
SAST_EXCLUDED_ANALYZERS: brakeman
|
||||
- scan: container_scanning
|
||||
- name: Enforce Cluster Image Scanning on production-cluster every 24h
|
||||
description: This policy enforces Cluster Image Scanning scan to run every 24 hours
|
||||
|
|
@ -406,7 +410,8 @@ In this example:
|
|||
`release/v1.2.1`), DAST scans run with `Scanner Profile A` and `Site Profile B`.
|
||||
- DAST and secret detection scans run every 10 minutes. The DAST scan runs with `Scanner Profile C`
|
||||
and `Site Profile D`.
|
||||
- Secret detection and container scanning scans run for every pipeline executed on the `main` branch.
|
||||
- Secret detection, container scanning, and SAST scans run for every pipeline executed on the `main`
|
||||
branch. The SAST scan runs with the `SAST_EXCLUDED_ANALYZER` variable set to `"brakeman"`.
|
||||
- Cluster Image Scanning scan runs every 24h. The scan runs on the `production-cluster` cluster and fetches vulnerabilities
|
||||
from the container with the name `database` configured for deployment with the name `production-application` in the `production-namespace` namespace.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,59 +4,66 @@ group: Integrations
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Atlassian Bamboo Service **(FREE)**
|
||||
# Atlassian Bamboo integration **(FREE)**
|
||||
|
||||
GitLab provides integration with Atlassian Bamboo for continuous integration.
|
||||
When configured, pushes to a project trigger a build in Bamboo automatically.
|
||||
Merge requests also display CI/CD status showing whether the build is pending,
|
||||
failed, or completed successfully. It also provides a link to the Bamboo build
|
||||
page for more information.
|
||||
You can automatically trigger builds in Atlassian Bamboo when you push changes
|
||||
to your project in GitLab.
|
||||
|
||||
Bamboo doesn't quite provide the same features as a traditional build system when
|
||||
it comes to accepting webhooks and commit data. There are a few things that
|
||||
need to be configured in a Bamboo build plan before GitLab can integrate.
|
||||
When this integration is configured, merge requests also display the following information:
|
||||
|
||||
## Setup
|
||||
- A CI/CD status that shows if the build is pending, failed, or has completed successfully.
|
||||
- A link to the Bamboo build page for more information.
|
||||
|
||||
### Complete these steps in Bamboo
|
||||
Bamboo doesn't provide the same features as a traditional build system when
|
||||
accepting webhooks and commit data. You must configure a Bamboo
|
||||
build plan before you configure the integration in GitLab.
|
||||
|
||||
1. Navigate to a Bamboo build plan and choose **Configure plan** from the **Actions**
|
||||
dropdown.
|
||||
## Configure Bamboo
|
||||
|
||||
1. In Bamboo, go to a build plan and choose **Actions > Configure plan**.
|
||||
1. Select the **Triggers** tab.
|
||||
1. Click **Add trigger**.
|
||||
1. Enter a description such as **GitLab trigger**.
|
||||
1. Choose **Repository triggers the build when changes are committed**.
|
||||
1. Select **Add trigger**.
|
||||
1. Enter a description like `GitLab trigger`.
|
||||
1. Select **Repository triggers the build when changes are committed**.
|
||||
1. Select the checkbox for one or more repositories.
|
||||
1. Enter the GitLab IP address in the **Trigger IP addresses** box. This is a
|
||||
list of IP addresses that are allowed to trigger Bamboo builds.
|
||||
1. Enter the GitLab IP address in **Trigger IP addresses**. These IP addresses
|
||||
are allowed to trigger Bamboo builds.
|
||||
1. Save the trigger.
|
||||
1. In the left pane, select a build stage. If you have multiple build stages
|
||||
you want to select the last stage that contains the Git checkout task.
|
||||
1. In the left pane, select a build stage. If you have multiple build stages,
|
||||
select the last stage that contains the Git checkout task.
|
||||
1. Select the **Miscellaneous** tab.
|
||||
1. Under **Pattern Match Labeling** put `${bamboo.repository.revision.number}`
|
||||
in the **Labels** box.
|
||||
1. Save
|
||||
1. Under **Pattern Match Labeling** enter `${bamboo.repository.revision.number}`
|
||||
in **Labels**.
|
||||
1. Select **Save**.
|
||||
|
||||
Bamboo is now ready to accept triggers from GitLab. Next, set up the Bamboo
|
||||
service in GitLab.
|
||||
Bamboo is ready to accept triggers from GitLab. Next, set up the Bamboo
|
||||
integration in GitLab.
|
||||
|
||||
### Complete these steps in GitLab
|
||||
## Configure GitLab
|
||||
|
||||
1. Navigate to the project you want to configure to trigger builds.
|
||||
1. Navigate to the [Integrations page](overview.md#accessing-integrations)
|
||||
1. Click **Atlassian Bamboo**.
|
||||
1. Ensure that the **Active** toggle is enabled.
|
||||
1. Enter the base URL of your Bamboo server. `https://bamboo.example.com`
|
||||
1. Enter the build key from your Bamboo build plan. Build keys are typically made
|
||||
up from the Project Key and Plan Key that are set on project/plan creation and
|
||||
separated with a dash (`-`), for example **PROJ-PLAN**. This is a short, all
|
||||
uppercase identifier that is unique. When viewing a plan in Bamboo, the
|
||||
build key is also shown in the browser URL, for example `https://bamboo.example.com/browse/PROJ-PLAN`.
|
||||
1. If necessary, enter username and password for a Bamboo user that has
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Integrations**.
|
||||
1. Select **Atlassian Bamboo**.
|
||||
1. Ensure the **Active** checkbox is selected.
|
||||
1. Enter the base URL of your Bamboo server. For example, `https://bamboo.example.com`.
|
||||
1. Enter the [build key](#identify-the-bamboo-build-plan-build-key) from your Bamboo
|
||||
build plan.
|
||||
1. If necessary, enter a username and password for a Bamboo user that has
|
||||
access to trigger the build plan. Leave these fields blank if you do not require
|
||||
authentication.
|
||||
1. Save or optionally click **Test Settings**. **Test Settings**
|
||||
actually triggers a build in Bamboo.
|
||||
1. Optional. To test the configuration and trigger a build in Bamboo,
|
||||
select **Test Settings**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
### Identify the Bamboo build plan build key
|
||||
|
||||
A build key is a unique identifier typically made up from the project key and
|
||||
plan key.
|
||||
Build keys are short, all uppercase, and separated with a dash (`-`),
|
||||
for example `PROJ-PLAN`.
|
||||
|
||||
The build key is included in the browser URL when you view a plan in
|
||||
Bamboo. For example, `https://bamboo.example.com/browse/PROJ-PLAN`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace :gitlab do
|
||||
namespace :background_migrations do
|
||||
desc 'Synchronously finish executing a batched background migration'
|
||||
task :finalize, [:job_class_name, :table_name, :column_name, :job_arguments] => :environment do |_, args|
|
||||
[:job_class_name, :table_name, :column_name, :job_arguments].each do |argument|
|
||||
unless args[argument]
|
||||
|
|
@ -19,5 +20,23 @@ namespace :gitlab do
|
|||
|
||||
puts "Done.".color(:green)
|
||||
end
|
||||
|
||||
desc 'Display the status of batched background migrations'
|
||||
task status: :environment do
|
||||
statuses = Gitlab::Database::BackgroundMigration::BatchedMigration.statuses
|
||||
max_status_length = statuses.keys.map(&:length).max
|
||||
format_string = "%-#{max_status_length}s | %s\n"
|
||||
|
||||
Gitlab::Database::BackgroundMigration::BatchedMigration.find_each(batch_size: 100) do |migration|
|
||||
identification_fields = [
|
||||
migration.job_class_name,
|
||||
migration.table_name,
|
||||
migration.column_name,
|
||||
migration.job_arguments.to_json
|
||||
].join(',')
|
||||
|
||||
printf(format_string, migration.status, identification_fields)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11531,6 +11531,9 @@ msgstr ""
|
|||
msgid "DependencyProxy|Cached %{time}"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyProxy|Clear the Dependency Proxy cache automatically"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyProxy|Contains %{count} blobs of images (%{size})"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11555,6 +11558,9 @@ msgstr ""
|
|||
msgid "DependencyProxy|Image list"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyProxy|Storage settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyProxy|The Dependency Proxy is disabled. %{docLinkStart}Learn how to enable it%{docLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11564,6 +11570,9 @@ msgstr ""
|
|||
msgid "DependencyProxy|To see the image prefix and what is in the cache, visit the %{linkStart}Dependency Proxy%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyProxy|When enabled, images older than 90 days will be removed from the cache."
|
||||
msgstr ""
|
||||
|
||||
msgid "Depends on %d merge request being merged"
|
||||
msgid_plural "Depends on %d merge requests being merged"
|
||||
msgstr[0] ""
|
||||
|
|
|
|||
|
|
@ -306,6 +306,26 @@ RSpec.describe ApplicationExperiment, :experiment do
|
|||
end
|
||||
end
|
||||
|
||||
context "when nesting experiments" do
|
||||
before do
|
||||
stub_experiments(top: :control, nested: :control)
|
||||
end
|
||||
|
||||
it "doesn't raise an exception" do
|
||||
expect { experiment(:top) { |e| e.control { experiment(:nested) { } } } }.not_to raise_error
|
||||
end
|
||||
|
||||
it "tracks an event", :snowplow do
|
||||
experiment(:top) { |e| e.control { experiment(:nested) { } } }
|
||||
|
||||
expect(Gitlab::Tracking).to have_received(:event).with( # rubocop:disable RSpec/ExpectGitlabTracking
|
||||
'top',
|
||||
'nested',
|
||||
hash_including(label: 'nested')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when caching" do
|
||||
let(:cache) { Gitlab::Experiment::Configuration.cache }
|
||||
|
||||
|
|
|
|||
|
|
@ -12,5 +12,13 @@ FactoryBot.define do
|
|||
sequence(:job_arguments) { |n| [["column_#{n}"], ["column_#{n}_convert_to_bigint"]] }
|
||||
total_tuple_count { 10_000 }
|
||||
pause_ms { 100 }
|
||||
|
||||
trait :finished do
|
||||
status { :finished }
|
||||
end
|
||||
|
||||
trait :failed do
|
||||
status { :failed }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ describe('Job Sidebar Details Container', () => {
|
|||
beforeEach(createWrapper);
|
||||
|
||||
it.each([
|
||||
['duration', 'Duration: 6 seconds'],
|
||||
['duration', 'Elapsed time: 6 seconds'],
|
||||
['erased_at', 'Erased: 3 weeks ago'],
|
||||
['finished_at', 'Finished: 3 weeks ago'],
|
||||
['queued', 'Queued: 9 seconds'],
|
||||
|
|
@ -86,6 +86,15 @@ describe('Job Sidebar Details Container', () => {
|
|||
|
||||
expect(findAllDetailsRow()).toHaveLength(7);
|
||||
});
|
||||
|
||||
describe('duration row', () => {
|
||||
it('renders all the details components', async () => {
|
||||
createWrapper();
|
||||
await store.dispatch('receiveJobSuccess', job);
|
||||
|
||||
expect(findAllDetailsRow().at(0).text()).toBe('Duration: 6 seconds');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('timeout', () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
exports[`settings_titles renders properly 1`] = `
|
||||
<div>
|
||||
<h5
|
||||
class="gl-border-b-solid gl-border-b-1 gl-border-gray-200"
|
||||
class="gl-border-b-solid gl-border-b-1 gl-border-gray-200 gl-pb-3"
|
||||
>
|
||||
|
||||
foo
|
||||
|
|
|
|||
|
|
@ -13,14 +13,21 @@ import {
|
|||
} from '~/packages_and_registries/settings/group/constants';
|
||||
|
||||
import updateDependencyProxySettings from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_settings.mutation.graphql';
|
||||
import updateDependencyProxyImageTtlGroupPolicy from '~/packages_and_registries/settings/group/graphql/mutations/update_dependency_proxy_image_ttl_group_policy.mutation.graphql';
|
||||
import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql';
|
||||
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
|
||||
import { updateGroupDependencyProxySettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
|
||||
import {
|
||||
updateGroupDependencyProxySettingsOptimisticResponse,
|
||||
updateDependencyProxyImageTtlGroupPolicyOptimisticResponse,
|
||||
} from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
import {
|
||||
dependencyProxySettings as dependencyProxySettingsMock,
|
||||
dependencyProxyImageTtlPolicy as dependencyProxyImageTtlPolicyMock,
|
||||
dependencyProxySettingMutationMock,
|
||||
groupPackageSettingsMock,
|
||||
dependencyProxySettingMutationErrorMock,
|
||||
mutationErrorMock,
|
||||
dependencyProxyUpdateTllPolicyMutationMock,
|
||||
} from '../mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
|
@ -31,6 +38,8 @@ const localVue = createLocalVue();
|
|||
describe('DependencyProxySettings', () => {
|
||||
let wrapper;
|
||||
let apolloProvider;
|
||||
let updateSettingsMutationResolver;
|
||||
let updateTtlPoliciesMutationResolver;
|
||||
|
||||
const defaultProvide = {
|
||||
defaultExpanded: false,
|
||||
|
|
@ -42,11 +51,14 @@ describe('DependencyProxySettings', () => {
|
|||
|
||||
const mountComponent = ({
|
||||
provide = defaultProvide,
|
||||
mutationResolver = jest.fn().mockResolvedValue(dependencyProxySettingMutationMock()),
|
||||
isLoading = false,
|
||||
dependencyProxySettings = dependencyProxySettingsMock(),
|
||||
dependencyProxyImageTtlPolicy = dependencyProxyImageTtlPolicyMock(),
|
||||
} = {}) => {
|
||||
const requestHandlers = [[updateDependencyProxySettings, mutationResolver]];
|
||||
const requestHandlers = [
|
||||
[updateDependencyProxySettings, updateSettingsMutationResolver],
|
||||
[updateDependencyProxyImageTtlGroupPolicy, updateTtlPoliciesMutationResolver],
|
||||
];
|
||||
|
||||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
||||
|
|
@ -56,6 +68,7 @@ describe('DependencyProxySettings', () => {
|
|||
provide,
|
||||
propsData: {
|
||||
dependencyProxySettings,
|
||||
dependencyProxyImageTtlPolicy,
|
||||
isLoading,
|
||||
},
|
||||
stubs: {
|
||||
|
|
@ -66,14 +79,26 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
updateSettingsMutationResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(dependencyProxySettingMutationMock());
|
||||
updateTtlPoliciesMutationResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(dependencyProxyUpdateTllPolicyMutationMock());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findSettingsBlock = () => wrapper.findComponent(SettingsBlock);
|
||||
const findSettingsTitles = () => wrapper.findComponent(SettingsTitles);
|
||||
const findDescription = () => wrapper.findByTestId('description');
|
||||
const findDescriptionLink = () => wrapper.findByTestId('description-link');
|
||||
const findToggle = () => wrapper.findComponent(GlToggle);
|
||||
const findEnableProxyToggle = () => wrapper.findByTestId('dependency-proxy-setting-toggle');
|
||||
const findEnableTtlPoliciesToggle = () =>
|
||||
wrapper.findByTestId('dependency-proxy-ttl-policies-toggle');
|
||||
const findToggleHelpLink = () => wrapper.findByTestId('toggle-help-link');
|
||||
|
||||
const fillApolloCache = () => {
|
||||
|
|
@ -86,10 +111,6 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const emitSettingsUpdate = (value = false) => {
|
||||
findToggle().vm.$emit('change', value);
|
||||
};
|
||||
|
||||
it('renders a settings block', () => {
|
||||
mountComponent();
|
||||
|
||||
|
|
@ -127,8 +148,8 @@ describe('DependencyProxySettings', () => {
|
|||
it('exists', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findToggle().props()).toMatchObject({
|
||||
label: component.i18n.label,
|
||||
expect(findEnableProxyToggle().props()).toMatchObject({
|
||||
label: component.i18n.enabledProxyLabel,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -138,13 +159,13 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
|
||||
it('has the help prop correctly set', () => {
|
||||
expect(findToggle().props()).toMatchObject({
|
||||
expect(findEnableProxyToggle().props()).toMatchObject({
|
||||
help: component.i18n.enabledProxyHelpText,
|
||||
});
|
||||
});
|
||||
|
||||
it('has help text with a link', () => {
|
||||
expect(findToggle().text()).toContain(
|
||||
expect(findEnableProxyToggle().text()).toContain(
|
||||
'To see the image prefix and what is in the cache, visit the Dependency Proxy',
|
||||
);
|
||||
expect(findToggleHelpLink().attributes()).toMatchObject({
|
||||
|
|
@ -161,7 +182,7 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
|
||||
it('has the help prop set to empty', () => {
|
||||
expect(findToggle().props()).toMatchObject({
|
||||
expect(findEnableProxyToggle().props()).toMatchObject({
|
||||
help: '',
|
||||
});
|
||||
});
|
||||
|
|
@ -172,13 +193,38 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('settings update', () => {
|
||||
describe('storage settings', () => {
|
||||
it('the component has the settings title', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findSettingsTitles().props()).toMatchObject({
|
||||
title: component.i18n.storageSettingsTitle,
|
||||
});
|
||||
});
|
||||
|
||||
describe('enable proxy ttl policies', () => {
|
||||
it('exists', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findEnableTtlPoliciesToggle().props()).toMatchObject({
|
||||
label: component.i18n.ttlPolicyEnabledLabel,
|
||||
help: component.i18n.ttlPolicyEnabledHelpText,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
toggleName | toggleFinder | localErrorMock | optimisticResponse
|
||||
${'enable proxy'} | ${findEnableProxyToggle} | ${dependencyProxySettingMutationMock} | ${updateGroupDependencyProxySettingsOptimisticResponse}
|
||||
${'enable ttl policies'} | ${findEnableTtlPoliciesToggle} | ${dependencyProxyUpdateTllPolicyMutationMock} | ${updateDependencyProxyImageTtlGroupPolicyOptimisticResponse}
|
||||
`('$toggleName settings update ', ({ optimisticResponse, toggleFinder, localErrorMock }) => {
|
||||
describe('success state', () => {
|
||||
it('emits a success event', async () => {
|
||||
mountComponent();
|
||||
|
||||
fillApolloCache();
|
||||
emitSettingsUpdate();
|
||||
toggleFinder().vm.$emit('change', false);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
|
|
@ -190,26 +236,28 @@ describe('DependencyProxySettings', () => {
|
|||
|
||||
fillApolloCache();
|
||||
|
||||
expect(findToggle().props('value')).toBe(true);
|
||||
expect(toggleFinder().props('value')).toBe(true);
|
||||
|
||||
emitSettingsUpdate();
|
||||
toggleFinder().vm.$emit('change', false);
|
||||
|
||||
expect(updateGroupDependencyProxySettingsOptimisticResponse).toHaveBeenCalledWith({
|
||||
enabled: false,
|
||||
});
|
||||
expect(optimisticResponse).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
enabled: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('mutation payload with root level errors', async () => {
|
||||
const mutationResolver = jest
|
||||
.fn()
|
||||
.mockResolvedValue(dependencyProxySettingMutationErrorMock);
|
||||
mountComponent({ mutationResolver });
|
||||
updateSettingsMutationResolver = jest.fn().mockResolvedValue(mutationErrorMock);
|
||||
updateTtlPoliciesMutationResolver = jest.fn().mockResolvedValue(mutationErrorMock);
|
||||
|
||||
mountComponent();
|
||||
|
||||
fillApolloCache();
|
||||
|
||||
emitSettingsUpdate();
|
||||
toggleFinder().vm.$emit('change', false);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
|
|
@ -217,14 +265,16 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
|
||||
it.each`
|
||||
type | mutationResolver
|
||||
${'local'} | ${jest.fn().mockResolvedValue(dependencyProxySettingMutationMock({ errors: ['foo'] }))}
|
||||
type | mutationResolverMock
|
||||
${'local'} | ${jest.fn().mockResolvedValue(localErrorMock({ errors: ['foo'] }))}
|
||||
${'network'} | ${jest.fn().mockRejectedValue()}
|
||||
`('mutation payload with $type error', async ({ mutationResolver }) => {
|
||||
mountComponent({ mutationResolver });
|
||||
`('mutation payload with $type error', async ({ mutationResolverMock }) => {
|
||||
updateSettingsMutationResolver = mutationResolverMock;
|
||||
updateTtlPoliciesMutationResolver = mutationResolverMock;
|
||||
mountComponent();
|
||||
|
||||
fillApolloCache();
|
||||
emitSettingsUpdate();
|
||||
toggleFinder().vm.$emit('change', false);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
|
|
@ -234,10 +284,16 @@ describe('DependencyProxySettings', () => {
|
|||
});
|
||||
|
||||
describe('when isLoading is true', () => {
|
||||
it('disables enable toggle', () => {
|
||||
it('disables enable proxy toggle', () => {
|
||||
mountComponent({ isLoading: true });
|
||||
|
||||
expect(findToggle().props('disabled')).toBe(true);
|
||||
expect(findEnableProxyToggle().props('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('disables enable ttl policies toggle', () => {
|
||||
mountComponent({ isLoading: true });
|
||||
|
||||
expect(findEnableTtlPoliciesToggle().props('disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ import DependencyProxySettings from '~/packages_and_registries/settings/group/co
|
|||
import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue';
|
||||
|
||||
import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql';
|
||||
import { groupPackageSettingsMock, packageSettings, dependencyProxySettings } from '../mock_data';
|
||||
import {
|
||||
groupPackageSettingsMock,
|
||||
packageSettings,
|
||||
dependencyProxySettings,
|
||||
dependencyProxyImageTtlPolicy,
|
||||
} from '../mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
|
|
@ -66,11 +71,17 @@ describe('Group Settings App', () => {
|
|||
await nextTick();
|
||||
};
|
||||
|
||||
const packageSettingsProps = { packageSettings: packageSettings() };
|
||||
const dependencyProxyProps = {
|
||||
dependencyProxySettings: dependencyProxySettings(),
|
||||
dependencyProxyImageTtlPolicy: dependencyProxyImageTtlPolicy(),
|
||||
};
|
||||
|
||||
describe.each`
|
||||
finder | entityProp | entityValue | successMessage | errorMessage
|
||||
${findPackageSettings} | ${'packageSettings'} | ${packageSettings()} | ${'Settings saved successfully'} | ${'An error occurred while saving the settings'}
|
||||
${findDependencyProxySettings} | ${'dependencyProxySettings'} | ${dependencyProxySettings()} | ${'Setting saved successfully'} | ${'An error occurred while saving the setting'}
|
||||
`('settings blocks', ({ finder, entityProp, entityValue, successMessage, errorMessage }) => {
|
||||
finder | entitySpecificProps | successMessage | errorMessage
|
||||
${findPackageSettings} | ${packageSettingsProps} | ${'Settings saved successfully'} | ${'An error occurred while saving the settings'}
|
||||
${findDependencyProxySettings} | ${dependencyProxyProps} | ${'Setting saved successfully'} | ${'An error occurred while saving the setting'}
|
||||
`('settings blocks', ({ finder, entitySpecificProps, successMessage, errorMessage }) => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
return waitForApolloQueryAndRender();
|
||||
|
|
@ -83,7 +94,7 @@ describe('Group Settings App', () => {
|
|||
it('binds the correctProps', () => {
|
||||
expect(finder().props()).toMatchObject({
|
||||
isLoading: false,
|
||||
[entityProp]: entityValue,
|
||||
...entitySpecificProps,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,19 @@ import SettingsTitles from '~/packages_and_registries/settings/group/components/
|
|||
describe('settings_titles', () => {
|
||||
let wrapper;
|
||||
|
||||
const mountComponent = () => {
|
||||
const defaultProps = {
|
||||
title: 'foo',
|
||||
subTitle: 'bar',
|
||||
};
|
||||
|
||||
const mountComponent = (propsData = defaultProps) => {
|
||||
wrapper = shallowMount(SettingsTitles, {
|
||||
propsData: {
|
||||
title: 'foo',
|
||||
subTitle: 'bar',
|
||||
},
|
||||
propsData,
|
||||
});
|
||||
};
|
||||
|
||||
const findSubTitle = () => wrapper.find('p');
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
|
@ -22,4 +26,10 @@ describe('settings_titles', () => {
|
|||
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not render the subtitle paragraph when no subtitle is passed', () => {
|
||||
mountComponent({ title: defaultProps.title });
|
||||
|
||||
expect(findSubTitle().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ describe('Package and Registries settings group cache updates', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const updateDependencyProxyImageTtlGroupPolicyPayload = {
|
||||
dependencyProxyImageTtlPolicy: {
|
||||
enabled: false,
|
||||
ttl: 45,
|
||||
},
|
||||
};
|
||||
|
||||
const cacheMock = {
|
||||
group: {
|
||||
packageSettings: {
|
||||
|
|
@ -26,6 +33,10 @@ describe('Package and Registries settings group cache updates', () => {
|
|||
dependencyProxySetting: {
|
||||
enabled: true,
|
||||
},
|
||||
dependencyProxyImageTtlPolicy: {
|
||||
enabled: true,
|
||||
ttl: 45,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -42,15 +53,26 @@ describe('Package and Registries settings group cache updates', () => {
|
|||
});
|
||||
|
||||
describe.each`
|
||||
updateNamespacePackageSettings | updateDependencyProxySettings
|
||||
${updateNamespacePackageSettingsPayload} | ${updateDependencyProxySettingsPayload}
|
||||
${undefined} | ${updateDependencyProxySettingsPayload}
|
||||
${updateNamespacePackageSettingsPayload} | ${undefined}
|
||||
${undefined} | ${undefined}
|
||||
updateNamespacePackageSettings | updateDependencyProxySettings | updateDependencyProxyImageTtlGroupPolicy
|
||||
${updateNamespacePackageSettingsPayload} | ${updateDependencyProxySettingsPayload} | ${undefined}
|
||||
${undefined} | ${updateDependencyProxySettingsPayload} | ${undefined}
|
||||
${updateNamespacePackageSettingsPayload} | ${undefined} | ${undefined}
|
||||
${undefined} | ${undefined} | ${updateDependencyProxyImageTtlGroupPolicyPayload}
|
||||
${undefined} | ${undefined} | ${undefined}
|
||||
`(
|
||||
'updateGroupPackageSettings',
|
||||
({ updateNamespacePackageSettings, updateDependencyProxySettings }) => {
|
||||
const payload = { data: { updateNamespacePackageSettings, updateDependencyProxySettings } };
|
||||
({
|
||||
updateNamespacePackageSettings,
|
||||
updateDependencyProxySettings,
|
||||
updateDependencyProxyImageTtlGroupPolicy,
|
||||
}) => {
|
||||
const payload = {
|
||||
data: {
|
||||
updateNamespacePackageSettings,
|
||||
updateDependencyProxySettings,
|
||||
updateDependencyProxyImageTtlGroupPolicy,
|
||||
},
|
||||
};
|
||||
it('calls readQuery', () => {
|
||||
updateGroupPackageSettings('foo')(client, payload);
|
||||
expect(client.readQuery).toHaveBeenCalledWith(queryAndVariables);
|
||||
|
|
@ -65,6 +87,7 @@ describe('Package and Registries settings group cache updates', () => {
|
|||
...cacheMock.group,
|
||||
...payload.data.updateNamespacePackageSettings,
|
||||
...payload.data.updateDependencyProxySettings,
|
||||
...payload.data.updateDependencyProxyImageTtlGroupPolicy,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
updateGroupPackagesSettingsOptimisticResponse,
|
||||
updateGroupDependencyProxySettingsOptimisticResponse,
|
||||
updateDependencyProxyImageTtlGroupPolicyOptimisticResponse,
|
||||
} from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
|
||||
describe('Optimistic responses', () => {
|
||||
|
|
@ -38,4 +39,22 @@ describe('Optimistic responses', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateDependencyProxyImageTtlGroupPolicyOptimisticResponse', () => {
|
||||
it('returns the correct structure', () => {
|
||||
expect(updateDependencyProxyImageTtlGroupPolicyOptimisticResponse({ foo: 'bar' }))
|
||||
.toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"__typename": "Mutation",
|
||||
"updateDependencyProxyImageTtlGroupPolicy": Object {
|
||||
"__typename": "UpdateDependencyProxyImageTtlGroupPolicyPayload",
|
||||
"dependencyProxyImageTtlPolicy": Object {
|
||||
"foo": "bar",
|
||||
},
|
||||
"errors": Array [],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ export const dependencyProxySettings = (extend) => ({
|
|||
...extend,
|
||||
});
|
||||
|
||||
export const dependencyProxyImageTtlPolicy = (extend) => ({
|
||||
ttl: 90,
|
||||
enabled: true,
|
||||
...extend,
|
||||
});
|
||||
|
||||
export const groupPackageSettingsMock = {
|
||||
data: {
|
||||
group: {
|
||||
|
|
@ -17,6 +23,7 @@ export const groupPackageSettingsMock = {
|
|||
fullPath: 'foo_group_path',
|
||||
packageSettings: packageSettings(),
|
||||
dependencyProxySetting: dependencyProxySettings(),
|
||||
dependencyProxyImageTtlPolicy: dependencyProxyImageTtlPolicy(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -46,6 +53,16 @@ export const dependencyProxySettingMutationMock = (override) => ({
|
|||
},
|
||||
});
|
||||
|
||||
export const dependencyProxyUpdateTllPolicyMutationMock = (override) => ({
|
||||
data: {
|
||||
updateDependencyProxyImageTtlGroupPolicy: {
|
||||
dependencyProxyImageTtlPolicy: dependencyProxyImageTtlPolicy(),
|
||||
errors: [],
|
||||
...override,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const groupPackageSettingsMutationErrorMock = {
|
||||
errors: [
|
||||
{
|
||||
|
|
@ -70,7 +87,8 @@ export const groupPackageSettingsMutationErrorMock = {
|
|||
},
|
||||
],
|
||||
};
|
||||
export const dependencyProxySettingMutationErrorMock = {
|
||||
|
||||
export const mutationErrorMock = {
|
||||
errors: [
|
||||
{
|
||||
message: 'Some error',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rake_helper'
|
||||
|
||||
RSpec.describe 'gitlab:background_migrations namespace rake tasks' do
|
||||
before do
|
||||
Rake.application.rake_require 'tasks/gitlab/background_migrations'
|
||||
end
|
||||
|
||||
describe 'finalize' do
|
||||
subject(:finalize_task) { run_rake_task('gitlab:background_migrations:finalize', *arguments) }
|
||||
|
||||
context 'without the proper arguments' do
|
||||
let(:arguments) { %w[CopyColumnUsingBackgroundMigrationJob events id] }
|
||||
|
||||
it 'exits without finalizing the migration' do
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner).not_to receive(:finalize)
|
||||
|
||||
expect { finalize_task }.to output(/Must specify job_arguments as an argument/).to_stdout
|
||||
.and raise_error(SystemExit) { |error| expect(error.status).to eq(1) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the proper arguments' do
|
||||
let(:arguments) { %w[CopyColumnUsingBackgroundMigrationJob events id [["id1"\,"id2"]]] }
|
||||
|
||||
it 'finalizes the matching migration' do
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner).to receive(:finalize)
|
||||
.with('CopyColumnUsingBackgroundMigrationJob', 'events', 'id', [%w[id1 id2]])
|
||||
|
||||
expect { finalize_task }.to output(/Done/).to_stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status' do
|
||||
subject(:status_task) { run_rake_task('gitlab:background_migrations:status') }
|
||||
|
||||
it 'outputs the status of background migrations' do
|
||||
migration1 = create(:batched_background_migration, :finished, job_arguments: [%w[id1 id2]])
|
||||
migration2 = create(:batched_background_migration, :failed, job_arguments: [])
|
||||
|
||||
expect { status_task }.to output(<<~OUTPUT).to_stdout
|
||||
finished | #{migration1.job_class_name},#{migration1.table_name},#{migration1.column_name},[["id1","id2"]]
|
||||
failed | #{migration2.job_class_name},#{migration2.table_name},#{migration2.column_name},[]
|
||||
OUTPUT
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue