summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-15 09:07:30 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-15 09:07:30 +0000
commit50e177d19bdeeb0fcc7b129b9c30841454df240b (patch)
tree11b30123cfec778cfa469bc23e17f40a45d43880
parent9cf113df885ac8959b9fab3aab5e50e2532fef75 (diff)
downloadgitlab-ce-50e177d19bdeeb0fcc7b129b9c30841454df240b.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG.md12
-rw-r--r--app/assets/javascripts/ci/runner/admin_new_runner/index.js7
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_platforms_radio_group.vue19
-rw-r--r--app/assets/javascripts/helpers/init_simple_app_helper.js39
-rw-r--r--app/assets/javascripts/ide/init_gitlab_web_ide.js2
-rw-r--r--app/assets/javascripts/ide/lib/gitlab_web_ide/handle_tracking_event.js20
-rw-r--r--app/assets/javascripts/ide/remote/index.js2
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue7
-rw-r--r--app/assets/javascripts/pages/projects/ml/candidates/show/index.js27
-rw-r--r--app/assets/javascripts/related_issues/components/add_issuable_form.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issuable_input.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issues_root.vue4
-rw-r--r--app/assets/javascripts/related_issues/constants.js27
-rw-r--r--app/helpers/projects/ml/experiments_helper.rb36
-rw-r--r--app/views/admin/runners/new.html.haml6
-rw-r--r--app/views/projects/ml/candidates/show.html.haml3
-rw-r--r--config/metrics/counts_all/20230209153034_projects_jira_server_deployment_type_active.yml25
-rw-r--r--config/metrics/counts_all/20230209153255_projects_jira_cloud_deployment_type_active.yml25
-rw-r--r--config/webpack.config.js14
-rw-r--r--data/deprecations/15-6-deprecate-post-api-v4-runner.yml4
-rw-r--r--data/deprecations/15-6-deprecate-runner-reg-token-helm.yml3
-rw-r--r--data/deprecations/15-6-deprecate-runner-register-command.yml4
-rw-r--r--data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml2
-rw-r--r--data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml2
-rw-r--r--db/post_migrate/20230127152727_add_fk_index_to_ci_job_artifacts_on_partition_id_and_job_id.rb17
-rw-r--r--db/post_migrate/20230127152728_add_fk_to_ci_job_artifacts_on_partition_id_and_job_id.rb37
-rw-r--r--db/schema_migrations/202301271527271
-rw-r--r--db/schema_migrations/202301271527281
-rw-r--r--db/structure.sql5
-rw-r--r--doc/api/runners.md4
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md8
-rw-r--r--doc/ci/yaml/includes.md81
-rw-r--r--doc/integration/jira/index.md6
-rw-r--r--doc/update/deprecations.md15
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md10
-rw-r--r--doc/user/project/integrations/slack.md8
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md7
-rw-r--r--jest.config.base.js1
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric.rb36
-rw-r--r--spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js6
-rw-r--r--spec/frontend/helpers/init_simple_app_helper_spec.js61
-rw-r--r--spec/frontend/ide/init_gitlab_web_ide_spec.js2
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/handle_tracking_event_spec.js32
-rw-r--r--spec/frontend/ide/remote/index_spec.js3
-rw-r--r--spec/frontend/issuable/related_issues/components/add_issuable_form_spec.js11
-rw-r--r--spec/frontend/issuable/related_issues/components/related_issues_block_spec.js6
-rw-r--r--spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js2
-rw-r--r--spec/frontend/related_issues/components/related_issuable_input_spec.js5
-rw-r--r--spec/helpers/projects/ml/experiments_helper_spec.rb4
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric_spec.rb39
-rw-r--r--workhorse/gitaly_integration_test.go447
-rw-r--r--workhorse/gitaly_test.go2
-rw-r--r--workhorse/go.mod4
-rw-r--r--workhorse/go.sum10
-rw-r--r--workhorse/internal/gitaly/gitaly.go10
55 files changed, 843 insertions, 336 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 721008aa495..b84328140e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 15.8.3 (2023-02-15)
+
+### Fixed (3 changes)
+
+- [Attempt reading schema file instead of a file named `#{report_version}`](gitlab-org/gitlab@f4b236c5f22c2da89bd4275cd8f5bf2807069ee4) ([merge request](gitlab-org/gitlab!111934))
+- [Revert changes on wiki replication/verification legacy code](gitlab-org/gitlab@71b29b669f0415fa371560139d699aa7ad568549) ([merge request](gitlab-org/gitlab!111934)) **GitLab Enterprise Edition**
+- [Revert changes on wiki replication/verification legacy code](gitlab-org/gitlab@fd824d99fb7b341088841edfaa6c401c4c20dad8) ([merge request](gitlab-org/gitlab!111879)) **GitLab Enterprise Edition**
+
+### Changed (1 change)
+
+- [Upgrade Alert - Add proper API support](gitlab-org/gitlab@6658efdbfb89847f20836e862710260e49c44778) ([merge request](gitlab-org/gitlab!111934))
+
## 15.8.2 (2023-02-10)
No changes.
diff --git a/app/assets/javascripts/ci/runner/admin_new_runner/index.js b/app/assets/javascripts/ci/runner/admin_new_runner/index.js
index 7aca19572b3..502d9d33b4d 100644
--- a/app/assets/javascripts/ci/runner/admin_new_runner/index.js
+++ b/app/assets/javascripts/ci/runner/admin_new_runner/index.js
@@ -12,7 +12,7 @@ export const initAdminNewRunner = (selector = '#js-admin-new-runner') => {
return null;
}
- const { legacyRegistrationToken, awsImgPath, dockerImgPath, kubernetesImgPath } = el.dataset;
+ const { legacyRegistrationToken } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -21,11 +21,6 @@ export const initAdminNewRunner = (selector = '#js-admin-new-runner') => {
return new Vue({
el,
apolloProvider,
- provide: {
- awsImgPath,
- dockerImgPath,
- kubernetesImgPath,
- },
render(h) {
return h(AdminNewRunnerApp, {
props: {
diff --git a/app/assets/javascripts/ci/runner/components/runner_platforms_radio_group.vue b/app/assets/javascripts/ci/runner/components/runner_platforms_radio_group.vue
index 304fdadbca2..273226141d2 100644
--- a/app/assets/javascripts/ci/runner/components/runner_platforms_radio_group.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_platforms_radio_group.vue
@@ -1,5 +1,9 @@
<script>
+import AWS_LOGO_URL from '@gitlab/svgs/dist/illustrations/logos/aws.svg?url';
+import DOCKER_LOGO_URL from '@gitlab/svgs/dist/illustrations/third-party-logos/ci_cd-template-logos/docker.png';
+import KUBERNETES_LOGO_URL from '@gitlab/svgs/dist/illustrations/logos/kubernetes.svg?url';
import { GlFormRadioGroup, GlIcon, GlLink } from '@gitlab/ui';
+
import {
LINUX_PLATFORM,
MACOS_PLATFORM,
@@ -18,7 +22,6 @@ export default {
GlIcon,
RunnerPlatformsRadio,
},
- inject: ['awsImgPath', 'dockerImgPath', 'kubernetesImgPath'],
props: {
value: {
type: String,
@@ -39,9 +42,13 @@ export default {
LINUX_PLATFORM,
MACOS_PLATFORM,
WINDOWS_PLATFORM,
+
AWS_PLATFORM,
+ AWS_LOGO_URL,
DOCKER_HELP_URL,
+ DOCKER_LOGO_URL,
KUBERNETES_HELP_URL,
+ KUBERNETES_LOGO_URL,
};
</script>
@@ -68,7 +75,11 @@ export default {
<label>{{ s__('Runners|Cloud templates') }}</label>
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
<div class="gl-display-flex gl-flex-wrap gl-gap-5">
- <runner-platforms-radio v-model="model" :image="awsImgPath" :value="$options.AWS_PLATFORM">
+ <runner-platforms-radio
+ v-model="model"
+ :image="$options.AWS_LOGO_URL"
+ :value="$options.AWS_PLATFORM"
+ >
AWS
</runner-platforms-radio>
</div>
@@ -79,13 +90,13 @@ export default {
<div class="gl-display-flex gl-flex-wrap gl-gap-5">
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
- <runner-platforms-radio :image="dockerImgPath">
+ <runner-platforms-radio :image="$options.DOCKER_LOGO_URL">
<gl-link :href="$options.DOCKER_HELP_URL" target="_blank">
Docker
<gl-icon name="external-link" />
</gl-link>
</runner-platforms-radio>
- <runner-platforms-radio :image="kubernetesImgPath">
+ <runner-platforms-radio :image="$options.KUBERNETES_LOGO_URL">
<gl-link :href="$options.KUBERNETES_HELP_URL" target="_blank">
Kubernetes
<gl-icon name="external-link" />
diff --git a/app/assets/javascripts/helpers/init_simple_app_helper.js b/app/assets/javascripts/helpers/init_simple_app_helper.js
new file mode 100644
index 00000000000..695fc455f13
--- /dev/null
+++ b/app/assets/javascripts/helpers/init_simple_app_helper.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+
+/**
+ * Initializes a component as a simple vue app, passing the necessary props. If the element
+ * has a data attribute named `data-view-model`, the content of that attributed will be
+ * converted from json and passed on to the component as a prop. The root component is then
+ * responsible for setting up it's children, injections, and other desired features.
+ *
+ * @param {string} selector css selector for where to build
+ * @param {Vue.component} component The Vue compoment to be built as the root of the app
+ *
+ * @example
+ * ```html
+ * <div id='#mount-here' data-view-model="{'some': 'object'}" />
+ * ```
+ *
+ * ```javascript
+ * initSimpleApp('#mount-here', MyApp)
+ * ```
+ *
+ * This will mount MyApp as root on '#mount-here'. It will receive {'some': 'object'} as it's
+ * view model prop.
+ */
+export const initSimpleApp = (selector, component) => {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ return null;
+ }
+
+ const props = element.dataset.viewModel ? JSON.parse(element.dataset.viewModel) : {};
+
+ return new Vue({
+ el: element,
+ render(h) {
+ return h(component, { props });
+ },
+ });
+};
diff --git a/app/assets/javascripts/ide/init_gitlab_web_ide.js b/app/assets/javascripts/ide/init_gitlab_web_ide.js
index e3fbce25cd2..4d3cefcb107 100644
--- a/app/assets/javascripts/ide/init_gitlab_web_ide.js
+++ b/app/assets/javascripts/ide/init_gitlab_web_ide.js
@@ -7,6 +7,7 @@ import csrf from '~/lib/utils/csrf';
import { getBaseConfig } from './lib/gitlab_web_ide/get_base_config';
import { setupRootElement } from './lib/gitlab_web_ide/setup_root_element';
import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from './constants';
+import { handleTracking } from './lib/gitlab_web_ide/handle_tracking_event';
const buildRemoteIdeURL = (ideRemotePath, remoteHost, remotePathArg) => {
const remotePath = cleanLeadingSeparator(remotePathArg);
@@ -72,6 +73,7 @@ export const initGitlabWebIDE = async (el) => {
fontFamily: editorFontFamily,
format: editorFontFormat,
},
+ handleTracking,
async handleStartRemote({ remoteHost, remotePath, connectionToken }) {
const confirmed = await confirmAction(
__('Are you sure you want to leave the Web IDE? All unsaved changes will be lost.'),
diff --git a/app/assets/javascripts/ide/lib/gitlab_web_ide/handle_tracking_event.js b/app/assets/javascripts/ide/lib/gitlab_web_ide/handle_tracking_event.js
new file mode 100644
index 00000000000..615dad02386
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/gitlab_web_ide/handle_tracking_event.js
@@ -0,0 +1,20 @@
+import { snakeCase } from 'lodash';
+import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
+import Tracking from '~/tracking';
+
+export const handleTracking = ({ name, data }) => {
+ const snakeCaseEventName = snakeCase(name);
+
+ if (data && Object.keys(data).length) {
+ Tracking.event(undefined, snakeCaseEventName, {
+ /* See GitLab snowplow schema for a definition of the extra field
+ * https://docs.gitlab.com/ee/development/snowplow/schemas.html#gitlab_standard.
+ */
+ extra: convertObjectPropsToSnakeCase(data, {
+ deep: true,
+ }),
+ });
+ } else {
+ Tracking.event(undefined, snakeCaseEventName);
+ }
+};
diff --git a/app/assets/javascripts/ide/remote/index.js b/app/assets/javascripts/ide/remote/index.js
index fb8db20c0c1..6966786ca4e 100644
--- a/app/assets/javascripts/ide/remote/index.js
+++ b/app/assets/javascripts/ide/remote/index.js
@@ -1,6 +1,7 @@
import { startRemote } from '@gitlab/web-ide';
import { getBaseConfig, setupRootElement } from '~/ide/lib/gitlab_web_ide';
import { isSameOriginUrl, joinPaths } from '~/lib/utils/url_utility';
+import { handleTracking } from '~/ide/lib/gitlab_web_ide/handle_tracking_event';
/**
* @param {Element} rootEl
@@ -36,5 +37,6 @@ export const mountRemoteIDE = async (el) => {
// TODO Handle error better
handleError: visitReturnUrl,
handleClose: visitReturnUrl,
+ handleTracking,
});
};
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue b/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue
index a199b8414ba..d0c42905ee2 100644
--- a/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/components/ml_candidate.vue
@@ -10,7 +10,12 @@ export default {
IncubationAlert,
GlLink,
},
- inject: ['candidate'],
+ props: {
+ candidate: {
+ type: Object,
+ required: true,
+ },
+ },
i18n: {
titleLabel: __('Model candidate details'),
infoLabel: __('Info'),
diff --git a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
index c1acef5ac13..fee6258eddc 100644
--- a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
+++ b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
@@ -1,27 +1,4 @@
-import Vue from 'vue';
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
import MlCandidate from '~/ml/experiment_tracking/components/ml_candidate.vue';
-const initShowCandidate = () => {
- const element = document.querySelector('#js-show-ml-candidate');
- if (!element) {
- return;
- }
-
- const container = document.createElement('div');
- element.appendChild(container);
-
- const candidate = JSON.parse(element.dataset.candidate);
-
- // eslint-disable-next-line no-new
- new Vue({
- el: container,
- provide: {
- candidate,
- },
- render(h) {
- return h(MlCandidate);
- },
- });
-};
-
-initShowCandidate();
+initSimpleApp('#js-show-ml-candidate', MlCandidate);
diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue
index 102f1228355..adae92a92e9 100644
--- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue
+++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue
@@ -1,10 +1,10 @@
<script>
import { GlFormGroup, GlFormRadioGroup, GlButton } from '@gitlab/ui';
+import { TYPE_ISSUE } from '~/issues/constants';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import {
- issuableTypesMap,
itemAddFailureTypesMap,
linkedIssueTypesMap,
addRelatedIssueErrorMap,
@@ -54,7 +54,7 @@ export default {
issuableType: {
type: String,
required: false,
- default: issuableTypesMap.ISSUE,
+ default: TYPE_ISSUE,
},
hasError: {
type: Boolean,
diff --git a/app/assets/javascripts/related_issues/components/related_issuable_input.vue b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
index 09ecad2d90e..8d6a3110f35 100644
--- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue
+++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
@@ -1,11 +1,11 @@
<script>
import $ from 'jquery';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
+import { TYPE_ISSUE } from '~/issues/constants';
import {
autoCompleteTextMap,
inputPlaceholderConfidentialTextMap,
inputPlaceholderTextMap,
- issuableTypesMap,
} from '../constants';
import IssueToken from './issue_token.vue';
@@ -54,7 +54,7 @@ export default {
issuableType: {
type: String,
required: false,
- default: issuableTypesMap.ISSUE,
+ default: TYPE_ISSUE,
},
confidential: {
type: Boolean,
diff --git a/app/assets/javascripts/related_issues/components/related_issues_root.vue b/app/assets/javascripts/related_issues/components/related_issues_root.vue
index 16d1fb9a95c..ed70e1ce8a8 100644
--- a/app/assets/javascripts/related_issues/components/related_issues_root.vue
+++ b/app/assets/javascripts/related_issues/components/related_issues_root.vue
@@ -25,13 +25,13 @@ Your caret can stop touching a `rawReference` can happen in a variety of ways:
*/
import { createAlert } from '~/flash';
import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
+import { TYPE_ISSUE } from '~/issues/constants';
import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
import { __ } from '~/locale';
import {
relatedIssuesRemoveErrorMap,
pathIndeterminateErrorMap,
addRelatedIssueErrorMap,
- issuableTypesMap,
PathIdSeparator,
} from '../constants';
import RelatedIssuesService from '../services/related_issues_service';
@@ -66,7 +66,7 @@ export default {
issuableType: {
type: String,
required: false,
- default: issuableTypesMap.ISSUE,
+ default: TYPE_ISSUE,
},
allowAutoComplete: {
type: Boolean,
diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js
index 7d462711f9e..821f6b525c5 100644
--- a/app/assets/javascripts/related_issues/constants.js
+++ b/app/assets/javascripts/related_issues/constants.js
@@ -1,4 +1,5 @@
import { __, sprintf } from '~/locale';
+import { TYPE_ISSUE } from '~/issues/constants';
export const issuableTypesMap = {
ISSUE: 'issue',
@@ -21,7 +22,7 @@ export const linkedIssueTypesTextMap = {
export const autoCompleteTextMap = {
true: {
- [issuableTypesMap.ISSUE]: sprintf(
+ [TYPE_ISSUE]: sprintf(
__(' or %{emphasisStart}#issue id%{emphasisEnd}'),
{ emphasisStart: '<', emphasisEnd: '>' },
false,
@@ -43,32 +44,32 @@ export const autoCompleteTextMap = {
),
},
false: {
- [issuableTypesMap.ISSUE]: '',
+ [TYPE_ISSUE]: '',
[issuableTypesMap.EPIC]: '',
[issuableTypesMap.MERGE_REQUEST]: __(' or references'),
},
};
export const inputPlaceholderTextMap = {
- [issuableTypesMap.ISSUE]: __('Paste issue link'),
+ [TYPE_ISSUE]: __('Paste issue link'),
[issuableTypesMap.INCIDENT]: __('Paste link'),
[issuableTypesMap.EPIC]: __('Paste epic link'),
[issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'),
};
export const inputPlaceholderConfidentialTextMap = {
- [issuableTypesMap.ISSUE]: __('Paste confidential issue link'),
+ [TYPE_ISSUE]: __('Paste confidential issue link'),
[issuableTypesMap.EPIC]: __('Paste confidential epic link'),
[issuableTypesMap.MERGE_REQUEST]: __('Enter merge request URLs'),
};
export const relatedIssuesRemoveErrorMap = {
- [issuableTypesMap.ISSUE]: __('An error occurred while removing issues.'),
+ [TYPE_ISSUE]: __('An error occurred while removing issues.'),
[issuableTypesMap.EPIC]: __('An error occurred while removing epics.'),
};
export const pathIndeterminateErrorMap = {
- [issuableTypesMap.ISSUE]: __('We could not determine the path to remove the issue'),
+ [TYPE_ISSUE]: __('We could not determine the path to remove the issue'),
[issuableTypesMap.EPIC]: __('We could not determine the path to remove the epic'),
};
@@ -78,7 +79,7 @@ export const itemAddFailureTypesMap = {
};
export const addRelatedIssueErrorMap = {
- [issuableTypesMap.ISSUE]: __('Issue cannot be found.'),
+ [TYPE_ISSUE]: __('Issue cannot be found.'),
[issuableTypesMap.EPIC]: __('Epic cannot be found.'),
};
@@ -94,7 +95,7 @@ export const addRelatedItemErrorMap = {
* them inside i18n functions.
*/
export const issuableIconMap = {
- [issuableTypesMap.ISSUE]: 'issues',
+ [TYPE_ISSUE]: 'issues',
[issuableTypesMap.INCIDENT]: 'issues',
[issuableTypesMap.EPIC]: 'epic',
};
@@ -105,30 +106,30 @@ export const PathIdSeparator = {
};
export const issuablesBlockHeaderTextMap = {
- [issuableTypesMap.ISSUE]: __('Linked items'),
+ [TYPE_ISSUE]: __('Linked items'),
[issuableTypesMap.INCIDENT]: __('Linked incidents or issues'),
[issuableTypesMap.EPIC]: __('Linked epics'),
};
export const issuablesBlockHelpTextMap = {
- [issuableTypesMap.ISSUE]: __('Learn more about linking issues'),
+ [TYPE_ISSUE]: __('Learn more about linking issues'),
[issuableTypesMap.INCIDENT]: __('Learn more about linking issues and incidents'),
[issuableTypesMap.EPIC]: __('Learn more about linking epics'),
};
export const issuablesBlockAddButtonTextMap = {
- [issuableTypesMap.ISSUE]: __('Add a related issue'),
+ [TYPE_ISSUE]: __('Add a related issue'),
[issuableTypesMap.EPIC]: __('Add a related epic'),
};
export const issuablesFormCategoryHeaderTextMap = {
- [issuableTypesMap.ISSUE]: __('The current issue'),
+ [TYPE_ISSUE]: __('The current issue'),
[issuableTypesMap.INCIDENT]: __('The current incident'),
[issuableTypesMap.EPIC]: __('The current epic'),
};
export const issuablesFormInputTextMap = {
- [issuableTypesMap.ISSUE]: __('the following issues'),
+ [TYPE_ISSUE]: __('the following issues'),
[issuableTypesMap.INCIDENT]: __('the following incidents or issues'),
[issuableTypesMap.EPIC]: __('the following epics'),
};
diff --git a/app/helpers/projects/ml/experiments_helper.rb b/app/helpers/projects/ml/experiments_helper.rb
index 8467ee61b35..55216d412a5 100644
--- a/app/helpers/projects/ml/experiments_helper.rb
+++ b/app/helpers/projects/ml/experiments_helper.rb
@@ -5,6 +5,25 @@ module Projects
require 'json'
include ActionView::Helpers::NumberHelper
+ def show_candidate_view_model(candidate)
+ data = {
+ candidate: {
+ params: candidate.params,
+ metrics: candidate.latest_metrics,
+ info: {
+ iid: candidate.iid,
+ path_to_artifact: link_to_artifact(candidate),
+ experiment_name: candidate.experiment.name,
+ path_to_experiment: link_to_experiment(candidate.project, candidate.experiment),
+ status: candidate.status
+ },
+ metadata: candidate.metadata
+ }
+ }
+
+ Gitlab::Json.generate(data)
+ end
+
def candidates_table_items(candidates)
items = candidates.map do |candidate|
{
@@ -25,23 +44,6 @@ module Projects
Gitlab::Json.generate(candidates.flat_map(&selector).map(&:name).uniq)
end
- def candidate_as_data(candidate)
- data = {
- params: candidate.params,
- metrics: candidate.latest_metrics,
- info: {
- iid: candidate.iid,
- path_to_artifact: link_to_artifact(candidate),
- experiment_name: candidate.experiment.name,
- path_to_experiment: link_to_experiment(candidate.project, candidate.experiment),
- status: candidate.status
- },
- metadata: candidate.metadata
- }
-
- Gitlab::Json.generate(data)
- end
-
def experiments_as_data(project, experiments)
data = experiments.map do |exp|
{
diff --git a/app/views/admin/runners/new.html.haml b/app/views/admin/runners/new.html.haml
index afe0d57e0a3..dd93ecfcf8c 100644
--- a/app/views/admin/runners/new.html.haml
+++ b/app/views/admin/runners/new.html.haml
@@ -2,8 +2,4 @@
- breadcrumb_title s_('Runners|New')
- page_title s_('Runners|Create an instance runner')
-#js-admin-new-runner{ data: {
- legacy_registration_token: Gitlab::CurrentSettings.runners_registration_token,
- aws_img_path: image_path('illustrations/logos/aws.svg'),
- kubernetes_img_path: image_path('illustrations/logos/kubernetes.svg'),
- docker_img_path: image_path('illustrations/third-party-logos/ci_cd-template-logos/docker.png') } }
+#js-admin-new-runner{ data: { legacy_registration_token: Gitlab::CurrentSettings.runners_registration_token } }
diff --git a/app/views/projects/ml/candidates/show.html.haml b/app/views/projects/ml/candidates/show.html.haml
index 7fa98f69edf..77262243efb 100644
--- a/app/views/projects/ml/candidates/show.html.haml
+++ b/app/views/projects/ml/candidates/show.html.haml
@@ -2,6 +2,5 @@
- add_to_breadcrumbs _("Experiments"), project_ml_experiments_path(@project)
- add_to_breadcrumbs experiment.name, project_ml_experiment_path(@project, experiment.iid)
- breadcrumb_title "Candidate #{@candidate.iid}"
-- data = candidate_as_data(@candidate)
-#js-show-ml-candidate{ data: { candidate: data } }
+#js-show-ml-candidate{ data: { view_model: show_candidate_view_model(@candidate) } }
diff --git a/config/metrics/counts_all/20230209153034_projects_jira_server_deployment_type_active.yml b/config/metrics/counts_all/20230209153034_projects_jira_server_deployment_type_active.yml
new file mode 100644
index 00000000000..53b8c280db4
--- /dev/null
+++ b/config/metrics/counts_all/20230209153034_projects_jira_server_deployment_type_active.yml
@@ -0,0 +1,25 @@
+---
+key_path: counts.projects_jira_server_deployment_type_active
+description: Count of active integrations with Jira Software with server deployment type
+product_section: dev
+product_stage: manage
+product_group: integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "15.9"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111355
+time_frame: all
+data_source: database
+data_category: operational
+instrumentation_class: JiraActiveIntegrationsMetric
+options:
+ deployment_type: server
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20230209153255_projects_jira_cloud_deployment_type_active.yml b/config/metrics/counts_all/20230209153255_projects_jira_cloud_deployment_type_active.yml
new file mode 100644
index 00000000000..093afb71275
--- /dev/null
+++ b/config/metrics/counts_all/20230209153255_projects_jira_cloud_deployment_type_active.yml
@@ -0,0 +1,25 @@
+---
+key_path: counts.projects_jira_cloud_deployment_type_active
+description: Count of active integrations with Jira Software with cloud deployment type
+product_section: dev
+product_stage: manage
+product_group: integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "15.9"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111355
+time_frame: all
+data_source: database
+data_category: operational
+instrumentation_class: JiraActiveIntegrationsMetric
+options:
+ deployment_type: cloud
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/webpack.config.js b/config/webpack.config.js
index fd58e22bb99..8ae803ca738 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -380,7 +380,19 @@ module.exports = {
{
test: /\.svg$/,
exclude: /icons\.svg$/,
- loader: 'raw-loader',
+ oneOf: [
+ {
+ resourceQuery: /url/,
+ loader: 'file-loader',
+ options: {
+ name: '[name].[contenthash:8].[ext]',
+ esModule: false,
+ },
+ },
+ {
+ loader: 'raw-loader',
+ },
+ ],
},
{
test: /\.(gif|png|mp4)$/,
diff --git a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
index fd1e13321eb..f71b8726de9 100644
--- a/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
+++ b/data/deprecations/15-6-deprecate-post-api-v4-runner.yml
@@ -11,11 +11,11 @@
with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
- In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
+ In GitLab 15.10, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
- end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: "16.6" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page
diff --git a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
index 9df458024d4..cbc50918c39 100644
--- a/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
+++ b/data/deprecations/15-6-deprecate-runner-reg-token-helm.yml
@@ -12,5 +12,6 @@
- A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
+
From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
- end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: "16.6" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
diff --git a/data/deprecations/15-6-deprecate-runner-register-command.yml b/data/deprecations/15-6-deprecate-runner-register-command.yml
index 709edc0afa2..a908e126bc8 100644
--- a/data/deprecations/15-6-deprecate-runner-register-command.yml
+++ b/data/deprecations/15-6-deprecate-runner-register-command.yml
@@ -7,10 +7,10 @@
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/380872 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
- GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
+ GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.10,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
The new method will involve creating the runner in the GitLab UI and passing the
[runner authentication token](https://docs.gitlab.com/ee/security/token_overview.html#runner-authentication-tokens-also-called-runner-tokens)
to the `gitlab-runner register` command.
- end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: "16.6" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
diff --git a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
index 4aed53f5ffd..7b66978f492 100644
--- a/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
+++ b/data/deprecations/15-6-deprecate-runner-register-token-k8s-operator.yml
@@ -7,7 +7,7 @@
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382077 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator) parameter that uses the OpenShift and k8s Vanilla Operator to install a runner on Kubernetes is deprecated. GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8, which introduces a new method for registering runners and eliminates the legacy runner registration token.
- end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: "16.6" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/runner/install/operator.html#install-the-kubernetes-operator # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
diff --git a/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml
index 97d21f14d3f..25b2e046f01 100644
--- a/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml
+++ b/data/deprecations/15-7-deprecate-api-v4-runner-registration-token-reset-endpoints.yml
@@ -19,6 +19,6 @@
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
From GitLab 17.0 and later, the runner registration methods implemented by the new GitLab Runner token architecture will be the only supported methods.
- end_of_support_milestone: "16.0" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
+ end_of_support_milestone: "16.6" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: https://docs.gitlab.com/ee/api/runners.html#register-a-new-runner # (optional) This is a link to the current documentation page
diff --git a/db/post_migrate/20230127152727_add_fk_index_to_ci_job_artifacts_on_partition_id_and_job_id.rb b/db/post_migrate/20230127152727_add_fk_index_to_ci_job_artifacts_on_partition_id_and_job_id.rb
new file mode 100644
index 00000000000..66933fa600e
--- /dev/null
+++ b/db/post_migrate/20230127152727_add_fk_index_to_ci_job_artifacts_on_partition_id_and_job_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddFkIndexToCiJobArtifactsOnPartitionIdAndJobId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = :index_ci_job_artifacts_on_partition_id_job_id
+ TABLE_NAME = :ci_job_artifacts
+ COLUMNS = [:partition_id, :job_id]
+
+ def up
+ add_concurrent_index(TABLE_NAME, COLUMNS, name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20230127152728_add_fk_to_ci_job_artifacts_on_partition_id_and_job_id.rb b/db/post_migrate/20230127152728_add_fk_to_ci_job_artifacts_on_partition_id_and_job_id.rb
new file mode 100644
index 00000000000..7c7cf7861ea
--- /dev/null
+++ b/db/post_migrate/20230127152728_add_fk_to_ci_job_artifacts_on_partition_id_and_job_id.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class AddFkToCiJobArtifactsOnPartitionIdAndJobId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ SOURCE_TABLE_NAME = :ci_job_artifacts
+ TARGET_TABLE_NAME = :ci_builds
+ COLUMN = :job_id
+ TARGET_COLUMN = :id
+ FK_NAME = :fk_rails_c5137cb2c1_p
+ PARTITION_COLUMN = :partition_id
+
+ def up
+ add_concurrent_foreign_key(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ column: [PARTITION_COLUMN, COLUMN],
+ target_column: [PARTITION_COLUMN, TARGET_COLUMN],
+ validate: false,
+ reverse_lock_order: true,
+ on_update: :cascade,
+ on_delete: :cascade,
+ name: FK_NAME
+ )
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ name: FK_NAME,
+ reverse_lock_order: true
+ )
+ end
+ end
+end
diff --git a/db/schema_migrations/20230127152727 b/db/schema_migrations/20230127152727
new file mode 100644
index 00000000000..e29d1af05ac
--- /dev/null
+++ b/db/schema_migrations/20230127152727
@@ -0,0 +1 @@
+03377600dfb6df35b3541c4a6a1fdf19d29179c55ed101141289442322086651 \ No newline at end of file
diff --git a/db/schema_migrations/20230127152728 b/db/schema_migrations/20230127152728
new file mode 100644
index 00000000000..e2fa412b5aa
--- /dev/null
+++ b/db/schema_migrations/20230127152728
@@ -0,0 +1 @@
+50e5a64558a8253e13514d158cc265e463dcfc92d3615a1f207d18b94778ec68 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 3e645adcb3c..e9bdeb7201f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -29293,6 +29293,8 @@ CREATE INDEX index_ci_job_artifacts_on_id_project_id_and_file_type ON ci_job_art
CREATE UNIQUE INDEX index_ci_job_artifacts_on_job_id_and_file_type ON ci_job_artifacts USING btree (job_id, file_type);
+CREATE INDEX index_ci_job_artifacts_on_partition_id_job_id ON ci_job_artifacts USING btree (partition_id, job_id);
+
CREATE INDEX index_ci_job_artifacts_on_project_id ON ci_job_artifacts USING btree (project_id);
CREATE INDEX index_ci_job_artifacts_on_project_id_and_id ON ci_job_artifacts USING btree (project_id, id);
@@ -35993,6 +35995,9 @@ ALTER TABLE ONLY boards_epic_board_recent_visits
ALTER TABLE ONLY ci_job_artifacts
ADD CONSTRAINT fk_rails_c5137cb2c1 FOREIGN KEY (job_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
+ALTER TABLE ONLY ci_job_artifacts
+ ADD CONSTRAINT fk_rails_c5137cb2c1_p FOREIGN KEY (partition_id, job_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
+
ALTER TABLE ONLY packages_events
ADD CONSTRAINT fk_rails_c6c20d0094 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE SET NULL;
diff --git a/doc/api/runners.md b/doc/api/runners.md
index e9c389119c4..66a638d4961 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -789,7 +789,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104691) in GitLab 15.7.
WARNING:
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 16.0. This change is a breaking change.
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 17.0. This change is a breaking change.
Reset the runner registration token for a project.
@@ -808,7 +808,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104691) in GitLab 15.7.
WARNING:
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 16.0. This change is a breaking change.
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/383341) in GitLab 15.7 and is planned for removal in 17.0. This change is a breaking change.
Reset the runner registration token for a group.
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index 1c2ae3a56d8..69a10674d7d 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -408,8 +408,8 @@ scope.
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Runner | `%16.0` | Do not allow runner to start if `.runner_system_id` file cannot be written. |
-| GitLab Rails app | `%16.0` | Enable `:enforce_create_runner_workflow` feature flag by default. |
-| GitLab Rails app | `%16.0` | Start reject job requests that don't include `system_id` value. |
+| GitLab Rails app | `%16.6` | Enable `:enforce_create_runner_workflow` feature flag by default. |
+| GitLab Rails app | `%16.6` | Start reject job requests that don't include `system_id` value. |
### Stage 7 - Removals
@@ -425,7 +425,7 @@ scope.
### Will my runner registration workflow break?
-If no action is taken before your GitLab instance is upgraded to 16.0, then your runner registration
+If no action is taken before your GitLab instance is upgraded to 16.6, then your runner registration
worflow will break.
For self-managed instances, to continue using the previous runner registration process,
you can disable the `enforce_create_runner_workflow` feature flag until GitLab 17.0.
@@ -456,7 +456,7 @@ This allows the GitLab instance to display which system executed a given job.
- In GitLab 15.10, we plan to implement runner creation directly in the runners administration page,
and prepare the runner to follow the new workflow.
-- In GitLab 16.0, we plan to disable registration tokens.
+- In GitLab 16.6, we plan to disable registration tokens.
For self-managed instances, to continue using
registration tokens, you can disable the `enforce_create_runner_workflow` feature flag until
GitLab 17.0.
diff --git a/doc/ci/yaml/includes.md b/doc/ci/yaml/includes.md
index d29d0226577..bf0b7444e78 100644
--- a/doc/ci/yaml/includes.md
+++ b/doc/ci/yaml/includes.md
@@ -157,6 +157,87 @@ and the `environment:url` of the `production` job defined in the `.gitlab-ci.yml
override the values defined in the `autodevops-template.yml` file. The other keywords
do not change. This method is called *merging*.
+### Merge method for `include`
+
+For a file containing `include` directives, the included files are read in order (possibly
+recursively), and the configuration in these files is likewise merged in order. If the parameters overlap, the last included file takes precedence. Finally, the directives in the
+file itself are merged with the configuration from the included files.
+
+This merge method is a _deep merge_, where hash maps are merged at any depth in the
+configuration. To merge hash map A (containing the configuration merged so far) and B (the next piece
+of configuration), the keys and values are processed as follows:
+
+- When the key only exists in A, use the key and value from A.
+- When the key exists in both A and B, and their values are both hash maps, merge those hash maps.
+- When the key exists in both A and B, and one of the values is not a hash map, use the value from B.
+- Otherwise, use the key and value from B.
+
+For example:
+
+We have a configuration consisting of two files.
+
+- The `.gitlab-ci.yml` file:
+
+ ```yaml
+ include: 'common.yml'
+
+ variables:
+ POSTGRES_USER: username
+
+ test:
+ rules:
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+ when: manual
+ artifacts:
+ reports:
+ junit: rspec.xml
+ ```
+
+- The `common.yml` file:
+
+ ```yaml
+ variables:
+ POSTGRES_USER: common_username
+ POSTGRES_PASSWORD: testing_password
+
+ test:
+ rules:
+ - when: never
+ script:
+ - echo LOGIN=${POSTGRES_USER} > deploy.env
+ - rake spec
+ artifacts:
+ reports:
+ dotenv: deploy.env
+ ```
+
+The merged result:
+
+```yaml
+variables:
+ POSTGRES_USER: username
+ POSTGRES_PASSWORD: testing_password
+
+test:
+ rules:
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+ when: manual
+ script:
+ - echo LOGIN=${POSTGRES_USER} > deploy.env
+ - rake spec
+ artifacts:
+ reports:
+ junit: rspec.xml
+ dotenv: deploy.env
+```
+
+In this example:
+
+- Variables are only evaluated after all the files are merged together. A job in an included file
+ might end up using a variable value defined in a different file.
+- `rules` is an array so it cannot be merged. The top-level file takes precedence.
+- `artifacts` is a hash map so it can be deep merged.
+
## Override included configuration arrays
You can use merging to extend and override configuration in an included template, but
diff --git a/doc/integration/jira/index.md b/doc/integration/jira/index.md
index 0bec7e7cec9..2ad71a19cf7 100644
--- a/doc/integration/jira/index.md
+++ b/doc/integration/jira/index.md
@@ -19,9 +19,9 @@ in your GitLab project with any of your projects in Jira.
### Jira integration
-This integration connects one or more GitLab projects to a Jira instance. The Jira instance
-can be hosted by you or in [Atlassian cloud](https://www.atlassian.com/migration/assess/why-cloud).
-The supported Jira versions are `v6.x`, `v7.x`, and `v8.x`.
+This integration connects one or more GitLab projects to a Jira instance. You can host
+the Jira instance yourself or in [Atlassian Cloud](https://www.atlassian.com/migration/assess/why-cloud).
+The supported Jira versions are `6.x`, `7.x`, `8.x`, and `9.x`.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Agile Management - GitLab-Jira Basic Integration](https://www.youtube.com/watch?v=fWvwkx5_00E&feature=youtu.be).
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 83b95ee1033..180f9d0c6ed 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -1000,7 +1000,7 @@ The endpoint to get [changes from a single merge request](https://docs.gitlab.co
### Support for REST API endpoints that reset runner registration tokens
-End of Support: GitLab <span class="removal-milestone">16.0</span> <span class="support-end-date"></span><br />
+End of Support: GitLab <span class="removal-milestone">16.6</span> <span class="support-end-date"></span><br />
Planned removal: GitLab <span class="removal-milestone">17.0</span> <span class="removal-date"></span>
WARNING:
@@ -1131,7 +1131,7 @@ From GitLab 13.6, users can [specify any runner configuration in the GitLab Runn
### GitLab Runner registration token in Runner Operator
-End of Support: GitLab <span class="removal-milestone">16.0</span> <span class="support-end-date"></span><br />
+End of Support: GitLab <span class="removal-milestone">16.6</span> <span class="support-end-date"></span><br />
Planned removal: GitLab <span class="removal-milestone">17.0</span> <span class="removal-date"></span>
WARNING:
@@ -1146,7 +1146,7 @@ The [`runner-registration-token`](https://docs.gitlab.com/runner/install/operato
### Registration tokens and server-side runner arguments in `POST /api/v4/runners` endpoint
-End of Support: GitLab <span class="removal-milestone">16.0</span> <span class="support-end-date"></span><br />
+End of Support: GitLab <span class="removal-milestone">16.6</span> <span class="support-end-date"></span><br />
Planned removal: GitLab <span class="removal-milestone">17.0</span> <span class="removal-date"></span>
WARNING:
@@ -1158,7 +1158,7 @@ This endpoint [registers](https://docs.gitlab.com/ee/api/runners.html#register-a
with a GitLab instance at the instance, group, or project level through the API. We plan to remove the support for
registration tokens and certain configuration arguments in this endpoint in GitLab 17.0.
-In GitLab 15.8, we plan to implement a new method to bind runners to a GitLab instance,
+In GitLab 15.10, we plan to implement a new method to bind runners to a GitLab instance,
as part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/).
This new architecture introduces a new method for registering runners and will eliminate the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
@@ -1170,7 +1170,7 @@ From GitLab 17.0 and later, the runner registration methods implemented by the n
### Registration tokens and server-side runner arguments in `gitlab-runner register` command
-End of Support: GitLab <span class="removal-milestone">16.0</span> <span class="support-end-date"></span><br />
+End of Support: GitLab <span class="removal-milestone">16.6</span> <span class="support-end-date"></span><br />
Planned removal: GitLab <span class="removal-milestone">17.0</span> <span class="removal-date"></span>
WARNING:
@@ -1178,7 +1178,7 @@ This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_g
Review the details carefully before upgrading.
The support for registration tokens and certain configuration arguments in the command to [register](https://docs.gitlab.com/runner/register/) a runner, `gitlab-runner register` is deprecated.
-GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.8,
+GitLab plans to introduce a new [GitLab Runner token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) in GitLab 15.10,
which introduces a new method for registering runners and eliminates the legacy
[runner registration token](https://docs.gitlab.com/ee/security/token_overview.html#runner-registration-tokens).
The new method will involve creating the runner in the GitLab UI and passing the
@@ -1191,7 +1191,7 @@ to the `gitlab-runner register` command.
### `runnerRegistrationToken` parameter for GitLab Runner Helm Chart
-End of Support: GitLab <span class="removal-milestone">16.0</span> <span class="support-end-date"></span><br />
+End of Support: GitLab <span class="removal-milestone">16.6</span> <span class="support-end-date"></span><br />
Planned removal: GitLab <span class="removal-milestone">17.0</span> <span class="removal-date"></span>
WARNING:
@@ -1204,6 +1204,7 @@ As part of the new [GitLab Runner token architecture](https://docs.gitlab.com/ee
- A new method to bind runners to a GitLab instance leveraging `runnerToken`.
- A unique system ID saved to the `config.toml`, which will ensure traceability between jobs and runners.
+
From GitLab 17.0 and later, the methods to register runners introduced by the new GitLab Runner token architecture will be the only supported methods.
</div>
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
index ac29a639436..649b0c51818 100644
--- a/doc/user/project/integrations/gitlab_slack_application.md
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -7,11 +7,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab for Slack app **(FREE SAAS)**
NOTE:
-The GitLab for Slack app is only configurable for GitLab SaaS customers.
-Self-managed GitLab customers should configure
-[Slack slash commands](slack_slash_commands.md) and [Slack notifications](slack.md) instead. See
-[Slack application integration for self-managed instances](https://gitlab.com/groups/gitlab-org/-/epics/1211)
-for our plans to make the app configurable for all GitLab installations.
+This feature is only configurable on GitLab.com.
+For self-managed GitLab instances, use
+[Slack slash commands](slack_slash_commands.md) and [Slack notifications](slack.md) instead.
+For more information about our plans to make this feature configurable for all GitLab installations,
+see [epic 1211](https://gitlab.com/groups/gitlab-org/-/epics/1211).
Slack provides a native application that you can enable with your project's
integrations on GitLab.com.
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index d14401a5c9d..1d452141dbd 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -7,10 +7,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Slack notifications integration **(FREE)**
WARNING:
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/372411) for GitLab SaaS customers
-in GitLab 15.10 and is [planned for removal](https://gitlab.com/groups/gitlab-org/-/epics/8673).
-GitLab SaaS customers can use the [GitLab for Slack app](gitlab_slack_application.md) instead.
-Self-managed GitLab customers can continue to use this feature.
+This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/372411) on GitLab.com
+in GitLab 15.9 and is [planned for removal](https://gitlab.com/groups/gitlab-org/-/epics/8673).
+For GitLab.com, use the [GitLab for Slack app](gitlab_slack_application.md) instead.
+For self-managed GitLab instances, you can continue to use this feature.
The Slack notifications integration enables your GitLab project to send events
(such as issue creation) to your existing Slack team as notifications. Setting up
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index bfa0ae94341..2563cec2b05 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -6,6 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Slack slash commands **(FREE SELF)**
+NOTE:
+This feature is only configurable on self-managed GitLab instances.
+For GitLab.com, use the [GitLab for Slack app](gitlab_slack_application.md) instead.
+
If you want to control and view GitLab content while you're
working in Slack, you can use Slack slash commands.
To use Slack slash commands, you must configure both Slack and GitLab.
@@ -13,9 +17,6 @@ To use Slack slash commands, you must configure both Slack and GitLab.
GitLab can also send events (for example, `issue created`) to Slack as notifications.
The [Slack notifications service](slack.md) is configured separately.
-NOTE:
-The Slack slash commands are only configurable on self-managed GitLab instances. For GitLab.com, use the [GitLab for Slack app](gitlab_slack_application.md) instead.
-
## Configure GitLab and Slack
Slack slash command integrations
diff --git a/jest.config.base.js b/jest.config.base.js
index 05967b51b88..26e7c8e8d18 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -60,6 +60,7 @@ module.exports = (path, options = {}) => {
[TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures$1',
'^test_fixtures_static(/.*)$': '<rootDir>/spec/frontend/fixtures/static$1',
'\\.(jpg|jpeg|png|svg|css)$': '<rootDir>/spec/frontend/__mocks__/file_mock.js',
+ '\\.svg\\?url$': '<rootDir>/spec/frontend/__mocks__/file_mock.js',
'^public(/.*)$': '<rootDir>/public$1',
'emojis(/.*).json': '<rootDir>/fixtures/emojis$1.json',
'^spec/test_constants$': '<rootDir>/spec/frontend/__helpers__/test_constants',
diff --git a/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric.rb b/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric.rb
new file mode 100644
index 00000000000..13af3937f43
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class JiraActiveIntegrationsMetric < DatabaseMetric
+ operation :count
+
+ def initialize(metric_definition)
+ super
+
+ deployment_type = options[:deployment_type]
+
+ return if deployment_type.in?(allowed_types)
+
+ raise ArgumentError, "deployment_type '#{deployment_type}' must be one of: #{allowed_types.join(', ')}"
+ end
+
+ relation do |options|
+ ::Integrations::Jira
+ .active
+ .joins(:jira_tracker_data)
+ .where(jira_tracker_data: { deployment_type: options[:deployment_type] })
+ end
+
+ private
+
+ def allowed_types
+ Integrations::JiraTrackerData.deployment_types.keys
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js b/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
index 12c9afe9758..db6fd2c369b 100644
--- a/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
+++ b/spec/frontend/ci/runner/components/runner_platforms_radio_group_spec.js
@@ -51,9 +51,9 @@ describe('RunnerPlatformsRadioGroup', () => {
['Linux', null],
['macOS', null],
['Windows', null],
- ['AWS', mockProvide.awsImgPath],
- ['Docker', mockProvide.dockerImgPath],
- ['Kubernetes', mockProvide.kubernetesImgPath],
+ ['AWS', expect.any(String)],
+ ['Docker', expect.any(String)],
+ ['Kubernetes', expect.any(String)],
]);
});
diff --git a/spec/frontend/helpers/init_simple_app_helper_spec.js b/spec/frontend/helpers/init_simple_app_helper_spec.js
new file mode 100644
index 00000000000..8dd3745e0ac
--- /dev/null
+++ b/spec/frontend/helpers/init_simple_app_helper_spec.js
@@ -0,0 +1,61 @@
+import { createWrapper } from '@vue/test-utils';
+import Vue from 'vue';
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
+
+const MockComponent = Vue.component('MockComponent', {
+ props: {
+ someKey: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ count: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ },
+ render: (createElement) => createElement('span'),
+});
+
+let wrapper;
+
+const findMock = () => wrapper.findComponent(MockComponent);
+
+const didCreateApp = () => wrapper !== undefined;
+
+const initMock = (html, props = {}) => {
+ setHTMLFixture(html);
+
+ const app = initSimpleApp('#mount-here', MockComponent, { props });
+
+ wrapper = app ? createWrapper(app) : undefined;
+};
+
+describe('helpers/init_simple_app_helper/initSimpleApp', () => {
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ it('mounts the component if the selector exists', async () => {
+ initMock('<div id="mount-here"></div>');
+
+ expect(findMock().exists()).toBe(true);
+ });
+
+ it('does not mount the component if selector does not exist', async () => {
+ initMock('<div id="do-not-mount-here"></div>');
+
+ expect(didCreateApp()).toBe(false);
+ });
+
+ it('passes the prop to the component if the prop exists', async () => {
+ initMock(`<div id="mount-here" data-view-model={"someKey":"thing","count":123}></div>`);
+
+ expect(findMock().props()).toEqual({
+ someKey: 'thing',
+ count: 123,
+ });
+ });
+});
diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js
index b5ad38addbd..bfc87f17092 100644
--- a/spec/frontend/ide/init_gitlab_web_ide_spec.js
+++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js
@@ -3,6 +3,7 @@ import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from '~/ide/constants';
import { initGitlabWebIDE } from '~/ide/init_gitlab_web_ide';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_action';
import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form';
+import { handleTracking } from '~/ide/lib/gitlab_web_ide/handle_tracking_event';
import { TEST_HOST } from 'helpers/test_constants';
import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -115,6 +116,7 @@ describe('ide/init_gitlab_web_ide', () => {
format: TEST_EDITOR_FONT_FORMAT,
},
handleStartRemote: expect.any(Function),
+ handleTracking,
});
});
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/handle_tracking_event_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/handle_tracking_event_spec.js
new file mode 100644
index 00000000000..5dff9b6f118
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/handle_tracking_event_spec.js
@@ -0,0 +1,32 @@
+import { snakeCase } from 'lodash';
+import { handleTracking } from '~/ide/lib/gitlab_web_ide/handle_tracking_event';
+import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
+import { mockTracking } from 'helpers/tracking_helper';
+
+describe('ide/handle_tracking_event', () => {
+ let trackingSpy;
+
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, null, jest.spyOn);
+ });
+
+ describe('when the event does not contain data', () => {
+ it('does not send extra property to snowplow', () => {
+ const event = { name: 'event-name' };
+
+ handleTracking(event);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, snakeCase(event.name));
+ });
+ });
+
+ describe('when the event contains data', () => {
+ it('sends extra property to snowplow', () => {
+ const event = { name: 'event-name', data: { 'extra-details': 'details' } };
+
+ handleTracking(event);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, snakeCase(event.name), {
+ extra: convertObjectPropsToSnakeCase(event.data),
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ide/remote/index_spec.js b/spec/frontend/ide/remote/index_spec.js
index 0f23b0a4e45..413e7b2e4b7 100644
--- a/spec/frontend/ide/remote/index_spec.js
+++ b/spec/frontend/ide/remote/index_spec.js
@@ -3,6 +3,7 @@ import { getBaseConfig, setupRootElement } from '~/ide/lib/gitlab_web_ide';
import { mountRemoteIDE } from '~/ide/remote';
import { TEST_HOST } from 'helpers/test_constants';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
+import { handleTracking } from '~/ide/lib/gitlab_web_ide/handle_tracking_event';
jest.mock('@gitlab/web-ide');
jest.mock('~/ide/lib/gitlab_web_ide');
@@ -24,7 +25,6 @@ const TEST_RETURN_URL_SAME_ORIGIN = `${TEST_HOST}/foo/example`;
describe('~/ide/remote/index', () => {
useMockLocationHelper();
const originalHref = window.location.href;
-
let el;
let rootEl;
@@ -56,6 +56,7 @@ describe('~/ide/remote/index', () => {
hostPath: `/${TEST_DATA.remotePath}`,
handleError: expect.any(Function),
handleClose: expect.any(Function),
+ handleTracking,
});
});
});
diff --git a/spec/frontend/issuable/related_issues/components/add_issuable_form_spec.js b/spec/frontend/issuable/related_issues/components/add_issuable_form_spec.js
index 16d4459f597..e1546f74a59 100644
--- a/spec/frontend/issuable/related_issues/components/add_issuable_form_spec.js
+++ b/spec/frontend/issuable/related_issues/components/add_issuable_form_spec.js
@@ -1,6 +1,7 @@
import { GlFormGroup } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { TYPE_ISSUE } from '~/issues/constants';
import AddIssuableForm from '~/related_issues/components/add_issuable_form.vue';
import IssueToken from '~/related_issues/components/issue_token.vue';
import { issuableTypesMap, linkedIssueTypesMap, PathIdSeparator } from '~/related_issues/constants';
@@ -125,7 +126,7 @@ describe('AddIssuableForm', () => {
wrapper = mount(AddIssuableForm, {
propsData: {
inputValue: '',
- issuableType: issuableTypesMap.ISSUE,
+ issuableType: TYPE_ISSUE,
pathIdSeparator,
pendingReferences: [],
},
@@ -156,9 +157,9 @@ describe('AddIssuableForm', () => {
describe('categorized issuables', () => {
it.each`
- issuableType | pathIdSeparator | contextHeader | contextFooter
- ${issuableTypesMap.ISSUE} | ${PathIdSeparator.Issue} | ${'The current issue'} | ${'the following issues'}
- ${issuableTypesMap.EPIC} | ${PathIdSeparator.Epic} | ${'The current epic'} | ${'the following epics'}
+ issuableType | pathIdSeparator | contextHeader | contextFooter
+ ${TYPE_ISSUE} | ${PathIdSeparator.Issue} | ${'The current issue'} | ${'the following issues'}
+ ${issuableTypesMap.EPIC} | ${PathIdSeparator.Epic} | ${'The current epic'} | ${'the following epics'}
`(
'show header text as "$contextHeader" and footer text as "$contextFooter" issuableType is set to $issuableType',
({ issuableType, contextHeader, contextFooter }) => {
@@ -184,7 +185,7 @@ describe('AddIssuableForm', () => {
propsData: {
inputValue: '',
showCategorizedIssues: true,
- issuableType: issuableTypesMap.ISSUE,
+ issuableType: TYPE_ISSUE,
pathIdSeparator,
pendingReferences: [],
},
diff --git a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
index 996b2406240..ff8d5073005 100644
--- a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
+++ b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
@@ -6,10 +6,10 @@ import {
issuable2,
issuable3,
} from 'jest/issuable/components/related_issuable_mock_data';
+import { TYPE_ISSUE } from '~/issues/constants';
import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import AddIssuableForm from '~/related_issues/components/add_issuable_form.vue';
import {
- issuableTypesMap,
linkedIssueTypesMap,
linkedIssueTypesTextMap,
PathIdSeparator,
@@ -34,7 +34,7 @@ describe('RelatedIssuesBlock', () => {
wrapper = mountExtended(RelatedIssuesBlock, {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
- issuableType: issuableTypesMap.ISSUE,
+ issuableType: TYPE_ISSUE,
},
});
});
@@ -237,7 +237,7 @@ describe('RelatedIssuesBlock', () => {
propsData: {
pathIdSeparator: PathIdSeparator.Issue,
relatedIssues: [issuable1, issuable2, issuable3],
- issuableType: issuableTypesMap.ISSUE,
+ issuableType: TYPE_ISSUE,
},
});
});
diff --git a/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js b/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js
index fb45c4b07a4..483e454d7d7 100644
--- a/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js
+++ b/spec/frontend/ml/experiment_tracking/components/ml_candidate_spec.js
@@ -28,7 +28,7 @@ describe('MlCandidate', () => {
},
};
- return mountExtended(MlCandidate, { provide: { candidate } });
+ return mountExtended(MlCandidate, { propsData: { candidate } });
};
const findAlert = () => wrapper.findComponent(GlAlert);
diff --git a/spec/frontend/related_issues/components/related_issuable_input_spec.js b/spec/frontend/related_issues/components/related_issuable_input_spec.js
index f6a13856042..f7333bf6893 100644
--- a/spec/frontend/related_issues/components/related_issuable_input_spec.js
+++ b/spec/frontend/related_issues/components/related_issuable_input_spec.js
@@ -1,8 +1,9 @@
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { TEST_HOST } from 'helpers/test_constants';
+import { TYPE_ISSUE } from '~/issues/constants';
import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
-import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
+import { PathIdSeparator } from '~/related_issues/constants';
jest.mock('ee_else_ce/gfm_auto_complete', () => {
return function gfmAutoComplete() {
@@ -21,7 +22,7 @@ describe('RelatedIssuableInput', () => {
inputValue: '',
references: [],
pathIdSeparator: PathIdSeparator.Issue,
- issuableType: issuableTypesMap.issue,
+ issuableType: TYPE_ISSUE,
autoCompleteSources: {
issues: `${TEST_HOST}/h5bp/html5-boilerplate/-/autocomplete_sources/issues`,
},
diff --git a/spec/helpers/projects/ml/experiments_helper_spec.rb b/spec/helpers/projects/ml/experiments_helper_spec.rb
index 6f6a5bceb2e..8ef81c49fa7 100644
--- a/spec/helpers/projects/ml/experiments_helper_spec.rb
+++ b/spec/helpers/projects/ml/experiments_helper_spec.rb
@@ -77,10 +77,10 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
end
end
- describe '#candidate_as_data' do
+ describe '#show_candidate_view_model' do
let(:candidate) { candidate0 }
- subject { Gitlab::Json.parse(helper.candidate_as_data(candidate)) }
+ subject { Gitlab::Json.parse(helper.show_candidate_view_model(candidate))['candidate'] }
it 'generates the correct params' do
expect(subject['params']).to include(
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric_spec.rb
new file mode 100644
index 00000000000..104fd18ba2d
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/jira_active_integrations_metric_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::JiraActiveIntegrationsMetric,
+ feature_category: :integrations do
+ let(:options) { { deployment_type: 'cloud', series: 0 } }
+ let(:integration_attributes) { { active: true, deployment_type: 'cloud' } }
+ let(:expected_value) { 3 }
+ let(:expected_query) do
+ 'SELECT COUNT("integrations"."id") FROM "integrations" ' \
+ 'INNER JOIN "jira_tracker_data" ON "jira_tracker_data"."integration_id" = "integrations"."id" ' \
+ 'WHERE "integrations"."type_new" = \'Integrations::Jira\' AND "integrations"."active" = TRUE ' \
+ 'AND "jira_tracker_data"."deployment_type" = 2'
+ end
+
+ before do
+ create_list :jira_integration, 3, integration_attributes
+
+ create :jira_integration, integration_attributes.merge(active: false)
+ create :jira_integration, integration_attributes.merge(deployment_type: 'server')
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ { options: { deployment_type: 'cloud' }, time_frame: 'all' }
+
+ it "raises an exception if option is not present" do
+ expect do
+ described_class.new(options: options.except(:deployment_type), time_frame: 'all')
+ end.to raise_error(ArgumentError, %r{deployment_type .* must be one of})
+ end
+
+ it "raises an exception if option has invalid value" do
+ expect do
+ options[:deployment_type] = 'cloood'
+ described_class.new(options: options, time_frame: 'all')
+ end.to raise_error(ArgumentError, %r{deployment_type .* must be one of})
+ end
+end
diff --git a/workhorse/gitaly_integration_test.go b/workhorse/gitaly_integration_test.go
index ed44aaddbc3..a21c31aea49 100644
--- a/workhorse/gitaly_integration_test.go
+++ b/workhorse/gitaly_integration_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"fmt"
+ "net/url"
"os"
"os/exec"
"path"
@@ -26,32 +27,52 @@ import (
)
var (
- gitalyAddress string
- jsonGitalyServer string
+ gitalyAddresses []string
)
+// Convert from tcp://127.0.0.1:8075 to dns scheme variants:
+// * dns:127.0.0.1:8075
+// * dns:///127.0.0.1:8075
+func convertToDNSSchemes(address string) []string {
+ uri, err := url.Parse(address)
+ if err != nil {
+ panic(fmt.Sprintf("invalid GITALY_ADDRESS url %s: %s", address, err))
+ }
+ return []string{
+ fmt.Sprintf("dns:///%s", uri.Host),
+ fmt.Sprintf("dns:%s", uri.Host),
+ }
+}
+
+func jsonGitalyServer(address string) string {
+ return fmt.Sprintf(`"GitalyServer":{"Address":"%s", "Token": ""}`, address)
+}
+
func init() {
- gitalyAddress = os.Getenv("GITALY_ADDRESS")
- jsonGitalyServer = fmt.Sprintf(`"GitalyServer":{"Address":"%s", "Token": ""}`, gitalyAddress)
+ rawAddress := os.Getenv("GITALY_ADDRESS")
+ if rawAddress != "" {
+ gitalyAddresses = append(gitalyAddresses, rawAddress)
+ gitalyAddresses = append(gitalyAddresses, convertToDNSSchemes(rawAddress)...)
+ }
}
func skipUnlessRealGitaly(t *testing.T) {
- t.Log(gitalyAddress)
- if gitalyAddress != "" {
+ t.Log(gitalyAddresses)
+ if len(gitalyAddresses) != 0 {
return
}
t.Skip(`Please set GITALY_ADDRESS="..." to run Gitaly integration tests`)
}
-func realGitalyAuthResponse(apiResponse *api.Response) *api.Response {
+func realGitalyAuthResponse(gitalyAddress string, apiResponse *api.Response) *api.Response {
apiResponse.GitalyServer.Address = gitalyAddress
return apiResponse
}
-func realGitalyOkBody(t *testing.T) *api.Response {
- return realGitalyAuthResponse(gitOkBody(t))
+func realGitalyOkBody(t *testing.T, gitalyAddress string) *api.Response {
+ return realGitalyAuthResponse(gitalyAddress, gitOkBody(t))
}
func ensureGitalyRepository(t *testing.T, apiResponse *api.Response) error {
@@ -105,241 +126,273 @@ func ensureGitalyRepository(t *testing.T, apiResponse *api.Response) error {
func TestAllowedClone(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- // Prepare test server and backend
- ts := testAuthServer(t, nil, nil, 200, apiResponse)
- defer ts.Close()
- ws := startWorkhorseServer(ts.URL)
- defer ws.Close()
-
- // Do the git clone
- require.NoError(t, os.RemoveAll(scratchDir))
- cloneCmd := exec.Command("git", "clone", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
- runOrFail(t, cloneCmd)
-
- // We may have cloned an 'empty' repository, 'git log' will fail in it
- logCmd := exec.Command("git", "log", "-1", "--oneline")
- logCmd.Dir = checkoutDir
- runOrFail(t, logCmd)
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Do the git clone
+ require.NoError(t, os.RemoveAll(scratchDir))
+ cloneCmd := exec.Command("git", "clone", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ runOrFail(t, cloneCmd)
+
+ // We may have cloned an 'empty' repository, 'git log' will fail in it
+ logCmd := exec.Command("git", "log", "-1", "--oneline")
+ logCmd.Dir = checkoutDir
+ runOrFail(t, logCmd)
+ })
+ }
}
func TestAllowedShallowClone(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- // Prepare test server and backend
- ts := testAuthServer(t, nil, nil, 200, apiResponse)
- defer ts.Close()
- ws := startWorkhorseServer(ts.URL)
- defer ws.Close()
-
- // Shallow git clone (depth 1)
- require.NoError(t, os.RemoveAll(scratchDir))
- cloneCmd := exec.Command("git", "clone", "--depth", "1", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
- runOrFail(t, cloneCmd)
-
- // We may have cloned an 'empty' repository, 'git log' will fail in it
- logCmd := exec.Command("git", "log", "-1", "--oneline")
- logCmd.Dir = checkoutDir
- runOrFail(t, logCmd)
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Shallow git clone (depth 1)
+ require.NoError(t, os.RemoveAll(scratchDir))
+ cloneCmd := exec.Command("git", "clone", "--depth", "1", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ runOrFail(t, cloneCmd)
+
+ // We may have cloned an 'empty' repository, 'git log' will fail in it
+ logCmd := exec.Command("git", "log", "-1", "--oneline")
+ logCmd.Dir = checkoutDir
+ runOrFail(t, logCmd)
+ })
+ }
}
func TestAllowedPush(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- // Prepare the test server and backend
- ts := testAuthServer(t, nil, nil, 200, apiResponse)
- defer ts.Close()
- ws := startWorkhorseServer(ts.URL)
- defer ws.Close()
-
- // Perform the git push
- pushCmd := exec.Command("git", "push", fmt.Sprintf("%s/%s", ws.URL, testRepo), fmt.Sprintf("master:%s", newBranch()))
- pushCmd.Dir = checkoutDir
- runOrFail(t, pushCmd)
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare the test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Perform the git push
+ pushCmd := exec.Command("git", "push", fmt.Sprintf("%s/%s", ws.URL, testRepo), fmt.Sprintf("master:%s", newBranch()))
+ pushCmd.Dir = checkoutDir
+ runOrFail(t, pushCmd)
+ })
+ }
}
func TestAllowedGetGitBlob(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- // the LICENSE file in the test repository
- oid := "50b27c6518be44c42c4d87966ae2481ce895624c"
- expectedBody := "The MIT License (MIT)"
- bodyLen := 1075
-
- jsonParams := fmt.Sprintf(
- `{
- %s,
- "GetBlobRequest":{
- "repository":{"storage_name":"%s", "relative_path":"%s"},
- "oid":"%s",
- "limit":-1
- }
- }`,
- jsonGitalyServer, apiResponse.Repository.StorageName, apiResponse.Repository.RelativePath, oid,
- )
-
- resp, body, err := doSendDataRequest("/something", "git-blob", jsonParams)
- require.NoError(t, err)
- shortBody := string(body[:len(expectedBody)])
-
- require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- require.Equal(t, expectedBody, shortBody, "GET %q: response body", resp.Request.URL)
- testhelper.RequireResponseHeader(t, resp, "Content-Length", strconv.Itoa(bodyLen))
- requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // the LICENSE file in the test repository
+ oid := "50b27c6518be44c42c4d87966ae2481ce895624c"
+ expectedBody := "The MIT License (MIT)"
+ bodyLen := 1075
+
+ jsonParams := fmt.Sprintf(
+ `{
+ %s,
+ "GetBlobRequest":{
+ "repository":{"storage_name":"%s", "relative_path":"%s"},
+ "oid":"%s",
+ "limit":-1
+ }
+ }`,
+ jsonGitalyServer(gitalyAddress), apiResponse.Repository.StorageName, apiResponse.Repository.RelativePath, oid,
+ )
+
+ resp, body, err := doSendDataRequest("/something", "git-blob", jsonParams)
+ require.NoError(t, err)
+ shortBody := string(body[:len(expectedBody)])
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ require.Equal(t, expectedBody, shortBody, "GET %q: response body", resp.Request.URL)
+ testhelper.RequireResponseHeader(t, resp, "Content-Length", strconv.Itoa(bodyLen))
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+ })
+ }
}
func TestAllowedGetGitArchive(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- archivePath := path.Join(scratchDir, "my/path")
- archivePrefix := "repo-1"
-
- msg := serializedProtoMessage("GetArchiveRequest", &gitalypb.GetArchiveRequest{
- Repository: &apiResponse.Repository,
- CommitId: "HEAD",
- Prefix: archivePrefix,
- Format: gitalypb.GetArchiveRequest_TAR,
- Path: []byte("files"),
- })
- jsonParams := buildGitalyRPCParams(gitalyAddress, rpcArg{"ArchivePath", archivePath}, msg)
-
- resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
- require.NoError(t, err)
- require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
-
- // Ensure the tar file is readable
- foundEntry := false
- tr := tar.NewReader(bytes.NewReader(body))
- for {
- hdr, err := tr.Next()
- if err != nil {
- break
- }
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ archivePath := path.Join(scratchDir, "my/path")
+ archivePrefix := "repo-1"
+
+ msg := serializedProtoMessage("GetArchiveRequest", &gitalypb.GetArchiveRequest{
+ Repository: &apiResponse.Repository,
+ CommitId: "HEAD",
+ Prefix: archivePrefix,
+ Format: gitalypb.GetArchiveRequest_TAR,
+ Path: []byte("files"),
+ })
+ jsonParams := buildGitalyRPCParams(gitalyAddress, rpcArg{"ArchivePath", archivePath}, msg)
+
+ resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
+ require.NoError(t, err)
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+
+ // Ensure the tar file is readable
+ foundEntry := false
+ tr := tar.NewReader(bytes.NewReader(body))
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ break
+ }
+
+ if hdr.Name == archivePrefix+"/" {
+ foundEntry = true
+ break
+ }
+ }
- if hdr.Name == archivePrefix+"/" {
- foundEntry = true
- break
- }
+ require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
+ })
}
-
- require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
}
func TestAllowedGetGitArchiveOldPayload(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- repo := &apiResponse.Repository
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ repo := &apiResponse.Repository
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
- archivePath := path.Join(scratchDir, "my/path")
- archivePrefix := "repo-1"
+ archivePath := path.Join(scratchDir, "my/path")
+ archivePrefix := "repo-1"
- jsonParams := fmt.Sprintf(
- `{
+ jsonParams := fmt.Sprintf(
+ `{
%s,
"GitalyRepository":{"storage_name":"%s","relative_path":"%s"},
"ArchivePath":"%s",
"ArchivePrefix":"%s",
"CommitId":"%s"
}`,
- jsonGitalyServer, repo.StorageName, repo.RelativePath, archivePath, archivePrefix, "HEAD",
- )
-
- resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
- require.NoError(t, err)
- require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
-
- // Ensure the tar file is readable
- foundEntry := false
- tr := tar.NewReader(bytes.NewReader(body))
- for {
- hdr, err := tr.Next()
- if err != nil {
- break
- }
+ jsonGitalyServer(gitalyAddress), repo.StorageName, repo.RelativePath, archivePath, archivePrefix, "HEAD",
+ )
+
+ resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
+ require.NoError(t, err)
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+
+ // Ensure the tar file is readable
+ foundEntry := false
+ tr := tar.NewReader(bytes.NewReader(body))
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ break
+ }
+
+ if hdr.Name == archivePrefix+"/" {
+ foundEntry = true
+ break
+ }
+ }
- if hdr.Name == archivePrefix+"/" {
- foundEntry = true
- break
- }
+ require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
+ })
}
-
- require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
}
func TestAllowedGetGitDiff(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- msg := serializedMessage("RawDiffRequest", &gitalypb.RawDiffRequest{
- Repository: &apiResponse.Repository,
- LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
- RightCommitId: "732401c65e924df81435deb12891ef570167d2e2",
- })
- jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
-
- resp, body, err := doSendDataRequest("/something", "git-diff", jsonParams)
- require.NoError(t, err)
-
- require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
-
- expectedBody := "diff --git a/LICENSE b/LICENSE\n"
- require.Equal(t, expectedBody, string(body[:len(expectedBody)]),
- "GET %q: response body", resp.Request.URL)
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ msg := serializedMessage("RawDiffRequest", &gitalypb.RawDiffRequest{
+ Repository: &apiResponse.Repository,
+ LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
+ RightCommitId: "732401c65e924df81435deb12891ef570167d2e2",
+ })
+ jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
+
+ resp, body, err := doSendDataRequest("/something", "git-diff", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+
+ expectedBody := "diff --git a/LICENSE b/LICENSE\n"
+ require.Equal(t, expectedBody, string(body[:len(expectedBody)]),
+ "GET %q: response body", resp.Request.URL)
+ })
+ }
}
func TestAllowedGetGitFormatPatch(t *testing.T) {
skipUnlessRealGitaly(t)
- // Create the repository in the Gitaly server
- apiResponse := realGitalyOkBody(t)
- require.NoError(t, ensureGitalyRepository(t, apiResponse))
-
- msg := serializedMessage("RawPatchRequest", &gitalypb.RawPatchRequest{
- Repository: &apiResponse.Repository,
- LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
- RightCommitId: "0e1b353b348f8477bdbec1ef47087171c5032cd9",
- })
- jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
-
- resp, body, err := doSendDataRequest("/something", "git-format-patch", jsonParams)
- require.NoError(t, err)
-
- require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
-
- requirePatchSeries(t, body,
- "732401c65e924df81435deb12891ef570167d2e2",
- "33bcff41c232a11727ac6d660bd4b0c2ba86d63d",
- "0e1b353b348f8477bdbec1ef47087171c5032cd9",
- )
+ for _, gitalyAddress := range gitalyAddresses {
+ t.Run(gitalyAddress, func(t *testing.T) {
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t, gitalyAddress)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ msg := serializedMessage("RawPatchRequest", &gitalypb.RawPatchRequest{
+ Repository: &apiResponse.Repository,
+ LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
+ RightCommitId: "0e1b353b348f8477bdbec1ef47087171c5032cd9",
+ })
+ jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
+
+ resp, body, err := doSendDataRequest("/something", "git-format-patch", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+
+ requirePatchSeries(t, body,
+ "732401c65e924df81435deb12891ef570167d2e2",
+ "33bcff41c232a11727ac6d660bd4b0c2ba86d63d",
+ "0e1b353b348f8477bdbec1ef47087171c5032cd9",
+ )
+ })
+ }
}
var extractPatchSeriesMatcher = regexp.MustCompile(`^From (\w+)`)
diff --git a/workhorse/gitaly_test.go b/workhorse/gitaly_test.go
index 2d7f727003f..de896475a99 100644
--- a/workhorse/gitaly_test.go
+++ b/workhorse/gitaly_test.go
@@ -439,7 +439,7 @@ func TestPostUploadPackProxiedToGitalySuccessfully(t *testing.T) {
bodySplit := strings.SplitN(body, "\000", 2)
require.Len(t, bodySplit, 2)
- gitalyRequest := &gitalypb.PostUploadPackRequest{}
+ gitalyRequest := &gitalypb.PostUploadPackWithSidechannelRequest{}
require.NoError(t, jsonpb.UnmarshalString(bodySplit[0], gitalyRequest))
require.Equal(t, apiResponse.Repository.StorageName, gitalyRequest.Repository.StorageName)
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 6e397a75019..76374a28f5c 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.1
- gitlab.com/gitlab-org/gitaly/v15 v15.8.1
+ gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc4
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.17.0
gocloud.dev v0.28.0
@@ -111,7 +111,7 @@ require (
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.4.0 // indirect
+ golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 5e1d22d2fa1..64749d157d3 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -1435,6 +1435,7 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
@@ -1845,8 +1846,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-gitlab.com/gitlab-org/gitaly/v15 v15.8.1 h1:i/38/jLhvXwiztdrUCh9YxCrYufWS0K6pKQnbF3vy8U=
-gitlab.com/gitlab-org/gitaly/v15 v15.8.1/go.mod h1:typ7BQ9JuglkeTvW3Vn1SwFcZ1FMn8P3GrWcPuZQ0EU=
+gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc4 h1:n6VW2djSoiGaajMUkVy+crRnyiUePcW1v1T3wPd2hhI=
+gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc4/go.mod h1:y0OCqEn0WWmyycVpde494A5qhCszhlx2b+od6vSzsOU=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE=
gitlab.com/gitlab-org/labkit v1.17.0 h1:mEkoLzXorLNdt8NkfgYS5xMDhdqCsIJaeEVtSf7d8cU=
@@ -2003,6 +2004,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
@@ -2328,8 +2330,8 @@ golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/workhorse/internal/gitaly/gitaly.go b/workhorse/internal/gitaly/gitaly.go
index af7425be1cf..eace87dda36 100644
--- a/workhorse/internal/gitaly/gitaly.go
+++ b/workhorse/internal/gitaly/gitaly.go
@@ -192,6 +192,16 @@ func newConnection(server api.GitalyServer) (*grpc.ClientConn, error) {
),
),
),
+
+ // In https://gitlab.com/groups/gitlab-org/-/epics/8971, we added DNS discovery support to Praefect. This was
+ // done by making two changes:
+ // - Configure client-side round-robin load-balancing in client dial options. We added that as a default option
+ // inside gitaly client in gitaly client since v15.9.0
+ // - Configure DNS resolving. Due to some technical limitations, we don't use gRPC's built-in DNS resolver.
+ // Instead, we implement our own DNS resolver. This resolver is exposed via the following configuration.
+ // Afterward, workhorse can detect and handle DNS discovery automatically. The user needs to setup and set
+ // Gitaly address to something like "dns:gitaly.service.dc1.consul"
+ gitalyclient.WithGitalyDNSResolver(gitalyclient.DefaultDNSResolverBuilderConfig()),
)
conn, connErr := gitalyclient.DialSidechannel(context.Background(), server.Address, sidechannelRegistry, connOpts) // lint:allow context.Background