Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-20 18:10:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-20 18:10:29 +0300
commitef8c47e97e1c178291e4857314a3f53875d75062 (patch)
tree89ef0c1cd34e864aad1346a0ee07bccb81a9ac65
parentf2ebc27236ecef67e67da1da864bbf58e9b9c267 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintrc.yml1
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue67
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue20
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/event_hub.js3
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/index.js70
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js23
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/list_item.vue10
-rw-r--r--app/assets/stylesheets/pages/login.scss3
-rw-r--r--app/assets/stylesheets/startup/startup-signin.scss3
-rw-r--r--app/finders/repositories/commits_with_trailer_finder.rb82
-rw-r--r--app/graphql/types/notes/discussion_type.rb12
-rw-r--r--app/graphql/types/notes/note_type.rb2
-rw-r--r--app/models/merge_request_context_commit.rb3
-rw-r--r--app/models/merge_request_diff_commit.rb6
-rw-r--r--app/models/project.rb20
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/models/service.rb11
-rw-r--r--app/services/bulk_create_integration_service.rb10
-rw-r--r--app/services/feature_flags/base_service.rb1
-rw-r--r--app/services/merge_requests/add_context_service.rb3
-rw-r--r--app/validators/json_schemas/git_trailers.json9
-rw-r--r--app/views/notify/provisioned_member_access_granted_email.html.haml (renamed from app/views/notify/provisioned_member_access_granted_email.haml)3
-rw-r--r--app/views/notify/provisioned_member_access_granted_email.text.erb (renamed from app/views/notify/provisioned_member_access_granted_email.erb)3
-rw-r--r--app/workers/jira_connect/sync_feature_flags_worker.rb1
-rw-r--r--changelogs/unreleased/290715-has_external_issue_tracker_trigger.yml5
-rw-r--r--changelogs/unreleased/294007-bump-workhorse.yml5
-rw-r--r--changelogs/unreleased/296948-container-and-package-list-ui-polish-condense-spacing-to-match-iss.yml5
-rw-r--r--changelogs/unreleased/296990-enable-jira-ff-sync.yml5
-rw-r--r--changelogs/unreleased/gitaly-trailer-parsing.yml5
-rw-r--r--changelogs/unreleased/yo-fix-border-radius.yml5
-rw-r--r--config/feature_flags/development/jira_sync_feature_flags.yml8
-rw-r--r--db/migrate/20210106155209_add_merge_request_diff_commit_trailers.rb22
-rw-r--r--db/migrate/20210107154615_add_merge_request_context_commit_trailers.rb12
-rw-r--r--db/migrate/20210117210226_add_has_external_issue_tracker_trigger.rb61
-rw-r--r--db/schema_migrations/202101061552091
-rw-r--r--db/schema_migrations/202101071546151
-rw-r--r--db/schema_migrations/202101172102261
-rw-r--r--db/structure.sql32
-rw-r--r--doc/.vale/gitlab/Admin.yml1
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql6
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json6
-rw-r--r--doc/api/graphql/reference/index.md6
-rw-r--r--doc/user/project/integrations/jira.md11
-rw-r--r--doc/user/project/merge_requests/getting_started.md3
-rw-r--r--lib/atlassian/jira_connect/client.rb2
-rw-r--r--lib/gitlab/git/commit.rb3
-rw-r--r--lib/gitlab/git/rugged_impl/commit.rb1
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb3
-rw-r--r--lib/gitlab/global_id.rb4
-rw-r--r--locale/gitlab.pot5
-rw-r--r--spec/finders/repositories/commits_with_trailer_finder_spec.rb38
-rw-r--r--spec/frontend/clusters/components/applications_spec.js56
-rw-r--r--spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap8
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js236
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js190
-rw-r--r--spec/frontend/projects/settings_service_desk/services/service_desk_service_spec.js111
-rw-r--r--spec/frontend/tracking_spec.js2
-rw-r--r--spec/lib/atlassian/jira_connect/client_spec.rb18
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb3
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml2
-rw-r--r--spec/migrations/add_has_external_issue_tracker_trigger_spec.rb164
-rw-r--r--spec/models/merge_request_diff_commit_spec.rb9
-rw-r--r--spec/models/merge_request_spec.rb13
-rw-r--r--spec/models/project_spec.rb150
-rw-r--r--spec/models/service_spec.rb46
-rw-r--r--spec/requests/api/graphql/issue/issue_spec.rb14
-rw-r--r--spec/requests/api/graphql/project/issue/designs/notes_spec.rb19
-rw-r--r--spec/services/bulk_create_integration_service_spec.rb51
-rw-r--r--spec/services/feature_flags/create_service_spec.rb12
-rw-r--r--spec/services/feature_flags/update_service_spec.rb12
-rw-r--r--spec/services/issues/close_service_spec.rb3
-rw-r--r--spec/services/merge_requests/build_service_spec.rb2
-rw-r--r--spec/services/system_notes/issuables_service_spec.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb8
-rw-r--r--spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb45
-rw-r--r--spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb62
-rw-r--r--spec/workers/jira_connect/sync_feature_flags_worker_spec.rb24
-rw-r--r--workhorse/CHANGELOG12
-rw-r--r--workhorse/VERSION2
-rw-r--r--workhorse/gitaly_test.go169
-rw-r--r--workhorse/go.mod18
-rw-r--r--workhorse/go.sum297
-rw-r--r--workhorse/internal/filestore/save_file_opts.go2
-rw-r--r--workhorse/internal/filestore/save_file_opts_test.go10
-rw-r--r--workhorse/internal/git/info-refs.go5
-rw-r--r--workhorse/internal/git/info-refs_test.go33
-rw-r--r--workhorse/internal/upstream/routes.go22
-rw-r--r--workhorse/main_test.go6
-rw-r--r--workhorse/upload_test.go70
94 files changed, 1566 insertions, 980 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 6a20ddc96c3..297eb5a5893 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -51,7 +51,6 @@ rules:
# new ones, to ease migration to v7, so violations of each can be fixed
# separately.
vue/no-mutating-props: off
- vue/one-component-per-file: off
vue/no-lone-template: off
# END eslint-plugin-vue@7 overrides
overrides:
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 8c20f999400..a6c6f8ed977 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.59.0
+8.60.0
diff --git a/Gemfile b/Gemfile
index c7ed1cd4d71..a2aff0f4b1e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -462,7 +462,7 @@ group :ed25519 do
end
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 13.8.0.pre.rc2'
+gem 'gitaly', '~> 13.8.0.pre.rc3'
gem 'grpc', '~> 1.30.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index b370024b95b..74d88417541 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -417,7 +417,7 @@ GEM
rails (>= 3.2.0)
git (1.7.0)
rchardet (~> 1.8)
- gitaly (13.8.0.pre.rc2)
+ gitaly (13.8.0.pre.rc3)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-chronic (0.10.5)
@@ -1357,7 +1357,7 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 13.8.0.pre.rc2)
+ gitaly (~> 13.8.0.pre.rc3)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-experiment (~> 0.4.5)
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
index 909f1afd9f6..43fe54d6e38 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
@@ -2,60 +2,42 @@
import { GlAlert } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import ServiceDeskSetting from './service_desk_setting.vue';
-import ServiceDeskService from '../services/service_desk_service';
-import eventHub from '../event_hub';
+import axios from '~/lib/utils/axios_utils';
export default {
- name: 'ServiceDeskRoot',
components: {
GlAlert,
ServiceDeskSetting,
},
- props: {
+ inject: {
initialIsEnabled: {
- type: Boolean,
- required: true,
+ default: false,
},
endpoint: {
- type: String,
- required: true,
+ default: '',
},
- incomingEmail: {
- type: String,
- required: false,
+ initialIncomingEmail: {
default: '',
},
customEmail: {
- type: String,
- required: false,
default: '',
},
customEmailEnabled: {
- type: Boolean,
- required: false,
+ default: false,
},
selectedTemplate: {
- type: String,
- required: false,
default: '',
},
outgoingName: {
- type: String,
- required: false,
default: '',
},
projectKey: {
- type: String,
- required: false,
default: '',
},
templates: {
- type: Array,
- required: false,
- default: () => [],
+ default: [],
},
},
-
data() {
return {
isEnabled: this.initialIsEnabled,
@@ -63,28 +45,21 @@ export default {
isAlertShowing: false,
alertVariant: 'danger',
alertMessage: '',
+ incomingEmail: this.initialIncomingEmail,
updatedCustomEmail: this.customEmail,
};
},
-
- created() {
- eventHub.$on('serviceDeskEnabledCheckboxToggled', this.onEnableToggled);
- eventHub.$on('serviceDeskTemplateSave', this.onSaveTemplate);
- this.service = new ServiceDeskService(this.endpoint);
- },
-
- beforeDestroy() {
- eventHub.$off('serviceDeskEnabledCheckboxToggled', this.onEnableToggled);
- eventHub.$off('serviceDeskTemplateSave', this.onSaveTemplate);
- },
-
methods: {
onEnableToggled(isChecked) {
this.isEnabled = isChecked;
this.incomingEmail = '';
- this.service
- .toggleServiceDesk(isChecked)
+ const body = {
+ service_desk_enabled: isChecked,
+ };
+
+ return axios
+ .put(this.endpoint, body)
.then(({ data }) => {
const email = data.service_desk_address;
if (isChecked && !email) {
@@ -104,8 +79,16 @@ export default {
onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) {
this.isTemplateSaving = true;
- this.service
- .updateTemplate({ selectedTemplate, outgoingName, projectKey }, this.isEnabled)
+
+ const body = {
+ issue_template_key: selectedTemplate,
+ outgoing_name: outgoingName,
+ project_key: projectKey,
+ service_desk_enabled: this.isEnabled,
+ };
+
+ return axios
+ .put(this.endpoint, body)
.then(({ data }) => {
this.updatedCustomEmail = data?.service_desk_address;
this.showAlert(__('Changes saved.'), 'success');
@@ -150,6 +133,8 @@ export default {
:initial-project-key="projectKey"
:templates="templates"
:is-template-saving="isTemplateSaving"
+ @save="onSaveTemplate"
+ @toggle="onEnableToggled"
/>
</div>
</template>
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
index a850374fc88..39d9a6a4239 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
@@ -1,12 +1,9 @@
<script>
import { GlButton, GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import eventHub from '../event_hub';
export default {
- name: 'ServiceDeskSetting',
components: {
ClipboardButton,
GlButton,
@@ -15,7 +12,6 @@ export default {
GlLoadingIcon,
GlSprintf,
},
- mixins: [glFeatureFlagsMixin()],
props: {
isEnabled: {
type: Boolean,
@@ -84,10 +80,10 @@ export default {
},
methods: {
onCheckboxToggle(isChecked) {
- eventHub.$emit('serviceDeskEnabledCheckboxToggled', isChecked);
+ this.$emit('toggle', isChecked);
},
onSaveTemplate() {
- eventHub.$emit('serviceDeskTemplateSave', {
+ this.$emit('save', {
selectedTemplate: this.selectedTemplate,
outgoingName: this.outgoingName,
projectKey: this.projectKey,
@@ -111,7 +107,11 @@ export default {
</label>
<div v-if="isEnabled" class="row mt-3">
<div class="col-md-9 mb-0">
- <strong id="incoming-email-describer" class="d-block mb-1">
+ <strong
+ id="incoming-email-describer"
+ class="gl-display-block gl-mb-1"
+ data-testid="incoming-email-describer"
+ >
{{ __('Email address to use for Support Desk') }}
</strong>
<template v-if="email">
@@ -128,11 +128,7 @@ export default {
disabled="true"
/>
<div class="input-group-append">
- <clipboard-button
- :title="__('Copy')"
- :text="email"
- css-class="input-group-text qa-clipboard-button"
- />
+ <clipboard-button :title="__('Copy')" :text="email" css-class="input-group-text" />
</div>
</div>
<span v-if="hasCustomEmail" class="form-text text-muted">
diff --git a/app/assets/javascripts/projects/settings_service_desk/event_hub.js b/app/assets/javascripts/projects/settings_service_desk/event_hub.js
deleted file mode 100644
index e31806ad199..00000000000
--- a/app/assets/javascripts/projects/settings_service_desk/event_hub.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import createEventHub from '~/helpers/event_hub_factory';
-
-export default createEventHub();
diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js
index 8f9828dd73d..f842ffaaa2b 100644
--- a/app/assets/javascripts/projects/settings_service_desk/index.js
+++ b/app/assets/javascripts/projects/settings_service_desk/index.js
@@ -3,43 +3,37 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import ServiceDeskRoot from './components/service_desk_root.vue';
export default () => {
- const serviceDeskRootElement = document.querySelector('.js-service-desk-setting-root');
- if (serviceDeskRootElement) {
- // eslint-disable-next-line no-new
- new Vue({
- el: serviceDeskRootElement,
- components: {
- ServiceDeskRoot,
- },
- data() {
- const { dataset } = serviceDeskRootElement;
- return {
- initialIsEnabled: parseBoolean(dataset.enabled),
- endpoint: dataset.endpoint,
- incomingEmail: dataset.incomingEmail,
- customEmail: dataset.customEmail,
- customEmailEnabled: parseBoolean(dataset.customEmailEnabled),
- selectedTemplate: dataset.selectedTemplate,
- outgoingName: dataset.outgoingName,
- projectKey: dataset.projectKey,
- templates: JSON.parse(dataset.templates),
- };
- },
- render(createElement) {
- return createElement('service-desk-root', {
- props: {
- initialIsEnabled: this.initialIsEnabled,
- endpoint: this.endpoint,
- incomingEmail: this.incomingEmail,
- customEmail: this.customEmail,
- customEmailEnabled: this.customEmailEnabled,
- selectedTemplate: this.selectedTemplate,
- outgoingName: this.outgoingName,
- projectKey: this.projectKey,
- templates: this.templates,
- },
- });
- },
- });
+ const el = document.querySelector('.js-service-desk-setting-root');
+
+ if (!el) {
+ return false;
}
+
+ const {
+ customEmail,
+ customEmailEnabled,
+ enabled,
+ endpoint,
+ incomingEmail,
+ outgoingName,
+ projectKey,
+ selectedTemplate,
+ templates,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ provide: {
+ customEmail,
+ customEmailEnabled: parseBoolean(customEmailEnabled),
+ endpoint,
+ initialIncomingEmail: incomingEmail,
+ initialIsEnabled: parseBoolean(enabled),
+ outgoingName,
+ projectKey,
+ selectedTemplate,
+ templates: JSON.parse(templates),
+ },
+ render: (createElement) => createElement(ServiceDeskRoot),
+ });
};
diff --git a/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js b/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js
deleted file mode 100644
index b68c5bb876f..00000000000
--- a/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import axios from '~/lib/utils/axios_utils';
-
-class ServiceDeskService {
- constructor(endpoint) {
- this.endpoint = endpoint;
- }
-
- toggleServiceDesk(enable) {
- return axios.put(this.endpoint, { service_desk_enabled: enable });
- }
-
- updateTemplate({ selectedTemplate, outgoingName, projectKey = '' }, isEnabled) {
- const body = {
- issue_template_key: selectedTemplate,
- outgoing_name: outgoingName,
- project_key: projectKey,
- service_desk_enabled: isEnabled,
- };
- return axios.put(this.endpoint, body);
- }
-}
-
-export default ServiceDeskService;
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
index 07ee3c6083b..823fa4ab413 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
@@ -1,4 +1,6 @@
<script>
+// Work around for https://github.com/vuejs/eslint-plugin-vue/issues/1411
+/* eslint-disable vue/one-component-per-file */
import { GlDropdown } from '@gitlab/ui';
import Tracking from '~/tracking';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 6d21936791c..a445a85a2f3 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -1,4 +1,6 @@
<script>
+// Work around for https://github.com/vuejs/eslint-plugin-vue/issues/1411
+/* eslint-disable vue/one-component-per-file */
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import Tracking from '~/tracking';
diff --git a/app/assets/javascripts/vue_shared/components/registry/list_item.vue b/app/assets/javascripts/vue_shared/components/registry/list_item.vue
index 8965dba3e83..b4930db7e98 100644
--- a/app/assets/javascripts/vue_shared/components/registry/list_item.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/list_item.vue
@@ -54,7 +54,7 @@ export default {
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1"
:class="optionalClasses"
>
- <div class="gl-display-flex gl-align-items-center gl-py-5">
+ <div class="gl-display-flex gl-align-items-center gl-py-3">
<div
v-if="$slots['left-action']"
class="gl-w-7 gl-display-none gl-display-sm-flex gl-justify-content-start gl-pl-2"
@@ -64,9 +64,7 @@ export default {
<div
class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-fill-1"
>
- <div
- class="gl-display-flex gl-flex-direction-column gl-justify-content-space-between gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1"
- >
+ <div class="gl-display-flex gl-flex-direction-column gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1">
<div
v-if="$slots['left-primary']"
class="gl-display-flex gl-align-items-center gl-text-body gl-font-weight-bold gl-min-h-6 gl-min-w-0"
@@ -83,7 +81,7 @@ export default {
</div>
<div
v-if="$slots['left-secondary']"
- class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1 gl-min-h-6 gl-min-w-0 gl-flex-fill-1"
+ class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-fill-1"
>
<slot name="left-secondary"></slot>
</div>
@@ -99,7 +97,7 @@ export default {
</div>
<div
v-if="$slots['right-secondary']"
- class="gl-display-flex gl-align-items-center gl-mt-1 gl-min-h-6"
+ class="gl-display-flex gl-align-items-center gl-min-h-6"
>
<slot name="right-secondary"></slot>
</div>
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 81a70470c65..019d827798c 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -42,8 +42,7 @@
.login-box,
.omniauth-container {
box-shadow: 0 0 0 1px $border-color;
- border-bottom-right-radius: $border-radius;
- border-bottom-left-radius: $border-radius;
+ border-radius: $border-radius;
padding: 15px;
.login-heading h3 {
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index af43c532b7c..608740078b6 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -2093,8 +2093,7 @@ table.code {
.login-page .login-box,
.login-page .omniauth-container {
box-shadow: 0 0 0 1px #dbdbdb;
- border-bottom-right-radius: 0.25rem;
- border-bottom-left-radius: 0.25rem;
+ border-radius: 0.25rem;
padding: 15px;
}
.login-page .login-box .nav .active a,
diff --git a/app/finders/repositories/commits_with_trailer_finder.rb b/app/finders/repositories/commits_with_trailer_finder.rb
new file mode 100644
index 00000000000..4bd643c345b
--- /dev/null
+++ b/app/finders/repositories/commits_with_trailer_finder.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+module Repositories
+ # Finder for obtaining commits between two refs, with a Git trailer set.
+ class CommitsWithTrailerFinder
+ # The maximum number of commits to retrieve per page.
+ #
+ # This value is arbitrarily chosen. Lowering it means more Gitaly calls, but
+ # less data being loaded into memory at once. Increasing it has the opposite
+ # effect.
+ #
+ # This amount is based around the number of commits that usually go in a
+ # GitLab release. Some examples for GitLab's own releases:
+ #
+ # * 13.6.0: 4636 commits
+ # * 13.5.0: 5912 commits
+ # * 13.4.0: 5541 commits
+ #
+ # Using this limit should result in most (very large) projects only needing
+ # 5-10 Gitaly calls, while keeping memory usage at a reasonable amount.
+ COMMITS_PER_PAGE = 1024
+
+ # The `project` argument specifies the project for which to obtain the
+ # commits.
+ #
+ # The `from` and `to` arguments specify the range of commits to include. The
+ # commit specified in `from` won't be included itself. The commit specified
+ # in `to` _is_ included.
+ #
+ # The `per_page` argument specifies how many commits are retrieved in a single
+ # Gitaly API call.
+ def initialize(project:, from:, to:, per_page: COMMITS_PER_PAGE)
+ @project = project
+ @from = from
+ @to = to
+ @per_page = per_page
+ end
+
+ # Fetches all commits that have the given trailer set.
+ #
+ # The commits are yielded to the supplied block in batches. This allows
+ # other code to process these commits in batches too, instead of first
+ # having to load all commits into memory.
+ #
+ # Example:
+ #
+ # CommitsWithTrailerFinder.new(...).each_page('Signed-off-by') do |commits|
+ # commits.each do |commit|
+ # ...
+ # end
+ # end
+ def each_page(trailer)
+ return to_enum(__method__, trailer) unless block_given?
+
+ offset = 0
+ response = fetch_commits
+
+ while response.any?
+ commits = []
+
+ response.each do |commit|
+ commits.push(commit) if commit.trailers.key?(trailer)
+ end
+
+ yield commits
+
+ offset += response.length
+ response = fetch_commits(offset)
+ end
+ end
+
+ private
+
+ def fetch_commits(offset = 0)
+ range = "#{@from}..#{@to}"
+
+ @project
+ .repository
+ .commits(range, limit: @per_page, offset: offset, trailers: true)
+ end
+ end
+end
diff --git a/app/graphql/types/notes/discussion_type.rb b/app/graphql/types/notes/discussion_type.rb
index a51d253097d..9b863990849 100644
--- a/app/graphql/types/notes/discussion_type.rb
+++ b/app/graphql/types/notes/discussion_type.rb
@@ -3,24 +3,26 @@
module Types
module Notes
class DiscussionType < BaseObject
+ DiscussionID = ::Types::GlobalIDType[::Discussion]
+
graphql_name 'Discussion'
authorize :read_note
implements(Types::ResolvableInterface)
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id, DiscussionID, null: false,
description: "ID of this discussion"
- field :reply_id, GraphQL::ID_TYPE, null: false,
+ field :reply_id, DiscussionID, null: false,
description: 'ID used to reply to this discussion'
field :created_at, Types::TimeType, null: false,
description: "Timestamp of the discussion's creation"
field :notes, Types::Notes::NoteType.connection_type, null: false,
description: 'All notes in the discussion'
- # The gem we use to generate Global IDs is hard-coded to work with
- # `id` properties. To generate a GID for the `reply_id` property,
- # we must use the ::Gitlab::GlobalId module.
+ # DiscussionID.coerce_result is suitable here, but will always mark this
+ # as being a 'Discussion'. Using `GlobalId.build` guarantees that we get
+ # the correct class, and that it matches `id`.
def reply_id
::Gitlab::GlobalId.build(object, id: object.reply_id)
end
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 84b61340e93..dea55fe7f9e 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -11,7 +11,7 @@ module Types
implements(Types::ResolvableInterface)
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id, ::Types::GlobalIDType[::Note], null: false,
description: 'ID of the note'
field :project, Types::ProjectType,
diff --git a/app/models/merge_request_context_commit.rb b/app/models/merge_request_context_commit.rb
index 59cc82cfaf5..e081a96dc10 100644
--- a/app/models/merge_request_context_commit.rb
+++ b/app/models/merge_request_context_commit.rb
@@ -12,6 +12,9 @@ class MergeRequestContextCommit < ApplicationRecord
validates :sha, presence: true
validates :sha, uniqueness: { message: 'has already been added' }
+ serialize :trailers, Serializers::JSON # rubocop:disable Cop/ActiveRecordSerialize
+ validates :trailers, json_schema: { filename: 'git_trailers' }
+
# Sort by committed date in descending order to ensure latest commits comes on the top
scope :order_by_committed_date_desc, -> { order('committed_date DESC') }
diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb
index 9f6933d0879..cf2d07b8561 100644
--- a/app/models/merge_request_diff_commit.rb
+++ b/app/models/merge_request_diff_commit.rb
@@ -10,6 +10,9 @@ class MergeRequestDiffCommit < ApplicationRecord
sha_attribute :sha
alias_attribute :id, :sha
+ serialize :trailers, Serializers::JSON # rubocop:disable Cop/ActiveRecordSerialize
+ validates :trailers, json_schema: { filename: 'git_trailers' }
+
# Deprecated; use `bulk_insert!` from `BulkInsertSafe` mixin instead.
# cf. https://gitlab.com/gitlab-org/gitlab/issues/207989 for progress
def self.create_bulk(merge_request_diff_id, commits)
@@ -23,7 +26,8 @@ class MergeRequestDiffCommit < ApplicationRecord
relative_order: index,
sha: Gitlab::Database::ShaAttribute.serialize(sha), # rubocop:disable Cop/ActiveRecordSerialize
authored_date: Gitlab::Database.sanitize_timestamp(commit_hash[:authored_date]),
- committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date])
+ committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date]),
+ trailers: commit_hash.fetch(:trailers, {}).to_json
)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index ec790798806..bfa85fc9d99 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1314,21 +1314,11 @@ class Project < ApplicationRecord
end
def external_issue_tracker
- if has_external_issue_tracker.nil?
- cache_has_external_issue_tracker
- end
+ cache_has_external_issue_tracker if has_external_issue_tracker.nil?
- if has_external_issue_tracker?
- strong_memoize(:external_issue_tracker) do
- services.external_issue_trackers.first
- end
- else
- nil
- end
- end
+ return unless has_external_issue_tracker?
- def cache_has_external_issue_tracker
- update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) if Gitlab::Database.read_write?
+ @external_issue_tracker ||= services.external_issue_trackers.first
end
def external_references_supported?
@@ -2690,6 +2680,10 @@ class Project < ApplicationRecord
def cache_has_external_wiki
update_column(:has_external_wiki, services.external_wikis.any?) if Gitlab::Database.read_write?
end
+
+ def cache_has_external_issue_tracker
+ update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) if Gitlab::Database.read_write?
+ end
end
Project.prepend_if_ee('EE::Project')
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c19448332f8..6e57e5c6620 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -151,7 +151,8 @@ class Repository
all: !!opts[:all],
first_parent: !!opts[:first_parent],
order: opts[:order],
- literal_pathspec: opts.fetch(:literal_pathspec, true)
+ literal_pathspec: opts.fetch(:literal_pathspec, true),
+ trailers: opts[:trailers]
}
commits = Gitlab::Git::Commit.where(options)
diff --git a/app/models/service.rb b/app/models/service.rb
index e5626462dd3..2b92b2cd7d8 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -46,7 +46,6 @@ class Service < ApplicationRecord
after_initialize :initialize_properties
after_commit :reset_updated_properties
- after_commit :cache_project_has_external_issue_tracker
belongs_to :project, inverse_of: :services
belongs_to :group, inverse_of: :services
@@ -438,10 +437,6 @@ class Service < ApplicationRecord
ProjectServiceWorker.perform_async(id, data)
end
- def external_issue_tracker?
- category == :issue_tracker && active?
- end
-
def external_wiki?
type == 'ExternalWikiService' && active?
end
@@ -461,12 +456,6 @@ class Service < ApplicationRecord
errors.add(:project_id, 'The service cannot belong to both a project and a group') if project_id && group_id
end
- def cache_project_has_external_issue_tracker
- if project && !project.destroyed?
- project.cache_has_external_issue_tracker
- end
- end
-
def valid_recipients?
activated? && !importing?
end
diff --git a/app/services/bulk_create_integration_service.rb b/app/services/bulk_create_integration_service.rb
index df78c3645c7..ae756d0856e 100644
--- a/app/services/bulk_create_integration_service.rb
+++ b/app/services/bulk_create_integration_service.rb
@@ -11,8 +11,6 @@ class BulkCreateIntegrationService
service_list = ServiceList.new(batch, service_hash, association).to_array
Service.transaction do
- run_callbacks(batch) if association == 'project'
-
results = bulk_insert(*service_list)
if integration.data_fields_present?
@@ -33,14 +31,6 @@ class BulkCreateIntegrationService
klass.insert_all(items_to_insert, returning: [:id])
end
- # rubocop: disable CodeReuse/ActiveRecord
- def run_callbacks(batch)
- if integration.external_issue_tracker?
- Project.where(id: batch.select(:id)).update_all(has_external_issue_tracker: true)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
def service_hash
if integration.template?
integration.to_service_hash
diff --git a/app/services/feature_flags/base_service.rb b/app/services/feature_flags/base_service.rb
index c11c465252e..f48f95e2550 100644
--- a/app/services/feature_flags/base_service.rb
+++ b/app/services/feature_flags/base_service.rb
@@ -41,7 +41,6 @@ module FeatureFlags
def sync_to_jira(feature_flag)
return unless feature_flag.present?
- return unless Feature.enabled?(:jira_sync_feature_flags, feature_flag.project)
seq_id = ::Atlassian::JiraConnect::Client.generate_update_sequence_id
feature_flag.run_after_commit do
diff --git a/app/services/merge_requests/add_context_service.rb b/app/services/merge_requests/add_context_service.rb
index bb82fa23468..b693f8509a2 100644
--- a/app/services/merge_requests/add_context_service.rb
+++ b/app/services/merge_requests/add_context_service.rb
@@ -66,7 +66,8 @@ module MergeRequests
relative_order: index,
sha: sha,
authored_date: Gitlab::Database.sanitize_timestamp(commit_hash[:authored_date]),
- committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date])
+ committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date]),
+ trailers: commit_hash.fetch(:trailers, {}).to_json
)
end
end
diff --git a/app/validators/json_schemas/git_trailers.json b/app/validators/json_schemas/git_trailers.json
new file mode 100644
index 00000000000..18ac97226a7
--- /dev/null
+++ b/app/validators/json_schemas/git_trailers.json
@@ -0,0 +1,9 @@
+{
+ "description": "Git trailer key/value pairs",
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ }
+}
diff --git a/app/views/notify/provisioned_member_access_granted_email.haml b/app/views/notify/provisioned_member_access_granted_email.html.haml
index 2f2fd33145a..515254d1454 100644
--- a/app/views/notify/provisioned_member_access_granted_email.haml
+++ b/app/views/notify/provisioned_member_access_granted_email.html.haml
@@ -16,7 +16,8 @@
%td.text-content
%p
= _('By authenticating with an account tied to an Enterprise e-mail address, it is understood that this account is an Enterprise User. ')
- = _('To ensure no loss of personal content, an Individual User should create a separate account under their own personal email address, not tied to the Enterprise email domain or name-space.')
+ = _('To ensure no loss of personal content, this account should only be used for matters related to %{group_name}.') % { group_name: member_source.human_name }
+ = _('For individual use, create a separate account under your personal email address, not tied to the Enterprise email domain or group.')
- unless @user.confirmed?
%p
= _('To get started, click the link below to confirm your account.')
diff --git a/app/views/notify/provisioned_member_access_granted_email.erb b/app/views/notify/provisioned_member_access_granted_email.text.erb
index 485ee5a5242..b143b8d6f54 100644
--- a/app/views/notify/provisioned_member_access_granted_email.erb
+++ b/app/views/notify/provisioned_member_access_granted_email.text.erb
@@ -7,7 +7,8 @@
<%= _('By authenticating with an account tied to an Enterprise e-mail address, it is understood that this account is an Enterprise User. ') %>
-<%= _('To ensure no loss of personal content, an Individual User should create a separate account under their own personal email address, not tied to the Enterprise email domain or name-space.') %>
+<%= _('To ensure no loss of personal content, this account should only be used for matters related to %{group_name}.') % { group_name: member_source.human_name } %>
+<%= _('For individual use, create a separate account under your personal email address, not tied to the Enterprise email domain or group.') %>
<%- unless @user.confirmed? %>
<%= _('To get started, click the link below to confirm your account.') %>
<%= confirmation_url(@user, confirmation_token: @user.confirmation_token) %>
diff --git a/app/workers/jira_connect/sync_feature_flags_worker.rb b/app/workers/jira_connect/sync_feature_flags_worker.rb
index 7e98d0eada7..496b9f1626d 100644
--- a/app/workers/jira_connect/sync_feature_flags_worker.rb
+++ b/app/workers/jira_connect/sync_feature_flags_worker.rb
@@ -14,7 +14,6 @@ module JiraConnect
feature_flag = ::Operations::FeatureFlag.find_by_id(feature_flag_id)
return unless feature_flag
- return unless Feature.enabled?(:jira_sync_feature_flags, feature_flag.project)
::JiraConnect::SyncService
.new(feature_flag.project)
diff --git a/changelogs/unreleased/290715-has_external_issue_tracker_trigger.yml b/changelogs/unreleased/290715-has_external_issue_tracker_trigger.yml
new file mode 100644
index 00000000000..d9d40c99c5c
--- /dev/null
+++ b/changelogs/unreleased/290715-has_external_issue_tracker_trigger.yml
@@ -0,0 +1,5 @@
+---
+title: Add PostgreSQL trigger to maintain projects.has_external_issue_tracker
+merge_request: 51852
+author:
+type: changed
diff --git a/changelogs/unreleased/294007-bump-workhorse.yml b/changelogs/unreleased/294007-bump-workhorse.yml
new file mode 100644
index 00000000000..9c9cc468715
--- /dev/null
+++ b/changelogs/unreleased/294007-bump-workhorse.yml
@@ -0,0 +1,5 @@
+---
+title: Update Workhorse to v8.60.0
+merge_request: 51965
+author:
+type: changed
diff --git a/changelogs/unreleased/296948-container-and-package-list-ui-polish-condense-spacing-to-match-iss.yml b/changelogs/unreleased/296948-container-and-package-list-ui-polish-condense-spacing-to-match-iss.yml
new file mode 100644
index 00000000000..74d5504951b
--- /dev/null
+++ b/changelogs/unreleased/296948-container-and-package-list-ui-polish-condense-spacing-to-match-iss.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce vertical padding of registry list item
+merge_request: 51961
+author:
+type: changed
diff --git a/changelogs/unreleased/296990-enable-jira-ff-sync.yml b/changelogs/unreleased/296990-enable-jira-ff-sync.yml
new file mode 100644
index 00000000000..ac9b71bc37d
--- /dev/null
+++ b/changelogs/unreleased/296990-enable-jira-ff-sync.yml
@@ -0,0 +1,5 @@
+---
+title: Enable synchronization of feature flags to Jira
+merge_request: 51796
+author:
+type: added
diff --git a/changelogs/unreleased/gitaly-trailer-parsing.yml b/changelogs/unreleased/gitaly-trailer-parsing.yml
new file mode 100644
index 00000000000..096ef4a94a7
--- /dev/null
+++ b/changelogs/unreleased/gitaly-trailer-parsing.yml
@@ -0,0 +1,5 @@
+---
+title: Add finder for getting commits with a trailer set
+merge_request: 49243
+author:
+type: added
diff --git a/changelogs/unreleased/yo-fix-border-radius.yml b/changelogs/unreleased/yo-fix-border-radius.yml
new file mode 100644
index 00000000000..f63d79b4d49
--- /dev/null
+++ b/changelogs/unreleased/yo-fix-border-radius.yml
@@ -0,0 +1,5 @@
+---
+title: Fix top border-radius of the login box
+merge_request: 51950
+author: Yogi (@yo)
+type: fixed
diff --git a/config/feature_flags/development/jira_sync_feature_flags.yml b/config/feature_flags/development/jira_sync_feature_flags.yml
deleted file mode 100644
index c5925766919..00000000000
--- a/config/feature_flags/development/jira_sync_feature_flags.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: jira_sync_feature_flags
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50390
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/296990
-milestone: '13.8'
-type: development
-group: group::ecosystem
-default_enabled: false
diff --git a/db/migrate/20210106155209_add_merge_request_diff_commit_trailers.rb b/db/migrate/20210106155209_add_merge_request_diff_commit_trailers.rb
new file mode 100644
index 00000000000..906efa58bcd
--- /dev/null
+++ b/db/migrate/20210106155209_add_merge_request_diff_commit_trailers.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# See https://docs.gitlab.com/ee/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddMergeRequestDiffCommitTrailers < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :merge_request_diff_commits, :trailers, :jsonb, default: {}, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :merge_request_diff_commits, :trailers
+ end
+ end
+end
diff --git a/db/migrate/20210107154615_add_merge_request_context_commit_trailers.rb b/db/migrate/20210107154615_add_merge_request_context_commit_trailers.rb
new file mode 100644
index 00000000000..e7bd7c2ea56
--- /dev/null
+++ b/db/migrate/20210107154615_add_merge_request_context_commit_trailers.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# See https://docs.gitlab.com/ee/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddMergeRequestContextCommitTrailers < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :merge_request_context_commits, :trailers, :jsonb, default: {}, null: false
+ end
+end
diff --git a/db/migrate/20210117210226_add_has_external_issue_tracker_trigger.rb b/db/migrate/20210117210226_add_has_external_issue_tracker_trigger.rb
new file mode 100644
index 00000000000..20fe0ee0fd1
--- /dev/null
+++ b/db/migrate/20210117210226_add_has_external_issue_tracker_trigger.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+class AddHasExternalIssueTrackerTrigger < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::SchemaHelpers
+
+ DOWNTIME = false
+ FUNCTION_NAME = 'set_has_external_issue_tracker'
+ TRIGGER_ON_INSERT_NAME = 'trigger_has_external_issue_tracker_on_insert'
+ TRIGGER_ON_UPDATE_NAME = 'trigger_has_external_issue_tracker_on_update'
+ TRIGGER_ON_DELETE_NAME = 'trigger_has_external_issue_tracker_on_delete'
+
+ def up
+ create_trigger_function(FUNCTION_NAME, replace: true) do
+ <<~SQL
+ UPDATE projects SET has_external_issue_tracker = (
+ EXISTS
+ (
+ SELECT 1
+ FROM services
+ WHERE project_id = COALESCE(NEW.project_id, OLD.project_id)
+ AND active = TRUE
+ AND category = 'issue_tracker'
+ )
+ )
+ WHERE projects.id = COALESCE(NEW.project_id, OLD.project_id);
+ RETURN NULL;
+ SQL
+ end
+
+ execute(<<~SQL)
+ CREATE TRIGGER #{TRIGGER_ON_INSERT_NAME}
+ AFTER INSERT ON services
+ FOR EACH ROW
+ WHEN (NEW.category = 'issue_tracker' AND NEW.active = TRUE AND NEW.project_id IS NOT NULL)
+ EXECUTE FUNCTION #{FUNCTION_NAME}();
+ SQL
+
+ execute(<<~SQL)
+ CREATE TRIGGER #{TRIGGER_ON_UPDATE_NAME}
+ AFTER UPDATE ON services
+ FOR EACH ROW
+ WHEN (NEW.category = 'issue_tracker' AND OLD.active != NEW.active AND NEW.project_id IS NOT NULL)
+ EXECUTE FUNCTION #{FUNCTION_NAME}();
+ SQL
+
+ execute(<<~SQL)
+ CREATE TRIGGER #{TRIGGER_ON_DELETE_NAME}
+ AFTER DELETE ON services
+ FOR EACH ROW
+ WHEN (OLD.category = 'issue_tracker' AND OLD.active = TRUE AND OLD.project_id IS NOT NULL)
+ EXECUTE FUNCTION #{FUNCTION_NAME}();
+ SQL
+ end
+
+ def down
+ drop_trigger(:services, TRIGGER_ON_INSERT_NAME)
+ drop_trigger(:services, TRIGGER_ON_UPDATE_NAME)
+ drop_trigger(:services, TRIGGER_ON_DELETE_NAME)
+ drop_function(FUNCTION_NAME)
+ end
+end
diff --git a/db/schema_migrations/20210106155209 b/db/schema_migrations/20210106155209
new file mode 100644
index 00000000000..10dde1cf874
--- /dev/null
+++ b/db/schema_migrations/20210106155209
@@ -0,0 +1 @@
+4aeff45663a9f5a41a8dd92298afb4b0b57aa6f190f4648455df2fa1e39e174f \ No newline at end of file
diff --git a/db/schema_migrations/20210107154615 b/db/schema_migrations/20210107154615
new file mode 100644
index 00000000000..f8b6f97d3fe
--- /dev/null
+++ b/db/schema_migrations/20210107154615
@@ -0,0 +1 @@
+3e867ceefcab4f043b89d3c04e6e0a1182423039e1a9245e611128efe77c0e88 \ No newline at end of file
diff --git a/db/schema_migrations/20210117210226 b/db/schema_migrations/20210117210226
new file mode 100644
index 00000000000..a68f7f6d93e
--- /dev/null
+++ b/db/schema_migrations/20210117210226
@@ -0,0 +1 @@
+c0d22d00d52a516347930e1a36f350113c0949214925176f08ceed81999746bd \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 38965f00aa6..261e9a1e73b 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10,6 +10,26 @@ CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
+CREATE FUNCTION set_has_external_issue_tracker() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+UPDATE projects SET has_external_issue_tracker = (
+ EXISTS
+ (
+ SELECT 1
+ FROM services
+ WHERE project_id = COALESCE(NEW.project_id, OLD.project_id)
+ AND active = TRUE
+ AND category = 'issue_tracker'
+ )
+ )
+WHERE projects.id = COALESCE(NEW.project_id, OLD.project_id);
+RETURN NULL;
+
+END
+$$;
+
CREATE FUNCTION set_has_external_wiki() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -13863,7 +13883,8 @@ CREATE TABLE merge_request_context_commits (
committer_name text,
committer_email text,
message text,
- merge_request_id bigint
+ merge_request_id bigint,
+ trailers jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE merge_request_context_commits_id_seq
@@ -13885,7 +13906,8 @@ CREATE TABLE merge_request_diff_commits (
author_email text,
committer_name text,
committer_email text,
- message text
+ message text,
+ trailers jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE TABLE merge_request_diff_details (
@@ -23637,6 +23659,12 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p
CREATE TRIGGER table_sync_trigger_ee39a25f9d AFTER INSERT OR DELETE OR UPDATE ON audit_events FOR EACH ROW EXECUTE PROCEDURE table_sync_function_2be879775d();
+CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON services FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_issue_tracker();
+
+CREATE TRIGGER trigger_has_external_issue_tracker_on_insert AFTER INSERT ON services FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (new.active = true) AND (new.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_issue_tracker();
+
+CREATE TRIGGER trigger_has_external_issue_tracker_on_update AFTER UPDATE ON services FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (old.active <> new.active) AND (new.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_issue_tracker();
+
CREATE TRIGGER trigger_has_external_wiki_on_delete AFTER DELETE ON services FOR EACH ROW WHEN ((((old.type)::text = 'ExternalWikiService'::text) AND (old.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_wiki();
CREATE TRIGGER trigger_has_external_wiki_on_insert AFTER INSERT ON services FOR EACH ROW WHEN (((new.active = true) AND ((new.type)::text = 'ExternalWikiService'::text) AND (new.project_id IS NOT NULL))) EXECUTE PROCEDURE set_has_external_wiki();
diff --git a/doc/.vale/gitlab/Admin.yml b/doc/.vale/gitlab/Admin.yml
index 96325ad2ef4..2300fa38e77 100644
--- a/doc/.vale/gitlab/Admin.yml
+++ b/doc/.vale/gitlab/Admin.yml
@@ -7,6 +7,7 @@
extends: substitution
message: 'Use "administration", "administrator", "administer", or "Admin Area" instead of "admin" or "admin area".'
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html
+# Do not set `level: error`, as our docs refer to other docs which use "admin" and "Admin"
level: warning
ignorecase: true
swap:
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index cf20d98ba83..64dd074a912 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -7878,7 +7878,7 @@ type Discussion implements ResolvableInterface {
"""
ID of this discussion
"""
- id: ID!
+ id: DiscussionID!
"""
All notes in the discussion
@@ -7908,7 +7908,7 @@ type Discussion implements ResolvableInterface {
"""
ID used to reply to this discussion
"""
- replyId: ID!
+ replyId: DiscussionID!
"""
Indicates if the object can be resolved
@@ -16154,7 +16154,7 @@ type Note implements ResolvableInterface {
"""
ID of the note
"""
- id: ID!
+ id: NoteID!
"""
The position of this note on a diff
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index f129382bd1e..790c641a6c0 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -21766,7 +21766,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
- "name": "ID",
+ "name": "DiscussionID",
"ofType": null
}
},
@@ -21841,7 +21841,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
- "name": "ID",
+ "name": "DiscussionID",
"ofType": null
}
},
@@ -47730,7 +47730,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
- "name": "ID",
+ "name": "NoteID",
"ofType": null
}
},
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 3a74bbeefda..31e9e46b6be 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1301,9 +1301,9 @@ Aggregated summary of changes.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `createdAt` | Time! | Timestamp of the discussion's creation |
-| `id` | ID! | ID of this discussion |
+| `id` | DiscussionID! | ID of this discussion |
| `notes` | NoteConnection! | All notes in the discussion |
-| `replyId` | ID! | ID used to reply to this discussion |
+| `replyId` | DiscussionID! | ID used to reply to this discussion |
| `resolvable` | Boolean! | Indicates if the object can be resolved |
| `resolved` | Boolean! | Indicates if the object is resolved |
| `resolvedAt` | Time | Timestamp of when the object was resolved |
@@ -2429,7 +2429,7 @@ Autogenerated return type of NamespaceIncreaseStorageTemporarily.
| `confidential` | Boolean | Indicates if this note is confidential |
| `createdAt` | Time! | Timestamp of the note creation |
| `discussion` | Discussion | The discussion this note is a part of |
-| `id` | ID! | ID of the note |
+| `id` | NoteID! | ID of the note |
| `position` | DiffPosition | The position of this note on a diff |
| `project` | Project | Project associated with the note |
| `resolvable` | Boolean! | Indicates if the object can be resolved |
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 306a16bd873..cafcc15d879 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -20,7 +20,13 @@ Features include:
- **Mention that a commit or MR resolves or closes a specific Jira issue** and when it's merged to the default branch:
- The GitLab MR displays a note that it closed the Jira issue. Prior to the merge, MRs indicate which issue they close.
- The Jira issue shows the activity and is closed or otherwise transitioned as specified in your GitLab settings.
-- **View a list of Jira issues directly in GitLab** **(PREMIUM)**
+- **Run a pipeline** on an MR linked to a Jira issue:
+ - The Jira issue shows the current pipeline status (in the sidebar as "builds").
+- **Deploy to an environment** from an MR linked to a Jira issue:
+ - The Jira issue shows the status of the deployment (in the sidebar as "deployments").
+- **Create or modify a feature flag** that mentions a Jira issue in its description:
+ - The Jira issue shows the details of the feature-flag (in the sidebar as "feature flags").
+- **View a list of Jira issues** directly in GitLab **(PREMIUM)**
For additional features, you can install the
[Jira Development Panel integration](../../../integration/jira_development_panel.md).
@@ -29,6 +35,9 @@ This enables you to:
- In a Jira issue, display relevant GitLab information in the [development panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/), including related branches, commits, and merge requests.
- Use Jira [Smart Commits](https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html) in GitLab to add Jira comments, log time spent on the issue, or apply any issue transition.
+Some features (such as showing pipeline, deployment, and feature flags in Jira
+issues) are only available when using the [Jira Development Panel integration](../../../integration/jira_development_panel.md).
+
See the [feature comparison](jira_integrations.md#feature-comparison) for more details.
## Configuration
diff --git a/doc/user/project/merge_requests/getting_started.md b/doc/user/project/merge_requests/getting_started.md
index dc5e1f81a63..271f8b42f36 100644
--- a/doc/user/project/merge_requests/getting_started.md
+++ b/doc/user/project/merge_requests/getting_started.md
@@ -176,8 +176,7 @@ This feature might not be available to you. Check the **version history** note a
When editing the **Reviewers** field in a new or existing merge request, GitLab
displays the name of the matching [approval rule](merge_request_approvals.md#approval-rules)
-below the name of each suggested reviewer. [Code Owners](../code_owners.md) are displayed as **Code Owner** without group detail.
-We intend to iterate on this feature in future releases.
+below the name of each suggested reviewer. [Code Owners](../code_owners.md) are displayed as `Codeowner` without group detail.
This example shows reviewers and approval rules when creating a new merge request:
diff --git a/lib/atlassian/jira_connect/client.rb b/lib/atlassian/jira_connect/client.rb
index 8b31c82510c..241f6e6cbc5 100644
--- a/lib/atlassian/jira_connect/client.rb
+++ b/lib/atlassian/jira_connect/client.rb
@@ -33,8 +33,6 @@ module Atlassian
private
def store_ff_info(project:, feature_flags:, **opts)
- return unless Feature.enabled?(:jira_sync_feature_flags, project)
-
items = feature_flags.map { |flag| ::Atlassian::JiraConnect::Serializers::FeatureFlagEntity.represent(flag, opts) }
items.reject! { |item| item.issue_keys.empty? }
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 0bc7ecccf5e..35c3dc5b0b3 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -16,7 +16,7 @@ module Gitlab
SERIALIZE_KEYS = [
:id, :message, :parent_ids,
:authored_date, :author_name, :author_email,
- :committed_date, :committer_name, :committer_email
+ :committed_date, :committer_name, :committer_email, :trailers
].freeze
attr_accessor(*SERIALIZE_KEYS)
@@ -389,6 +389,7 @@ module Gitlab
@committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup
@parent_ids = Array(commit.parent_ids)
+ @trailers = Hash[commit.trailers.map { |t| [t.key, t.value] }]
end
# Gitaly provides a UNIX timestamp in author.date.seconds, and a timezone
diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb
index 0eff35ab1c4..0607b151de2 100644
--- a/lib/gitlab/git/rugged_impl/commit.rb
+++ b/lib/gitlab/git/rugged_impl/commit.rb
@@ -103,6 +103,7 @@ module Gitlab
@committer_name = committer[:name]
@committer_email = committer[:email]
@parent_ids = commit.parents.map(&:oid)
+ @trailers = Hash[commit.trailers]
end
end
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index ea940150941..ef5221a8042 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -335,7 +335,8 @@ module Gitlab
all: !!options[:all],
first_parent: !!options[:first_parent],
global_options: parse_global_options!(options),
- disable_walk: true # This option is deprecated. The 'walk' implementation is being removed.
+ disable_walk: true, # This option is deprecated. The 'walk' implementation is being removed.
+ trailers: options[:trailers]
)
request.after = GitalyClient.timestamp(options[:after]) if options[:after]
request.before = GitalyClient.timestamp(options[:before]) if options[:before]
diff --git a/lib/gitlab/global_id.rb b/lib/gitlab/global_id.rb
index e8a6006dce1..7e9412236cf 100644
--- a/lib/gitlab/global_id.rb
+++ b/lib/gitlab/global_id.rb
@@ -19,8 +19,8 @@ module Gitlab
value
when URI::GID
GlobalID.new(value)
- when Integer
- raise CoerceError, 'Cannot coerce Integer' unless model_name.present?
+ when Integer, String
+ raise CoerceError, "Cannot coerce #{value.class}" unless model_name.present?
GlobalID.new(::Gitlab::GlobalId.build(model_name: model_name, id: value))
else
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 57a5ab17b25..317678714e3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12650,6 +12650,9 @@ msgstr ""
msgid "For help setting up the Service Desk for your instance, please contact an administrator."
msgstr ""
+msgid "For individual use, create a separate account under your personal email address, not tied to the Enterprise email domain or group."
+msgstr ""
+
msgid "For internal projects, any logged in user except external users can view pipelines and access job details (output logs and artifacts)"
msgstr ""
@@ -29658,7 +29661,7 @@ msgstr ""
msgid "To define internal users, first enable new users set to external"
msgstr ""
-msgid "To ensure no loss of personal content, an Individual User should create a separate account under their own personal email address, not tied to the Enterprise email domain or name-space."
+msgid "To ensure no loss of personal content, this account should only be used for matters related to %{group_name}."
msgstr ""
msgid "To further protect your account, consider configuring a %{mfa_link_start}two-factor authentication%{mfa_link_end} method."
diff --git a/spec/finders/repositories/commits_with_trailer_finder_spec.rb b/spec/finders/repositories/commits_with_trailer_finder_spec.rb
new file mode 100644
index 00000000000..0c457aae340
--- /dev/null
+++ b/spec/finders/repositories/commits_with_trailer_finder_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Repositories::CommitsWithTrailerFinder do
+ let(:project) { create(:project, :repository) }
+
+ describe '#each_page' do
+ it 'only yields commits with the given trailer' do
+ finder = described_class.new(
+ project: project,
+ from: '570e7b2abdd848b95f2f578043fc23bd6f6fd24d',
+ to: 'c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd'
+ )
+
+ commits = finder.each_page('Signed-off-by').to_a.flatten
+
+ expect(commits.length).to eq(1)
+ expect(commits.first.id).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ expect(commits.first.trailers).to eq(
+ 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>'
+ )
+ end
+
+ it 'supports paginating of commits' do
+ finder = described_class.new(
+ project: project,
+ from: 'c1acaa58bbcbc3eafe538cb8274ba387047b69f8',
+ to: '5937ac0a7beb003549fc5fd26fc247adbce4a52e',
+ per_page: 1
+ )
+
+ commits = finder.each_page('Signed-off-by')
+
+ expect(commits.count).to eq(4)
+ end
+ end
+end
diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js
index cf89246c1a5..431b042cce7 100644
--- a/spec/frontend/clusters/components/applications_spec.js
+++ b/spec/frontend/clusters/components/applications_spec.js
@@ -16,7 +16,7 @@ describe('Applications', () => {
gon.features = gon.features || {};
});
- const createApp = ({ applications, type, propsData } = {}, isShallow) => {
+ const createComponent = ({ applications, type, propsData } = {}, isShallow) => {
const mountMethod = isShallow ? shallowMount : mount;
wrapper = mountMethod(Applications, {
@@ -29,7 +29,7 @@ describe('Applications', () => {
});
};
- const createShallowApp = (options) => createApp(options, true);
+ const createShallowComponent = (options) => createComponent(options, true);
const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`);
afterEach(() => {
wrapper.destroy();
@@ -37,7 +37,7 @@ describe('Applications', () => {
describe('Project cluster applications', () => {
beforeEach(() => {
- createApp({ type: CLUSTER_TYPE.PROJECT });
+ createComponent({ type: CLUSTER_TYPE.PROJECT });
});
it('renders a row for Ingress', () => {
@@ -82,7 +82,7 @@ describe('Applications', () => {
describe('Group cluster applications', () => {
beforeEach(() => {
- createApp({ type: CLUSTER_TYPE.GROUP });
+ createComponent({ type: CLUSTER_TYPE.GROUP });
});
it('renders a row for Ingress', () => {
@@ -128,7 +128,7 @@ describe('Applications', () => {
describe('Instance cluster applications', () => {
beforeEach(() => {
- createApp({ type: CLUSTER_TYPE.INSTANCE });
+ createComponent({ type: CLUSTER_TYPE.INSTANCE });
});
it('renders a row for Ingress', () => {
@@ -174,14 +174,14 @@ describe('Applications', () => {
describe('Helm application', () => {
it('does not render a row for Helm Tiller', () => {
- createApp();
+ createComponent();
expect(wrapper.find('.js-cluster-application-row-helm').exists()).toBe(false);
});
});
describe('Ingress application', () => {
it('shows the correct warning message', () => {
- createApp();
+ createComponent();
expect(findByTestId('ingressCostWarning').element).toMatchSnapshot();
});
@@ -195,7 +195,7 @@ describe('Applications', () => {
},
};
- beforeEach(() => createShallowApp(propsData));
+ beforeEach(() => createShallowComponent(propsData));
it('renders IngressModsecuritySettings', () => {
const modsecuritySettings = wrapper.find(IngressModsecuritySettings);
@@ -206,7 +206,7 @@ describe('Applications', () => {
describe('when installed', () => {
describe('with ip address', () => {
it('renders ip address with a clipboard button', () => {
- createApp({
+ createComponent({
applications: {
ingress: {
title: 'Ingress',
@@ -225,7 +225,7 @@ describe('Applications', () => {
describe('with hostname', () => {
it('renders hostname with a clipboard button', () => {
- createApp({
+ createComponent({
applications: {
ingress: {
title: 'Ingress',
@@ -255,7 +255,7 @@ describe('Applications', () => {
describe('without ip address', () => {
it('renders an input text with a loading icon and an alert text', () => {
- createApp({
+ createComponent({
applications: {
ingress: {
title: 'Ingress',
@@ -272,7 +272,7 @@ describe('Applications', () => {
describe('before installing', () => {
it('does not render the IP address', () => {
- createApp();
+ createComponent();
expect(wrapper.text()).not.toContain('Ingress IP Address');
expect(wrapper.find('.js-endpoint').exists()).toBe(false);
@@ -282,13 +282,13 @@ describe('Applications', () => {
describe('Cert-Manager application', () => {
it('shows the correct description', () => {
- createApp();
+ createComponent();
expect(findByTestId('certManagerDescription').element).toMatchSnapshot();
});
describe('when not installed', () => {
it('renders email & allows editing', () => {
- createApp({
+ createComponent({
applications: {
cert_manager: {
title: 'Cert-Manager',
@@ -305,7 +305,7 @@ describe('Applications', () => {
describe('when installed', () => {
it('renders email in readonly', () => {
- createApp({
+ createComponent({
applications: {
cert_manager: {
title: 'Cert-Manager',
@@ -324,7 +324,7 @@ describe('Applications', () => {
describe('Jupyter application', () => {
describe('with ingress installed with ip & jupyter installable', () => {
it('renders hostname active input', () => {
- createApp({
+ createComponent({
applications: {
ingress: {
title: 'Ingress',
@@ -342,7 +342,7 @@ describe('Applications', () => {
describe('with ingress installed without external ip', () => {
it('does not render hostname input', () => {
- createApp({
+ createComponent({
applications: {
ingress: { title: 'Ingress', status: 'installed' },
},
@@ -356,7 +356,7 @@ describe('Applications', () => {
describe('with ingress & jupyter installed', () => {
it('renders readonly input', () => {
- createApp({
+ createComponent({
applications: {
ingress: {
title: 'Ingress',
@@ -375,7 +375,7 @@ describe('Applications', () => {
describe('without ingress installed', () => {
beforeEach(() => {
- createApp();
+ createComponent();
});
it('does not render input', () => {
@@ -388,7 +388,7 @@ describe('Applications', () => {
describe('Prometheus application', () => {
it('shows the correct description', () => {
- createApp();
+ createComponent();
expect(findByTestId('prometheusDescription').element).toMatchSnapshot();
});
});
@@ -414,14 +414,14 @@ describe('Applications', () => {
let knativeDomainEditor;
beforeEach(() => {
- createShallowApp(propsData);
+ createShallowComponent(propsData);
jest.spyOn(eventHub, '$emit');
knativeDomainEditor = wrapper.find(KnativeDomainEditor);
});
it('shows the correct description', async () => {
- createApp();
+ createComponent();
wrapper.setProps({
providerType: PROVIDER_TYPE.GCP,
preInstalledKnative: true,
@@ -487,7 +487,7 @@ describe('Applications', () => {
},
};
- beforeEach(() => createShallowApp(propsData));
+ beforeEach(() => createShallowComponent(propsData));
it('renders the correct Component', () => {
const crossplane = wrapper.find(CrossplaneProviderStack);
@@ -495,7 +495,7 @@ describe('Applications', () => {
});
it('shows the correct description', () => {
- createApp();
+ createComponent();
expect(findByTestId('crossplaneDescription').element).toMatchSnapshot();
});
});
@@ -503,7 +503,7 @@ describe('Applications', () => {
describe('Elastic Stack application', () => {
describe('with elastic stack installable', () => {
it('renders the install button enabled', () => {
- createApp();
+ createComponent();
expect(
wrapper
@@ -517,7 +517,7 @@ describe('Applications', () => {
describe('elastic stack installed', () => {
it('renders uninstall button', () => {
- createApp({
+ createComponent({
applications: {
elastic_stack: { title: 'Elastic Stack', status: 'installed' },
},
@@ -535,7 +535,7 @@ describe('Applications', () => {
});
describe('Fluentd application', () => {
- beforeEach(() => createShallowApp());
+ beforeEach(() => createShallowComponent());
it('renders the correct Component', () => {
expect(wrapper.find(FluentdOutputSettings).exists()).toBe(true);
@@ -544,7 +544,7 @@ describe('Applications', () => {
describe('Cilium application', () => {
it('shows the correct description', () => {
- createApp({ propsData: { ciliumHelpPath: 'cilium-help-path' } });
+ createComponent({ propsData: { ciliumHelpPath: 'cilium-help-path' } });
expect(findByTestId('ciliumDescription').element).toMatchSnapshot();
});
});
diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
index 5faae5690db..df49f256aa3 100644
--- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
@@ -6,7 +6,7 @@ exports[`packages_list_row renders 1`] = `
data-qa-selector="package_row"
>
<div
- class="gl-display-flex gl-align-items-center gl-py-5"
+ class="gl-display-flex gl-align-items-center gl-py-3"
>
<!---->
@@ -14,7 +14,7 @@ exports[`packages_list_row renders 1`] = `
class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-fill-1"
>
<div
- class="gl-display-flex gl-flex-direction-column gl-justify-content-space-between gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1"
+ class="gl-display-flex gl-flex-direction-column gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1"
>
<div
class="gl-display-flex gl-align-items-center gl-text-body gl-font-weight-bold gl-min-h-6 gl-min-w-0"
@@ -40,7 +40,7 @@ exports[`packages_list_row renders 1`] = `
</div>
<div
- class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1 gl-min-h-6 gl-min-w-0 gl-flex-fill-1"
+ class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-fill-1"
>
<div
class="gl-display-flex"
@@ -85,7 +85,7 @@ exports[`packages_list_row renders 1`] = `
</div>
<div
- class="gl-display-flex gl-align-items-center gl-mt-1 gl-min-h-6"
+ class="gl-display-flex gl-align-items-center gl-min-h-6"
>
<span>
<gl-sprintf-stub
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
index c83b1852147..f9fbb1b3016 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
@@ -1,20 +1,36 @@
-import { mount } from '@vue/test-utils';
+import { GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
-import ServiceDeskRoot from '~/projects/settings_service_desk/components/service_desk_root.vue';
-import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
+import ServiceDeskRoot from '~/projects/settings_service_desk/components/service_desk_root.vue';
+import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
describe('ServiceDeskRoot', () => {
- const endpoint = '/gitlab-org/gitlab-test/service_desk';
- const initialIncomingEmail = 'servicedeskaddress@example.com';
let axiosMock;
let wrapper;
let spy;
+ const provideData = {
+ customEmail: 'custom.email@example.com',
+ customEmailEnabled: true,
+ endpoint: '/gitlab-org/gitlab-test/service_desk',
+ initialIncomingEmail: 'servicedeskaddress@example.com',
+ initialIsEnabled: true,
+ outgoingName: 'GitLab Support Bot',
+ projectKey: 'key',
+ selectedTemplate: 'Bug',
+ templates: ['Bug', 'Documentation'],
+ };
+
+ const getAlertText = () => wrapper.find(GlAlert).text();
+
+ const createComponent = () => shallowMount(ServiceDeskRoot, { provide: provideData });
+
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
+ spy = jest.spyOn(axios, 'put');
});
afterEach(() => {
@@ -25,156 +41,122 @@ describe('ServiceDeskRoot', () => {
}
});
- it('sends a request to toggle service desk off when the toggle is clicked from the on state', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK);
+ describe('ServiceDeskSetting component', () => {
+ it('is rendered', () => {
+ wrapper = createComponent();
+
+ expect(wrapper.find(ServiceDeskSetting).props()).toEqual({
+ customEmail: provideData.customEmail,
+ customEmailEnabled: provideData.customEmailEnabled,
+ incomingEmail: provideData.initialIncomingEmail,
+ initialOutgoingName: provideData.outgoingName,
+ initialProjectKey: provideData.projectKey,
+ initialSelectedTemplate: provideData.selectedTemplate,
+ isEnabled: provideData.initialIsEnabled,
+ isTemplateSaving: false,
+ templates: provideData.templates,
+ });
+ });
+
+ describe('toggle event', () => {
+ describe('when toggling service desk on', () => {
+ beforeEach(async () => {
+ wrapper = createComponent();
- spy = jest.spyOn(axios, 'put');
+ wrapper.find(ServiceDeskSetting).vm.$emit('toggle', true);
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: true,
- initialIncomingEmail,
- endpoint,
- },
- });
+ await waitForPromises();
+ });
+
+ it('sends a request to turn service desk on', () => {
+ axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
- wrapper.find('button.gl-toggle').trigger('click');
+ expect(spy).toHaveBeenCalledWith(provideData.endpoint, { service_desk_enabled: true });
+ });
- return wrapper.vm
- .$nextTick()
- .then(waitForPromises)
- .then(() => {
- expect(spy).toHaveBeenCalledWith(endpoint, { service_desk_enabled: false });
+ it('shows a message when there is an error', () => {
+ axiosMock.onPut(provideData.endpoint).networkError();
+
+ expect(getAlertText()).toContain('An error occurred while enabling Service Desk.');
+ });
});
- });
- it('sends a request to toggle service desk on when the toggle is clicked from the off state', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK);
+ describe('when toggling service desk off', () => {
+ beforeEach(async () => {
+ wrapper = createComponent();
- spy = jest.spyOn(axios, 'put');
+ wrapper.find(ServiceDeskSetting).vm.$emit('toggle', false);
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: false,
- initialIncomingEmail: '',
- endpoint,
- },
- });
+ await waitForPromises();
+ });
- wrapper.find('button.gl-toggle').trigger('click');
+ it('sends a request to turn service desk off', () => {
+ axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
- return wrapper.vm.$nextTick(() => {
- expect(spy).toHaveBeenCalledWith(endpoint, { service_desk_enabled: true });
- });
- });
+ expect(spy).toHaveBeenCalledWith(provideData.endpoint, { service_desk_enabled: false });
+ });
- it('shows an error message when there is an issue toggling service desk on', () => {
- axiosMock.onPut(endpoint).networkError();
+ it('shows a message when there is an error', () => {
+ axiosMock.onPut(provideData.endpoint).networkError();
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: false,
- initialIncomingEmail: '',
- endpoint,
- },
+ expect(getAlertText()).toContain('An error occurred while disabling Service Desk.');
+ });
+ });
});
- wrapper.find('button.gl-toggle').trigger('click');
+ describe('save event', () => {
+ describe('successful request', () => {
+ beforeEach(async () => {
+ axiosMock.onPut(provideData.endpoint).replyOnce(httpStatusCodes.OK);
- return wrapper.vm
- .$nextTick()
- .then(waitForPromises)
- .then(() => {
- expect(wrapper.html()).toContain('An error occurred while enabling Service Desk.');
- });
- });
+ wrapper = createComponent();
- it('sends a request to update template when the "Save template" button is clicked', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK);
+ const payload = {
+ selectedTemplate: 'Bug',
+ outgoingName: 'GitLab Support Bot',
+ projectKey: 'key',
+ };
- spy = jest.spyOn(axios, 'put');
+ wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: true,
- endpoint,
- initialIncomingEmail,
- selectedTemplate: 'Bug',
- outgoingName: 'GitLab Support Bot',
- templates: ['Bug', 'Documentation'],
- projectKey: 'key',
- },
- });
+ await waitForPromises();
+ });
- wrapper.find('button.btn-success').trigger('click');
+ it('sends a request to update template', async () => {
+ expect(spy).toHaveBeenCalledWith(provideData.endpoint, {
+ issue_template_key: 'Bug',
+ outgoing_name: 'GitLab Support Bot',
+ project_key: 'key',
+ service_desk_enabled: true,
+ });
+ });
- return wrapper.vm.$nextTick(() => {
- expect(spy).toHaveBeenCalledWith(endpoint, {
- issue_template_key: 'Bug',
- outgoing_name: 'GitLab Support Bot',
- project_key: 'key',
- service_desk_enabled: true,
+ it('shows success message', () => {
+ expect(getAlertText()).toContain('Changes saved.');
+ });
});
- });
- });
- it('saves the template when the "Save template" button is clicked', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK);
-
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: true,
- endpoint,
- initialIncomingEmail,
- selectedTemplate: 'Bug',
- templates: ['Bug', 'Documentation'],
- },
- });
+ describe('unsuccessful request', () => {
+ beforeEach(async () => {
+ axiosMock.onPut(provideData.endpoint).networkError();
- wrapper.find('button.btn-success').trigger('click');
+ wrapper = createComponent();
- return wrapper.vm
- .$nextTick()
- .then(waitForPromises)
- .then(() => {
- expect(wrapper.html()).toContain('Changes saved.');
- });
- });
+ const payload = {
+ selectedTemplate: 'Bug',
+ outgoingName: 'GitLab Support Bot',
+ projectKey: 'key',
+ };
- it('shows an error message when there is an issue saving the template', () => {
- axiosMock.onPut(endpoint).networkError();
-
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: true,
- endpoint,
- initialIncomingEmail,
- selectedTemplate: 'Bug',
- templates: ['Bug', 'Documentation'],
- },
- });
+ wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
- wrapper.find('button.btn-success').trigger('click');
+ await waitForPromises();
+ });
- return wrapper.vm
- .$nextTick()
- .then(waitForPromises)
- .then(() => {
- expect(wrapper.html()).toContain('An error occured while saving changes:');
+ it('shows an error message', () => {
+ expect(getAlertText()).toContain('An error occured while saving changes:');
+ });
});
- });
-
- it('passes customEmail through updatedCustomEmail correctly', () => {
- const customEmail = 'foo';
-
- wrapper = mount(ServiceDeskRoot, {
- propsData: {
- initialIsEnabled: true,
- endpoint,
- customEmail,
- },
});
-
- expect(wrapper.find(ServiceDeskSetting).props('customEmail')).toEqual(customEmail);
});
});
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
index ddd9a7b2fad..51d656e0ea4 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
@@ -1,63 +1,68 @@
import { shallowMount, mount } from '@vue/test-utils';
-import { GlLoadingIcon } from '@gitlab/ui';
-import eventHub from '~/projects/settings_service_desk/event_hub';
+import { GlButton, GlFormSelect, GlLoadingIcon, GlToggle } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ServiceDeskSetting from '~/projects/settings_service_desk/components/service_desk_setting.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('ServiceDeskSetting', () => {
let wrapper;
+ const findButton = () => wrapper.find(GlButton);
+ const findClipboardButton = () => wrapper.find(ClipboardButton);
+ const findIncomingEmail = () => wrapper.findByTestId('incoming-email');
+ const findIncomingEmailLabel = () => wrapper.findByTestId('incoming-email-describer');
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findTemplateDropdown = () => wrapper.find(GlFormSelect);
+ const findToggle = () => wrapper.find(GlToggle);
+
+ const createComponent = ({ props = {}, mountFunction = shallowMount } = {}) =>
+ extendedWrapper(
+ mountFunction(ServiceDeskSetting, {
+ propsData: {
+ isEnabled: true,
+ ...props,
+ },
+ }),
+ );
+
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
- const findTemplateDropdown = () => wrapper.find('#service-desk-template-select');
- const findIncomingEmail = () => wrapper.find('[data-testid="incoming-email"]');
-
describe('when isEnabled=true', () => {
describe('only isEnabled', () => {
describe('as project admin', () => {
beforeEach(() => {
- wrapper = shallowMount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- },
- });
+ wrapper = createComponent();
});
it('should see activation checkbox', () => {
- expect(wrapper.find('#service-desk-checkbox').exists()).toBe(true);
+ expect(findToggle().exists()).toBe(true);
});
it('should see main panel with the email info', () => {
- expect(wrapper.find('#incoming-email-describer').exists()).toBe(true);
+ expect(findIncomingEmailLabel().exists()).toBe(true);
});
it('should see loading spinner and not the incoming email', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(findLoadingIcon().exists()).toBe(true);
expect(findIncomingEmail().exists()).toBe(false);
});
});
});
describe('service desk toggle', () => {
- it('emits an event to turn on Service Desk when clicked', () => {
- const eventSpy = jest.fn();
- eventHub.$on('serviceDeskEnabledCheckboxToggled', eventSpy);
-
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: false,
- },
- });
+ it('emits an event to turn on Service Desk when clicked', async () => {
+ wrapper = createComponent();
- wrapper.find('#service-desk-checkbox').trigger('click');
+ findToggle().vm.$emit('change', true);
- expect(eventSpy).toHaveBeenCalledWith(true);
+ await nextTick();
- eventHub.$off('serviceDeskEnabledCheckboxToggled', eventSpy);
- eventSpy.mockRestore();
+ expect(wrapper.emitted('toggle')[0]).toEqual([true]);
});
});
@@ -65,23 +70,23 @@ describe('ServiceDeskSetting', () => {
const incomingEmail = 'foo@bar.com';
beforeEach(() => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- incomingEmail,
- },
+ wrapper = createComponent({
+ props: { incomingEmail },
});
});
it('should see email and not the loading spinner', () => {
expect(findIncomingEmail().element.value).toEqual(incomingEmail);
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(findLoadingIcon().exists()).toBe(false);
});
it('renders a copy to clipboard button', () => {
- expect(wrapper.find('.qa-clipboard-button').exists()).toBe(true);
- expect(wrapper.find('.qa-clipboard-button').element.dataset.clipboardText).toBe(
- incomingEmail,
+ expect(findClipboardButton().exists()).toBe(true);
+ expect(findClipboardButton().props()).toEqual(
+ expect.objectContaining({
+ title: 'Copy',
+ text: incomingEmail,
+ }),
);
});
});
@@ -92,12 +97,8 @@ describe('ServiceDeskSetting', () => {
const customEmail = 'custom@bar.com';
beforeEach(() => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- incomingEmail,
- customEmail,
- },
+ wrapper = createComponent({
+ props: { incomingEmail, customEmail },
});
});
@@ -110,12 +111,8 @@ describe('ServiceDeskSetting', () => {
const email = 'foo@bar.com';
beforeEach(() => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- incomingEmail: email,
- customEmail: email,
- },
+ wrapper = createComponent({
+ props: { incomingEmail: email, customEmail: email },
});
});
@@ -127,21 +124,13 @@ describe('ServiceDeskSetting', () => {
describe('templates dropdown', () => {
it('renders a dropdown to choose a template', () => {
- wrapper = shallowMount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- },
- });
+ wrapper = createComponent();
- expect(wrapper.find('#service-desk-template-select').exists()).toBe(true);
+ expect(findTemplateDropdown().exists()).toBe(true);
});
it('renders a dropdown with a default value of ""', () => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- },
- });
+ wrapper = createComponent({ mountFunction: mount });
expect(findTemplateDropdown().element.value).toEqual('');
});
@@ -149,23 +138,18 @@ describe('ServiceDeskSetting', () => {
it('renders a dropdown with a value of "Bug" when it is the initial value', () => {
const templates = ['Bug', 'Documentation', 'Security release'];
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- initialSelectedTemplate: 'Bug',
- templates,
- },
+ wrapper = createComponent({
+ props: { initialSelectedTemplate: 'Bug', templates },
+ mountFunction: mount,
});
expect(findTemplateDropdown().element.value).toEqual('Bug');
});
it('renders a dropdown with no options when the project has no templates', () => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- templates: [],
- },
+ wrapper = createComponent({
+ props: { templates: [] },
+ mountFunction: mount,
});
// The dropdown by default has one empty option
@@ -174,11 +158,10 @@ describe('ServiceDeskSetting', () => {
it('renders a dropdown with options when the project has templates', () => {
const templates = ['Bug', 'Documentation', 'Security release'];
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- templates,
- },
+
+ wrapper = createComponent({
+ props: { templates },
+ mountFunction: mount,
});
// An empty-named template is prepended so the user can select no template
@@ -199,78 +182,59 @@ describe('ServiceDeskSetting', () => {
describe('save button', () => {
it('renders a save button to save a template', () => {
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- },
- });
+ wrapper = createComponent();
- expect(wrapper.find('button.btn-success').text()).toContain('Save changes');
+ expect(findButton().text()).toContain('Save changes');
});
- it('emits a save event with the chosen template when the save button is clicked', () => {
- const eventSpy = jest.fn();
- eventHub.$on('serviceDeskTemplateSave', eventSpy);
-
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
+ it('emits a save event with the chosen template when the save button is clicked', async () => {
+ wrapper = createComponent({
+ props: {
initialSelectedTemplate: 'Bug',
initialOutgoingName: 'GitLab Support Bot',
initialProjectKey: 'key',
},
});
- wrapper.find('button.btn-success').trigger('click');
+ findButton().vm.$emit('click');
+
+ await nextTick();
- expect(eventSpy).toHaveBeenCalledWith({
+ const payload = {
selectedTemplate: 'Bug',
outgoingName: 'GitLab Support Bot',
projectKey: 'key',
- });
+ };
- eventHub.$off('serviceDeskTemplateSave', eventSpy);
- eventSpy.mockRestore();
+ expect(wrapper.emitted('save')[0]).toEqual([payload]);
});
});
describe('when isEnabled=false', () => {
beforeEach(() => {
- wrapper = shallowMount(ServiceDeskSetting, {
- propsData: {
- isEnabled: false,
- },
+ wrapper = createComponent({
+ props: { isEnabled: false },
});
});
it('does not render email panel', () => {
- expect(wrapper.find('#incoming-email-describer').exists()).toBe(false);
+ expect(findIncomingEmailLabel().exists()).toBe(false);
});
it('does not render template dropdown', () => {
- expect(wrapper.find('#service-desk-template-select').exists()).toBe(false);
+ expect(findTemplateDropdown().exists()).toBe(false);
});
it('does not render template save button', () => {
- expect(wrapper.find('button.btn-success').exists()).toBe(false);
+ expect(findButton().exists()).toBe(false);
});
- it('emits an event to turn on Service Desk when the toggle is clicked', () => {
- const eventSpy = jest.fn();
- eventHub.$on('serviceDeskEnabledCheckboxToggled', eventSpy);
-
- wrapper = mount(ServiceDeskSetting, {
- propsData: {
- isEnabled: true,
- },
- });
-
- wrapper.find('#service-desk-checkbox').trigger('click');
+ it('emits an event to turn on Service Desk when the toggle is clicked', async () => {
+ findToggle().vm.$emit('change', false);
- expect(eventSpy).toHaveBeenCalledWith(false);
+ await nextTick();
- eventHub.$off('serviceDeskEnabledCheckboxToggled', eventSpy);
- eventSpy.mockRestore();
+ expect(wrapper.emitted('toggle')[0]).toEqual([false]);
});
});
});
diff --git a/spec/frontend/projects/settings_service_desk/services/service_desk_service_spec.js b/spec/frontend/projects/settings_service_desk/services/service_desk_service_spec.js
deleted file mode 100644
index d5340df03fe..00000000000
--- a/spec/frontend/projects/settings_service_desk/services/service_desk_service_spec.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import AxiosMockAdapter from 'axios-mock-adapter';
-import ServiceDeskService from '~/projects/settings_service_desk/services/service_desk_service';
-import axios from '~/lib/utils/axios_utils';
-import httpStatusCodes from '~/lib/utils/http_status';
-
-describe('ServiceDeskService', () => {
- const endpoint = `/gitlab-org/gitlab-test/service_desk`;
- const dummyResponse = { message: 'Dummy response' };
- const errorMessage = 'Network Error';
- let axiosMock;
- let service;
-
- beforeEach(() => {
- axiosMock = new AxiosMockAdapter(axios);
- service = new ServiceDeskService(endpoint);
- });
-
- afterEach(() => {
- axiosMock.restore();
- });
-
- describe('toggleServiceDesk', () => {
- it('makes a request to set service desk', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
-
- return service.toggleServiceDesk(true).then((response) => {
- expect(response.data).toEqual(dummyResponse);
- });
- });
-
- it('fails on error response', () => {
- axiosMock.onPut(endpoint).networkError();
-
- return service.toggleServiceDesk(true).catch((error) => {
- expect(error.message).toBe(errorMessage);
- });
- });
-
- it('makes a request with the expected body', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
-
- const spy = jest.spyOn(axios, 'put');
-
- service.toggleServiceDesk(true);
-
- expect(spy).toHaveBeenCalledWith(endpoint, {
- service_desk_enabled: true,
- });
-
- spy.mockRestore();
- });
- });
-
- describe('updateTemplate', () => {
- it('makes a request to update template', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
-
- return service
- .updateTemplate(
- {
- selectedTemplate: 'Bug',
- outgoingName: 'GitLab Support Bot',
- },
- true,
- )
- .then((response) => {
- expect(response.data).toEqual(dummyResponse);
- });
- });
-
- it('fails on error response', () => {
- axiosMock.onPut(endpoint).networkError();
-
- return service
- .updateTemplate(
- {
- selectedTemplate: 'Bug',
- outgoingName: 'GitLab Support Bot',
- },
- true,
- )
- .catch((error) => {
- expect(error.message).toBe(errorMessage);
- });
- });
-
- it('makes a request with the expected body', () => {
- axiosMock.onPut(endpoint).replyOnce(httpStatusCodes.OK, dummyResponse);
-
- const spy = jest.spyOn(axios, 'put');
-
- service.updateTemplate(
- {
- selectedTemplate: 'Bug',
- outgoingName: 'GitLab Support Bot',
- projectKey: 'key',
- },
- true,
- );
-
- expect(spy).toHaveBeenCalledWith(endpoint, {
- issue_template_key: 'Bug',
- outgoing_name: 'GitLab Support Bot',
- project_key: 'key',
- service_desk_enabled: true,
- });
-
- spy.mockRestore();
- });
- });
-});
diff --git a/spec/frontend/tracking_spec.js b/spec/frontend/tracking_spec.js
index a516a4a8269..6fd1a330bb2 100644
--- a/spec/frontend/tracking_spec.js
+++ b/spec/frontend/tracking_spec.js
@@ -1,3 +1,5 @@
+// Work around for https://github.com/vuejs/eslint-plugin-vue/issues/1411
+/* eslint-disable vue/one-component-per-file */
import { setHTMLFixture } from 'helpers/fixtures';
import Tracking, { initUserTracking, initDefaultTrackers } from '~/tracking';
diff --git a/spec/lib/atlassian/jira_connect/client_spec.rb b/spec/lib/atlassian/jira_connect/client_spec.rb
index 63f6573818b..2a4363ff2af 100644
--- a/spec/lib/atlassian/jira_connect/client_spec.rb
+++ b/spec/lib/atlassian/jira_connect/client_spec.rb
@@ -303,24 +303,6 @@ RSpec.describe Atlassian::JiraConnect::Client do
expect(response['errorMessages']).to eq(['a: X', 'a: Y', 'b: Z'])
end
end
-
- it 'does not call the API if the feature flag is not enabled' do
- stub_feature_flags(jira_sync_feature_flags: false)
-
- expect(subject).not_to receive(:post)
-
- subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
- end
-
- it 'does call the API if the feature flag enabled for the project' do
- stub_feature_flags(jira_sync_feature_flags: project)
-
- expect(subject).to receive(:post).with('/rest/featureflags/0.1/bulk', {
- flags: Array, properties: Hash
- }).and_call_original
-
- subject.send(:store_ff_info, project: project, feature_flags: feature_flags)
- end
end
describe '#store_build_info' do
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 8961cdcae7d..49f1e6e994f 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -720,7 +720,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
committer_name: "Dmitriy Zaporozhets",
id: SeedRepo::Commit::ID,
message: "tree css fixes",
- parent_ids: ["874797c3a73b60d2187ed6e2fcabd289ff75171e"]
+ parent_ids: ["874797c3a73b60d2187ed6e2fcabd289ff75171e"],
+ trailers: {}
}
end
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index a93ee051ccf..79769f7ba7a 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -231,6 +231,7 @@ MergeRequestDiffCommit:
- committer_name
- committer_email
- message
+- trailers
MergeRequestDiffFile:
- merge_request_diff_id
- relative_order
@@ -255,6 +256,7 @@ MergeRequestContextCommit:
- committer_email
- message
- merge_request_id
+- trailers
MergeRequestContextCommitDiffFile:
- sha
- relative_order
diff --git a/spec/migrations/add_has_external_issue_tracker_trigger_spec.rb b/spec/migrations/add_has_external_issue_tracker_trigger_spec.rb
new file mode 100644
index 00000000000..72983c7cfbf
--- /dev/null
+++ b/spec/migrations/add_has_external_issue_tracker_trigger_spec.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+RSpec.describe AddHasExternalIssueTrackerTrigger do
+ let(:migration) { described_class.new }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:services) { table(:services) }
+
+ before do
+ @namespace = namespaces.create!(name: 'foo', path: 'foo')
+ @project = projects.create!(namespace_id: @namespace.id)
+ end
+
+ describe '#up' do
+ before do
+ migrate!
+ end
+
+ describe 'INSERT trigger' do
+ it 'sets `has_external_issue_tracker` to true when active `issue_tracker` is inserted' do
+ expect do
+ services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+ end.to change { @project.reload.has_external_issue_tracker }.to(true)
+ end
+
+ it 'does not set `has_external_issue_tracker` to true when service is for a different project' do
+ different_project = projects.create!(namespace_id: @namespace.id)
+
+ expect do
+ services.create!(category: 'issue_tracker', active: true, project_id: different_project.id)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'does not set `has_external_issue_tracker` to true when inactive `issue_tracker` is inserted' do
+ expect do
+ services.create!(category: 'issue_tracker', active: false, project_id: @project.id)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'does not set `has_external_issue_tracker` to true when a non-`issue tracker` active service is inserted' do
+ expect do
+ services.create!(category: 'my_type', active: true, project_id: @project.id)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+ end
+
+ describe 'UPDATE trigger' do
+ it 'sets `has_external_issue_tracker` to true when `issue_tracker` is made active' do
+ service = services.create!(category: 'issue_tracker', active: false, project_id: @project.id)
+
+ expect do
+ service.update!(active: true)
+ end.to change { @project.reload.has_external_issue_tracker }.to(true)
+ end
+
+ it 'sets `has_external_issue_tracker` to false when `issue_tracker` is made inactive' do
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.update!(active: false)
+ end.to change { @project.reload.has_external_issue_tracker }.to(false)
+ end
+
+ it 'sets `has_external_issue_tracker` to false when `issue_tracker` is made inactive, and an inactive `issue_tracker` exists' do
+ services.create!(category: 'issue_tracker', active: false, project_id: @project.id)
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.update!(active: false)
+ end.to change { @project.reload.has_external_issue_tracker }.to(false)
+ end
+
+ it 'does not change `has_external_issue_tracker` when `issue_tracker` is made inactive, if an active `issue_tracker` exists' do
+ services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.update!(active: false)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'does not change `has_external_issue_tracker` when service is for a different project' do
+ different_project = projects.create!(namespace_id: @namespace.id)
+ service = services.create!(category: 'issue_tracker', active: false, project_id: different_project.id)
+
+ expect do
+ service.update!(active: true)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+ end
+
+ describe 'DELETE trigger' do
+ it 'sets `has_external_issue_tracker` to false when `issue_tracker` is deleted' do
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.delete
+ end.to change { @project.reload.has_external_issue_tracker }.to(false)
+ end
+
+ it 'sets `has_external_issue_tracker` to false when `issue_tracker` is deleted, if an inactive `issue_tracker` still exists' do
+ services.create!(category: 'issue_tracker', active: false, project_id: @project.id)
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.delete
+ end.to change { @project.reload.has_external_issue_tracker }.to(false)
+ end
+
+ it 'does not change `has_external_issue_tracker` when `issue_tracker` is deleted, if an active `issue_tracker` still exists' do
+ services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+
+ expect do
+ service.delete
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'does not change `has_external_issue_tracker` when service is for a different project' do
+ different_project = projects.create!(namespace_id: @namespace.id)
+ service = services.create!(category: 'issue_tracker', active: true, project_id: different_project.id)
+
+ expect do
+ service.delete
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+ end
+ end
+
+ describe '#down' do
+ before do
+ migration.up
+ migration.down
+ end
+
+ it 'drops the INSERT trigger' do
+ expect do
+ services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'drops the UPDATE trigger' do
+ service = services.create!(category: 'issue_tracker', active: false, project_id: @project.id)
+ @project.update!(has_external_issue_tracker: false)
+
+ expect do
+ service.update!(active: true)
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+
+ it 'drops the DELETE trigger' do
+ service = services.create!(category: 'issue_tracker', active: true, project_id: @project.id)
+ @project.update!(has_external_issue_tracker: true)
+
+ expect do
+ service.delete
+ end.not_to change { @project.reload.has_external_issue_tracker }
+ end
+ end
+end
diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb
index 2edf44ecdc4..a24628b0f9d 100644
--- a/spec/models/merge_request_diff_commit_spec.rb
+++ b/spec/models/merge_request_diff_commit_spec.rb
@@ -48,7 +48,8 @@ RSpec.describe MergeRequestDiffCommit do
"committer_email": "dmitriy.zaporozhets@gmail.com",
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 0,
- "sha": Gitlab::Database::ShaAttribute.serialize("5937ac0a7beb003549fc5fd26fc247adbce4a52e")
+ "sha": Gitlab::Database::ShaAttribute.serialize("5937ac0a7beb003549fc5fd26fc247adbce4a52e"),
+ "trailers": {}.to_json
},
{
"message": "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n",
@@ -60,7 +61,8 @@ RSpec.describe MergeRequestDiffCommit do
"committer_email": "dmitriy.zaporozhets@gmail.com",
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 1,
- "sha": Gitlab::Database::ShaAttribute.serialize("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ "sha": Gitlab::Database::ShaAttribute.serialize("570e7b2abdd848b95f2f578043fc23bd6f6fd24d"),
+ "trailers": {}.to_json
}
]
end
@@ -92,7 +94,8 @@ RSpec.describe MergeRequestDiffCommit do
"committer_email": "alejorro70@gmail.com",
"merge_request_diff_id": merge_request_diff_id,
"relative_order": 0,
- "sha": Gitlab::Database::ShaAttribute.serialize("ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69")
+ "sha": Gitlab::Database::ShaAttribute.serialize("ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69"),
+ "trailers": {}.to_json
}]
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 1cf197322f5..b8a3f3bef6e 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -754,9 +754,8 @@ RSpec.describe MergeRequest, factory_default: :keep do
context 'when both internal and external issue trackers are enabled' do
before do
- subject.project.has_external_issue_tracker = true
- subject.project.save!
create(:jira_service, project: subject.project)
+ subject.project.reload
end
it 'does not cache issues from external trackers' do
@@ -1263,9 +1262,10 @@ RSpec.describe MergeRequest, factory_default: :keep do
describe '#issues_mentioned_but_not_closing' do
let(:closing_issue) { create :issue, project: subject.project }
let(:mentioned_issue) { create :issue, project: subject.project }
-
let(:commit) { double('commit', safe_message: "Fixes #{closing_issue.to_reference}") }
+ subject { create(:merge_request, source_project: create(:project)) }
+
it 'detects issues mentioned in description but not closed' do
subject.project.add_developer(subject.author)
subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
@@ -1279,13 +1279,12 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
context 'when the project has an external issue tracker' do
- subject { create(:merge_request, source_project: create(:project, :repository)) }
-
before do
subject.project.add_developer(subject.author)
commit = double(:commit, safe_message: 'Fixes TEST-3')
create(:jira_service, project: subject.project)
+ subject.project.reload
allow(subject).to receive(:commits).and_return([commit])
allow(subject).to receive(:description).and_return('Is related to TEST-2 and TEST-3')
@@ -1469,6 +1468,8 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
describe '#default_merge_commit_message' do
+ subject { create(:merge_request, source_project: create(:project)) }
+
it 'includes merge information as the title' do
request = build(:merge_request, source_branch: 'source', target_branch: 'target')
@@ -1645,7 +1646,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
it_behaves_like 'an editable mentionable' do
- subject { create(:merge_request, :simple) }
+ subject { create(:merge_request, :simple, source_project: create(:project, :repository)) }
let(:backref_text) { "merge request #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt) { subject.description = txt } }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a2b51684d4d..8d0b7336955 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1002,103 +1002,125 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#external_issue_tracker' do
- let(:project) { create(:project) }
- let(:ext_project) { create(:redmine_project) }
+ describe '#has_wiki?' do
+ let(:no_wiki_project) { create(:project, :wiki_disabled, has_external_wiki: false) }
+ let(:wiki_enabled_project) { create(:project) }
+ let(:external_wiki_project) { create(:project, has_external_wiki: true) }
- context 'on existing projects with no value for has_external_issue_tracker' do
- before do
- project.update_column(:has_external_issue_tracker, nil)
- ext_project.update_column(:has_external_issue_tracker, nil)
+ it 'returns true if project is wiki enabled or has external wiki' do
+ expect(wiki_enabled_project).to have_wiki
+ expect(external_wiki_project).to have_wiki
+ expect(no_wiki_project).not_to have_wiki
+ end
+ end
+
+ describe '#default_owner' do
+ let_it_be(:owner) { create(:user) }
+ let_it_be(:namespace) { create(:namespace, owner: owner) }
+
+ context 'the project does not have a group' do
+ let(:project) { build(:project, namespace: namespace) }
+
+ it 'is the namespace owner' do
+ expect(project.default_owner).to eq(owner)
end
+ end
- it 'updates the has_external_issue_tracker boolean' do
- expect do
- project.external_issue_tracker
- end.to change { project.reload.has_external_issue_tracker }.to(false)
+ context 'the project is in a group' do
+ let(:group) { build(:group) }
+ let(:project) { build(:project, group: group, namespace: namespace) }
- expect do
- ext_project.external_issue_tracker
- end.to change { ext_project.reload.has_external_issue_tracker }.to(true)
+ it 'is the group owner' do
+ allow(group).to receive(:default_owner).and_return(Object.new)
+
+ expect(project.default_owner).to eq(group.default_owner)
end
end
+ end
+
+ describe '#external_issue_tracker' do
+ it 'sets Project#has_external_issue_tracker when it is nil' do
+ project_with_no_tracker = create(:project, has_external_issue_tracker: nil)
+ project_with_tracker = create(:redmine_project, has_external_issue_tracker: nil)
+
+ expect do
+ project_with_no_tracker.external_issue_tracker
+ end.to change { project_with_no_tracker.reload.has_external_issue_tracker }.from(nil).to(false)
+
+ expect do
+ project_with_tracker.external_issue_tracker
+ end.to change { project_with_tracker.reload.has_external_issue_tracker }.from(nil).to(true)
+ end
it 'returns nil and does not query services when there is no external issue tracker' do
- expect(project).not_to receive(:services)
+ project = create(:project)
+ expect(project).not_to receive(:services)
expect(project.external_issue_tracker).to eq(nil)
end
it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do
- ext_project.reload # Factory returns a project with changed attributes
- expect(ext_project).to receive(:services).once.and_call_original
+ project = create(:redmine_project)
- 2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) }
+ expect(project).to receive(:services).once.and_call_original
+ 2.times { expect(project.external_issue_tracker).to be_a_kind_of(RedmineService) }
end
end
- describe '#cache_has_external_issue_tracker' do
- let_it_be(:project) { create(:project, has_external_issue_tracker: nil) }
-
- it 'stores true if there is any external_issue_tracker' do
- services = double(:service, external_issue_trackers: [RedmineService.new])
- expect(project).to receive(:services).and_return(services)
+ describe '#has_external_issue_tracker' do
+ let_it_be(:project) { create(:project) }
- expect do
- project.cache_has_external_issue_tracker
- end.to change { project.has_external_issue_tracker}.to(true)
+ def subject
+ project.reload.has_external_issue_tracker
end
- it 'stores false if there is no external_issue_tracker' do
- services = double(:service, external_issue_trackers: [])
- expect(project).to receive(:services).and_return(services)
+ it 'is false when external issue tracker service is not active' do
+ create(:service, project: project, category: 'issue_tracker', active: false)
- expect do
- project.cache_has_external_issue_tracker
- end.to change { project.has_external_issue_tracker}.to(false)
+ is_expected.to eq(false)
end
- it 'does not cache data when in a read-only GitLab instance' do
- allow(Gitlab::Database).to receive(:read_only?) { true }
+ it 'is false when other service is active' do
+ create(:service, project: project, category: 'not_issue_tracker', active: true)
- expect do
- project.cache_has_external_issue_tracker
- end.not_to change { project.has_external_issue_tracker }
+ is_expected.to eq(false)
end
- end
- describe '#has_wiki?' do
- let(:no_wiki_project) { create(:project, :wiki_disabled, has_external_wiki: false) }
- let(:wiki_enabled_project) { create(:project) }
- let(:external_wiki_project) { create(:project, has_external_wiki: true) }
-
- it 'returns true if project is wiki enabled or has external wiki' do
- expect(wiki_enabled_project).to have_wiki
- expect(external_wiki_project).to have_wiki
- expect(no_wiki_project).not_to have_wiki
- end
- end
+ context 'when there is an active external issue tracker service' do
+ let!(:service) do
+ create(:service, project: project, type: 'JiraService', category: 'issue_tracker', active: true)
+ end
- describe '#default_owner' do
- let_it_be(:owner) { create(:user) }
- let_it_be(:namespace) { create(:namespace, owner: owner) }
+ specify { is_expected.to eq(true) }
- context 'the project does not have a group' do
- let(:project) { build(:project, namespace: namespace) }
+ it 'becomes false when external issue tracker service is destroyed' do
+ expect do
+ Service.find(service.id).delete
+ end.to change { subject }.to(false)
+ end
- it 'is the namespace owner' do
- expect(project.default_owner).to eq(owner)
+ it 'becomes false when external issue tracker service becomes inactive' do
+ expect do
+ service.update_column(:active, false)
+ end.to change { subject }.to(false)
end
- end
- context 'the project is in a group' do
- let(:group) { build(:group) }
- let(:project) { build(:project, group: group, namespace: namespace) }
+ context 'when there are two active external issue tracker services' do
+ let_it_be(:second_service) do
+ create(:service, project: project, type: 'CustomIssueTracker', category: 'issue_tracker', active: true)
+ end
- it 'is the group owner' do
- allow(group).to receive(:default_owner).and_return(Object.new)
+ it 'does not become false when external issue tracker service is destroyed' do
+ expect do
+ Service.find(service.id).delete
+ end.not_to change { subject }
+ end
- expect(project.default_owner).to eq(group.default_owner)
+ it 'does not become false when external issue tracker service becomes inactive' do
+ expect do
+ service.update_column(:active, false)
+ end.not_to change { subject }
+ end
end
end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 04b3920cd6c..25e947b6222 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -753,38 +753,6 @@ RSpec.describe Service do
end
end
- describe "callbacks" do
- let!(:service) do
- RedmineService.new(
- project: project,
- active: true,
- properties: {
- project_url: 'http://redmine/projects/project_name_in_redmine',
- issues_url: "http://redmine/#{project.id}/project_name_in_redmine/:id",
- new_issue_url: 'http://redmine/projects/project_name_in_redmine/issues/new'
- }
- )
- end
-
- describe "on create" do
- it "updates the has_external_issue_tracker boolean" do
- expect do
- service.save!
- end.to change { service.project.has_external_issue_tracker }.from(false).to(true)
- end
- end
-
- describe "on update" do
- it "updates the has_external_issue_tracker boolean" do
- service.save!
-
- expect do
- service.update(active: false)
- end.to change { service.project.has_external_issue_tracker }.from(true).to(false)
- end
- end
- end
-
describe '#api_field_names' do
let(:fake_service) do
Class.new(Service) do
@@ -864,20 +832,6 @@ RSpec.describe Service do
end
end
- describe '#external_issue_tracker?' do
- where(:category, :active, :result) do
- :issue_tracker | true | true
- :issue_tracker | false | false
- :common | true | false
- end
-
- with_them do
- it 'returns the right result' do
- expect(build(:service, category: category, active: active).external_issue_tracker?).to eq(result)
- end
- end
- end
-
describe '#external_wiki?' do
where(:type, :active, :result) do
'ExternalWikiService' | true | true
diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb
index 42c8e0cc9c0..09e89f65882 100644
--- a/spec/requests/api/graphql/issue/issue_spec.rb
+++ b/spec/requests/api/graphql/issue/issue_spec.rb
@@ -24,6 +24,20 @@ RSpec.describe 'Query.issue(id)' do
end
end
+ it_behaves_like 'a noteable graphql type we can query' do
+ let(:noteable) { issue }
+ let(:project) { issue.project }
+ let(:path_to_noteable) { [:issue] }
+
+ before do
+ project.add_guest(current_user)
+ end
+
+ def query(fields)
+ graphql_query_for('issue', issue_params, fields)
+ end
+ end
+
context 'when the user does not have access to the issue' do
it 'returns nil' do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
diff --git a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
index a671ddc7ab1..7148750b6cb 100644
--- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
+++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb
@@ -22,6 +22,23 @@ RSpec.describe 'Getting designs related to an issue' do
end
end
+ it_behaves_like 'a noteable graphql type we can query' do
+ let(:noteable) { design }
+ let(:note_factory) { :diff_note_on_design }
+ let(:discussion_factory) { :diff_note_on_design }
+ let(:path_to_noteable) { [:issue, :design_collection, :designs, :nodes, 0] }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ def query(fields)
+ graphql_query_for(:issue, { id: global_id_of(issue) }, <<~FIELDS)
+ designCollection { designs { nodes { #{fields} } } }
+ FIELDS
+ end
+ end
+
it 'is not too deep for anonymous users' do
note_fields = <<~FIELDS
id
@@ -37,7 +54,7 @@ RSpec.describe 'Getting designs related to an issue' do
expect(note_data['id']).to eq(note.to_global_id.to_s)
end
- def query(note_fields = all_graphql_fields_for(Note))
+ def query(note_fields = all_graphql_fields_for(Note, max_depth: 1))
design_node = <<~NODE
designs {
nodes {
diff --git a/spec/services/bulk_create_integration_service_spec.rb b/spec/services/bulk_create_integration_service_spec.rb
index 674382ee14f..3ac993972c6 100644
--- a/spec/services/bulk_create_integration_service_spec.rb
+++ b/spec/services/bulk_create_integration_service_spec.rb
@@ -43,46 +43,6 @@ RSpec.describe BulkCreateIntegrationService do
end
end
- shared_examples 'updates project callbacks' do
- it 'updates projects#has_external_issue_tracker for issue tracker services' do
- described_class.new(integration, batch, association).execute
-
- expect(project.reload.has_external_issue_tracker).to eq(true)
- expect(excluded_project.reload.has_external_issue_tracker).to eq(false)
- end
-
- context 'with an external wiki integration' do
- before do
- integration.update!(category: 'common', type: 'ExternalWikiService')
- end
-
- it 'updates projects#has_external_wiki for external wiki services' do
- described_class.new(integration, batch, association).execute
-
- expect(project.reload.has_external_wiki).to eq(true)
- expect(excluded_project.reload.has_external_wiki).to eq(false)
- end
- end
- end
-
- shared_examples 'does not update project callbacks' do
- it 'does not update projects#has_external_issue_tracker for issue tracker services' do
- described_class.new(integration, batch, association).execute
-
- expect(project.reload.has_external_issue_tracker).to eq(false)
- end
-
- context 'with an inactive external wiki integration' do
- let(:integration) { create(:external_wiki_service, :instance, active: false) }
-
- it 'does not update projects#has_external_wiki for external wiki services' do
- described_class.new(integration, batch, association).execute
-
- expect(project.reload.has_external_wiki).to eq(false)
- end
- end
- end
-
context 'passing an instance-level integration' do
let(:integration) { instance_integration }
let(:inherit_from_id) { integration.id }
@@ -95,15 +55,6 @@ RSpec.describe BulkCreateIntegrationService do
it_behaves_like 'creates integration from batch ids'
it_behaves_like 'updates inherit_from_id'
- it_behaves_like 'updates project callbacks'
-
- context 'when integration is not active' do
- before do
- integration.update!(active: false)
- end
-
- it_behaves_like 'does not update project callbacks'
- end
end
context 'with a group association' do
@@ -130,7 +81,6 @@ RSpec.describe BulkCreateIntegrationService do
it_behaves_like 'creates integration from batch ids'
it_behaves_like 'updates inherit_from_id'
- it_behaves_like 'updates project callbacks'
end
context 'with a group association' do
@@ -157,7 +107,6 @@ RSpec.describe BulkCreateIntegrationService do
let(:inherit_from_id) { integration.id }
it_behaves_like 'creates integration from batch ids'
- it_behaves_like 'updates project callbacks'
end
end
end
diff --git a/spec/services/feature_flags/create_service_spec.rb b/spec/services/feature_flags/create_service_spec.rb
index e115d8098c9..128fab114fe 100644
--- a/spec/services/feature_flags/create_service_spec.rb
+++ b/spec/services/feature_flags/create_service_spec.rb
@@ -66,18 +66,6 @@ RSpec.describe FeatureFlags::CreateService do
subject
end
- context 'the feature flag is disabled' do
- before do
- stub_feature_flags(jira_sync_feature_flags: false)
- end
-
- it 'does not sync the feature flag to Jira' do
- expect(::JiraConnect::SyncFeatureFlagsWorker).not_to receive(:perform_async)
-
- subject
- end
- end
-
it 'creates audit event' do
expected_message = 'Created feature flag <strong>feature_flag</strong> '\
'with description <strong>"description"</strong>. '\
diff --git a/spec/services/feature_flags/update_service_spec.rb b/spec/services/feature_flags/update_service_spec.rb
index 8c4055ddd9e..9639cf3081d 100644
--- a/spec/services/feature_flags/update_service_spec.rb
+++ b/spec/services/feature_flags/update_service_spec.rb
@@ -26,18 +26,6 @@ RSpec.describe FeatureFlags::UpdateService do
expect(subject[:status]).to eq(:success)
end
- context 'the feature flag is disabled' do
- before do
- stub_feature_flags(jira_sync_feature_flags: false)
- end
-
- it 'does not sync the feature flag to Jira' do
- expect(::JiraConnect::SyncFeatureFlagsWorker).not_to receive(:perform_async)
-
- subject
- end
- end
-
it 'syncs the feature flag to Jira' do
expect(::JiraConnect::SyncFeatureFlagsWorker).to receive(:perform_async).with(Integer, Integer)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index dc545f57d23..3cf45143594 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -84,6 +84,7 @@ RSpec.describe Issues::CloseService do
let!(:external_issue_tracker) { create(:jira_service, project: project) }
it 'closes the issue on the external issue tracker' do
+ project.reload
expect(project.external_issue_tracker).to receive(:close_issue)
described_class.new(project, user).close_issue(external_issue)
@@ -94,6 +95,7 @@ RSpec.describe Issues::CloseService do
let!(:external_issue_tracker) { create(:jira_service, project: project, active: false) }
it 'does not close the issue on the external issue tracker' do
+ project.reload
expect(project.external_issue_tracker).not_to receive(:close_issue)
described_class.new(project, user).close_issue(external_issue)
@@ -104,6 +106,7 @@ RSpec.describe Issues::CloseService do
let!(:external_issue_tracker) { create(:bugzilla_service, project: project) }
it 'does not close the issue on the external issue tracker' do
+ project.reload
expect(project.external_issue_tracker).not_to receive(:close_issue)
described_class.new(project, user).close_issue(external_issue)
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index f83b8d98ce8..22b3456708f 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -252,6 +252,7 @@ RSpec.describe MergeRequests::BuildService do
issue.update!(iid: 123)
else
create(:"#{issue_tracker}_service", project: project)
+ project.reload
end
end
@@ -351,6 +352,7 @@ RSpec.describe MergeRequests::BuildService do
issue.update!(iid: 123)
else
create(:"#{issue_tracker}_service", project: project)
+ project.reload
end
end
diff --git a/spec/services/system_notes/issuables_service_spec.rb b/spec/services/system_notes/issuables_service_spec.rb
index 96afca2f2cb..ae18bc23c17 100644
--- a/spec/services/system_notes/issuables_service_spec.rb
+++ b/spec/services/system_notes/issuables_service_spec.rb
@@ -729,12 +729,14 @@ RSpec.describe ::SystemNotes::IssuablesService do
it 'is false with issue tracker supporting referencing' do
create(:jira_service, project: project)
+ project.reload
expect(service.cross_reference_disallowed?(noteable)).to be_falsey
end
it 'is true with issue tracker not supporting referencing' do
create(:bugzilla_service, project: project)
+ project.reload
expect(service.cross_reference_disallowed?(noteable)).to be_truthy
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 35c298a4d48..013a870796c 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -510,8 +510,12 @@ module GraphqlHelpers
end
end
- def global_id_of(model)
- model.to_global_id.to_s
+ def global_id_of(model, id: nil, model_name: nil)
+ if id || model_name
+ ::Gitlab::GlobalId.build(model, id: id, model_name: model_name).to_s
+ else
+ model.to_global_id.to_s
+ end
end
def missing_required_argument(path, argument)
diff --git a/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb b/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb
deleted file mode 100644
index 2f0b95427d2..00000000000
--- a/spec/support/shared_examples/models/concerns/can_housekeep_repository_shared_examples.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'can housekeep repository' do
- context 'with a clean redis state', :clean_gitlab_redis_shared_state do
- describe '#pushes_since_gc' do
- context 'without any pushes' do
- it 'returns 0' do
- expect(resource.pushes_since_gc).to eq(0)
- end
- end
-
- context 'with a number of pushes' do
- it 'returns the number of pushes' do
- 3.times { resource.increment_pushes_since_gc }
-
- expect(resource.pushes_since_gc).to eq(3)
- end
- end
- end
-
- describe '#increment_pushes_since_gc' do
- it 'increments the number of pushes since the last GC' do
- 3.times { resource.increment_pushes_since_gc }
-
- expect(resource.pushes_since_gc).to eq(3)
- end
- end
-
- describe '#reset_pushes_since_gc' do
- it 'resets the number of pushes since the last GC' do
- 3.times { resource.increment_pushes_since_gc }
-
- resource.reset_pushes_since_gc
-
- expect(resource.pushes_since_gc).to eq(0)
- end
- end
-
- describe '#pushes_since_gc_redis_shared_state_key' do
- it 'returns the proper redis key format' do
- expect(resource.send(:pushes_since_gc_redis_shared_state_key)).to eq("#{resource_key}/#{resource.id}/pushes_since_gc")
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
new file mode 100644
index 00000000000..9cf5bc04f65
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+# Requires `query(fields)`, `path_to_noteable`, `project`, and `noteable` bindings
+RSpec.shared_examples 'a noteable graphql type we can query' do
+ let(:note_factory) { :note }
+ let(:discussion_factory) { :discussion_note }
+
+ describe '.discussions' do
+ let(:fields) do
+ "discussions { nodes { #{all_graphql_fields_for('Discussion')} } }"
+ end
+
+ def expected
+ noteable.discussions.map do |discussion|
+ include(
+ 'id' => global_id_of(discussion),
+ 'replyId' => global_id_of(discussion, id: discussion.reply_id),
+ 'createdAt' => discussion.created_at.iso8601,
+ 'notes' => include(
+ 'nodes' => have_attributes(size: discussion.notes.size)
+ )
+ )
+ end
+ end
+
+ it 'can fetch discussions' do
+ create(discussion_factory, project: project, noteable: noteable)
+
+ post_graphql(query(fields), current_user: current_user)
+
+ expect(graphql_data_at(*path_to_noteable, :discussions, :nodes))
+ .to match_array(expected)
+ end
+ end
+
+ describe '.notes' do
+ let(:fields) do
+ "notes { nodes { #{all_graphql_fields_for('Note', max_depth: 2)} } }"
+ end
+
+ def expected
+ noteable.notes.map do |note|
+ include(
+ 'id' => global_id_of(note),
+ 'project' => include('id' => global_id_of(project)),
+ 'author' => include('id' => global_id_of(note.author)),
+ 'createdAt' => note.created_at.iso8601,
+ 'body' => eq(note.note)
+ )
+ end
+ end
+
+ it 'can fetch notes' do
+ create(note_factory, project: project, noteable: noteable)
+
+ post_graphql(query(fields), current_user: current_user)
+
+ expect(graphql_data_at(*path_to_noteable, :notes, :nodes))
+ .to match_array(expected)
+ end
+ end
+end
diff --git a/spec/workers/jira_connect/sync_feature_flags_worker_spec.rb b/spec/workers/jira_connect/sync_feature_flags_worker_spec.rb
index 035f4ebdd3c..038eed7b9f1 100644
--- a/spec/workers/jira_connect/sync_feature_flags_worker_spec.rb
+++ b/spec/workers/jira_connect/sync_feature_flags_worker_spec.rb
@@ -32,29 +32,5 @@ RSpec.describe ::JiraConnect::SyncFeatureFlagsWorker do
subject
end
end
-
- context 'when the feature flag is disabled' do
- before do
- stub_feature_flags(jira_sync_feature_flags: false)
- end
-
- it 'does not call the sync service' do
- expect_next(::JiraConnect::SyncService).not_to receive(:execute)
-
- subject
- end
- end
-
- context 'when the feature flag is enabled for this project' do
- before do
- stub_feature_flags(jira_sync_feature_flags: feature_flag.project)
- end
-
- it 'calls the sync service' do
- expect_next(::JiraConnect::SyncService).to receive(:execute)
-
- subject
- end
- end
end
end
diff --git a/workhorse/CHANGELOG b/workhorse/CHANGELOG
index 42c13465ba9..a871f671201 100644
--- a/workhorse/CHANGELOG
+++ b/workhorse/CHANGELOG
@@ -1,5 +1,17 @@
# Changelog for gitlab-workhorse
+## v8.60.0
+
+### Added
+- Support Git HTTP on toplevel repositories
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/670
+- Update GoCloud to v0.21.1+
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/675
+
+### Changed
+- Allow blank S3 regions to be used
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/677
+
## v8.59.0
### Fixed
diff --git a/workhorse/VERSION b/workhorse/VERSION
index 8c20f999400..a6c6f8ed977 100644
--- a/workhorse/VERSION
+++ b/workhorse/VERSION
@@ -1 +1 @@
-8.59.0
+8.60.0
diff --git a/workhorse/gitaly_test.go b/workhorse/gitaly_test.go
index 95d6907ac6a..3c3c957063c 100644
--- a/workhorse/gitaly_test.go
+++ b/workhorse/gitaly_test.go
@@ -169,6 +169,57 @@ func TestGetInfoRefsProxiedToGitalyInterruptedStream(t *testing.T) {
waitDone(t, done)
}
+func TestGetInfoRefsRouting(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse := gitOkBody(t)
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ testCases := []struct {
+ method string
+ path string
+ match bool
+ }{
+ {"GET", "/toplevel.git/info/refs?service=git-receive-pack", true},
+ {"GET", "/toplevel.wiki.git/info/refs?service=git-upload-pack", true},
+ {"GET", "/toplevel/child/project.git/info/refs?service=git-receive-pack", true},
+ {"GET", "/toplevel/child/project.wiki.git/info/refs?service=git-upload-pack", true},
+ {"GET", "/toplevel/child/project/snippets/123.git/info/refs?service=git-receive-pack", true},
+ {"GET", "/snippets/123.git/info/refs?service=git-upload-pack", true},
+ {"GET", "/foo/bar.git/info/refs", false},
+ {"GET", "/foo/bar.git/info/refs?service=git-zzz-pack", false},
+ {"GET", "/.git/info/refs?service=git-upload-pack", false},
+ {"POST", "/toplevel.git/info/refs?service=git-receive-pack", false},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.path, func(t *testing.T) {
+ req, err := http.NewRequest(tc.method, ws.URL+tc.path, nil)
+ require.NoError(t, err)
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ body := string(testhelper.ReadAll(t, resp.Body))
+
+ if tc.match {
+ require.Equal(t, 200, resp.StatusCode)
+ require.Contains(t, body, "\x00", "expect response generated by test gitaly server")
+ } else {
+ require.Equal(t, 204, resp.StatusCode)
+ require.Empty(t, body, "normal request has empty response body")
+ }
+ })
+ }
+}
+
func waitDone(t *testing.T, done chan struct{}) {
t.Helper()
select {
@@ -259,6 +310,65 @@ func TestPostReceivePackProxiedToGitalyInterrupted(t *testing.T) {
waitDone(t, done)
}
+func TestPostReceivePackRouting(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse := gitOkBody(t)
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ testCases := []struct {
+ method string
+ path string
+ contentType string
+ match bool
+ }{
+ {"POST", "/toplevel.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/toplevel.wiki.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/toplevel/child/project.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/toplevel/child/project.wiki.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/toplevel/child/project/snippets/123.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/snippets/123.git/git-receive-pack", "application/x-git-receive-pack-request", true},
+ {"POST", "/foo/bar/git-receive-pack", "application/x-git-receive-pack-request", false},
+ {"POST", "/foo/bar.git/git-zzz-pack", "application/x-git-receive-pack-request", false},
+ {"POST", "/.git/git-receive-pack", "application/x-git-receive-pack-request", false},
+ {"POST", "/toplevel.git/git-receive-pack", "application/x-git-upload-pack-request", false},
+ {"GET", "/toplevel.git/git-receive-pack", "application/x-git-receive-pack-request", false},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.path, func(t *testing.T) {
+ req, err := http.NewRequest(
+ tc.method,
+ ws.URL+tc.path,
+ bytes.NewReader(testhelper.GitalyReceivePackResponseMock),
+ )
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", tc.contentType)
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ body := string(testhelper.ReadAll(t, resp.Body))
+
+ if tc.match {
+ require.Equal(t, 200, resp.StatusCode)
+ require.Contains(t, body, "\x00", "expect response generated by test gitaly server")
+ } else {
+ require.Equal(t, 204, resp.StatusCode)
+ require.Empty(t, body, "normal request has empty response body")
+ }
+ })
+ }
+}
+
// ReaderFunc is an adapter to turn a conforming function into an io.Reader.
type ReaderFunc func(b []byte) (int, error)
@@ -376,6 +486,65 @@ func TestPostUploadPackProxiedToGitalyInterrupted(t *testing.T) {
waitDone(t, done)
}
+func TestPostUploadPackRouting(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse := gitOkBody(t)
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ testCases := []struct {
+ method string
+ path string
+ contentType string
+ match bool
+ }{
+ {"POST", "/toplevel.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/toplevel.wiki.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/toplevel/child/project.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/toplevel/child/project.wiki.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/toplevel/child/project/snippets/123.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/snippets/123.git/git-upload-pack", "application/x-git-upload-pack-request", true},
+ {"POST", "/foo/bar/git-upload-pack", "application/x-git-upload-pack-request", false},
+ {"POST", "/foo/bar.git/git-zzz-pack", "application/x-git-upload-pack-request", false},
+ {"POST", "/.git/git-upload-pack", "application/x-git-upload-pack-request", false},
+ {"POST", "/toplevel.git/git-upload-pack", "application/x-git-receive-pack-request", false},
+ {"GET", "/toplevel.git/git-upload-pack", "application/x-git-upload-pack-request", false},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.path, func(t *testing.T) {
+ req, err := http.NewRequest(
+ tc.method,
+ ws.URL+tc.path,
+ bytes.NewReader(testhelper.GitalyReceivePackResponseMock),
+ )
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", tc.contentType)
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ body := string(testhelper.ReadAll(t, resp.Body))
+
+ if tc.match {
+ require.Equal(t, 200, resp.StatusCode)
+ require.Contains(t, body, "\x00", "expect response generated by test gitaly server")
+ } else {
+ require.Equal(t, 204, resp.StatusCode)
+ require.Empty(t, body, "normal request has empty response body")
+ }
+ })
+ }
+}
+
func TestGetDiffProxiedToGitalySuccessfully(t *testing.T) {
gitalyServer, socketPath := startGitalyServer(t, codes.OK)
defer gitalyServer.GracefulStop()
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 5fed8de4796..6396e419487 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -3,11 +3,11 @@ module gitlab.com/gitlab-org/gitlab-workhorse
go 1.13
require (
- github.com/Azure/azure-storage-blob-go v0.10.0
+ github.com/Azure/azure-storage-blob-go v0.11.1-0.20201209121048-6df5d9af221d
github.com/BurntSushi/toml v0.3.1
github.com/FZambia/sentinel v1.0.0
github.com/alecthomas/chroma v0.7.3
- github.com/aws/aws-sdk-go v1.31.13
+ github.com/aws/aws-sdk-go v1.36.1
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/disintegration/imaging v1.6.2
@@ -15,7 +15,7 @@ require (
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721
github.com/golang/protobuf v1.4.3
github.com/gomodule/redigo v2.0.0+incompatible
- github.com/gorilla/websocket v1.4.0
+ github.com/gorilla/websocket v1.4.1
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec
@@ -30,11 +30,15 @@ require (
github.com/stretchr/testify v1.6.1
gitlab.com/gitlab-org/gitaly v1.74.0
gitlab.com/gitlab-org/labkit v1.0.0
- gocloud.dev v0.20.0
+ gocloud.dev v0.21.1-0.20201223184910-5094f54ed8bb
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
- golang.org/x/net v0.0.0-20200625001655-4c5254603344
- golang.org/x/tools v0.0.0-20200608174601-1b747fd94509
- google.golang.org/grpc v1.29.1
+ golang.org/x/net v0.0.0-20201224014010-6772e930b67b
+ golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 // indirect
+ golang.org/x/text v0.3.5 // indirect
+ golang.org/x/tools v0.0.0-20201203202102-a1a1cbeaa516
+ google.golang.org/genproto v0.0.0-20210111234610-22ae2b108f89 // indirect
+ google.golang.org/grpc v1.34.1
+ google.golang.org/grpc/examples v0.0.0-20201226181154-53788aa5dcb4 // indirect
honnef.co/go/tools v0.0.1-2020.1.5
)
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 0b92dda088c..4796d40638b 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -6,18 +6,19 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.58.0 h1:vtAfVc723K3xKq1BQydk/FyCldnaNFhGhpJxaJzgRMQ=
-cloud.google.com/go v0.58.0/go.mod h1:W+9FnSUw6nhVwXlFcp1eL+krq5+HQUJeUogSeJZZiWg=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko=
+cloud.google.com/go v0.72.0 h1:eWRCuwubtDrCJG0oSUMgnsbD4CmPFQF2ei4OFbXvwww=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -28,60 +29,64 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.2.0/go.mod h1:iISCjWnTpnoJT1R287xRdjvQHJrxQOpeah4phb5D3h0=
+cloud.google.com/go/firestore v1.4.0/go.mod h1:NjjGEnxCS3CAKYp+vmALu20QzcqasGodQp48WxJGAYc=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/pubsub v1.9.0/go.mod h1:G3o6/kJvEMIEAN5urdkaP4be49WQsjNiykBIto9LFtY=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.9.0 h1:oXnZyBjHB6hC8TnSle0AWW6pGJ29EuSo5ww+SFmdNBg=
-cloud.google.com/go/storage v1.9.0/go.mod h1:m+/etGaqZbylxaNT876QGXqEHp4PR2Rq5GMqICWb9bU=
-contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
-contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
-contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4=
+cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
+contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
+contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
-github.com/Azure/azure-amqp-common-go/v3 v3.0.0/go.mod h1:SY08giD/XbhTz07tJdpw1SoxQXHPN30+DI3Z04SYqyg=
-github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
-github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY=
-github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
+github.com/Azure/azure-amqp-common-go/v3 v3.0.1/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
+github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
+github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
+github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v37.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-service-bus-go v0.10.1/go.mod h1:E/FOceuKAFUfpbIJDKWz/May6guE+eGibfGT6q+n1to=
-github.com/Azure/azure-storage-blob-go v0.9.0/go.mod h1:8UBPbiOhrMQ4pLPi3gA1tXnpjrS76UYE/fo5A40vf4g=
-github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
-github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
-github.com/Azure/go-amqp v0.12.6/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo=
-github.com/Azure/go-amqp v0.12.7/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.3 h1:OZEIaBbMdUE/Js+BQKlpO81XlISgipr6yDJ+PSwsgi4=
-github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
-github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw=
-github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk=
-github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
-github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
-github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
-github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/Azure/azure-sdk-for-go v49.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/azure-service-bus-go v0.10.7/go.mod h1:o5z/3lDG1iT/T/G7vgIwIqVDTx9Qa2wndf5OdzSzpF8=
+github.com/Azure/azure-storage-blob-go v0.11.1-0.20201209121048-6df5d9af221d h1:YEjZNZ0HS7ITX+BJ7wUXtTk6GXM3g8xftaqQ94XU/cs=
+github.com/Azure/azure-storage-blob-go v0.11.1-0.20201209121048-6df5d9af221d/go.mod h1:A0u4VjtpgZJ7Y7um/+ix2DHBuEKFC6sEIlj0xc13a4Q=
+github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
+github.com/Azure/go-amqp v0.13.1/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
+github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
+github.com/Azure/go-autorest/autorest v0.11.7/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs=
+github.com/Azure/go-autorest/autorest v0.11.9/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
+github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE=
+github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
+github.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/adal v0.9.6 h1:d3pSDwvBWBLqdA91u+keH1zs1cCEzrQdHKY6iqbQNkE=
+github.com/Azure/go-autorest/autorest/adal v0.9.6/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.3 h1:lZifaPRAk1bqg5vGqreL6F8uLC5V0fDpY8nFvc3boFc=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.3/go.mod h1:4bJZhUhcq8LB20TruwHbAQsmUs2Xh+QR7utuJpLXX3A=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
+github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
+github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
+github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
+github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
+github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -89,7 +94,7 @@ github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EF
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
-github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
+github.com/GoogleCloudPlatform/cloudsql-proxy v1.19.1/go.mod h1:+yYmuKqcBVkgRePGpUhTA9OEg0XsnFE96eZ6nJ2yCQM=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -123,10 +128,10 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.31.13 h1:UeWMTRTL0XAKLR7vxDL4/u7KOtz/LtfJr+lXtxN4YEQ=
-github.com/aws/aws-sdk-go v1.31.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/aws/aws-sdk-go v1.36.1 h1:rDgSL20giXXu48Ycx6Qa4vWaNTVTltUl6vA73ObCSVk=
+github.com/aws/aws-sdk-go v1.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -137,8 +142,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
-github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20180905225744-ee1a9a0726d2/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
@@ -153,6 +158,7 @@ github.com/client9/reopen v1.0.0 h1:8tpLVR74DLpLObrn2KvsyxJY++2iORGR17WLUdSzUws=
github.com/client9/reopen v1.0.0/go.mod h1:caXVCEr+lUtoN1FlsRiOWdfQtdRHIYfcb0ai8qKWtkQ=
github.com/cloudflare/tableflip v1.2.1-0.20200514155827-4baec9811f2b/go.mod h1:vhhSlJqV8uUnxGkRSgyvGthfGlkAwJ4UuSV51fSrCQY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
@@ -171,6 +177,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@@ -178,6 +185,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
+github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
+github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
@@ -193,17 +202,22 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/getsentry/raven-go v0.1.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
@@ -213,7 +227,9 @@ github.com/getsentry/sentry-go v0.7.0 h1:MR2yfR4vFfv/2+iBuSnkdQwVg7N9cJzihZ6KJu7
github.com/getsentry/sentry-go v0.7.0/go.mod h1:pLFpD2Y5RHIKF9Bw3KH6/68DeN2K/XBJd8awjdPnUwg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -228,6 +244,10 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -261,6 +281,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -288,28 +310,40 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
-github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
-github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
-github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
+github.com/google/go-replayers/grpcreplay v1.0.0 h1:B5kVOzJ1hBgnevTgIWhSTatQ3608yu/2NnU0Ta1d0kY=
+github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
+github.com/google/go-replayers/httpreplay v0.1.2 h1:HCfx+dQzwN9XbGTHF8qJ+67WN8glL9FTWV5rraCJ/jU=
+github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c h1:lIC98ZUNah83ky7d9EXktLFe4H7Nwus59dTOLXr8xAI=
-github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c h1:Jx2lEv4nMccTJE+IIZOVIvk+DjNKlRsW0sm1uBr896U=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE=
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
@@ -325,6 +359,8 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
@@ -357,6 +393,7 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
@@ -366,8 +403,10 @@ github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
-github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec h1:jEZFmuFe51KdrceqM4NL3dJiuog0zojzcN/VculG26o=
github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec/go.mod h1:fNiSoOiEI5KlkWXn26OwKnNe58ilTIkpBlgOrt7Olu8=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
@@ -377,6 +416,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
@@ -399,6 +439,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -411,8 +453,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
-github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libgit2/git2go/v30 v30.0.5/go.mod h1:YReiQ7xhMoyAL4ISYFLZt+OGqn6xtLqvTC1xJ9oAH7Y=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
@@ -424,8 +467,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
-github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -457,6 +498,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -475,6 +518,8 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=
@@ -642,6 +687,8 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
gitlab.com/gitlab-org/gitaly v1.87.1-0.20201001041716-3f5e218def93 h1:5qkRBchgs4IvlbRdJTMISuktLF1ZtLMowyhzQteEeKI=
gitlab.com/gitlab-org/gitaly v1.87.1-0.20201001041716-3f5e218def93/go.mod h1:NEpGSBkjMt7yV5SB1MFySVQqTKFEUdfTDxS76Rt7GC8=
@@ -660,6 +707,9 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
@@ -669,8 +719,8 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-gocloud.dev v0.20.0 h1:mbEKMfnyPV7W1Rj35R1xXfjszs9dXkwSOq2KoFr25g8=
-gocloud.dev v0.20.0/go.mod h1:+Y/RpSXrJthIOM8uFNzWp6MRu9pFPNFEEZrQMxpkfIc=
+gocloud.dev v0.21.1-0.20201223184910-5094f54ed8bb h1:3EJw/ZRo3jKHY2WP8HiAxCpGlfsrKpaIeCl6VfMprKw=
+gocloud.dev v0.21.1-0.20201223184910-5094f54ed8bb/go.mod h1:iI47kpBb27cms1+KJCbcO2NbZBOg4V6SJWFeO/Kpp1c=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -681,9 +731,12 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -719,6 +772,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -737,11 +792,12 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -755,16 +811,28 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19 h1:ZD+2Sd/BnevwJp8PSli8WgGAGzb9IZtxBsv1iZMYeEA=
+golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -773,6 +841,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -798,6 +868,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -811,7 +882,6 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -820,21 +890,37 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201202213521-69691e467435 h1:25AvDqqB9PrNqj1FLf2/70I4W0L19qqoaFq3gjNwbKk=
+golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 h1:DQmQoKxQWtyybCtX/3dIuDBcAhFszqq8YiNeS6sNu1c=
+golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -858,6 +944,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -879,26 +966,36 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200608174601-1b747fd94509 h1:MI14dOfl3OG6Zd32w3ugsrvcUO810fDZdWakTq39dH4=
-golang.org/x/tools v0.0.0-20200608174601-1b747fd94509/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201202200335-bef1c476418a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201203202102-a1a1cbeaa516 h1:E8xavSjXY8LFvcMSu/8Fjztt+SerwKnuAUOdS+aCXUM=
+golang.org/x/tools v0.0.0-20201203202102-a1a1cbeaa516/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -908,16 +1005,25 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.26.0 h1:VJZ8h6E8ip82FRpQl848c5vAadxlTXrUh8RzQzSRm08=
-google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
+google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -940,17 +1046,27 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200325114520-5b2d0af7952b/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482 h1:i+Aiej6cta/Frzp13/swvwz5O00kYcSe0A/C5Wd7zX8=
-google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497 h1:jDYzwXmX9tLnuG4sL85HPmE1ruErXOopALp2i/0AHnI=
+google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210111234610-22ae2b108f89 h1:R2owLnwrU3BdTJ5R9cnHDNsnEmBQ7n5lZjKShnbISe4=
+google.golang.org/genproto v0.0.0-20210111234610-22ae2b108f89/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -968,6 +1084,16 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.34.1 h1:ugq+9++ZQPFzM2pKUMCIK8gj9M0pFyuUWO9Q8kwEDQw=
+google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc/examples v0.0.0-20201226181154-53788aa5dcb4 h1:tfxAh8kBsG9GdCdaDiSCA1qqpd8lMOqgEebUyqTtnH8=
+google.golang.org/grpc/examples v0.0.0-20201226181154-53788aa5dcb4/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -978,6 +1104,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/DataDog/dd-trace-go.v1 v1.7.0 h1:7wbMayb6JXcbAS95RN7MI42W3o1BCxCcdIzZfVWBAiE=
gopkg.in/DataDog/dd-trace-go.v1 v1.7.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -985,6 +1113,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
@@ -1018,6 +1148,7 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/workhorse/internal/filestore/save_file_opts.go b/workhorse/internal/filestore/save_file_opts.go
index 1eb708c3f55..d0b2c6ec809 100644
--- a/workhorse/internal/filestore/save_file_opts.go
+++ b/workhorse/internal/filestore/save_file_opts.go
@@ -146,7 +146,7 @@ func (c *ObjectStorageConfig) IsGoCloud() bool {
func (c *ObjectStorageConfig) IsValid() bool {
if c.IsAWS() {
- return c.S3Config.Bucket != "" && c.S3Config.Region != "" && c.s3CredentialsValid()
+ return c.S3Config.Bucket != "" && c.s3CredentialsValid()
} else if c.IsGoCloud() {
// We could parse and validate the URL, but GoCloud providers
// such as AzureRM don't have a fallback to normal HTTP, so we
diff --git a/workhorse/internal/filestore/save_file_opts_test.go b/workhorse/internal/filestore/save_file_opts_test.go
index 2d6cd683b51..facfb1cdc85 100644
--- a/workhorse/internal/filestore/save_file_opts_test.go
+++ b/workhorse/internal/filestore/save_file_opts_test.go
@@ -187,6 +187,9 @@ func TestUseWorkhorseClientEnabled(t *testing.T) {
iamConfig := missingCfg
iamConfig.S3Config.UseIamProfile = true
+ missingRegion := cfg
+ missingRegion.S3Config.Region = ""
+
tests := []struct {
name string
UseWorkhorseClient bool
@@ -245,6 +248,13 @@ func TestUseWorkhorseClientEnabled(t *testing.T) {
},
expected: false,
},
+ {
+ name: "missing S3 region",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: missingRegion,
+ expected: true,
+ },
}
for _, test := range tests {
diff --git a/workhorse/internal/git/info-refs.go b/workhorse/internal/git/info-refs.go
index e5491a7b733..6d0826a22f9 100644
--- a/workhorse/internal/git/info-refs.go
+++ b/workhorse/internal/git/info-refs.go
@@ -20,6 +20,11 @@ func GetInfoRefsHandler(a *api.API) http.Handler {
return repoPreAuthorizeHandler(a, handleGetInfoRefs)
}
+func IsSmartInfoRefs(r *http.Request) bool {
+ service := r.URL.Query().Get("service")
+ return r.Method == "GET" && (service == "git-upload-pack" || service == "git-receive-pack")
+}
+
func handleGetInfoRefs(rw http.ResponseWriter, r *http.Request, a *api.Response) {
responseWriter := NewHttpResponseWriter(rw)
// Log 0 bytes in because we ignore the request body (and there usually is none anyway).
diff --git a/workhorse/internal/git/info-refs_test.go b/workhorse/internal/git/info-refs_test.go
new file mode 100644
index 00000000000..ed66518b8d3
--- /dev/null
+++ b/workhorse/internal/git/info-refs_test.go
@@ -0,0 +1,33 @@
+package git
+
+import (
+ "net/http"
+ "net/url"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIsSmartInfoRefs(t *testing.T) {
+ testCases := []struct {
+ method string
+ url string
+ match bool
+ }{
+ {"GET", "?service=git-upload-pack", true},
+ {"GET", "?service=git-receive-pack", true},
+ {"GET", "", false},
+ {"GET", "?service=", false},
+ {"GET", "?service=foo", false},
+ {"POST", "?service=git-upload-pack", false},
+ {"POST", "?service=git-receive-pack", false},
+ }
+
+ for _, tc := range testCases {
+ url, err := url.Parse(tc.url)
+ require.NoError(t, err)
+
+ r := http.Request{Method: tc.method, URL: url}
+ require.Equal(t, tc.match, IsSmartInfoRefs(&r))
+ }
+}
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index 5bbd245719b..b8a97a1b5f6 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -53,13 +53,13 @@ type uploadPreparers struct {
}
const (
- apiPattern = `^/api/`
- ciAPIPattern = `^/ci/api/`
- gitProjectPattern = `^/([^/]+/){1,}[^/]+\.git/`
- projectPattern = `^/([^/]+/){1,}[^/]+/`
- snippetUploadPattern = `^/uploads/personal_snippet`
- userUploadPattern = `^/uploads/user`
- importPattern = `^/import/`
+ apiPattern = `\A/api/`
+ ciAPIPattern = `\A/ci/api/`
+ gitRepositoryPattern = `\A/.+\.git/`
+ projectPattern = `\A/([^/]+/){1,}[^/]+/`
+ snippetUploadPattern = `\A/uploads/personal_snippet`
+ userUploadPattern = `\A/uploads/user`
+ importPattern = `\A/import/`
)
func compileRegexp(regexpStr string) *regexp.Regexp {
@@ -222,10 +222,10 @@ func (u *upstream) configureRoutes() {
u.Routes = []routeEntry{
// Git Clone
- u.route("GET", gitProjectPattern+`info/refs\z`, git.GetInfoRefsHandler(api)),
- u.route("POST", gitProjectPattern+`git-upload-pack\z`, contentEncodingHandler(git.UploadPack(api)), withMatcher(isContentType("application/x-git-upload-pack-request"))),
- u.route("POST", gitProjectPattern+`git-receive-pack\z`, contentEncodingHandler(git.ReceivePack(api)), withMatcher(isContentType("application/x-git-receive-pack-request"))),
- u.route("PUT", gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, lfs.PutStore(api, signingProxy, preparers.lfs), withMatcher(isContentType("application/octet-stream"))),
+ u.route("GET", gitRepositoryPattern+`info/refs\z`, git.GetInfoRefsHandler(api), withMatcher(git.IsSmartInfoRefs)),
+ u.route("POST", gitRepositoryPattern+`git-upload-pack\z`, contentEncodingHandler(git.UploadPack(api)), withMatcher(isContentType("application/x-git-upload-pack-request"))),
+ u.route("POST", gitRepositoryPattern+`git-receive-pack\z`, contentEncodingHandler(git.ReceivePack(api)), withMatcher(isContentType("application/x-git-receive-pack-request"))),
+ u.route("PUT", gitRepositoryPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, lfs.PutStore(api, signingProxy, preparers.lfs), withMatcher(isContentType("application/octet-stream"))),
// CI Artifacts
u.route("POST", apiPattern+`v4/jobs/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, signingProxy, preparers.artifacts))),
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
index d15af1d3e4c..b725f36a68a 100644
--- a/workhorse/main_test.go
+++ b/workhorse/main_test.go
@@ -694,6 +694,12 @@ func testAuthServer(t *testing.T, url *regexp.Regexp, params url.Values, code in
return testhelper.TestServerWithHandler(url, func(w http.ResponseWriter, r *http.Request) {
require.NotEmpty(t, r.Header.Get("X-Request-Id"))
+ // return a 204 No Content response if we don't receive the JWT header
+ if r.Header.Get(secret.RequestHeader) == "" {
+ w.WriteHeader(204)
+ return
+ }
+
w.Header().Set("Content-Type", api.ResponseContentType)
logEntry := log.WithFields(log.Fields{
diff --git a/workhorse/upload_test.go b/workhorse/upload_test.go
index 1e5d9bd00e9..4ec65b6607f 100644
--- a/workhorse/upload_test.go
+++ b/workhorse/upload_test.go
@@ -234,7 +234,7 @@ func TestLfsUpload(t *testing.T) {
reqBody := "test data"
rspBody := "test success"
oid := "916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9"
- resource := fmt.Sprintf("/%s/gitlab-lfs/objects/%s/%d", testRepo, oid, len(reqBody))
+ resource := fmt.Sprintf("/gitlab-org/gitlab-test.git/gitlab-lfs/objects/%s/%d", oid, len(reqBody))
lfsApiResponse := fmt.Sprintf(
`{"TempPath":%q, "LfsOid":%q, "LfsSize": %d}`,
@@ -292,6 +292,74 @@ func TestLfsUpload(t *testing.T) {
require.Equal(t, rspBody, string(rspData))
}
+func TestLfsUploadRouting(t *testing.T) {
+ reqBody := "test data"
+ rspBody := "test success"
+ oid := "916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9"
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get(secret.RequestHeader) == "" {
+ w.WriteHeader(204)
+ } else {
+ fmt.Fprint(w, rspBody)
+ }
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ testCases := []struct {
+ method string
+ path string
+ contentType string
+ match bool
+ }{
+ {"PUT", "/toplevel.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/toplevel.wiki.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/toplevel/child/project.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/toplevel/child/project.wiki.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/toplevel/child/project/snippets/123.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/snippets/123.git/gitlab-lfs/objects", "application/octet-stream", true},
+ {"PUT", "/foo/bar/gitlab-lfs/objects", "application/octet-stream", false},
+ {"PUT", "/foo/bar.git/gitlab-lfs/objects/zzz", "application/octet-stream", false},
+ {"PUT", "/.git/gitlab-lfs/objects", "application/octet-stream", false},
+ {"PUT", "/toplevel.git/gitlab-lfs/objects", "application/zzz", false},
+ {"POST", "/toplevel.git/gitlab-lfs/objects", "application/octet-stream", false},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.path, func(t *testing.T) {
+ resource := fmt.Sprintf(tc.path+"/%s/%d", oid, len(reqBody))
+
+ req, err := http.NewRequest(
+ tc.method,
+ ws.URL+resource,
+ strings.NewReader(reqBody),
+ )
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", tc.contentType)
+ req.ContentLength = int64(len(reqBody))
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ rspData, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ if tc.match {
+ require.Equal(t, 200, resp.StatusCode)
+ require.Equal(t, rspBody, string(rspData), "expect response generated by test upstream server")
+ } else {
+ require.Equal(t, 204, resp.StatusCode)
+ require.Empty(t, rspData, "normal request has empty response body")
+ }
+ })
+ }
+}
+
func packageUploadTestServer(t *testing.T, resource string, reqBody string, rspBody string) *httptest.Server {
return testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, r.Method, "PUT")