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
|
||||
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"
|
||||
|
||||
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:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
|
@ -247,6 +247,7 @@ Gitlab/Json:
|
|||
- 'lib/gitlab/database/background_migration/batched_migration.rb'
|
||||
- 'lib/gitlab/database/background_migration_job.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/runner.rb'
|
||||
- 'lib/gitlab/database/postgres_hll/buckets.rb'
|
||||
|
@ -284,6 +285,7 @@ Gitlab/Json:
|
|||
- 'lib/microsoft_teams/notifier.rb'
|
||||
- 'lib/tasks/gitlab/background_migrations.rake'
|
||||
- 'lib/version_check.rb'
|
||||
- 'spec/commands/diagnostic_reports/uploader_smoke_spec.rb'
|
||||
- 'spec/controllers/admin/integrations_controller_spec.rb'
|
||||
- 'spec/controllers/concerns/product_analytics_tracking_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/namespaces_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/settings/access_tokens_controller_spec.rb'
|
||||
- 'spec/requests/users_controller_spec.rb'
|
||||
- 'spec/requests/whats_new_controller_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/shared/_mirror_status.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/_trial_status.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/tree/show.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/projects/_project.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/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/shared/access_tokens/_table.html.haml_spec.rb'
|
||||
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
|
||||
- 'lib/api/api_guard.rb'
|
||||
- 'lib/api/boards_responses.rb'
|
||||
|
@ -1202,7 +1201,6 @@ Style/IfUnlessModifier:
|
|||
- 'spec/views/groups/edit.html.haml_spec.rb'
|
||||
- 'spec/views/profiles/keys/_key.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'
|
||||
- 'tooling/danger/product_intelligence.rb'
|
||||
- 'tooling/lib/tooling/find_codeowners.rb'
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -428,7 +428,7 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :development, :test, :danger do
|
||||
gem 'gitlab-dangerfiles', '~> 3.5.2', require: false
|
||||
gem 'gitlab-dangerfiles', '~> 3.6.1', require: false
|
||||
end
|
||||
|
||||
group :development, :test, :coverage do
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
{"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-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-multipart","version":"1.0.4","platform":"ruby","checksum":"9012021ab57790f7d712f590b48d5f948b19b43cfa11ca83e6459f06090b0725"},
|
||||
{"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":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
|
||||
{"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-fog-azure-rm","version":"1.3.0","platform":"ruby","checksum":"2fef5317d6515f95f803099afa860fe3019ce6e1907bf49f66b5e06468a617b5"},
|
||||
{"name":"gitlab-labkit","version":"0.24.0","platform":"ruby","checksum":"8f16e5aa4e0a05be58958fe880bdd53c84b659a081ea9981d2b510922a4a0548"},
|
||||
|
|
|
@ -448,7 +448,7 @@ GEM
|
|||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-http-cache (2.4.0)
|
||||
faraday-http-cache (2.4.1)
|
||||
faraday (>= 0.8)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
|
@ -555,7 +555,7 @@ GEM
|
|||
terminal-table (~> 1.5, >= 1.5.1)
|
||||
gitlab-chronic (0.10.5)
|
||||
numerizer (~> 0.2)
|
||||
gitlab-dangerfiles (3.5.2)
|
||||
gitlab-dangerfiles (3.6.1)
|
||||
danger (>= 8.4.5)
|
||||
danger-gitlab (>= 8.0.0)
|
||||
rake
|
||||
|
@ -1627,7 +1627,7 @@ DEPENDENCIES
|
|||
gitaly (~> 15.4.0.pre.rc2)
|
||||
github-markup (~> 1.7.0)
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 3.5.2)
|
||||
gitlab-dangerfiles (~> 3.6.1)
|
||||
gitlab-experiment (~> 0.7.1)
|
||||
gitlab-fog-azure-rm (~> 1.3.0)
|
||||
gitlab-labkit (~> 0.24.0)
|
||||
|
|
|
@ -141,6 +141,7 @@ export default {
|
|||
<gl-link
|
||||
:href="computedPath"
|
||||
class="sortable-link gl-font-weight-normal"
|
||||
target="_blank"
|
||||
@click="handleTitleClick"
|
||||
>
|
||||
{{ title }}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<script>
|
||||
import { GlAlert, GlLoadingIcon, GlModal } from '@gitlab/ui';
|
||||
import { GlAlert, GlBadge, GlButton, GlLoadingIcon, GlModal, GlTabs, GlTab } from '@gitlab/ui';
|
||||
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 getPipelineSchedulesQuery from '../graphql/queries/get_pipeline_schedules.query.graphql';
|
||||
import PipelineSchedulesTable from './table/pipeline_schedules_table.vue';
|
||||
|
@ -11,6 +13,7 @@ export default {
|
|||
scheduleDeleteError: s__(
|
||||
'PipelineSchedules|There was a problem deleting the pipeline schedule.',
|
||||
),
|
||||
newSchedule: s__('PipelineSchedules|New schedule'),
|
||||
},
|
||||
modal: {
|
||||
id: 'delete-pipeline-schedule-modal',
|
||||
|
@ -28,8 +31,12 @@ export default {
|
|||
},
|
||||
components: {
|
||||
GlAlert,
|
||||
GlBadge,
|
||||
GlButton,
|
||||
GlLoadingIcon,
|
||||
GlModal,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
PipelineSchedulesTable,
|
||||
},
|
||||
inject: {
|
||||
|
@ -43,10 +50,16 @@ export default {
|
|||
variables() {
|
||||
return {
|
||||
projectPath: this.fullPath,
|
||||
status: this.scope,
|
||||
};
|
||||
},
|
||||
update({ project }) {
|
||||
return project?.pipelineSchedules?.nodes || [];
|
||||
update(data) {
|
||||
const { pipelineSchedules: { nodes: list = [], count } = {} } = data.project || {};
|
||||
|
||||
return {
|
||||
list,
|
||||
count,
|
||||
};
|
||||
},
|
||||
error() {
|
||||
this.reportError(this.$options.i18n.schedulesFetchError);
|
||||
|
@ -54,18 +67,58 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const { scope } = queryToObject(window.location.search);
|
||||
return {
|
||||
schedules: [],
|
||||
schedules: {
|
||||
list: [],
|
||||
},
|
||||
scope,
|
||||
hasError: false,
|
||||
errorMessage: '',
|
||||
scheduleToDeleteId: null,
|
||||
showModal: false,
|
||||
count: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isLoading() {
|
||||
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: {
|
||||
reportError(error) {
|
||||
|
@ -100,6 +153,10 @@ export default {
|
|||
this.reportError(this.$options.i18n.scheduleDeleteError);
|
||||
}
|
||||
},
|
||||
fetchPipelineSchedulesByStatus(scope) {
|
||||
this.scope = scope;
|
||||
this.$apollo.queries.schedules.refetch();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -110,12 +167,45 @@ export default {
|
|||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
|
||||
<gl-loading-icon v-if="isLoading" size="lg" />
|
||||
|
||||
<!-- Tabs will be addressed in #371989 -->
|
||||
|
||||
<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
|
||||
:visible="showModal"
|
||||
|
|
|
@ -12,31 +12,37 @@ export default {
|
|||
{
|
||||
key: 'description',
|
||||
label: s__('PipelineSchedules|Description'),
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-40p',
|
||||
},
|
||||
{
|
||||
key: 'target',
|
||||
label: s__('PipelineSchedules|Target'),
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-10p',
|
||||
},
|
||||
{
|
||||
key: 'pipeline',
|
||||
label: s__('PipelineSchedules|Last Pipeline'),
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-10p',
|
||||
},
|
||||
{
|
||||
key: 'next',
|
||||
label: s__('PipelineSchedules|Next Run'),
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-15p',
|
||||
},
|
||||
{
|
||||
key: 'owner',
|
||||
label: s__('PipelineSchedules|Owner'),
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-10p',
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
label: '',
|
||||
thClass: 'gl-border-t-none!',
|
||||
columnClass: 'gl-w-15p',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
query getPipelineSchedulesQuery($projectPath: ID!) {
|
||||
query getPipelineSchedulesQuery($projectPath: ID!, $status: PipelineScheduleStatus) {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
pipelineSchedules {
|
||||
pipelineSchedules(status: $status) {
|
||||
count
|
||||
nodes {
|
||||
id
|
||||
description
|
||||
|
|
|
@ -64,7 +64,7 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
|
|||
private
|
||||
|
||||
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?
|
||||
|
||||
|
|
|
@ -40,9 +40,9 @@ class Namespace < ApplicationRecord
|
|||
|
||||
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
|
||||
# Determines when we start enforcing namespace storage
|
||||
MIN_STORAGE_ENFORCEMENT_DATE = Date.new(2022, 10, 19)
|
||||
# This date is just a placeholder until namespace storage enforcement timeline is confirmed at which point
|
||||
# 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 = 3.months.from_now.to_date
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/367531
|
||||
MIN_STORAGE_ENFORCEMENT_USAGE = 5.gigabytes
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@ class PersonalAccessToken < ApplicationRecord
|
|||
|
||||
add_authentication_token_field :token, digest: true
|
||||
|
||||
REDIS_EXPIRY_TIME = 3.minutes
|
||||
|
||||
# PATs are 20 characters + optional configurable settings prefix (0..20)
|
||||
TOKEN_LENGTH_RANGE = (20..40).freeze
|
||||
|
||||
|
@ -34,8 +32,6 @@ class PersonalAccessToken < ApplicationRecord
|
|||
scope :for_user, -> (user) { where(user: user) }
|
||||
scope :for_users, -> (users) { where(user: users) }
|
||||
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 :project_access_token, -> { includes(:user).where(user: { user_type: :project_bot }) }
|
||||
scope :owner_is_human, -> { includes(:user).where(user: { user_type: :human }) }
|
||||
|
@ -55,35 +51,10 @@ class PersonalAccessToken < ApplicationRecord
|
|||
!revoked? && !expired?
|
||||
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
|
||||
def self.simple_sorts
|
||||
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 }
|
||||
}
|
||||
)
|
||||
|
@ -121,10 +92,6 @@ class PersonalAccessToken < ApplicationRecord
|
|||
|
||||
self.scopes = Gitlab::Auth::DEFAULT_SCOPES if self.scopes.empty?
|
||||
end
|
||||
|
||||
def self.redis_shared_state_key(user_id)
|
||||
"gitlab:personal_access_token:#{user_id}"
|
||||
end
|
||||
end
|
||||
|
||||
PersonalAccessToken.prepend_mod_with('PersonalAccessToken')
|
||||
|
|
|
@ -123,4 +123,4 @@
|
|||
= render 'admin/application_settings/eks'
|
||||
= render 'admin/application_settings/floc'
|
||||
= 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:
|
||||
- SoftwareLicensePolicy
|
||||
feature_categories:
|
||||
- license_compliance
|
||||
- security_policy_management
|
||||
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
|
||||
milestone: '11.2'
|
||||
|
|
|
@ -3,7 +3,7 @@ table_name: software_licenses
|
|||
classes:
|
||||
- SoftwareLicense
|
||||
feature_categories:
|
||||
- license_compliance
|
||||
- security_policy_management
|
||||
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
|
||||
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)**
|
||||
|
||||
[Push mirrors](../user/project/repository/mirror/push.md)
|
||||
defined on a project's repository settings are called "remote mirrors", and the
|
||||
state of these mirrors can be queried and modified via the remote mirror API
|
||||
outlined below.
|
||||
defined on a project's repository settings are called "remote mirrors". You
|
||||
can query and modify the state of these mirrors with the remote mirror API.
|
||||
|
||||
For security reasons, the `url` attribute in the API response is always scrubbed of username
|
||||
and password information.
|
||||
|
||||
## 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
|
||||
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
|
||||
|
||||
> [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
|
||||
|
||||
Learn how to [configure a pull mirror](projects.md#configure-pull-mirroring-for-a-project) using the Projects API.
|
||||
|
||||
## Create a push mirror
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24189) in GitLab 12.9.
|
||||
|
||||
Push mirroring is disabled by default. You can enable it by including the optional parameter `enabled` when creating it:
|
||||
Push mirroring is disabled by default. To enable it, include the optional parameter
|
||||
`enabled` when you create the mirror:
|
||||
|
||||
```plaintext
|
||||
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. |
|
||||
| `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. |
|
||||
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -135,8 +126,6 @@ Example response:
|
|||
|
||||
## 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
|
||||
mirrored:
|
||||
|
||||
|
@ -148,8 +137,8 @@ PUT /projects/:id/remote_mirrors/:mirror_id
|
|||
| :---------- | :----- | :--------- | :------------ |
|
||||
| `mirror_id` | Integer | yes | The remote mirror ID. |
|
||||
| `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. |
|
||||
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
|
||||
|
||||
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.
|
||||
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.
|
||||
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,
|
||||
isolated, reusable and resolvable.
|
||||
For best experience with any systems made of components it's fundamental that components:
|
||||
|
||||
- **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.
|
||||
- **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.
|
||||
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
|
||||
|
||||
|
|
|
@ -170,7 +170,10 @@ Use:
|
|||
- 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
|
||||
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
|
||||
supported.
|
||||
|
||||
|
|
|
@ -18,7 +18,10 @@ Use the following job in `.gitlab-ci.yml`. This includes the `artifacts:paths` k
|
|||
```yaml
|
||||
## Use https://github.com/sj26/rspec_junit_formatter to generate a JUnit report format XML file with rspec
|
||||
ruby:
|
||||
image: ruby:3.0.4
|
||||
stage: test
|
||||
before_script:
|
||||
- apt-get update -y && apt-get install -y bundler
|
||||
script:
|
||||
- bundle install
|
||||
- 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** |
|
||||
|--------------|-----------------------------|
|
||||
| `: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. |
|
||||
| `: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'
|
||||
end
|
||||
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])
|
||||
installation = JiraConnectInstallation.find_by_client_key(jwt.iss_claim)
|
||||
|
|
|
@ -94,7 +94,8 @@ module Gitlab
|
|||
# when the variables are sent to Runner.
|
||||
Gitlab::AppJsonLogger.info(
|
||||
event: 'file_variable_is_referenced_in_another_variable',
|
||||
project_id: project.id
|
||||
project_id: project.id,
|
||||
variable: variable_name
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ module Gitlab
|
|||
include Gitlab::Database::MigrationHelpers
|
||||
include Gitlab::Database::SchemaHelpers
|
||||
|
||||
DuplicatedIndexesError = Class.new(StandardError)
|
||||
|
||||
ERROR_SCOPE = 'index'
|
||||
|
||||
# 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'] } }
|
||||
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
|
||||
|
||||
def find_indexes(table_name, schema_name: connection.current_schema)
|
||||
|
@ -124,6 +162,18 @@ module Gitlab
|
|||
def generated_index_name(partition_name, index_name)
|
||||
object_name("#{partition_name}_#{index_name}", 'index')
|
||||
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
|
||||
|
|
|
@ -34,7 +34,7 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
|
|||
env["BUNDLE_DEPLOYMENT"] = 'false'
|
||||
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?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Infrastructure', '@sselhorn'),
|
||||
CodeOwnerRule.new('Integrations', '@ashrafkhamis'),
|
||||
CodeOwnerRule.new('Knowledge', '@aqualls'),
|
||||
CodeOwnerRule.new('Application Performance', '@sselhorn'),
|
||||
CodeOwnerRule.new('Application Performance', '@jglassman1'),
|
||||
CodeOwnerRule.new('Monitor', '@msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Observability', 'msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Optimize', '@lciutacu'),
|
||||
|
@ -67,7 +67,7 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Release', '@rdickenson'),
|
||||
CodeOwnerRule.new('Respond', '@msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Runner', '@sselhorn'),
|
||||
CodeOwnerRule.new('Pods', '@sselhorn'),
|
||||
CodeOwnerRule.new('Pods', '@jglassman1'),
|
||||
CodeOwnerRule.new('Security Policies', '@claytoncornell'),
|
||||
CodeOwnerRule.new('Source Code', '@aqualls'),
|
||||
CodeOwnerRule.new('Static Analysis', '@rdickenson'),
|
||||
|
|
|
@ -2125,9 +2125,6 @@ msgstr ""
|
|||
msgid "Active %{accessTokenTypePlural} (%{totalAccessTokens})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active %{type} (%{token_length})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active Sessions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5215,9 +5212,6 @@ msgstr ""
|
|||
msgid "Are you sure you want to revoke this %{accessTokenType}? This action cannot be undone."
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
|
@ -10782,9 +10776,6 @@ msgstr ""
|
|||
msgid "Copy %{protocol} clone URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy ID"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29693,6 +29684,9 @@ msgstr ""
|
|||
msgid "PipelineSchedules|Last Pipeline"
|
||||
msgstr ""
|
||||
|
||||
msgid "PipelineSchedules|New schedule"
|
||||
msgstr ""
|
||||
|
||||
msgid "PipelineSchedules|Next Run"
|
||||
msgstr ""
|
||||
|
||||
|
@ -41750,9 +41744,6 @@ msgstr ""
|
|||
msgid "This user has no active %{accessTokenTypePlural}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This user has no active %{type}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This user has no identities"
|
||||
msgstr ""
|
||||
|
||||
|
@ -47024,9 +47015,6 @@ msgstr ""
|
|||
msgid "Your new %{accessTokenType} has been created."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your new %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your new comment"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -18,19 +18,11 @@ module QA
|
|||
element :expiry_date_field
|
||||
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
|
||||
element :access_token_name_field
|
||||
element :create_token_button
|
||||
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
|
||||
element :api_label, '#{scope}_label' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Project access token', :reliable do
|
||||
describe 'Project access token' do
|
||||
before(:all) do
|
||||
@project_access_token = QA::Resource::ProjectAccessToken.fabricate_via_api! do |pat|
|
||||
pat.project = Resource::ReusableProject.fabricate_via_api!
|
||||
|
@ -12,7 +12,7 @@ module QA
|
|||
end
|
||||
|
||||
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
|
||||
Resource::File.fabricate_via_api! do |file|
|
||||
file.api_client = @user_api_client
|
||||
|
@ -44,7 +44,7 @@ module QA
|
|||
@different_project = Resource::Project.fabricate!
|
||||
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
|
||||
Resource::File.fabricate_via_api! do |file|
|
||||
file.api_client = @user_api_client
|
||||
|
@ -57,7 +57,7 @@ module QA
|
|||
end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden/)
|
||||
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
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
commit.api_client = @user_api_client
|
||||
|
|
|
@ -24,7 +24,7 @@ RSpec.describe "User sorts issues" do
|
|||
sign_in(user)
|
||||
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))
|
||||
|
||||
click_button 'Created date'
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { GlAlert, GlLoadingIcon, GlModal } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlAlert, GlLoadingIcon, GlModal, GlTabs } from '@gitlab/ui';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { trimText } from 'helpers/text_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
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 PipelineSchedulesTable from '~/pipeline_schedules/components/table/pipeline_schedules_table.vue';
|
||||
import deletePipelineScheduleMutation from '~/pipeline_schedules/graphql/mutations/delete_pipeline_schedule.mutation.graphql';
|
||||
|
@ -32,7 +33,7 @@ describe('Pipeline schedules app', () => {
|
|||
};
|
||||
|
||||
const createComponent = (requestHandlers) => {
|
||||
wrapper = shallowMount(PipelineSchedules, {
|
||||
wrapper = mountExtended(PipelineSchedules, {
|
||||
provide: {
|
||||
fullPath: 'gitlab-org/gitlab',
|
||||
},
|
||||
|
@ -44,17 +45,24 @@ describe('Pipeline schedules app', () => {
|
|||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
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(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('displays table', async () => {
|
||||
it('displays table, tabs and new button', async () => {
|
||||
createComponent();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findTable().exists()).toBe(true);
|
||||
expect(findNewButton().exists()).toBe(true);
|
||||
expect(findTabs().exists()).toBe(true);
|
||||
expect(findAlert().exists()).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -158,4 +166,38 @@ describe('Pipeline schedules app', () => {
|
|||
|
||||
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
|
||||
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
||||
event: 'file_variable_is_referenced_in_another_variable',
|
||||
project_id: project.id
|
||||
project_id: project.id,
|
||||
variable: 'FILEVAR4'
|
||||
).once
|
||||
|
||||
sort_and_expand_all
|
||||
|
|
|
@ -231,4 +231,165 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
describe 'storage_enforcement_date' do
|
||||
describe 'storage_enforcement_date', :freeze_time do
|
||||
let_it_be(:namespace) { create(:group) }
|
||||
|
||||
before do
|
||||
|
@ -2364,7 +2364,7 @@ RSpec.describe Namespace do
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
it 'returns the current date', :freeze_time do
|
||||
it 'returns the current date' do
|
||||
expect(namespace.storage_enforcement_date).to eq(Date.current)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -194,47 +194,6 @@ RSpec.describe PersonalAccessToken do
|
|||
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
|
||||
let(:personal_access_token) { build(:personal_access_token) }
|
||||
|
||||
|
@ -365,7 +324,7 @@ RSpec.describe PersonalAccessToken do
|
|||
|
||||
describe '.simple_sorts' 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
|
||||
|
||||
|
@ -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(: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
|
||||
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
|
||||
expect(Gitlab::AppJsonLogger).to receive(:info).with(
|
||||
event: 'file_variable_is_referenced_in_another_variable',
|
||||
project_id: project.id
|
||||
project_id: project.id,
|
||||
variable: 'file_var'
|
||||
).once
|
||||
|
||||
runner_variables
|
||||
|
|
|
@ -22,7 +22,7 @@ RSpec.describe API::Integrations::JiraConnect::Subscriptions do
|
|||
|
||||
context 'with feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_connect_oauth: false)
|
||||
stub_feature_flags(jira_connect_oauth_self_managed: false)
|
||||
end
|
||||
|
||||
let(:jwt) { '123' }
|
||||
|
|
|
@ -28,9 +28,9 @@ RSpec.describe JiraConnect::SubscriptionsController do
|
|||
it { is_expected.not_to include('http://self-managed-gitlab.com/api/') }
|
||||
end
|
||||
|
||||
context 'with jira_connect_oauth_self_managed feature disabled' do
|
||||
context 'with jira_connect_oauth_self_managed_setting feature disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_connect_oauth_self_managed: false)
|
||||
stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
|
||||
end
|
||||
|
||||
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/show.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.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/search/_results.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/groups/_dropdown.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])
|
||||
.and_return(['/usr/bin/gmake', 0])
|
||||
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])
|
||||
|
||||
subject
|
||||
|
@ -78,7 +78,7 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do
|
|||
.with(%w[which gmake])
|
||||
.and_return(['/usr/bin/gmake', 0])
|
||||
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])
|
||||
|
||||
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
|
||||
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])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when Rails.env is test' do
|
||||
let(:command) { %w[make clean-build all git] }
|
||||
let(:command) { %w[make clean-build all] }
|
||||
|
||||
before do
|
||||
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')
|
||||
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
|
||||
stub_feature_flags(jira_connect_oauth: false)
|
||||
stub_feature_flags(jira_connect_oauth_self_managed_setting: false)
|
||||
end
|
||||
|
||||
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