Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
049d16d168
commit
a3764262c0
|
@ -127,7 +127,7 @@ variables:
|
||||||
# Run with decomposed databases by default
|
# Run with decomposed databases by default
|
||||||
DECOMPOSED_DB: "true"
|
DECOMPOSED_DB: "true"
|
||||||
|
|
||||||
DOCS_REVIEW_APPS_DOMAIN: "35.193.151.162.nip.io"
|
DOCS_REVIEW_APPS_DOMAIN: "docs.gitlab-review-app"
|
||||||
DOCS_GITLAB_REPO_SUFFIX: "ee"
|
DOCS_GITLAB_REPO_SUFFIX: "ee"
|
||||||
|
|
||||||
REVIEW_APPS_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/ruby-3.0:gcloud-383-kubectl-1.23-helm-3.5"
|
REVIEW_APPS_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/ruby-3.0:gcloud-383-kubectl-1.23-helm-3.5"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --autocorrect.
|
||||||
Gitlab/Json:
|
Gitlab/Json:
|
||||||
Details: grace period
|
Details: grace period
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -247,6 +247,7 @@ Gitlab/Json:
|
||||||
- 'lib/gitlab/database/background_migration/batched_migration.rb'
|
- 'lib/gitlab/database/background_migration/batched_migration.rb'
|
||||||
- 'lib/gitlab/database/background_migration_job.rb'
|
- 'lib/gitlab/database/background_migration_job.rb'
|
||||||
- 'lib/gitlab/database/migration_helpers.rb'
|
- 'lib/gitlab/database/migration_helpers.rb'
|
||||||
|
- 'lib/gitlab/database/migrations/batched_background_migration_helpers.rb'
|
||||||
- 'lib/gitlab/database/migrations/instrumentation.rb'
|
- 'lib/gitlab/database/migrations/instrumentation.rb'
|
||||||
- 'lib/gitlab/database/migrations/runner.rb'
|
- 'lib/gitlab/database/migrations/runner.rb'
|
||||||
- 'lib/gitlab/database/postgres_hll/buckets.rb'
|
- 'lib/gitlab/database/postgres_hll/buckets.rb'
|
||||||
|
@ -284,6 +285,7 @@ Gitlab/Json:
|
||||||
- 'lib/microsoft_teams/notifier.rb'
|
- 'lib/microsoft_teams/notifier.rb'
|
||||||
- 'lib/tasks/gitlab/background_migrations.rake'
|
- 'lib/tasks/gitlab/background_migrations.rake'
|
||||||
- 'lib/version_check.rb'
|
- 'lib/version_check.rb'
|
||||||
|
- 'spec/commands/diagnostic_reports/uploader_smoke_spec.rb'
|
||||||
- 'spec/controllers/admin/integrations_controller_spec.rb'
|
- 'spec/controllers/admin/integrations_controller_spec.rb'
|
||||||
- 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
|
- 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
|
||||||
- 'spec/controllers/groups/settings/integrations_controller_spec.rb'
|
- 'spec/controllers/groups/settings/integrations_controller_spec.rb'
|
||||||
|
@ -417,7 +419,9 @@ Gitlab/Json:
|
||||||
- 'spec/requests/api/merge_requests_spec.rb'
|
- 'spec/requests/api/merge_requests_spec.rb'
|
||||||
- 'spec/requests/api/namespaces_spec.rb'
|
- 'spec/requests/api/namespaces_spec.rb'
|
||||||
- 'spec/requests/api/project_snapshots_spec.rb'
|
- 'spec/requests/api/project_snapshots_spec.rb'
|
||||||
|
- 'spec/requests/groups/settings/access_tokens_controller_spec.rb'
|
||||||
- 'spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
|
- 'spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
|
||||||
|
- 'spec/requests/projects/settings/access_tokens_controller_spec.rb'
|
||||||
- 'spec/requests/users_controller_spec.rb'
|
- 'spec/requests/users_controller_spec.rb'
|
||||||
- 'spec/requests/whats_new_controller_spec.rb'
|
- 'spec/requests/whats_new_controller_spec.rb'
|
||||||
- 'spec/scripts/pipeline_test_report_builder_spec.rb'
|
- 'spec/scripts/pipeline_test_report_builder_spec.rb'
|
||||||
|
|
|
@ -2855,7 +2855,6 @@ Layout/LineLength:
|
||||||
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
|
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb'
|
- 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb'
|
- 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb'
|
- 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb'
|
- 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
|
- 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb'
|
||||||
|
@ -5968,7 +5967,6 @@ Layout/LineLength:
|
||||||
- 'spec/views/projects/tags/index.html.haml_spec.rb'
|
- 'spec/views/projects/tags/index.html.haml_spec.rb'
|
||||||
- 'spec/views/projects/tree/show.html.haml_spec.rb'
|
- 'spec/views/projects/tree/show.html.haml_spec.rb'
|
||||||
- 'spec/views/search/_results.html.haml_spec.rb'
|
- 'spec/views/search/_results.html.haml_spec.rb'
|
||||||
- 'spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
|
- 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
|
||||||
- 'spec/views/shared/projects/_project.html.haml_spec.rb'
|
- 'spec/views/shared/projects/_project.html.haml_spec.rb'
|
||||||
- 'spec/views/shared/snippets/_snippet.html.haml_spec.rb'
|
- 'spec/views/shared/snippets/_snippet.html.haml_spec.rb'
|
||||||
|
|
|
@ -743,7 +743,6 @@ Style/IfUnlessModifier:
|
||||||
- 'ee/spec/support/http_io/http_io_helpers.rb'
|
- 'ee/spec/support/http_io/http_io_helpers.rb'
|
||||||
- 'ee/spec/support/shared_examples/requests/api/graphql/geo/registries_shared_examples.rb'
|
- 'ee/spec/support/shared_examples/requests/api/graphql/geo/registries_shared_examples.rb'
|
||||||
- 'ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
|
- 'ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
|
||||||
- 'ee/spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
|
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
|
||||||
- 'lib/api/api_guard.rb'
|
- 'lib/api/api_guard.rb'
|
||||||
- 'lib/api/boards_responses.rb'
|
- 'lib/api/boards_responses.rb'
|
||||||
|
@ -1202,7 +1201,6 @@ Style/IfUnlessModifier:
|
||||||
- 'spec/views/groups/edit.html.haml_spec.rb'
|
- 'spec/views/groups/edit.html.haml_spec.rb'
|
||||||
- 'spec/views/profiles/keys/_key.html.haml_spec.rb'
|
- 'spec/views/profiles/keys/_key.html.haml_spec.rb'
|
||||||
- 'spec/views/projects/edit.html.haml_spec.rb'
|
- 'spec/views/projects/edit.html.haml_spec.rb'
|
||||||
- 'spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
|
- 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
|
||||||
- 'tooling/danger/product_intelligence.rb'
|
- 'tooling/danger/product_intelligence.rb'
|
||||||
- 'tooling/lib/tooling/find_codeowners.rb'
|
- 'tooling/lib/tooling/find_codeowners.rb'
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -428,7 +428,7 @@ group :development, :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test, :danger do
|
group :development, :test, :danger do
|
||||||
gem 'gitlab-dangerfiles', '~> 3.5.2', require: false
|
gem 'gitlab-dangerfiles', '~> 3.6.1', require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test, :coverage do
|
group :development, :test, :coverage do
|
||||||
|
|
|
@ -151,7 +151,7 @@
|
||||||
{"name":"faraday-em_http","version":"1.0.0","platform":"ruby","checksum":"7a3d4c7079789121054f57e08cd4ef7e40ad1549b63101f38c7093a9d6c59689"},
|
{"name":"faraday-em_http","version":"1.0.0","platform":"ruby","checksum":"7a3d4c7079789121054f57e08cd4ef7e40ad1549b63101f38c7093a9d6c59689"},
|
||||||
{"name":"faraday-em_synchrony","version":"1.0.0","platform":"ruby","checksum":"460dad1c30cc692d6e77d4c391ccadb4eca4854b315632cd7e560f74275cf9ed"},
|
{"name":"faraday-em_synchrony","version":"1.0.0","platform":"ruby","checksum":"460dad1c30cc692d6e77d4c391ccadb4eca4854b315632cd7e560f74275cf9ed"},
|
||||||
{"name":"faraday-excon","version":"1.1.0","platform":"ruby","checksum":"b055c842376734d7f74350fe8611542ae2000c5387348d9ba9708109d6e40940"},
|
{"name":"faraday-excon","version":"1.1.0","platform":"ruby","checksum":"b055c842376734d7f74350fe8611542ae2000c5387348d9ba9708109d6e40940"},
|
||||||
{"name":"faraday-http-cache","version":"2.4.0","platform":"ruby","checksum":"388f901d63bd5903b470c5696bc886ed94fab0c4206b25c3761e7b9bdbbf6c90"},
|
{"name":"faraday-http-cache","version":"2.4.1","platform":"ruby","checksum":"fb51b2e9ee72f89e81cc277ee574dbc5940f3db95431b3533de9882f92635ee3"},
|
||||||
{"name":"faraday-httpclient","version":"1.0.1","platform":"ruby","checksum":"4c8ff1f0973ff835be8d043ef16aaf54f47f25b7578f6d916deee8399a04d33b"},
|
{"name":"faraday-httpclient","version":"1.0.1","platform":"ruby","checksum":"4c8ff1f0973ff835be8d043ef16aaf54f47f25b7578f6d916deee8399a04d33b"},
|
||||||
{"name":"faraday-multipart","version":"1.0.4","platform":"ruby","checksum":"9012021ab57790f7d712f590b48d5f948b19b43cfa11ca83e6459f06090b0725"},
|
{"name":"faraday-multipart","version":"1.0.4","platform":"ruby","checksum":"9012021ab57790f7d712f590b48d5f948b19b43cfa11ca83e6459f06090b0725"},
|
||||||
{"name":"faraday-net_http","version":"1.0.1","platform":"ruby","checksum":"3245ce406ebb77b40e17a77bfa66191dda04be2fd4e13a78d8a4305854d328ba"},
|
{"name":"faraday-net_http","version":"1.0.1","platform":"ruby","checksum":"3245ce406ebb77b40e17a77bfa66191dda04be2fd4e13a78d8a4305854d328ba"},
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
{"name":"github-markup","version":"1.7.0","platform":"ruby","checksum":"97eb27c70662d9cc1d5997cd6c99832026fae5d4913b5dce1ce6c9f65078e69d"},
|
{"name":"github-markup","version":"1.7.0","platform":"ruby","checksum":"97eb27c70662d9cc1d5997cd6c99832026fae5d4913b5dce1ce6c9f65078e69d"},
|
||||||
{"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
|
{"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
|
||||||
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
|
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
|
||||||
{"name":"gitlab-dangerfiles","version":"3.5.2","platform":"ruby","checksum":"fae28a55b83b6c7f8298b9b1d90354ae73636729fd829ad58326bef46bd2f01f"},
|
{"name":"gitlab-dangerfiles","version":"3.6.1","platform":"ruby","checksum":"f7b69b093d52acb89095d411cb7b8849f5f3b9e76f8baa4c99b5671f1564865f"},
|
||||||
{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
|
{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
|
||||||
{"name":"gitlab-fog-azure-rm","version":"1.3.0","platform":"ruby","checksum":"2fef5317d6515f95f803099afa860fe3019ce6e1907bf49f66b5e06468a617b5"},
|
{"name":"gitlab-fog-azure-rm","version":"1.3.0","platform":"ruby","checksum":"2fef5317d6515f95f803099afa860fe3019ce6e1907bf49f66b5e06468a617b5"},
|
||||||
{"name":"gitlab-labkit","version":"0.24.0","platform":"ruby","checksum":"8f16e5aa4e0a05be58958fe880bdd53c84b659a081ea9981d2b510922a4a0548"},
|
{"name":"gitlab-labkit","version":"0.24.0","platform":"ruby","checksum":"8f16e5aa4e0a05be58958fe880bdd53c84b659a081ea9981d2b510922a4a0548"},
|
||||||
|
|
|
@ -448,7 +448,7 @@ GEM
|
||||||
faraday-em_http (1.0.0)
|
faraday-em_http (1.0.0)
|
||||||
faraday-em_synchrony (1.0.0)
|
faraday-em_synchrony (1.0.0)
|
||||||
faraday-excon (1.1.0)
|
faraday-excon (1.1.0)
|
||||||
faraday-http-cache (2.4.0)
|
faraday-http-cache (2.4.1)
|
||||||
faraday (>= 0.8)
|
faraday (>= 0.8)
|
||||||
faraday-httpclient (1.0.1)
|
faraday-httpclient (1.0.1)
|
||||||
faraday-multipart (1.0.4)
|
faraday-multipart (1.0.4)
|
||||||
|
@ -555,7 +555,7 @@ GEM
|
||||||
terminal-table (~> 1.5, >= 1.5.1)
|
terminal-table (~> 1.5, >= 1.5.1)
|
||||||
gitlab-chronic (0.10.5)
|
gitlab-chronic (0.10.5)
|
||||||
numerizer (~> 0.2)
|
numerizer (~> 0.2)
|
||||||
gitlab-dangerfiles (3.5.2)
|
gitlab-dangerfiles (3.6.1)
|
||||||
danger (>= 8.4.5)
|
danger (>= 8.4.5)
|
||||||
danger-gitlab (>= 8.0.0)
|
danger-gitlab (>= 8.0.0)
|
||||||
rake
|
rake
|
||||||
|
@ -1627,7 +1627,7 @@ DEPENDENCIES
|
||||||
gitaly (~> 15.4.0.pre.rc2)
|
gitaly (~> 15.4.0.pre.rc2)
|
||||||
github-markup (~> 1.7.0)
|
github-markup (~> 1.7.0)
|
||||||
gitlab-chronic (~> 0.10.5)
|
gitlab-chronic (~> 0.10.5)
|
||||||
gitlab-dangerfiles (~> 3.5.2)
|
gitlab-dangerfiles (~> 3.6.1)
|
||||||
gitlab-experiment (~> 0.7.1)
|
gitlab-experiment (~> 0.7.1)
|
||||||
gitlab-fog-azure-rm (~> 1.3.0)
|
gitlab-fog-azure-rm (~> 1.3.0)
|
||||||
gitlab-labkit (~> 0.24.0)
|
gitlab-labkit (~> 0.24.0)
|
||||||
|
|
|
@ -141,6 +141,7 @@ export default {
|
||||||
<gl-link
|
<gl-link
|
||||||
:href="computedPath"
|
:href="computedPath"
|
||||||
class="sortable-link gl-font-weight-normal"
|
class="sortable-link gl-font-weight-normal"
|
||||||
|
target="_blank"
|
||||||
@click="handleTitleClick"
|
@click="handleTitleClick"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlAlert, GlLoadingIcon, GlModal } from '@gitlab/ui';
|
import { GlAlert, GlBadge, GlButton, GlLoadingIcon, GlModal, GlTabs, GlTab } from '@gitlab/ui';
|
||||||
import { s__, __ } from '~/locale';
|
import { s__, __ } from '~/locale';
|
||||||
|
import { limitedCounterWithDelimiter } from '~/lib/utils/text_utility';
|
||||||
|
import { queryToObject } from '~/lib/utils/url_utility';
|
||||||
import deletePipelineScheduleMutation from '../graphql/mutations/delete_pipeline_schedule.mutation.graphql';
|
import deletePipelineScheduleMutation from '../graphql/mutations/delete_pipeline_schedule.mutation.graphql';
|
||||||
import getPipelineSchedulesQuery from '../graphql/queries/get_pipeline_schedules.query.graphql';
|
import getPipelineSchedulesQuery from '../graphql/queries/get_pipeline_schedules.query.graphql';
|
||||||
import PipelineSchedulesTable from './table/pipeline_schedules_table.vue';
|
import PipelineSchedulesTable from './table/pipeline_schedules_table.vue';
|
||||||
|
@ -11,6 +13,7 @@ export default {
|
||||||
scheduleDeleteError: s__(
|
scheduleDeleteError: s__(
|
||||||
'PipelineSchedules|There was a problem deleting the pipeline schedule.',
|
'PipelineSchedules|There was a problem deleting the pipeline schedule.',
|
||||||
),
|
),
|
||||||
|
newSchedule: s__('PipelineSchedules|New schedule'),
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
id: 'delete-pipeline-schedule-modal',
|
id: 'delete-pipeline-schedule-modal',
|
||||||
|
@ -28,8 +31,12 @@ export default {
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
GlAlert,
|
GlAlert,
|
||||||
|
GlBadge,
|
||||||
|
GlButton,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
GlModal,
|
GlModal,
|
||||||
|
GlTabs,
|
||||||
|
GlTab,
|
||||||
PipelineSchedulesTable,
|
PipelineSchedulesTable,
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
|
@ -43,10 +50,16 @@ export default {
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
projectPath: this.fullPath,
|
projectPath: this.fullPath,
|
||||||
|
status: this.scope,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
update({ project }) {
|
update(data) {
|
||||||
return project?.pipelineSchedules?.nodes || [];
|
const { pipelineSchedules: { nodes: list = [], count } = {} } = data.project || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
count,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
error() {
|
error() {
|
||||||
this.reportError(this.$options.i18n.schedulesFetchError);
|
this.reportError(this.$options.i18n.schedulesFetchError);
|
||||||
|
@ -54,18 +67,58 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const { scope } = queryToObject(window.location.search);
|
||||||
return {
|
return {
|
||||||
schedules: [],
|
schedules: {
|
||||||
|
list: [],
|
||||||
|
},
|
||||||
|
scope,
|
||||||
hasError: false,
|
hasError: false,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
scheduleToDeleteId: null,
|
scheduleToDeleteId: null,
|
||||||
showModal: false,
|
showModal: false,
|
||||||
|
count: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoading() {
|
isLoading() {
|
||||||
return this.$apollo.queries.schedules.loading;
|
return this.$apollo.queries.schedules.loading;
|
||||||
},
|
},
|
||||||
|
schedulesCount() {
|
||||||
|
return this.schedules.count;
|
||||||
|
},
|
||||||
|
tabs() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: s__('PipelineSchedules|All'),
|
||||||
|
count: limitedCounterWithDelimiter(this.count),
|
||||||
|
scope: null,
|
||||||
|
showBadge: true,
|
||||||
|
attrs: { 'data-testid': 'pipeline-schedules-all-tab' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: s__('PipelineSchedules|Active'),
|
||||||
|
scope: 'ACTIVE',
|
||||||
|
showBadge: false,
|
||||||
|
attrs: { 'data-testid': 'pipeline-schedules-active-tab' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: s__('PipelineSchedules|Inactive'),
|
||||||
|
scope: 'INACTIVE',
|
||||||
|
showBadge: false,
|
||||||
|
attrs: { 'data-testid': 'pipeline-schedules-inactive-tab' },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// this watcher ensures that the count on the all tab
|
||||||
|
// is not updated when switching to other tabs
|
||||||
|
schedulesCount(newCount) {
|
||||||
|
if (!this.scope) {
|
||||||
|
this.count = newCount;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reportError(error) {
|
reportError(error) {
|
||||||
|
@ -100,6 +153,10 @@ export default {
|
||||||
this.reportError(this.$options.i18n.scheduleDeleteError);
|
this.reportError(this.$options.i18n.scheduleDeleteError);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fetchPipelineSchedulesByStatus(scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.$apollo.queries.schedules.refetch();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -110,12 +167,45 @@ export default {
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</gl-alert>
|
</gl-alert>
|
||||||
|
|
||||||
<gl-loading-icon v-if="isLoading" size="lg" />
|
|
||||||
|
|
||||||
<!-- Tabs will be addressed in #371989 -->
|
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<pipeline-schedules-table :schedules="schedules" @showDeleteModal="showDeleteModal" />
|
<gl-tabs
|
||||||
|
sync-active-tab-with-query-params
|
||||||
|
query-param-name="scope"
|
||||||
|
nav-class="gl-flex-grow-1 gl-align-items-center"
|
||||||
|
>
|
||||||
|
<gl-tab
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.text"
|
||||||
|
:title-link-attributes="tab.attrs"
|
||||||
|
:query-param-value="tab.scope"
|
||||||
|
@click="fetchPipelineSchedulesByStatus(tab.scope)"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span>{{ tab.text }}</span>
|
||||||
|
|
||||||
|
<template v-if="tab.showBadge">
|
||||||
|
<gl-loading-icon v-if="tab.scope === scope && isLoading" class="gl-ml-2" />
|
||||||
|
|
||||||
|
<gl-badge v-else-if="tab.count" size="sm" class="gl-tab-counter-badge">
|
||||||
|
{{ tab.count }}
|
||||||
|
</gl-badge>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<gl-loading-icon v-if="isLoading" size="lg" />
|
||||||
|
<pipeline-schedules-table
|
||||||
|
v-else
|
||||||
|
:schedules="schedules.list"
|
||||||
|
@showDeleteModal="showDeleteModal"
|
||||||
|
/>
|
||||||
|
</gl-tab>
|
||||||
|
|
||||||
|
<template #tabs-end>
|
||||||
|
<gl-button variant="confirm" class="gl-ml-auto" data-testid="new-schedule-button">
|
||||||
|
{{ $options.i18n.newSchedule }}
|
||||||
|
</gl-button>
|
||||||
|
</template>
|
||||||
|
</gl-tabs>
|
||||||
|
|
||||||
<gl-modal
|
<gl-modal
|
||||||
:visible="showModal"
|
:visible="showModal"
|
||||||
|
|
|
@ -12,31 +12,37 @@ export default {
|
||||||
{
|
{
|
||||||
key: 'description',
|
key: 'description',
|
||||||
label: s__('PipelineSchedules|Description'),
|
label: s__('PipelineSchedules|Description'),
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-40p',
|
columnClass: 'gl-w-40p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'target',
|
key: 'target',
|
||||||
label: s__('PipelineSchedules|Target'),
|
label: s__('PipelineSchedules|Target'),
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-10p',
|
columnClass: 'gl-w-10p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'pipeline',
|
key: 'pipeline',
|
||||||
label: s__('PipelineSchedules|Last Pipeline'),
|
label: s__('PipelineSchedules|Last Pipeline'),
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-10p',
|
columnClass: 'gl-w-10p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'next',
|
key: 'next',
|
||||||
label: s__('PipelineSchedules|Next Run'),
|
label: s__('PipelineSchedules|Next Run'),
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-15p',
|
columnClass: 'gl-w-15p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'owner',
|
key: 'owner',
|
||||||
label: s__('PipelineSchedules|Owner'),
|
label: s__('PipelineSchedules|Owner'),
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-10p',
|
columnClass: 'gl-w-10p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
label: '',
|
label: '',
|
||||||
|
thClass: 'gl-border-t-none!',
|
||||||
columnClass: 'gl-w-15p',
|
columnClass: 'gl-w-15p',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
query getPipelineSchedulesQuery($projectPath: ID!) {
|
query getPipelineSchedulesQuery($projectPath: ID!, $status: PipelineScheduleStatus) {
|
||||||
project(fullPath: $projectPath) {
|
project(fullPath: $projectPath) {
|
||||||
id
|
id
|
||||||
pipelineSchedules {
|
pipelineSchedules(status: $status) {
|
||||||
|
count
|
||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
description
|
description
|
||||||
|
|
|
@ -64,7 +64,7 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def allow_self_managed_content_security_policy
|
def allow_self_managed_content_security_policy
|
||||||
return unless Feature.enabled?(:jira_connect_oauth_self_managed)
|
return unless Feature.enabled?(:jira_connect_oauth_self_managed_setting)
|
||||||
|
|
||||||
return unless current_jira_installation.instance_url?
|
return unless current_jira_installation.instance_url?
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,9 @@ class Namespace < ApplicationRecord
|
||||||
|
|
||||||
PATH_TRAILING_VIOLATIONS = %w[.git .atom .].freeze
|
PATH_TRAILING_VIOLATIONS = %w[.git .atom .].freeze
|
||||||
|
|
||||||
# The first date in https://docs.gitlab.com/ee/user/usage_quotas.html#namespace-storage-limit-enforcement-schedule
|
# This date is just a placeholder until namespace storage enforcement timeline is confirmed at which point
|
||||||
# Determines when we start enforcing namespace storage
|
# this should be replaced, see https://about.gitlab.com/pricing/faq-efficient-free-tier/#user-limits-on-gitlab-saas-free-tier
|
||||||
MIN_STORAGE_ENFORCEMENT_DATE = Date.new(2022, 10, 19)
|
MIN_STORAGE_ENFORCEMENT_DATE = 3.months.from_now.to_date
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/367531
|
# https://gitlab.com/gitlab-org/gitlab/-/issues/367531
|
||||||
MIN_STORAGE_ENFORCEMENT_USAGE = 5.gigabytes
|
MIN_STORAGE_ENFORCEMENT_USAGE = 5.gigabytes
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@ class PersonalAccessToken < ApplicationRecord
|
||||||
|
|
||||||
add_authentication_token_field :token, digest: true
|
add_authentication_token_field :token, digest: true
|
||||||
|
|
||||||
REDIS_EXPIRY_TIME = 3.minutes
|
|
||||||
|
|
||||||
# PATs are 20 characters + optional configurable settings prefix (0..20)
|
# PATs are 20 characters + optional configurable settings prefix (0..20)
|
||||||
TOKEN_LENGTH_RANGE = (20..40).freeze
|
TOKEN_LENGTH_RANGE = (20..40).freeze
|
||||||
|
|
||||||
|
@ -34,8 +32,6 @@ class PersonalAccessToken < ApplicationRecord
|
||||||
scope :for_user, -> (user) { where(user: user) }
|
scope :for_user, -> (user) { where(user: user) }
|
||||||
scope :for_users, -> (users) { where(user: users) }
|
scope :for_users, -> (users) { where(user: users) }
|
||||||
scope :preload_users, -> { preload(:user) }
|
scope :preload_users, -> { preload(:user) }
|
||||||
scope :order_expires_at_asc, -> { reorder(expires_at: :asc) }
|
|
||||||
scope :order_expires_at_desc, -> { reorder(expires_at: :desc) }
|
|
||||||
scope :order_expires_at_asc_id_desc, -> { reorder(expires_at: :asc, id: :desc) }
|
scope :order_expires_at_asc_id_desc, -> { reorder(expires_at: :asc, id: :desc) }
|
||||||
scope :project_access_token, -> { includes(:user).where(user: { user_type: :project_bot }) }
|
scope :project_access_token, -> { includes(:user).where(user: { user_type: :project_bot }) }
|
||||||
scope :owner_is_human, -> { includes(:user).where(user: { user_type: :human }) }
|
scope :owner_is_human, -> { includes(:user).where(user: { user_type: :human }) }
|
||||||
|
@ -55,35 +51,10 @@ class PersonalAccessToken < ApplicationRecord
|
||||||
!revoked? && !expired?
|
!revoked? && !expired?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.redis_getdel(user_id)
|
|
||||||
Gitlab::Redis::SharedState.with do |redis|
|
|
||||||
redis_key = redis_shared_state_key(user_id)
|
|
||||||
encrypted_token = redis.get(redis_key)
|
|
||||||
redis.del(redis_key)
|
|
||||||
|
|
||||||
begin
|
|
||||||
Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_token)
|
|
||||||
rescue StandardError => e
|
|
||||||
logger.warn "Failed to decrypt #{self.name} value stored in Redis for key ##{redis_key}: #{e.class}"
|
|
||||||
encrypted_token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.redis_store!(user_id, token)
|
|
||||||
encrypted_token = Gitlab::CryptoHelper.aes256_gcm_encrypt(token)
|
|
||||||
|
|
||||||
Gitlab::Redis::SharedState.with do |redis|
|
|
||||||
redis.set(redis_shared_state_key(user_id), encrypted_token, ex: REDIS_EXPIRY_TIME)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
override :simple_sorts
|
override :simple_sorts
|
||||||
def self.simple_sorts
|
def self.simple_sorts
|
||||||
super.merge(
|
super.merge(
|
||||||
{
|
{
|
||||||
'expires_at_asc' => -> { order_expires_at_asc },
|
|
||||||
'expires_at_desc' => -> { order_expires_at_desc },
|
|
||||||
'expires_at_asc_id_desc' => -> { order_expires_at_asc_id_desc }
|
'expires_at_asc_id_desc' => -> { order_expires_at_asc_id_desc }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -121,10 +92,6 @@ class PersonalAccessToken < ApplicationRecord
|
||||||
|
|
||||||
self.scopes = Gitlab::Auth::DEFAULT_SCOPES if self.scopes.empty?
|
self.scopes = Gitlab::Auth::DEFAULT_SCOPES if self.scopes.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.redis_shared_state_key(user_id)
|
|
||||||
"gitlab:personal_access_token:#{user_id}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
PersonalAccessToken.prepend_mod_with('PersonalAccessToken')
|
PersonalAccessToken.prepend_mod_with('PersonalAccessToken')
|
||||||
|
|
|
@ -123,4 +123,4 @@
|
||||||
= render 'admin/application_settings/eks'
|
= render 'admin/application_settings/eks'
|
||||||
= render 'admin/application_settings/floc'
|
= render 'admin/application_settings/floc'
|
||||||
= render_if_exists 'admin/application_settings/add_license'
|
= render_if_exists 'admin/application_settings/add_license'
|
||||||
= render 'admin/application_settings/jira_connect_application_key' if Feature.enabled?(:jira_connect_oauth, current_user)
|
= render 'admin/application_settings/jira_connect_application_key' if Feature.enabled?(:jira_connect_oauth_self_managed_setting, current_user)
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
- form_field = local_assigns.fetch(:form_field, nil)
|
|
||||||
- variable = local_assigns.fetch(:variable, nil)
|
|
||||||
|
|
||||||
- key = variable[0]
|
|
||||||
- value = variable[1]
|
|
||||||
- variable_type = variable[2] || "env_var"
|
|
||||||
|
|
||||||
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
|
|
||||||
- variable_type_input_name = "#{form_field}[variables_attributes][][variable_type]"
|
|
||||||
- key_input_name = "#{form_field}[variables_attributes][][key]"
|
|
||||||
- value_input_name = "#{form_field}[variables_attributes][][secret_value]"
|
|
||||||
|
|
||||||
%li.js-row.ci-variable-row
|
|
||||||
.ci-variable-row-body.border-bottom
|
|
||||||
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
|
|
||||||
%select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.custom-select.table-section.section-15{ name: variable_type_input_name }
|
|
||||||
= options_for_select(ci_variable_type_options, variable_type)
|
|
||||||
%input.js-ci-variable-input-key.ci-variable-body-item.form-control.table-section.section-15{ type: "text",
|
|
||||||
name: key_input_name,
|
|
||||||
value: key,
|
|
||||||
placeholder: s_('CiVariables|Input variable key') }
|
|
||||||
.ci-variable-body-item.gl-show-field-errors.table-section.section-15.border-top-0.p-0
|
|
||||||
%textarea.js-ci-variable-input-value.js-secret-value.form-control{ rows: 1,
|
|
||||||
name: value_input_name,
|
|
||||||
placeholder: s_('CiVariables|Input variable value') }
|
|
||||||
= value
|
|
||||||
%button.gl-button.btn.btn-default.btn-icon.btn-item-remove.js-row-remove-button.ci-variable-row-remove-button.table-section{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
|
|
||||||
= sprite_icon('close')
|
|
|
@ -1,12 +0,0 @@
|
||||||
.created-personal-access-token-container
|
|
||||||
%h5.gl-mt-0
|
|
||||||
= _('Your new %{type}') % { type: type }
|
|
||||||
.form-group
|
|
||||||
.input-group
|
|
||||||
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'form-control js-select-on-focus', data: { qa_selector: 'created_access_token_field' }, 'aria-describedby' => 'created-token-help-block'
|
|
||||||
%span.input-group-append
|
|
||||||
= clipboard_button(text: new_token_value, title: _('Copy %{type}') % { type: type }, placement: 'left', class: 'input-group-text btn-default btn-clipboard')
|
|
||||||
%span#created-token-help-block.form-text.text-muted.text-danger
|
|
||||||
= _("Make sure you save it - you won't be able to access it again.")
|
|
||||||
|
|
||||||
%hr
|
|
|
@ -1,51 +0,0 @@
|
||||||
- no_active_tokens_message = local_assigns.fetch(:no_active_tokens_message, _('This user has no active %{type}.') % { type: type_plural })
|
|
||||||
- impersonation = local_assigns.fetch(:impersonation, false)
|
|
||||||
- resource = local_assigns.fetch(:resource, false)
|
|
||||||
|
|
||||||
%hr
|
|
||||||
|
|
||||||
%h5
|
|
||||||
= _('Active %{type} (%{token_length})') % { type: type_plural, token_length: active_tokens.length }
|
|
||||||
|
|
||||||
- if impersonation
|
|
||||||
%p.profile-settings-content
|
|
||||||
= _("To see all the user's personal access tokens you must impersonate them first.")
|
|
||||||
|
|
||||||
- if active_tokens.present?
|
|
||||||
.table-responsive
|
|
||||||
%table.table.active-tokens
|
|
||||||
%thead
|
|
||||||
%tr
|
|
||||||
%th= _('Token name')
|
|
||||||
%th= _('Scopes')
|
|
||||||
%th= s_('AccessTokens|Created')
|
|
||||||
%th
|
|
||||||
= _('Last Used')
|
|
||||||
= link_to sprite_icon('question-o'), help_page_path('user/profile/personal_access_tokens.md', anchor: 'view-the-last-time-a-token-was-used'), target: '_blank', rel: 'noopener noreferrer'
|
|
||||||
%th= _('Expires')
|
|
||||||
- if resource
|
|
||||||
%th= _('Role')
|
|
||||||
%th
|
|
||||||
%tbody
|
|
||||||
- active_tokens.each do |token|
|
|
||||||
%tr
|
|
||||||
%td= token.name
|
|
||||||
%td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
|
|
||||||
%td= token.created_at.to_date.to_s(:medium)
|
|
||||||
%td
|
|
||||||
- if token.last_used_at?
|
|
||||||
%span.token-last-used-label= _(time_ago_with_tooltip(token.last_used_at))
|
|
||||||
- else
|
|
||||||
%span.token-never-used-label= _('Never')
|
|
||||||
%td
|
|
||||||
- if token.expires?
|
|
||||||
%span{ class: ('text-warning' if token.expires_soon?) }
|
|
||||||
= time_ago_with_tooltip(token.expires_at)
|
|
||||||
- else
|
|
||||||
%span.token-never-expires-label= _('Never')
|
|
||||||
- if resource
|
|
||||||
%td= resource.member(token.user).human_access
|
|
||||||
%td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: "gl-button btn btn-danger btn-sm float-right #{'btn-danger-secondary' unless token.expires?}", aria: { label: _('Revoke') }, data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type }, 'confirm-btn-variant': 'danger', qa_selector: 'revoke_button' }
|
|
||||||
- else
|
|
||||||
.settings-message.text-center
|
|
||||||
= no_active_tokens_message
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: jira_connect_oauth_self_managed_setting
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100725
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377679
|
||||||
|
milestone: '15.6'
|
||||||
|
type: development
|
||||||
|
group: group::integrations
|
||||||
|
default_enabled: false
|
|
@ -3,7 +3,7 @@ table_name: software_license_policies
|
||||||
classes:
|
classes:
|
||||||
- SoftwareLicensePolicy
|
- SoftwareLicensePolicy
|
||||||
feature_categories:
|
feature_categories:
|
||||||
- license_compliance
|
- security_policy_management
|
||||||
description: Allows user to approve or deny the use certain software licenses in their project.
|
description: Allows user to approve or deny the use certain software licenses in their project.
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
|
||||||
milestone: '11.2'
|
milestone: '11.2'
|
||||||
|
|
|
@ -3,7 +3,7 @@ table_name: software_licenses
|
||||||
classes:
|
classes:
|
||||||
- SoftwareLicense
|
- SoftwareLicense
|
||||||
feature_categories:
|
feature_categories:
|
||||||
- license_compliance
|
- security_policy_management
|
||||||
description: Normalized software licenses to use in conjunction with License Compliance features (like software license policies)
|
description: Normalized software licenses to use in conjunction with License Compliance features (like software license policies)
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6246
|
||||||
milestone: '11.2'
|
milestone: '11.2'
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CleanupVulnerabilityStateTransitionsWithSameFromStateToState < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||||
|
|
||||||
|
class VulnerabilityStateTransition < MigrationRecord
|
||||||
|
self.table_name = 'vulnerability_state_transitions'
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
VulnerabilityStateTransition.where('from_state = to_state').delete_all
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# no-op
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
2ab913b0b479fc29d939d03b5df95dc2a8c5a155f1b35a606e300802cb3aa9d3
|
|
@ -8,15 +8,15 @@ type: reference, api
|
||||||
# Project remote mirrors API **(FREE)**
|
# Project remote mirrors API **(FREE)**
|
||||||
|
|
||||||
[Push mirrors](../user/project/repository/mirror/push.md)
|
[Push mirrors](../user/project/repository/mirror/push.md)
|
||||||
defined on a project's repository settings are called "remote mirrors", and the
|
defined on a project's repository settings are called "remote mirrors". You
|
||||||
state of these mirrors can be queried and modified via the remote mirror API
|
can query and modify the state of these mirrors with the remote mirror API.
|
||||||
outlined below.
|
|
||||||
|
For security reasons, the `url` attribute in the API response is always scrubbed of username
|
||||||
|
and password information.
|
||||||
|
|
||||||
## List a project's remote mirrors
|
## List a project's remote mirrors
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38121) in GitLab 12.9.
|
Returns an array of remote mirrors and their statuses:
|
||||||
|
|
||||||
Returns an Array of remote mirrors and their statuses:
|
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /projects/:id/remote_mirrors
|
GET /projects/:id/remote_mirrors
|
||||||
|
@ -47,10 +47,6 @@ Example response:
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE:
|
|
||||||
For security reasons, the `url` attribute is always scrubbed of username
|
|
||||||
and password information.
|
|
||||||
|
|
||||||
## Get a single project's remote mirror
|
## Get a single project's remote mirror
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82770) in GitLab 14.10.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82770) in GitLab 14.10.
|
||||||
|
@ -84,19 +80,14 @@ Example response:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE:
|
|
||||||
For security reasons, the `url` attribute is always scrubbed of username
|
|
||||||
and password information.
|
|
||||||
|
|
||||||
## Create a pull mirror
|
## Create a pull mirror
|
||||||
|
|
||||||
Learn how to [configure a pull mirror](projects.md#configure-pull-mirroring-for-a-project) using the Projects API.
|
Learn how to [configure a pull mirror](projects.md#configure-pull-mirroring-for-a-project) using the Projects API.
|
||||||
|
|
||||||
## Create a push mirror
|
## Create a push mirror
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24189) in GitLab 12.9.
|
Push mirroring is disabled by default. To enable it, include the optional parameter
|
||||||
|
`enabled` when you create the mirror:
|
||||||
Push mirroring is disabled by default. You can enable it by including the optional parameter `enabled` when creating it:
|
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
POST /projects/:id/remote_mirrors
|
POST /projects/:id/remote_mirrors
|
||||||
|
@ -106,8 +97,8 @@ POST /projects/:id/remote_mirrors
|
||||||
| :---------- | :----- | :--------- | :------------ |
|
| :---------- | :----- | :--------- | :------------ |
|
||||||
| `url` | String | yes | The target URL to which the repository is mirrored. |
|
| `url` | String | yes | The target URL to which the repository is mirrored. |
|
||||||
| `enabled` | Boolean | no | Determines if the mirror is enabled. |
|
| `enabled` | Boolean | no | Determines if the mirror is enabled. |
|
||||||
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
|
||||||
| `keep_divergent_refs` | Boolean | no | Determines if divergent refs are skipped. |
|
| `keep_divergent_refs` | Boolean | no | Determines if divergent refs are skipped. |
|
||||||
|
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
||||||
|
|
||||||
Example request:
|
Example request:
|
||||||
|
|
||||||
|
@ -135,8 +126,6 @@ Example response:
|
||||||
|
|
||||||
## Update a remote mirror's attributes
|
## Update a remote mirror's attributes
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38121) in GitLab 12.9.
|
|
||||||
|
|
||||||
Toggle a remote mirror on or off, or change which types of branches are
|
Toggle a remote mirror on or off, or change which types of branches are
|
||||||
mirrored:
|
mirrored:
|
||||||
|
|
||||||
|
@ -148,8 +137,8 @@ PUT /projects/:id/remote_mirrors/:mirror_id
|
||||||
| :---------- | :----- | :--------- | :------------ |
|
| :---------- | :----- | :--------- | :------------ |
|
||||||
| `mirror_id` | Integer | yes | The remote mirror ID. |
|
| `mirror_id` | Integer | yes | The remote mirror ID. |
|
||||||
| `enabled` | Boolean | no | Determines if the mirror is enabled. |
|
| `enabled` | Boolean | no | Determines if the mirror is enabled. |
|
||||||
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
|
||||||
| `keep_divergent_refs` | Boolean | no | Determines if divergent refs are skipped. |
|
| `keep_divergent_refs` | Boolean | no | Determines if divergent refs are skipped. |
|
||||||
|
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
||||||
|
|
||||||
Example request:
|
Example request:
|
||||||
|
|
||||||
|
|
|
@ -115,21 +115,22 @@ while encapsulating and isolating implementation details.
|
||||||
|
|
||||||
Components allow a pipeline to be assembled by using abstractions instead of having all the details defined in one place.
|
Components allow a pipeline to be assembled by using abstractions instead of having all the details defined in one place.
|
||||||
When using a component in a pipeline, a user shouldn't need to know the implementation details of the component and should
|
When using a component in a pipeline, a user shouldn't need to know the implementation details of the component and should
|
||||||
only rely on the provided interface. The interface will have a version / revision, so that users understand which revision they are interfacing with.
|
only rely on the provided interface.
|
||||||
|
|
||||||
A pipeline component defines its type which indicates in which context of the pipeline configuration the component can be used.
|
A pipeline component defines its type which indicates in which context of the pipeline configuration the component can be used.
|
||||||
For example, a component of type X can only be used according to the type X use-case.
|
For example, a component of type X can only be used according to the type X use-case.
|
||||||
|
|
||||||
For best experience with any systems made of components it's fundamental that components are single purpose,
|
For best experience with any systems made of components it's fundamental that components:
|
||||||
isolated, reusable and resolvable.
|
|
||||||
|
|
||||||
- **Single purpose**: a component must focus on a single goal and the scope be as small as possible.
|
- **Single purpose**: a component must focus on a single goal and the scope be as small as possible.
|
||||||
- **Isolation**: when a component is used in a pipeline, its implementation details should not leak outside the
|
- **Isolated**: when a component is used in a pipeline, its implementation details should not leak outside the
|
||||||
component itself and into the main pipeline.
|
component itself and into the main pipeline.
|
||||||
- **Reusability:** a component is designed to be used in different pipelines.
|
- **Reusable**: a component is designed to be used in different pipelines.
|
||||||
Depending on the assumptions it's built on a component can be more or less generic.
|
Depending on the assumptions it's built on a component can be more or less generic.
|
||||||
Generic components are more reusable but may require more customization.
|
Generic components are more reusable but may require more customization.
|
||||||
- **Resolvable:** When a component depends on another component, this dependency must be explicit and trackable.
|
- **Versioned**: when using a component we must specify the version we are interested in.
|
||||||
|
The version identifies the exact interface and behavior of the component.
|
||||||
|
- **Resolvable**: when a component depends on another component, this dependency must be explicit and trackable.
|
||||||
|
|
||||||
## Proposal
|
## Proposal
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,10 @@ Use:
|
||||||
- The `project` keyword to specify the full path to a downstream project.
|
- The `project` keyword to specify the full path to a downstream project.
|
||||||
In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660), variable expansion is
|
In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660), variable expansion is
|
||||||
supported.
|
supported.
|
||||||
- The `branch` keyword to specify the name of a branch in the project specified by `project`.
|
- The `branch` keyword to specify the name of a branch or [tag](../../topics/git/tags.md)
|
||||||
|
in the project specified by `project`. If you use a tag when a branch exists with the same
|
||||||
|
name, the downstream pipeline fails to create with the error: `downstream pipeline can not be created, Ref is ambiguous`.
|
||||||
|
|
||||||
In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
|
In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
|
||||||
supported.
|
supported.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,10 @@ Use the following job in `.gitlab-ci.yml`. This includes the `artifacts:paths` k
|
||||||
```yaml
|
```yaml
|
||||||
## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report format XML file with rspec
|
## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report format XML file with rspec
|
||||||
ruby:
|
ruby:
|
||||||
|
image: ruby:3.0.4
|
||||||
stage: test
|
stage: test
|
||||||
|
before_script:
|
||||||
|
- apt-get update -y && apt-get install -y bundler
|
||||||
script:
|
script:
|
||||||
- bundle install
|
- bundle install
|
||||||
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
||||||
|
|
|
@ -277,7 +277,7 @@ they prefer read replicas and will wait for replicas to catch up:
|
||||||
|
|
||||||
| **Data Consistency** | **Description** |
|
| **Data Consistency** | **Description** |
|
||||||
|--------------|-----------------------------|
|
|--------------|-----------------------------|
|
||||||
| `:always` | The job is required to use the primary database (default). It should be used for workers that primarily perform writes or that have strict requirements around data consistency when reading their own writes. |
|
| `:always` | The job is required to use the primary database (default). It should be used for workers that primarily perform writes, have strict requirements around data consistency when reading their own writes, or are cron jobs. |
|
||||||
| `:sticky` | The job prefers replicas, but switches to the primary for writes or when encountering replication lag. It should be used for jobs that require to be executed as fast as possible but can sustain a small initial queuing delay. |
|
| `:sticky` | The job prefers replicas, but switches to the primary for writes or when encountering replication lag. It should be used for jobs that require to be executed as fast as possible but can sustain a small initial queuing delay. |
|
||||||
| `:delayed` | The job prefers replicas, but switches to the primary for writes. When encountering replication lag before the job starts, the job is retried once. If the replica is still not up to date on the next retry, it switches to the primary. It should be used for jobs where delaying execution further typically does not matter, such as cache expiration or web hooks execution. |
|
| `:delayed` | The job prefers replicas, but switches to the primary for writes. When encountering replication lag before the job starts, the job is retried once. If the replica is still not up to date on the next retry, it switches to the primary. It should be used for jobs where delaying execution further typically does not matter, such as cache expiration or web hooks execution. |
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ module API
|
||||||
requires :namespace_path, type: String, desc: 'Path for the namespace that should be subscribed'
|
requires :namespace_path, type: String, desc: 'Path for the namespace that should be subscribed'
|
||||||
end
|
end
|
||||||
post do
|
post do
|
||||||
not_found! unless Feature.enabled?(:jira_connect_oauth, current_user)
|
not_found! unless Feature.enabled?(:jira_connect_oauth_self_managed, current_user)
|
||||||
|
|
||||||
jwt = Atlassian::JiraConnect::Jwt::Symmetric.new(params[:jwt])
|
jwt = Atlassian::JiraConnect::Jwt::Symmetric.new(params[:jwt])
|
||||||
installation = JiraConnectInstallation.find_by_client_key(jwt.iss_claim)
|
installation = JiraConnectInstallation.find_by_client_key(jwt.iss_claim)
|
||||||
|
|
|
@ -94,7 +94,8 @@ module Gitlab
|
||||||
# when the variables are sent to Runner.
|
# when the variables are sent to Runner.
|
||||||
Gitlab::AppJsonLogger.info(
|
Gitlab::AppJsonLogger.info(
|
||||||
event: 'file_variable_is_referenced_in_another_variable',
|
event: 'file_variable_is_referenced_in_another_variable',
|
||||||
project_id: project.id
|
project_id: project.id,
|
||||||
|
variable: variable_name
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ module Gitlab
|
||||||
include Gitlab::Database::MigrationHelpers
|
include Gitlab::Database::MigrationHelpers
|
||||||
include Gitlab::Database::SchemaHelpers
|
include Gitlab::Database::SchemaHelpers
|
||||||
|
|
||||||
|
DuplicatedIndexesError = Class.new(StandardError)
|
||||||
|
|
||||||
ERROR_SCOPE = 'index'
|
ERROR_SCOPE = 'index'
|
||||||
|
|
||||||
# Concurrently creates a new index on a partitioned table. In concept this works similarly to
|
# Concurrently creates a new index on a partitioned table. In concept this works similarly to
|
||||||
|
@ -92,6 +94,42 @@ module Gitlab
|
||||||
.map { |_, indexes| indexes.map { |index| index['index_name'] } }
|
.map { |_, indexes| indexes.map { |index| index['index_name'] } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Retrieves a hash of index names for a given table and schema, by index
|
||||||
|
# definition.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# indexes_by_definition_for_table('table_name_goes_here')
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
#
|
||||||
|
# {
|
||||||
|
# "CREATE _ btree (created_at)" => "index_on_created_at"
|
||||||
|
# }
|
||||||
|
def indexes_by_definition_for_table(table_name, schema_name: connection.current_schema)
|
||||||
|
duplicate_indexes = find_duplicate_indexes(table_name, schema_name: schema_name)
|
||||||
|
|
||||||
|
unless duplicate_indexes.empty?
|
||||||
|
raise DuplicatedIndexesError, "#{table_name} has duplicate indexes: #{duplicate_indexes}"
|
||||||
|
end
|
||||||
|
|
||||||
|
find_indexes(table_name, schema_name: schema_name)
|
||||||
|
.each_with_object({}) { |row, hash| hash[row['index_id']] = row['index_name'] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Renames indexes for a given table and schema, mapping by index
|
||||||
|
# definition, to a hash of new index names.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# index_names = indexes_by_definition_for_table('source_table_name_goes_here')
|
||||||
|
# drop_table('source_table_name_goes_here')
|
||||||
|
# rename_indexes_for_table('destination_table_name_goes_here', index_names)
|
||||||
|
def rename_indexes_for_table(table_name, new_index_names, schema_name: connection.current_schema)
|
||||||
|
current_index_names = indexes_by_definition_for_table(table_name, schema_name: schema_name)
|
||||||
|
rename_indexes(current_index_names, new_index_names, schema_name: schema_name)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_indexes(table_name, schema_name: connection.current_schema)
|
def find_indexes(table_name, schema_name: connection.current_schema)
|
||||||
|
@ -124,6 +162,18 @@ module Gitlab
|
||||||
def generated_index_name(partition_name, index_name)
|
def generated_index_name(partition_name, index_name)
|
||||||
object_name("#{partition_name}_#{index_name}", 'index')
|
object_name("#{partition_name}_#{index_name}", 'index')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rename_indexes(from, to, schema_name: connection.current_schema)
|
||||||
|
indexes_to_rename = from.select { |index_id, _| to.has_key?(index_id) }
|
||||||
|
statements = indexes_to_rename.map do |index_id, index_name|
|
||||||
|
<<~SQL
|
||||||
|
ALTER INDEX #{connection.quote_table_name("#{schema_name}.#{connection.quote_column_name(index_name)}")}
|
||||||
|
RENAME TO #{connection.quote_column_name(to[index_id])}
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
connection.execute(statements.join(';'))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
|
||||||
env["BUNDLE_DEPLOYMENT"] = 'false'
|
env["BUNDLE_DEPLOYMENT"] = 'false'
|
||||||
end
|
end
|
||||||
|
|
||||||
output, status = Gitlab::Popen.popen([make_cmd, 'clean-build', 'all', 'git'], nil, env)
|
output, status = Gitlab::Popen.popen([make_cmd, 'clean-build', 'all'], nil, env)
|
||||||
raise "Gitaly failed to compile: #{output}" unless status&.zero?
|
raise "Gitaly failed to compile: #{output}" unless status&.zero?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace :tw do
|
||||||
CodeOwnerRule.new('Infrastructure', '@sselhorn'),
|
CodeOwnerRule.new('Infrastructure', '@sselhorn'),
|
||||||
CodeOwnerRule.new('Integrations', '@ashrafkhamis'),
|
CodeOwnerRule.new('Integrations', '@ashrafkhamis'),
|
||||||
CodeOwnerRule.new('Knowledge', '@aqualls'),
|
CodeOwnerRule.new('Knowledge', '@aqualls'),
|
||||||
CodeOwnerRule.new('Application Performance', '@sselhorn'),
|
CodeOwnerRule.new('Application Performance', '@jglassman1'),
|
||||||
CodeOwnerRule.new('Monitor', '@msedlakjakubowski'),
|
CodeOwnerRule.new('Monitor', '@msedlakjakubowski'),
|
||||||
CodeOwnerRule.new('Observability', 'msedlakjakubowski'),
|
CodeOwnerRule.new('Observability', 'msedlakjakubowski'),
|
||||||
CodeOwnerRule.new('Optimize', '@lciutacu'),
|
CodeOwnerRule.new('Optimize', '@lciutacu'),
|
||||||
|
@ -67,7 +67,7 @@ namespace :tw do
|
||||||
CodeOwnerRule.new('Release', '@rdickenson'),
|
CodeOwnerRule.new('Release', '@rdickenson'),
|
||||||
CodeOwnerRule.new('Respond', '@msedlakjakubowski'),
|
CodeOwnerRule.new('Respond', '@msedlakjakubowski'),
|
||||||
CodeOwnerRule.new('Runner', '@sselhorn'),
|
CodeOwnerRule.new('Runner', '@sselhorn'),
|
||||||
CodeOwnerRule.new('Pods', '@sselhorn'),
|
CodeOwnerRule.new('Pods', '@jglassman1'),
|
||||||
CodeOwnerRule.new('Security Policies', '@claytoncornell'),
|
CodeOwnerRule.new('Security Policies', '@claytoncornell'),
|
||||||
CodeOwnerRule.new('Source Code', '@aqualls'),
|
CodeOwnerRule.new('Source Code', '@aqualls'),
|
||||||
CodeOwnerRule.new('Static Analysis', '@rdickenson'),
|
CodeOwnerRule.new('Static Analysis', '@rdickenson'),
|
||||||
|
|
|
@ -2125,9 +2125,6 @@ msgstr ""
|
||||||
msgid "Active %{accessTokenTypePlural} (%{totalAccessTokens})"
|
msgid "Active %{accessTokenTypePlural} (%{totalAccessTokens})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Active %{type} (%{token_length})"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Active Sessions"
|
msgid "Active Sessions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -5215,9 +5212,6 @@ msgstr ""
|
||||||
msgid "Are you sure you want to revoke this %{accessTokenType}? This action cannot be undone."
|
msgid "Are you sure you want to revoke this %{accessTokenType}? This action cannot be undone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Are you sure you want to revoke this %{type}? This action cannot be undone."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Are you sure you want to revoke this personal access token? This action cannot be undone."
|
msgid "Are you sure you want to revoke this personal access token? This action cannot be undone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10782,9 +10776,6 @@ msgstr ""
|
||||||
msgid "Copy %{protocol} clone URL"
|
msgid "Copy %{protocol} clone URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Copy %{type}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Copy ID"
|
msgid "Copy ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -29693,6 +29684,9 @@ msgstr ""
|
||||||
msgid "PipelineSchedules|Last Pipeline"
|
msgid "PipelineSchedules|Last Pipeline"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PipelineSchedules|New schedule"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "PipelineSchedules|Next Run"
|
msgid "PipelineSchedules|Next Run"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -41750,9 +41744,6 @@ msgstr ""
|
||||||
msgid "This user has no active %{accessTokenTypePlural}."
|
msgid "This user has no active %{accessTokenTypePlural}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "This user has no active %{type}."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "This user has no identities"
|
msgid "This user has no identities"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -47024,9 +47015,6 @@ msgstr ""
|
||||||
msgid "Your new %{accessTokenType} has been created."
|
msgid "Your new %{accessTokenType} has been created."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Your new %{type}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your new comment"
|
msgid "Your new comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -18,19 +18,11 @@ module QA
|
||||||
element :expiry_date_field
|
element :expiry_date_field
|
||||||
end
|
end
|
||||||
|
|
||||||
base.view 'app/views/shared/access_tokens/_created_container.html.haml' do
|
|
||||||
element :created_access_token_field
|
|
||||||
end
|
|
||||||
|
|
||||||
base.view 'app/views/shared/access_tokens/_form.html.haml' do
|
base.view 'app/views/shared/access_tokens/_form.html.haml' do
|
||||||
element :access_token_name_field
|
element :access_token_name_field
|
||||||
element :create_token_button
|
element :create_token_button
|
||||||
end
|
end
|
||||||
|
|
||||||
base.view 'app/views/shared/access_tokens/_table.html.haml' do
|
|
||||||
element :revoke_button
|
|
||||||
end
|
|
||||||
|
|
||||||
base.view 'app/views/shared/tokens/_scopes_form.html.haml' do
|
base.view 'app/views/shared/tokens/_scopes_form.html.haml' do
|
||||||
element :api_label, '#{scope}_label' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
|
element :api_label, '#{scope}_label' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module QA
|
module QA
|
||||||
RSpec.describe 'Manage' do
|
RSpec.describe 'Manage' do
|
||||||
describe 'Project access token', :reliable do
|
describe 'Project access token' do
|
||||||
before(:all) do
|
before(:all) do
|
||||||
@project_access_token = QA::Resource::ProjectAccessToken.fabricate_via_api! do |pat|
|
@project_access_token = QA::Resource::ProjectAccessToken.fabricate_via_api! do |pat|
|
||||||
pat.project = Resource::ReusableProject.fabricate_via_api!
|
pat.project = Resource::ReusableProject.fabricate_via_api!
|
||||||
|
@ -12,7 +12,7 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for the same project' do
|
context 'for the same project' do
|
||||||
it 'can be used to create a file via the project API', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347858' do
|
it 'can be used to create a file via the project API', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347858' do
|
||||||
expect do
|
expect do
|
||||||
Resource::File.fabricate_via_api! do |file|
|
Resource::File.fabricate_via_api! do |file|
|
||||||
file.api_client = @user_api_client
|
file.api_client = @user_api_client
|
||||||
|
@ -44,7 +44,7 @@ module QA
|
||||||
@different_project = Resource::Project.fabricate!
|
@different_project = Resource::Project.fabricate!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'cannot be used to create a file via the project API', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347860' do
|
it 'cannot be used to create a file via the project API', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347860' do
|
||||||
expect do
|
expect do
|
||||||
Resource::File.fabricate_via_api! do |file|
|
Resource::File.fabricate_via_api! do |file|
|
||||||
file.api_client = @user_api_client
|
file.api_client = @user_api_client
|
||||||
|
@ -57,7 +57,7 @@ module QA
|
||||||
end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden/)
|
end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden/)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'cannot be used to commit via the API', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347861' do
|
it 'cannot be used to commit via the API', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347861' do
|
||||||
expect do
|
expect do
|
||||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||||
commit.api_client = @user_api_client
|
commit.api_client = @user_api_client
|
||||||
|
|
|
@ -24,7 +24,7 @@ RSpec.describe "User sorts issues" do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'keeps the sort option', :js do
|
it 'keeps the sort option', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/378184' do
|
||||||
visit(project_issues_path(project))
|
visit(project_issues_path(project))
|
||||||
|
|
||||||
click_button 'Created date'
|
click_button 'Created date'
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { GlAlert, GlLoadingIcon, GlModal } from '@gitlab/ui';
|
import { GlAlert, GlLoadingIcon, GlModal, GlTabs } from '@gitlab/ui';
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import Vue, { nextTick } from 'vue';
|
import Vue, { nextTick } from 'vue';
|
||||||
import VueApollo from 'vue-apollo';
|
import VueApollo from 'vue-apollo';
|
||||||
|
import { trimText } from 'helpers/text_helper';
|
||||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||||
import waitForPromises from 'helpers/wait_for_promises';
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
|
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||||
import PipelineSchedules from '~/pipeline_schedules/components/pipeline_schedules.vue';
|
import PipelineSchedules from '~/pipeline_schedules/components/pipeline_schedules.vue';
|
||||||
import PipelineSchedulesTable from '~/pipeline_schedules/components/table/pipeline_schedules_table.vue';
|
import PipelineSchedulesTable from '~/pipeline_schedules/components/table/pipeline_schedules_table.vue';
|
||||||
import deletePipelineScheduleMutation from '~/pipeline_schedules/graphql/mutations/delete_pipeline_schedule.mutation.graphql';
|
import deletePipelineScheduleMutation from '~/pipeline_schedules/graphql/mutations/delete_pipeline_schedule.mutation.graphql';
|
||||||
|
@ -32,7 +33,7 @@ describe('Pipeline schedules app', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const createComponent = (requestHandlers) => {
|
const createComponent = (requestHandlers) => {
|
||||||
wrapper = shallowMount(PipelineSchedules, {
|
wrapper = mountExtended(PipelineSchedules, {
|
||||||
provide: {
|
provide: {
|
||||||
fullPath: 'gitlab-org/gitlab',
|
fullPath: 'gitlab-org/gitlab',
|
||||||
},
|
},
|
||||||
|
@ -44,17 +45,24 @@ describe('Pipeline schedules app', () => {
|
||||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||||
const findModal = () => wrapper.findComponent(GlModal);
|
const findModal = () => wrapper.findComponent(GlModal);
|
||||||
|
const findTabs = () => wrapper.findComponent(GlTabs);
|
||||||
|
const findNewButton = () => wrapper.findByTestId('new-schedule-button');
|
||||||
|
const findAllTab = () => wrapper.findByTestId('pipeline-schedules-all-tab');
|
||||||
|
const findActiveTab = () => wrapper.findByTestId('pipeline-schedules-active-tab');
|
||||||
|
const findInactiveTab = () => wrapper.findByTestId('pipeline-schedules-inactive-tab');
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays table', async () => {
|
it('displays table, tabs and new button', async () => {
|
||||||
createComponent();
|
createComponent();
|
||||||
|
|
||||||
await waitForPromises();
|
await waitForPromises();
|
||||||
|
|
||||||
expect(findTable().exists()).toBe(true);
|
expect(findTable().exists()).toBe(true);
|
||||||
|
expect(findNewButton().exists()).toBe(true);
|
||||||
|
expect(findTabs().exists()).toBe(true);
|
||||||
expect(findAlert().exists()).toBe(false);
|
expect(findAlert().exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,4 +166,38 @@ describe('Pipeline schedules app', () => {
|
||||||
|
|
||||||
expect(findModal().props('visible')).toBe(false);
|
expect(findModal().props('visible')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('tabs', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
await waitForPromises();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays All tab with count', () => {
|
||||||
|
expect(trimText(findAllTab().text())).toBe(`All ${mockPipelineScheduleNodes.length}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays Active tab with no count', () => {
|
||||||
|
expect(findActiveTab().text()).toBe('Active');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays Inactive tab with no count', () => {
|
||||||
|
expect(findInactiveTab().text()).toBe('Inactive');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should refetch the schedules query on a tab click', async () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
await waitForPromises();
|
||||||
|
|
||||||
|
jest.spyOn(wrapper.vm.$apollo.queries.schedules, 'refetch').mockImplementation(jest.fn());
|
||||||
|
|
||||||
|
expect(wrapper.vm.$apollo.queries.schedules.refetch).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
await findAllTab().trigger('click');
|
||||||
|
|
||||||
|
expect(wrapper.vm.$apollo.queries.schedules.refetch).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -601,7 +601,8 @@ RSpec.describe Gitlab::Ci::Variables::Collection do
|
||||||
it 'logs file_variable_is_referenced_in_another_variable once for VAR5' do
|
it 'logs file_variable_is_referenced_in_another_variable once for VAR5' do
|
||||||
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
||||||
event: 'file_variable_is_referenced_in_another_variable',
|
event: 'file_variable_is_referenced_in_another_variable',
|
||||||
project_id: project.id
|
project_id: project.id,
|
||||||
|
variable: 'FILEVAR4'
|
||||||
).once
|
).once
|
||||||
|
|
||||||
sort_and_expand_all
|
sort_and_expand_all
|
||||||
|
|
|
@ -231,4 +231,165 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#indexes_by_definition_for_table' do
|
||||||
|
context 'when a partitioned table has indexes' do
|
||||||
|
subject do
|
||||||
|
migration.indexes_by_definition_for_table(table_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
CREATE INDEX #{index_name} ON #{table_name} (#{column_name});
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'captures partitioned index names by index definition' do
|
||||||
|
expect(subject).to match(a_hash_including({ "CREATE _ btree (#{column_name})" => index_name }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a non-partitioned table has indexes' do
|
||||||
|
let(:regular_table_name) { '_test_regular_table' }
|
||||||
|
let(:regular_index_name) { '_test_regular_index_name' }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
migration.indexes_by_definition_for_table(regular_table_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
CREATE TABLE #{regular_table_name} (
|
||||||
|
#{column_name} timestamptz NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX #{regular_index_name} ON #{regular_table_name} (#{column_name});
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'captures index names by index definition' do
|
||||||
|
expect(subject).to match(a_hash_including({ "CREATE _ btree (#{column_name})" => regular_index_name }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a non-partitioned table has duplicate indexes' do
|
||||||
|
let(:regular_table_name) { '_test_regular_table' }
|
||||||
|
let(:regular_index_name) { '_test_regular_index_name' }
|
||||||
|
let(:duplicate_index_name) { '_test_duplicate_index_name' }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
migration.indexes_by_definition_for_table(regular_table_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
CREATE TABLE #{regular_table_name} (
|
||||||
|
#{column_name} timestamptz NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX #{regular_index_name} ON #{regular_table_name} (#{column_name});
|
||||||
|
CREATE INDEX #{duplicate_index_name} ON #{regular_table_name} (#{column_name});
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { subject }.to raise_error { described_class::DuplicatedIndexesError }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#rename_indexes_for_table' do
|
||||||
|
let(:original_table_name) { '_test_rename_indexes_table' }
|
||||||
|
let(:first_partition_name) { '_test_rename_indexes_table_1' }
|
||||||
|
let(:transient_table_name) { '_test_rename_indexes_table_child' }
|
||||||
|
let(:custom_column_name) { 'created_at' }
|
||||||
|
let(:generated_column_name) { 'updated_at' }
|
||||||
|
let(:custom_index_name) { 'index_test_rename_indexes_table_on_created_at' }
|
||||||
|
let(:custom_index_name_regenerated) { '_test_rename_indexes_table_created_at_idx' }
|
||||||
|
let(:generated_index_name) { '_test_rename_indexes_table_updated_at_idx' }
|
||||||
|
let(:generated_index_name_collided) { '_test_rename_indexes_table_updated_at_idx1' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
CREATE TABLE #{original_table_name} (
|
||||||
|
#{custom_column_name} timestamptz NOT NULL,
|
||||||
|
#{generated_column_name} timestamptz NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX #{custom_index_name} ON #{original_table_name} (#{custom_column_name});
|
||||||
|
CREATE INDEX ON #{original_table_name} (#{generated_column_name});
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when changing a table within the current schema' do
|
||||||
|
let!(:identifiers) { migration.indexes_by_definition_for_table(original_table_name) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
ALTER TABLE #{original_table_name} RENAME TO #{first_partition_name};
|
||||||
|
CREATE TABLE #{original_table_name} (LIKE #{first_partition_name} INCLUDING ALL);
|
||||||
|
DROP TABLE #{first_partition_name};
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'maps index names after they are changed' do
|
||||||
|
migration.rename_indexes_for_table(original_table_name, identifiers)
|
||||||
|
|
||||||
|
expect_index_to_exist(custom_index_name)
|
||||||
|
expect_index_to_exist(generated_index_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not rename an index which does not exist in the to_hash' do
|
||||||
|
partial_identifiers = identifiers.reject { |_, name| name == custom_index_name }
|
||||||
|
|
||||||
|
migration.rename_indexes_for_table(original_table_name, partial_identifiers)
|
||||||
|
|
||||||
|
expect_index_not_to_exist(custom_index_name)
|
||||||
|
expect_index_to_exist(generated_index_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when partitioning an existing table' do
|
||||||
|
before do
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
/* Create new parent table */
|
||||||
|
CREATE TABLE #{first_partition_name} (LIKE #{original_table_name} INCLUDING ALL);
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renames indexes across schemas' do
|
||||||
|
# Capture index names generated by postgres
|
||||||
|
generated_index_names = migration.indexes_by_definition_for_table(first_partition_name)
|
||||||
|
|
||||||
|
# Capture index names from original table
|
||||||
|
original_index_names = migration.indexes_by_definition_for_table(original_table_name)
|
||||||
|
|
||||||
|
connection.execute(<<~SQL)
|
||||||
|
/* Rename original table out of the way */
|
||||||
|
ALTER TABLE #{original_table_name} RENAME TO #{transient_table_name};
|
||||||
|
|
||||||
|
/* Rename new parent table to original name */
|
||||||
|
ALTER TABLE #{first_partition_name} RENAME TO #{original_table_name};
|
||||||
|
|
||||||
|
/* Move original table to gitlab_partitions_dynamic schema */
|
||||||
|
ALTER TABLE #{transient_table_name} SET SCHEMA #{partition_schema};
|
||||||
|
|
||||||
|
/* Rename original table to be the first partition */
|
||||||
|
ALTER TABLE #{partition_schema}.#{transient_table_name} RENAME TO #{first_partition_name};
|
||||||
|
SQL
|
||||||
|
|
||||||
|
# Apply index names generated by postgres to first partition
|
||||||
|
migration.rename_indexes_for_table(first_partition_name, generated_index_names, schema_name: partition_schema)
|
||||||
|
|
||||||
|
expect_index_to_exist('_test_rename_indexes_table_1_created_at_idx')
|
||||||
|
expect_index_to_exist('_test_rename_indexes_table_1_updated_at_idx')
|
||||||
|
|
||||||
|
# Apply index names from original table to new parent table
|
||||||
|
migration.rename_indexes_for_table(original_table_name, original_index_names)
|
||||||
|
|
||||||
|
expect_index_to_exist(custom_index_name)
|
||||||
|
expect_index_to_exist(generated_index_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe CleanupVulnerabilityStateTransitionsWithSameFromStateToState, :migration do
|
||||||
|
let_it_be(:namespace) { table(:namespaces).create!(name: 'namespace', type: 'Group', path: 'namespace') }
|
||||||
|
let_it_be(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
|
||||||
|
let_it_be(:project) do
|
||||||
|
table(:projects).create!(
|
||||||
|
path: 'project',
|
||||||
|
namespace_id: namespace.id,
|
||||||
|
project_namespace_id: namespace.id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let_it_be(:vulnerability) do
|
||||||
|
table(:vulnerabilities).create!(
|
||||||
|
project_id: project.id,
|
||||||
|
author_id: user.id,
|
||||||
|
title: 'test',
|
||||||
|
severity: 7,
|
||||||
|
confidence: 7,
|
||||||
|
report_type: 0
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let_it_be(:state_transitions) { table(:vulnerability_state_transitions) }
|
||||||
|
|
||||||
|
let!(:state_transition_with_no_state_change) do
|
||||||
|
state_transitions.create!(
|
||||||
|
vulnerability_id: vulnerability.id,
|
||||||
|
from_state: 2,
|
||||||
|
to_state: 2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:state_transition_with_state_change) do
|
||||||
|
state_transitions.create!(
|
||||||
|
vulnerability_id: vulnerability.id,
|
||||||
|
from_state: 1,
|
||||||
|
to_state: 2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes state transitions with no state change' do
|
||||||
|
expect { migrate! }.to change(state_transitions, :count).from(2).to(1)
|
||||||
|
end
|
||||||
|
end
|
|
@ -2356,7 +2356,7 @@ RSpec.describe Namespace do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'storage_enforcement_date' do
|
describe 'storage_enforcement_date', :freeze_time do
|
||||||
let_it_be(:namespace) { create(:group) }
|
let_it_be(:namespace) { create(:group) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -2364,7 +2364,7 @@ RSpec.describe Namespace do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correct date' do
|
it 'returns correct date' do
|
||||||
expect(namespace.storage_enforcement_date).to eql(Date.new(2022, 10, 19))
|
expect(namespace.storage_enforcement_date).to eql(3.months.from_now.to_date)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when :storage_banner_bypass_date_check is enabled' do
|
context 'when :storage_banner_bypass_date_check is enabled' do
|
||||||
|
@ -2372,7 +2372,7 @@ RSpec.describe Namespace do
|
||||||
stub_feature_flags(namespace_storage_limit_bypass_date_check: true)
|
stub_feature_flags(namespace_storage_limit_bypass_date_check: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the current date', :freeze_time do
|
it 'returns the current date' do
|
||||||
expect(namespace.storage_enforcement_date).to eq(Date.current)
|
expect(namespace.storage_enforcement_date).to eq(Date.current)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -194,47 +194,6 @@ RSpec.describe PersonalAccessToken do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Redis storage' do
|
|
||||||
let(:user_id) { 123 }
|
|
||||||
let(:token) { 'KS3wegQYXBLYhQsciwsj' }
|
|
||||||
|
|
||||||
context 'reading encrypted data' do
|
|
||||||
before do
|
|
||||||
subject.redis_store!(user_id, token)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns stored data' do
|
|
||||||
expect(subject.redis_getdel(user_id)).to eq(token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'reading unencrypted data' do
|
|
||||||
before do
|
|
||||||
Gitlab::Redis::SharedState.with do |redis|
|
|
||||||
redis.set(described_class.redis_shared_state_key(user_id),
|
|
||||||
token,
|
|
||||||
ex: PersonalAccessToken::REDIS_EXPIRY_TIME)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns stored data unmodified' do
|
|
||||||
expect(subject.redis_getdel(user_id)).to eq(token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'after deletion' do
|
|
||||||
before do
|
|
||||||
subject.redis_store!(user_id, token)
|
|
||||||
|
|
||||||
expect(subject.redis_getdel(user_id)).to eq(token)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'token is removed' do
|
|
||||||
expect(subject.redis_getdel(user_id)).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "validations" do
|
context "validations" do
|
||||||
let(:personal_access_token) { build(:personal_access_token) }
|
let(:personal_access_token) { build(:personal_access_token) }
|
||||||
|
|
||||||
|
@ -365,7 +324,7 @@ RSpec.describe PersonalAccessToken do
|
||||||
|
|
||||||
describe '.simple_sorts' do
|
describe '.simple_sorts' do
|
||||||
it 'includes overridden keys' do
|
it 'includes overridden keys' do
|
||||||
expect(described_class.simple_sorts.keys).to include(*%w(expires_at_asc expires_at_desc expires_at_asc_id_desc))
|
expect(described_class.simple_sorts.keys).to include(*%w(expires_at_asc_id_desc))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -373,18 +332,6 @@ RSpec.describe PersonalAccessToken do
|
||||||
let_it_be(:earlier_token) { create(:personal_access_token, expires_at: 2.days.ago) }
|
let_it_be(:earlier_token) { create(:personal_access_token, expires_at: 2.days.ago) }
|
||||||
let_it_be(:later_token) { create(:personal_access_token, expires_at: 1.day.ago) }
|
let_it_be(:later_token) { create(:personal_access_token, expires_at: 1.day.ago) }
|
||||||
|
|
||||||
describe '.order_expires_at_asc' do
|
|
||||||
it 'returns ordered list in asc order of expiry date' do
|
|
||||||
expect(described_class.order_expires_at_asc).to match [earlier_token, later_token]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.order_expires_at_desc' do
|
|
||||||
it 'returns ordered list in desc order of expiry date' do
|
|
||||||
expect(described_class.order_expires_at_desc).to match [later_token, earlier_token]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.order_expires_at_asc_id_desc' do
|
describe '.order_expires_at_asc_id_desc' do
|
||||||
let_it_be(:earlier_token_2) { create(:personal_access_token, expires_at: 2.days.ago) }
|
let_it_be(:earlier_token_2) { create(:personal_access_token, expires_at: 2.days.ago) }
|
||||||
|
|
||||||
|
|
|
@ -353,7 +353,8 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
||||||
it 'logs file_variable_is_referenced_in_another_variable' do
|
it 'logs file_variable_is_referenced_in_another_variable' do
|
||||||
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
||||||
event: 'file_variable_is_referenced_in_another_variable',
|
event: 'file_variable_is_referenced_in_another_variable',
|
||||||
project_id: project.id
|
project_id: project.id,
|
||||||
|
variable: 'file_var'
|
||||||
).once
|
).once
|
||||||
|
|
||||||
runner_variables
|
runner_variables
|
||||||
|
|
|
@ -22,7 +22,7 @@ RSpec.describe API::Integrations::JiraConnect::Subscriptions do
|
||||||
|
|
||||||
context 'with feature flag disabled' do
|
context 'with feature flag disabled' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(jira_connect_oauth: false)
|
stub_feature_flags(jira_connect_oauth_self_managed: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:jwt) { '123' }
|
let(:jwt) { '123' }
|
||||||
|
|
|
@ -28,9 +28,9 @@ RSpec.describe JiraConnect::SubscriptionsController do
|
||||||
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
|
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with jira_connect_oauth_self_managed feature disabled' do
|
context 'with jira_connect_oauth_self_managed_setting feature disabled' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(jira_connect_oauth_self_managed: false)
|
stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
|
it { is_expected.not_to include('http://self-managed-gitlab.com/-/jira_connect/') }
|
||||||
|
|
|
@ -3379,7 +3379,6 @@
|
||||||
- './ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
|
- './ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb'
|
||||||
- './ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
|
- './ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
|
||||||
- './ee/spec/views/search/_category.html.haml_spec.rb'
|
- './ee/spec/views/search/_category.html.haml_spec.rb'
|
||||||
- './ee/spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
|
- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
|
||||||
- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
|
- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
|
||||||
- './ee/spec/views/shared/billings/_billing_plans.html.haml_spec.rb'
|
- './ee/spec/views/shared/billings/_billing_plans.html.haml_spec.rb'
|
||||||
|
@ -10767,7 +10766,6 @@
|
||||||
- './spec/views/registrations/welcome/show.html.haml_spec.rb'
|
- './spec/views/registrations/welcome/show.html.haml_spec.rb'
|
||||||
- './spec/views/search/_results.html.haml_spec.rb'
|
- './spec/views/search/_results.html.haml_spec.rb'
|
||||||
- './spec/views/search/show.html.haml_spec.rb'
|
- './spec/views/search/show.html.haml_spec.rb'
|
||||||
- './spec/views/shared/access_tokens/_table.html.haml_spec.rb'
|
|
||||||
- './spec/views/shared/deploy_tokens/_form.html.haml_spec.rb'
|
- './spec/views/shared/deploy_tokens/_form.html.haml_spec.rb'
|
||||||
- './spec/views/shared/groups/_dropdown.html.haml_spec.rb'
|
- './spec/views/shared/groups/_dropdown.html.haml_spec.rb'
|
||||||
- './spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
|
- './spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
|
||||||
|
|
|
@ -66,7 +66,7 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do
|
||||||
.with(%w[which gmake])
|
.with(%w[which gmake])
|
||||||
.and_return(['/usr/bin/gmake', 0])
|
.and_return(['/usr/bin/gmake', 0])
|
||||||
expect(Gitlab::Popen).to receive(:popen)
|
expect(Gitlab::Popen).to receive(:popen)
|
||||||
.with(%w[gmake clean-build all git], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
.with(%w[gmake clean-build all], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
||||||
.and_return(['ok', 0])
|
.and_return(['ok', 0])
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
@ -78,7 +78,7 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do
|
||||||
.with(%w[which gmake])
|
.with(%w[which gmake])
|
||||||
.and_return(['/usr/bin/gmake', 0])
|
.and_return(['/usr/bin/gmake', 0])
|
||||||
expect(Gitlab::Popen).to receive(:popen)
|
expect(Gitlab::Popen).to receive(:popen)
|
||||||
.with(%w[gmake clean-build all git], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
.with(%w[gmake clean-build all], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
||||||
.and_return(['output', 1])
|
.and_return(['output', 1])
|
||||||
|
|
||||||
expect { subject }.to raise_error /Gitaly failed to compile: output/
|
expect { subject }.to raise_error /Gitaly failed to compile: output/
|
||||||
|
@ -95,14 +95,14 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do
|
||||||
|
|
||||||
it 'calls make in the gitaly directory' do
|
it 'calls make in the gitaly directory' do
|
||||||
expect(Gitlab::Popen).to receive(:popen)
|
expect(Gitlab::Popen).to receive(:popen)
|
||||||
.with(%w[make clean-build all git], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
.with(%w[make clean-build all], nil, { "BUNDLE_GEMFILE" => nil, "RUBYOPT" => nil })
|
||||||
.and_return(['output', 0])
|
.and_return(['output', 0])
|
||||||
|
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when Rails.env is test' do
|
context 'when Rails.env is test' do
|
||||||
let(:command) { %w[make clean-build all git] }
|
let(:command) { %w[make clean-build all] }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_rails_env('test')
|
stub_rails_env('test')
|
||||||
|
|
|
@ -76,9 +76,9 @@ RSpec.describe 'admin/application_settings/general.html.haml' do
|
||||||
expect(rendered).to have_css('#js-jira_connect-settings')
|
expect(rendered).to have_css('#js-jira_connect-settings')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the jira_connect_oauth feature flag is disabled' do
|
context 'when the jira_connect_oauth_self_managed_setting feature flag is disabled' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(jira_connect_oauth: false)
|
stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not show the jira connect application key section' do
|
it 'does not show the jira connect application key section' do
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'shared/access_tokens/_table.html.haml' do
|
|
||||||
let(:type) { 'token' }
|
|
||||||
let(:type_plural) { 'tokens' }
|
|
||||||
let(:empty_message) { nil }
|
|
||||||
let(:impersonation) { false }
|
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
let_it_be(:tokens) { [create(:personal_access_token, user: user)] }
|
|
||||||
let_it_be(:resource) { false }
|
|
||||||
|
|
||||||
before do
|
|
||||||
if resource
|
|
||||||
resource.add_maintainer(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Forcibly removing scopes from one token as it's not possible to do with the current modal on creation
|
|
||||||
# But the check exists in the template (it may be there for legacy reasons), so we should test the outcome
|
|
||||||
if tokens.size > 1
|
|
||||||
tokens[1].scopes = []
|
|
||||||
end
|
|
||||||
|
|
||||||
locals = {
|
|
||||||
type: type,
|
|
||||||
type_plural: type_plural,
|
|
||||||
active_tokens: tokens,
|
|
||||||
resource: resource,
|
|
||||||
impersonation: impersonation,
|
|
||||||
revoke_route_helper: ->(token) { 'path/' }
|
|
||||||
}
|
|
||||||
|
|
||||||
if empty_message
|
|
||||||
locals[:no_active_tokens_message] = empty_message
|
|
||||||
end
|
|
||||||
|
|
||||||
render partial: 'shared/access_tokens/table', locals: locals
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'if personal' do
|
|
||||||
it 'does not show non-personal content', :aggregate_failures do
|
|
||||||
expect(rendered).not_to have_content 'To see all the user\'s personal access tokens you must impersonate them first.'
|
|
||||||
expect(rendered).not_to have_selector 'th', text: 'Role'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'if impersonation' do
|
|
||||||
let(:impersonation) { true }
|
|
||||||
|
|
||||||
it 'shows the impersonation content', :aggregate_failures do
|
|
||||||
expect(rendered).to have_content 'To see all the user\'s personal access tokens you must impersonate them first.'
|
|
||||||
|
|
||||||
expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.'
|
|
||||||
expect(rendered).not_to have_selector 'th', text: 'Role'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'if resource is project' do
|
|
||||||
let_it_be(:resource) { create(:project) }
|
|
||||||
|
|
||||||
it 'shows the project content', :aggregate_failures do
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Role'
|
|
||||||
expect(rendered).to have_selector 'td', text: 'Maintainer'
|
|
||||||
|
|
||||||
expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.'
|
|
||||||
expect(rendered).not_to have_content 'To see all the user\'s personal access tokens you must impersonate them first.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'if resource is group' do
|
|
||||||
let_it_be(:resource) { create(:group) }
|
|
||||||
|
|
||||||
it 'shows the group content', :aggregate_failures do
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Role'
|
|
||||||
expect(rendered).to have_selector 'td', text: 'Maintainer'
|
|
||||||
|
|
||||||
expect(rendered).not_to have_content 'Personal access tokens are not revoked upon expiration.'
|
|
||||||
expect(rendered).not_to have_content 'To see all the user\'s personal access tokens you must impersonate them first.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without tokens' do
|
|
||||||
let_it_be(:tokens) { [] }
|
|
||||||
|
|
||||||
it 'has the correct content', :aggregate_failures do
|
|
||||||
expect(rendered).to have_content 'Active tokens (0)'
|
|
||||||
expect(rendered).to have_content 'This user has no active tokens.'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with a custom empty text' do
|
|
||||||
let(:empty_message) { 'Custom empty message' }
|
|
||||||
|
|
||||||
it 'shows the custom empty text' do
|
|
||||||
expect(rendered).to have_content empty_message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with tokens' do
|
|
||||||
let_it_be(:tokens) do
|
|
||||||
[
|
|
||||||
create(:personal_access_token, user: user, name: 'Access token', last_used_at: 4.days.from_now, expires_at: nil, scopes: [:read_api, :read_user]),
|
|
||||||
create(:personal_access_token, user: user, expires_at: 1.day.from_now, scopes: [:read_api, :read_user])
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
let_it_be(:expired_token) { build(:personal_access_token, name: "Expired token", expires_at: 2.days.ago).tap { |t| t.save!(validate: false) } }
|
|
||||||
|
|
||||||
it 'has the correct content', :aggregate_failures do
|
|
||||||
# Heading content
|
|
||||||
expect(rendered).to have_content 'Active tokens (2)'
|
|
||||||
|
|
||||||
# Table headers
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Token name'
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Scopes'
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Created'
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Last Used'
|
|
||||||
expect(rendered).to have_selector 'th', text: 'Expires'
|
|
||||||
|
|
||||||
# Table contents
|
|
||||||
expect(rendered).to have_content 'Access token'
|
|
||||||
expect(rendered).not_to have_content 'Expired token'
|
|
||||||
expect(rendered).to have_content 'read_api, read_user'
|
|
||||||
expect(rendered).to have_content 'no scopes selected'
|
|
||||||
expect(rendered).to have_content Time.now.to_date.to_s(:medium)
|
|
||||||
expect(rendered).to have_content l(4.days.from_now, format: "%b %d, %Y")
|
|
||||||
|
|
||||||
# Revoke buttons
|
|
||||||
expect(rendered).to have_link 'Revoke', href: 'path/', class: 'btn-danger-secondary', count: 1
|
|
||||||
expect(rendered).to have_link 'Revoke', href: 'path/', count: 2
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without the last used time' do
|
|
||||||
let_it_be(:tokens) { [create(:personal_access_token, user: user, expires_at: 5.days.ago)] }
|
|
||||||
|
|
||||||
it 'shows the last used empty text' do
|
|
||||||
expect(rendered).to have_content 'Never'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without expired at' do
|
|
||||||
let_it_be(:tokens) { [create(:personal_access_token, user: user, expires_at: nil, last_used_at: 1.day.ago)] }
|
|
||||||
|
|
||||||
it 'shows the expired at empty text' do
|
|
||||||
expect(rendered).to have_content 'Never'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue