Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
93e4425400
commit
6559f0ee67
|
@ -571,3 +571,9 @@ Gitlab/RailsLogger:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/**/*.rb'
|
- 'spec/**/*.rb'
|
||||||
- 'ee/spec/**/*.rb'
|
- 'ee/spec/**/*.rb'
|
||||||
|
|
||||||
|
# WIP See https://gitlab.com/gitlab-org/gitlab/-/issues/267606
|
||||||
|
FactoryBot/InlineAssociation:
|
||||||
|
Include:
|
||||||
|
- 'spec/factories/**/*.rb'
|
||||||
|
- 'ee/spec/factories/**/*.rb'
|
||||||
|
|
|
@ -1278,3 +1278,44 @@ Graphql/IDType:
|
||||||
- 'app/graphql/resolvers/snippets_resolver.rb'
|
- 'app/graphql/resolvers/snippets_resolver.rb'
|
||||||
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
|
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
|
||||||
- 'app/graphql/resolvers/user_resolver.rb'
|
- 'app/graphql/resolvers/user_resolver.rb'
|
||||||
|
|
||||||
|
# Offense count: 86
|
||||||
|
# Cop supports --auto-correct.
|
||||||
|
FactoryBot/InlineAssociation:
|
||||||
|
Exclude:
|
||||||
|
- 'ee/spec/factories/analytics/cycle_analytics/group_stages.rb'
|
||||||
|
- 'ee/spec/factories/ci/reports/security/findings.rb'
|
||||||
|
- 'ee/spec/factories/ci/reports/security/reports.rb'
|
||||||
|
- 'ee/spec/factories/geo/event_log.rb'
|
||||||
|
- 'ee/spec/factories/groups.rb'
|
||||||
|
- 'ee/spec/factories/merge_request_blocks.rb'
|
||||||
|
- 'ee/spec/factories/resource_iteration_event.rb'
|
||||||
|
- 'ee/spec/factories/resource_weight_events.rb'
|
||||||
|
- 'ee/spec/factories/vulnerabilities/feedback.rb'
|
||||||
|
- 'spec/factories/atlassian_identities.rb'
|
||||||
|
- 'spec/factories/audit_events.rb'
|
||||||
|
- 'spec/factories/design_management/design_at_version.rb'
|
||||||
|
- 'spec/factories/design_management/designs.rb'
|
||||||
|
- 'spec/factories/design_management/versions.rb'
|
||||||
|
- 'spec/factories/events.rb'
|
||||||
|
- 'spec/factories/git_wiki_commit_details.rb'
|
||||||
|
- 'spec/factories/gitaly/commit.rb'
|
||||||
|
- 'spec/factories/go_module_commits.rb'
|
||||||
|
- 'spec/factories/go_module_versions.rb'
|
||||||
|
- 'spec/factories/go_modules.rb'
|
||||||
|
- 'spec/factories/group_group_links.rb'
|
||||||
|
- 'spec/factories/import_export_uploads.rb'
|
||||||
|
- 'spec/factories/merge_requests.rb'
|
||||||
|
- 'spec/factories/notes.rb'
|
||||||
|
- 'spec/factories/packages.rb'
|
||||||
|
- 'spec/factories/packages/package_file.rb'
|
||||||
|
- 'spec/factories/prometheus_alert.rb'
|
||||||
|
- 'spec/factories/resource_label_events.rb'
|
||||||
|
- 'spec/factories/resource_milestone_event.rb'
|
||||||
|
- 'spec/factories/resource_state_event.rb'
|
||||||
|
- 'spec/factories/sent_notifications.rb'
|
||||||
|
- 'spec/factories/serverless/domain.rb'
|
||||||
|
- 'spec/factories/serverless/domain_cluster.rb'
|
||||||
|
- 'spec/factories/terraform/state.rb'
|
||||||
|
- 'spec/factories/uploads.rb'
|
||||||
|
- 'spec/factories/wiki_pages.rb'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* eslint-disable no-underscore-dangle, class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import ListLabel from './label';
|
import ListLabel from './label';
|
||||||
import ListAssignee from './assignee';
|
import ListAssignee from './assignee';
|
||||||
|
@ -34,7 +34,6 @@ const TYPES = {
|
||||||
class List {
|
class List {
|
||||||
constructor(obj) {
|
constructor(obj) {
|
||||||
this.id = obj.id;
|
this.id = obj.id;
|
||||||
this._uid = this.guid();
|
|
||||||
this.position = obj.position;
|
this.position = obj.position;
|
||||||
this.title = (obj.list_type || obj.listType) === 'backlog' ? __('Open') : obj.title;
|
this.title = (obj.list_type || obj.listType) === 'backlog' ? __('Open') : obj.title;
|
||||||
this.type = obj.list_type || obj.listType;
|
this.type = obj.list_type || obj.listType;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
|
||||||
|
import boardListsQuery from 'ee_else_ce/boards/queries/board_lists.query.graphql';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||||
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
||||||
|
@ -15,7 +17,6 @@ import {
|
||||||
import boardStore from '~/boards/stores/boards_store';
|
import boardStore from '~/boards/stores/boards_store';
|
||||||
|
|
||||||
import listsIssuesQuery from '../queries/lists_issues.query.graphql';
|
import listsIssuesQuery from '../queries/lists_issues.query.graphql';
|
||||||
import boardListsQuery from '../queries/board_lists.query.graphql';
|
|
||||||
import createBoardListMutation from '../queries/board_list_create.mutation.graphql';
|
import createBoardListMutation from '../queries/board_list_create.mutation.graphql';
|
||||||
import updateBoardListMutation from '../queries/board_list_update.mutation.graphql';
|
import updateBoardListMutation from '../queries/board_list_update.mutation.graphql';
|
||||||
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
|
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
|
||||||
|
@ -76,10 +77,10 @@ export default {
|
||||||
variables,
|
variables,
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const { lists } = data[boardType]?.board;
|
const { lists, hideBacklogList } = data[boardType]?.board;
|
||||||
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, formatBoardLists(lists));
|
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, formatBoardLists(lists));
|
||||||
// Backlog list needs to be created if it doesn't exist
|
// Backlog list needs to be created if it doesn't exist and it's not hidden
|
||||||
if (!lists.nodes.find(l => l.listType === ListType.backlog)) {
|
if (!lists.nodes.find(l => l.listType === ListType.backlog) && !hideBacklogList) {
|
||||||
dispatch('createList', { backlog: true });
|
dispatch('createList', { backlog: true });
|
||||||
}
|
}
|
||||||
dispatch('showWelcomeList');
|
dispatch('showWelcomeList');
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { GlButton } from '@gitlab/ui';
|
||||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||||
import Dropdown from './dropdown.vue';
|
import Dropdown from './dropdown.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
GlButton,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['activeFile']),
|
...mapGetters(['activeFile']),
|
||||||
|
@ -65,9 +67,9 @@ export default {
|
||||||
@click="selectTemplate"
|
@click="selectTemplate"
|
||||||
/>
|
/>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<button v-show="updateSuccess" type="button" class="btn btn-default" @click="undo">
|
<gl-button v-show="updateSuccess" category="secondary" variant="default" @click="undo">
|
||||||
{{ __('Undo') }}
|
{{ __('Undo') }}
|
||||||
</button>
|
</gl-button>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -73,11 +73,9 @@ export function initIde(el, options = {}) {
|
||||||
* @param {Objects} options - Extra options for the IDE (Used by EE).
|
* @param {Objects} options - Extra options for the IDE (Used by EE).
|
||||||
*/
|
*/
|
||||||
export function startIde(options) {
|
export function startIde(options) {
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
const ideElement = document.getElementById('ide');
|
||||||
const ideElement = document.getElementById('ide');
|
if (ideElement) {
|
||||||
if (ideElement) {
|
resetServiceWorkersPublicPath();
|
||||||
resetServiceWorkersPublicPath();
|
initIde(ideElement, options);
|
||||||
initIde(ideElement, options);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import initNotes from '~/init_notes';
|
import initNotes from '~/init_notes';
|
||||||
import loadAwardsHandler from '~/awards_handler';
|
import loadAwardsHandler from '~/awards_handler';
|
||||||
import { SnippetShowInit } from '~/snippets';
|
import SnippetsShow from '~/snippets/components/show.vue';
|
||||||
|
import SnippetsAppFactory from '~/snippets';
|
||||||
import ZenMode from '~/zen_mode';
|
import ZenMode from '~/zen_mode';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
SnippetsAppFactory(document.getElementById('js-snippet-view'), SnippetsShow);
|
||||||
SnippetShowInit();
|
|
||||||
initNotes();
|
|
||||||
loadAwardsHandler();
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
initNotes();
|
||||||
new ZenMode();
|
loadAwardsHandler();
|
||||||
});
|
|
||||||
|
// eslint-disable-next-line no-new
|
||||||
|
new ZenMode();
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/const
|
||||||
Vue.use(VueApollo);
|
Vue.use(VueApollo);
|
||||||
Vue.use(Translate);
|
Vue.use(Translate);
|
||||||
|
|
||||||
function appFactory(el, Component) {
|
export default function appFactory(el, Component) {
|
||||||
if (!el) {
|
if (!el) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -45,14 +45,6 @@ function appFactory(el, Component) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SnippetShowInit = () => {
|
|
||||||
import('./components/show.vue')
|
|
||||||
.then(({ default: SnippetsShow }) => {
|
|
||||||
appFactory(document.getElementById('js-snippet-view'), SnippetsShow);
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SnippetEditInit = () => {
|
export const SnippetEditInit = () => {
|
||||||
import('./components/edit.vue')
|
import('./components/edit.vue')
|
||||||
.then(({ default: SnippetsEdit }) => {
|
.then(({ default: SnippetsEdit }) => {
|
||||||
|
|
|
@ -20,6 +20,10 @@ class ProjectRepositoryStorageMove < ApplicationRecord
|
||||||
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
||||||
validate :project_repository_writable, on: :create
|
validate :project_repository_writable, on: :create
|
||||||
|
|
||||||
|
default_value_for(:destination_storage_name, allows_nil: false) do
|
||||||
|
pick_repository_storage
|
||||||
|
end
|
||||||
|
|
||||||
state_machine initial: :initial do
|
state_machine initial: :initial do
|
||||||
event :schedule do
|
event :schedule do
|
||||||
transition initial: :scheduled
|
transition initial: :scheduled
|
||||||
|
@ -77,6 +81,12 @@ class ProjectRepositoryStorageMove < ApplicationRecord
|
||||||
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
||||||
scope :with_projects, -> { includes(project: :route) }
|
scope :with_projects, -> { includes(project: :route) }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def pick_repository_storage
|
||||||
|
Project.pick_repository_storage
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def project_repository_writable
|
def project_repository_writable
|
||||||
|
|
|
@ -283,8 +283,7 @@ class Snippet < ApplicationRecord
|
||||||
::Gitlab::RepositorySizeChecker.new(
|
::Gitlab::RepositorySizeChecker.new(
|
||||||
current_size_proc: -> { repository.size.megabytes },
|
current_size_proc: -> { repository.size.megabytes },
|
||||||
limit: Gitlab::CurrentSettings.snippet_size_limit,
|
limit: Gitlab::CurrentSettings.snippet_size_limit,
|
||||||
total_repository_size_excess: nil,
|
namespace: nil
|
||||||
additional_purchased_storage: nil
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,6 +75,7 @@ module Ci
|
||||||
|
|
||||||
unless live_chunks_pending?
|
unless live_chunks_pending?
|
||||||
metrics.increment_trace_operation(operation: :finalized)
|
metrics.increment_trace_operation(operation: :finalized)
|
||||||
|
metrics.observe_migration_duration(pending_state_seconds)
|
||||||
end
|
end
|
||||||
|
|
||||||
::Gitlab::Ci::Trace::Checksum.new(build).then do |checksum|
|
::Gitlab::Ci::Trace::Checksum.new(build).then do |checksum|
|
||||||
|
@ -130,7 +131,15 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def pending_state_outdated?
|
def pending_state_outdated?
|
||||||
Time.current - pending_state.created_at > ACCEPT_TIMEOUT
|
pending_state_duration > ACCEPT_TIMEOUT
|
||||||
|
end
|
||||||
|
|
||||||
|
def pending_state_duration
|
||||||
|
Time.current - pending_state.created_at
|
||||||
|
end
|
||||||
|
|
||||||
|
def pending_state_seconds
|
||||||
|
pending_state_duration.seconds
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_state
|
def build_state
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
%p
|
%p
|
||||||
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
|
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
|
||||||
- if current_user.two_factor_enabled?
|
- if current_user.two_factor_enabled?
|
||||||
= link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-info'
|
= link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-info'
|
||||||
- else
|
- else
|
||||||
.gl-mb-3
|
.gl-mb-3
|
||||||
= link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'btn btn-success', data: { qa_selector: 'enable_2fa_button' }
|
= link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-success', data: { qa_selector: 'enable_2fa_button' }
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
- if display_providers_on_profile?
|
- if display_providers_on_profile?
|
||||||
|
|
|
@ -30,6 +30,6 @@
|
||||||
= link_to(revoke_session_path(active_session),
|
= link_to(revoke_session_path(active_session),
|
||||||
{ data: { confirm: _('Are you sure? The device will be signed out of GitLab and all remember me tokens revoked.') },
|
{ data: { confirm: _('Are you sure? The device will be signed out of GitLab and all remember me tokens revoked.') },
|
||||||
method: :delete,
|
method: :delete,
|
||||||
class: "btn btn-danger gl-ml-3" }) do
|
class: "gl-button btn btn-danger gl-ml-3" }) do
|
||||||
%span.sr-only= _('Revoke')
|
%span.sr-only= _('Revoke')
|
||||||
= _('Revoke')
|
= _('Revoke')
|
||||||
|
|
|
@ -24,4 +24,4 @@
|
||||||
= _('Never')
|
= _('Never')
|
||||||
|
|
||||||
%td
|
%td
|
||||||
= link_to _('Remove'), profile_chat_name_path(chat_name), method: :delete, class: 'btn btn-danger float-right', data: { confirm: _('Are you sure you want to revoke this nickname?') }
|
= link_to _('Remove'), profile_chat_name_path(chat_name), method: :delete, class: 'gl-button btn btn-danger float-right', data: { confirm: _('Are you sure you want to revoke this nickname?') }
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
.actions
|
.actions
|
||||||
= form_tag profile_chat_names_path, method: :post do
|
= form_tag profile_chat_names_path, method: :post do
|
||||||
= hidden_field_tag :token, @chat_name_token.token
|
= hidden_field_tag :token, @chat_name_token.token
|
||||||
= submit_tag _("Authorize"), class: "btn btn-success wide float-left"
|
= submit_tag _("Authorize"), class: "gl-button btn btn-success wide float-left"
|
||||||
= form_tag deny_profile_chat_names_path, method: :delete do
|
= form_tag deny_profile_chat_names_path, method: :delete do
|
||||||
= hidden_field_tag :token, @chat_name_token.token
|
= hidden_field_tag :token, @chat_name_token.token
|
||||||
= submit_tag _("Deny"), class: "btn btn-danger gl-ml-3"
|
= submit_tag _("Deny"), class: "gl-button btn btn-danger gl-ml-3"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
= f.label :email, _('Email'), class: 'label-bold'
|
= f.label :email, _('Email'), class: 'label-bold'
|
||||||
= f.text_field :email, class: 'form-control', data: { qa_selector: 'email_address_field' }
|
= f.text_field :email, class: 'form-control', data: { qa_selector: 'email_address_field' }
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= f.submit _('Add email address'), class: 'btn btn-success', data: { qa_selector: 'add_email_address_button' }
|
= f.submit _('Add email address'), class: 'gl-button btn btn-success', data: { qa_selector: 'add_email_address_button' }
|
||||||
%hr
|
%hr
|
||||||
%h4.gl-mt-0
|
%h4.gl-mt-0
|
||||||
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
|
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
|
||||||
|
@ -56,8 +56,8 @@
|
||||||
%span.badge.badge-info= s_('Profiles|Notification email')
|
%span.badge.badge-info= s_('Profiles|Notification email')
|
||||||
- unless email.confirmed?
|
- unless email.confirmed?
|
||||||
- confirm_title = "#{email.confirmation_sent_at ? _('Resend confirmation email') : _('Send confirmation email')}"
|
- confirm_title = "#{email.confirmation_sent_at ? _('Resend confirmation email') : _('Send confirmation email')}"
|
||||||
= link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'btn btn-sm btn-warning gl-ml-3'
|
= link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'gl-button btn btn-sm btn-warning gl-ml-3'
|
||||||
|
|
||||||
= link_to profile_email_path(email), data: { confirm: _('Are you sure?'), qa_selector: 'delete_email_link'}, method: :delete, class: 'btn btn-sm btn-danger gl-ml-3' do
|
= link_to profile_email_path(email), data: { confirm: _('Are you sure?'), qa_selector: 'delete_email_link'}, method: :delete, class: 'gl-button btn btn-sm btn-danger gl-ml-3' do
|
||||||
%span.sr-only= _('Remove')
|
%span.sr-only= _('Remove')
|
||||||
= sprite_icon('remove')
|
= sprite_icon('remove')
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
= f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: _("Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'.")
|
= f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: _("Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'.")
|
||||||
|
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= f.submit s_('Profiles|Add key'), class: "btn btn-success"
|
= f.submit s_('Profiles|Add key'), class: "gl-button btn btn-success"
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
.float-right
|
.float-right
|
||||||
%span.key-created-at
|
%span.key-created-at
|
||||||
= s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)}
|
= s_('Profiles|Created %{time_ago}'.html_safe) % { time_ago:time_ago_with_tooltip(key.created_at)}
|
||||||
= link_to profile_gpg_key_path(key), data: { confirm: _('Are you sure? Removing this GPG key does not affect already signed commits.') }, method: :delete, class: "btn btn-danger gl-ml-3" do
|
= link_to profile_gpg_key_path(key), data: { confirm: _('Are you sure? Removing this GPG key does not affect already signed commits.') }, method: :delete, class: "gl-button btn btn-danger gl-ml-3" do
|
||||||
%span.sr-only= _('Remove')
|
%span.sr-only= _('Remove')
|
||||||
= sprite_icon('remove')
|
= sprite_icon('remove')
|
||||||
= link_to revoke_profile_gpg_key_path(key), data: { confirm: _('Are you sure? All commits that were signed with this GPG key will be unverified.') }, method: :put, class: "btn btn-danger gl-ml-3" do
|
= link_to revoke_profile_gpg_key_path(key), data: { confirm: _('Are you sure? All commits that were signed with this GPG key will be unverified.') }, method: :put, class: "gl-button btn btn-danger gl-ml-3" do
|
||||||
%span.sr-only= _('Revoke')
|
%span.sr-only= _('Revoke')
|
||||||
= _('Revoke')
|
= _('Revoke')
|
||||||
|
|
|
@ -24,4 +24,4 @@
|
||||||
%button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
|
%button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
|
||||||
|
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= f.submit s_('Profiles|Add key'), class: "btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button"
|
= f.submit s_('Profiles|Add key'), class: "gl-button btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button"
|
||||||
|
|
|
@ -30,6 +30,6 @@
|
||||||
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
|
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
|
||||||
= f.password_field :password_confirmation, required: true, class: 'form-control', data: { qa_selector: 'confirm_password_field' }
|
= f.password_field :password_confirmation, required: true, class: 'form-control', data: { qa_selector: 'confirm_password_field' }
|
||||||
.gl-mt-3.gl-mb-3
|
.gl-mt-3.gl-mb-3
|
||||||
= f.submit _('Save password'), class: "btn btn-success gl-mr-3", data: { qa_selector: 'save_password_button' }
|
= f.submit _('Save password'), class: "gl-button btn btn-success gl-mr-3", data: { qa_selector: 'save_password_button' }
|
||||||
- unless @user.password_automatically_set?
|
- unless @user.password_automatically_set?
|
||||||
= link_to _('I forgot my password'), reset_profile_password_path, method: :put
|
= link_to _('I forgot my password'), reset_profile_password_path, method: :put
|
||||||
|
|
|
@ -28,4 +28,4 @@
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.password_field :password_confirmation, required: true, class: 'form-control'
|
= f.password_field :password_confirmation, required: true, class: 'form-control'
|
||||||
.form-actions
|
.form-actions
|
||||||
= f.submit _('Set new password'), class: 'btn btn-success'
|
= f.submit _('Set new password'), class: 'gl-button btn btn-success'
|
||||||
|
|
|
@ -143,4 +143,4 @@
|
||||||
.col-lg-4.profile-settings-sidebar
|
.col-lg-4.profile-settings-sidebar
|
||||||
.col-lg-8
|
.col-lg-8
|
||||||
.form-group
|
.form-group
|
||||||
= f.submit _('Save changes'), class: 'btn btn-success'
|
= f.submit _('Save changes'), class: 'gl-button btn btn-success'
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
.form-text.text-muted= s_("Profiles|The maximum file size allowed is 200KB.")
|
.form-text.text-muted= s_("Profiles|The maximum file size allowed is 200KB.")
|
||||||
- if @user.avatar?
|
- if @user.avatar?
|
||||||
%hr
|
%hr
|
||||||
= link_to s_("Profiles|Remove avatar"), profile_avatar_path, data: { confirm: s_("Profiles|Avatar will be removed. Are you sure?") }, method: :delete, class: 'btn btn-danger btn-inverted'
|
= link_to s_("Profiles|Remove avatar"), profile_avatar_path, data: { confirm: s_("Profiles|Avatar will be removed. Are you sure?") }, method: :delete, class: 'gl-button btn btn-danger btn-inverted'
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
.row
|
.row
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
.col-lg-8
|
.col-lg-8
|
||||||
= f.fields_for :status, @user.status do |status_form|
|
= f.fields_for :status, @user.status do |status_form|
|
||||||
- emoji_button = button_tag type: :button,
|
- emoji_button = button_tag type: :button,
|
||||||
class: 'js-toggle-emoji-menu emoji-menu-toggle-button btn has-tooltip',
|
class: 'js-toggle-emoji-menu emoji-menu-toggle-button gl-button btn has-tooltip',
|
||||||
title: s_("Profiles|Add status emoji") do
|
title: s_("Profiles|Add status emoji") do
|
||||||
- if @user.status
|
- if @user.status
|
||||||
= emoji_icon @user.status.emoji
|
= emoji_icon @user.status.emoji
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
= sprite_icon('smile', css_class: 'award-control-icon-super-positive')
|
= sprite_icon('smile', css_class: 'award-control-icon-super-positive')
|
||||||
- reset_message_button = button_tag type: :button,
|
- reset_message_button = button_tag type: :button,
|
||||||
id: 'js-clear-user-status-button',
|
id: 'js-clear-user-status-button',
|
||||||
class: 'clear-user-status btn has-tooltip',
|
class: 'clear-user-status gl-button btn has-tooltip',
|
||||||
title: s_("Profiles|Clear status") do
|
title: s_("Profiles|Clear status") do
|
||||||
= sprite_icon("close")
|
= sprite_icon("close")
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
-# TODO: might need an entry in user/profile.md to describe some of these settings
|
-# TODO: might need an entry in user/profile.md to describe some of these settings
|
||||||
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/60070
|
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/60070
|
||||||
%h5= ("Time zone")
|
%h5= ("Time zone")
|
||||||
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown input-lg', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
|
= dropdown_tag(_("Select a timezone"), options: { toggle_class: 'gl-button btn js-timezone-dropdown input-lg', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
|
||||||
%input.hidden{ :type => 'hidden', :id => 'user_timezone', :name => 'user[timezone]', value: @user.timezone }
|
%input.hidden{ :type => 'hidden', :id => 'user_timezone', :name => 'user[timezone]', value: @user.timezone }
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
|
@ -119,8 +119,8 @@
|
||||||
.help-block
|
.help-block
|
||||||
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
|
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
|
||||||
.gl-mt-3.gl-mb-3
|
.gl-mt-3.gl-mb-3
|
||||||
= f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
|
= f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-success'
|
||||||
= link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'
|
= link_to _("Cancel"), user_path(current_user), class: 'gl-button btn btn-cancel'
|
||||||
|
|
||||||
.modal.modal-profile-crop{ data: { cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css') } }
|
.modal.modal-profile-crop{ data: { cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css') } }
|
||||||
.modal-dialog
|
.modal-dialog
|
||||||
|
|
|
@ -9,5 +9,5 @@
|
||||||
%span.monospace{ data: { qa_selector: 'code_content' } }= code
|
%span.monospace{ data: { qa_selector: 'code_content' } }= code
|
||||||
|
|
||||||
.d-flex
|
.d-flex
|
||||||
= link_to _('Proceed'), profile_account_path, class: 'btn btn-success gl-mr-3', data: { qa_selector: 'proceed_button' }
|
= link_to _('Proceed'), profile_account_path, class: 'gl-button btn btn-success gl-mr-3', data: { qa_selector: 'proceed_button' }
|
||||||
= link_to _('Download codes'), "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'btn btn-default'
|
= link_to _('Download codes'), "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'gl-button btn btn-default'
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
= link_to _('Disable two-factor authentication'), profile_two_factor_auth_path,
|
= link_to _('Disable two-factor authentication'), profile_two_factor_auth_path,
|
||||||
method: :delete,
|
method: :delete,
|
||||||
data: { confirm: webauthn_enabled ? _('Are you sure? This will invalidate your registered applications and U2F / WebAuthn devices.') : _('Are you sure? This will invalidate your registered applications and U2F devices.') },
|
data: { confirm: webauthn_enabled ? _('Are you sure? This will invalidate your registered applications and U2F / WebAuthn devices.') : _('Are you sure? This will invalidate your registered applications and U2F devices.') },
|
||||||
class: 'btn btn-danger gl-mr-3'
|
class: 'gl-button btn btn-danger gl-mr-3'
|
||||||
= form_tag codes_profile_two_factor_auth_path, {style: 'display: inline-block', method: :post} do |f|
|
= form_tag codes_profile_two_factor_auth_path, {style: 'display: inline-block', method: :post} do |f|
|
||||||
= submit_tag _('Regenerate recovery codes'), class: 'btn'
|
= submit_tag _('Regenerate recovery codes'), class: 'btn'
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
= label_tag :pin_code, _('Pin code'), class: "label-bold"
|
= label_tag :pin_code, _('Pin code'), class: "label-bold"
|
||||||
= text_field_tag :pin_code, nil, class: "form-control", required: true, data: { qa_selector: 'pin_code_field' }
|
= text_field_tag :pin_code, nil, class: "form-control", required: true, data: { qa_selector: 'pin_code_field' }
|
||||||
.gl-mt-3
|
.gl-mt-3
|
||||||
= submit_tag _('Register with two-factor app'), class: 'btn btn-success', data: { qa_selector: 'register_2fa_app_button' }
|
= submit_tag _('Register with two-factor app'), class: 'gl-button btn btn-success', data: { qa_selector: 'register_2fa_app_button' }
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
%span.gl-text-gray-500
|
%span.gl-text-gray-500
|
||||||
= _("no name set")
|
= _("no name set")
|
||||||
%td= registration[:created_at].to_date.to_s(:medium)
|
%td= registration[:created_at].to_date.to_s(:medium)
|
||||||
%td= link_to _('Delete'), registration[:delete_path], method: :delete, class: "btn btn-danger float-right", data: { confirm: _('Are you sure you want to delete this device? This action cannot be undone.') }
|
%td= link_to _('Delete'), registration[:delete_path], method: :delete, class: "gl-button btn btn-danger float-right", data: { confirm: _('Are you sure you want to delete this device? This action cannot be undone.') }
|
||||||
|
|
||||||
- else
|
- else
|
||||||
.settings-message.text-center
|
.settings-message.text-center
|
||||||
|
|
|
@ -22,4 +22,4 @@
|
||||||
- if partial_exists? "registrations/welcome/button"
|
- if partial_exists? "registrations/welcome/button"
|
||||||
= render "registrations/welcome/button"
|
= render "registrations/welcome/button"
|
||||||
- else
|
- else
|
||||||
= f.submit _('Get started!'), class: 'btn-register btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
|
= f.submit _('Get started!'), class: 'btn-register gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add Middleman Logo for Project Templates
|
||||||
|
merge_request: 44617
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Allow automatically selecting repository storage on move
|
||||||
|
merge_request: 45338
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update Cycle Analytics with Value Stream Analytics in University
|
||||||
|
merge_request: 44244
|
||||||
|
author: Takuya Noguchi
|
||||||
|
type: other
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Include PostgreSQL system identifier in usage ping
|
||||||
|
merge_request: 44972
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
name: ci_new_artifact_file_reader
|
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40268
|
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249588
|
|
||||||
group: group::pipeline authoring
|
|
||||||
type: development
|
|
||||||
default_enabled: true
|
|
|
@ -194,7 +194,7 @@ Parameters:
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `project_id` | integer | yes | ID of the project |
|
| `project_id` | integer | yes | ID of the project |
|
||||||
| `destination_storage_name` | string | yes | Name of the destination storage shard |
|
| `destination_storage_name` | string | no | Name of the destination storage shard. If not provided the storage will be selected automatically. |
|
||||||
|
|
||||||
Example request:
|
Example request:
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ become available, you will be able to share job templates like this
|
||||||
Dependencies should be kept to the minimum. The introduction of a new
|
Dependencies should be kept to the minimum. The introduction of a new
|
||||||
dependency should be argued in the merge request, as per our [Approval
|
dependency should be argued in the merge request, as per our [Approval
|
||||||
Guidelines](../code_review.md#approval-guidelines). Both [License
|
Guidelines](../code_review.md#approval-guidelines). Both [License
|
||||||
Management](../../user/compliance/license_compliance/index.md)
|
Scanning](../../user/compliance/license_compliance/index.md)
|
||||||
**(ULTIMATE)** and [Dependency
|
**(ULTIMATE)** and [Dependency
|
||||||
Scanning](../../user/application_security/dependency_scanning/index.md)
|
Scanning](../../user/application_security/dependency_scanning/index.md)
|
||||||
**(ULTIMATE)** should be activated on all projects to ensure new dependencies
|
**(ULTIMATE)** should be activated on all projects to ensure new dependencies
|
||||||
|
|
|
@ -750,7 +750,8 @@ The following is example content of the Usage Ping payload.
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"adapter": "postgresql",
|
"adapter": "postgresql",
|
||||||
"version": "9.6.15"
|
"version": "9.6.15",
|
||||||
|
"pg_system_id": 6842684531675334351
|
||||||
},
|
},
|
||||||
"avg_cycle_analytics": {
|
"avg_cycle_analytics": {
|
||||||
"issue": {
|
"issue": {
|
||||||
|
@ -910,6 +911,10 @@ The following is example content of the Usage Ping payload.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notable changes
|
||||||
|
|
||||||
|
In GitLab 13.5, `pg_system_id` was added to send the [PostgreSQL system identifier](https://www.2ndquadrant.com/en/blog/support-for-postgresqls-system-identifier-in-barman/).
|
||||||
|
|
||||||
## Exporting Usage Ping SQL queries and definitions
|
## Exporting Usage Ping SQL queries and definitions
|
||||||
|
|
||||||
Two Rake tasks exist to export Usage Ping definitions.
|
Two Rake tasks exist to export Usage Ping definitions.
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
---
|
||||||
|
stage: Create
|
||||||
|
group: Source Code
|
||||||
|
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
|
||||||
|
type: reference, how-to
|
||||||
|
---
|
||||||
|
|
||||||
# Kerberos integration **(STARTER ONLY)**
|
# Kerberos integration **(STARTER ONLY)**
|
||||||
|
|
||||||
GitLab can integrate with [Kerberos](https://web.mit.edu/kerberos/) as an authentication mechanism.
|
GitLab can integrate with [Kerberos](https://web.mit.edu/kerberos/) as an authentication mechanism.
|
||||||
|
@ -157,6 +164,13 @@ GitLab users with a linked Kerberos account can also `git pull` and `git push`
|
||||||
using Kerberos tokens, i.e., without having to send their password with each
|
using Kerberos tokens, i.e., without having to send their password with each
|
||||||
operation.
|
operation.
|
||||||
|
|
||||||
|
DANGER: **Danger:**
|
||||||
|
There is a [known issue](https://github.com/curl/curl/issues/1261) with `libcurl`
|
||||||
|
older than version 7.64.1 wherein it won't reuse connections when negotiating.
|
||||||
|
This leads to authorization issues when push is larger than `http.postBuffer`
|
||||||
|
config. Ensure that Git is using at least `libcurl` 7.64.1 to avoid this. To
|
||||||
|
know the `libcurl` version installed, run `curl-config --version`.
|
||||||
|
|
||||||
### HTTP Git access with Kerberos token (passwordless authentication)
|
### HTTP Git access with Kerberos token (passwordless authentication)
|
||||||
|
|
||||||
#### Support for Git before 2.4
|
#### Support for Git before 2.4
|
||||||
|
|
|
@ -175,10 +175,10 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
|
||||||
1. [High Availability - Video](https://www.youtube.com/watch?v=36KS808u6bE&index=15&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
|
1. [High Availability - Video](https://www.youtube.com/watch?v=36KS808u6bE&index=15&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
|
||||||
1. [High Availability Documentation](https://about.gitlab.com/solutions/reference-architectures/)
|
1. [High Availability Documentation](https://about.gitlab.com/solutions/reference-architectures/)
|
||||||
|
|
||||||
### 3.8 Cycle Analytics
|
### 3.8 Value Stream Analytics
|
||||||
|
|
||||||
1. [GitLab Cycle Analytics Overview](https://about.gitlab.com/blog/2016/09/21/cycle-analytics-feature-highlight/)
|
1. [GitLab Value Stream Analytics Overview (as of 2016)](https://about.gitlab.com/blog/2016/09/21/cycle-analytics-feature-highlight/)
|
||||||
1. [GitLab Cycle Analytics - Product Page](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/)
|
1. [GitLab Value Stream Analytics - Product Page](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/)
|
||||||
|
|
||||||
### 3.9. Integrations
|
### 3.9. Integrations
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -5,7 +5,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
|
||||||
type: reference, how-to
|
type: reference, how-to
|
||||||
---
|
---
|
||||||
|
|
||||||
# Wiki
|
# Wiki **(CORE)**
|
||||||
|
|
||||||
A separate system for documentation called Wiki, is built right into each
|
A separate system for documentation called Wiki, is built right into each
|
||||||
GitLab project. It is enabled by default on all new projects and you can find
|
GitLab project. It is enabled by default on all new projects and you can find
|
||||||
|
@ -130,10 +130,12 @@ be preceded by the slash (`/`) character.
|
||||||
|
|
||||||
## Viewing a list of all created wiki pages
|
## Viewing a list of all created wiki pages
|
||||||
|
|
||||||
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17673/) in GitLab 13.5, wiki pages are displayed as a nested tree in the sidebar and pages overview.
|
||||||
|
|
||||||
Every wiki has a sidebar from which a short list of the created pages can be
|
Every wiki has a sidebar from which a short list of the created pages can be
|
||||||
found. The list is ordered alphabetically.
|
found. The list is ordered alphabetically.
|
||||||
|
|
||||||
![Wiki sidebar](img/wiki_sidebar.png)
|
![Wiki sidebar](img/wiki_sidebar_v13_5.png)
|
||||||
|
|
||||||
If you have many pages, not all will be listed in the sidebar. Click on
|
If you have many pages, not all will be listed in the sidebar. Click on
|
||||||
**View All Pages** to see all of them.
|
**View All Pages** to see all of them.
|
||||||
|
|
|
@ -69,7 +69,7 @@ module API
|
||||||
success Entities::ProjectRepositoryStorageMove
|
success Entities::ProjectRepositoryStorageMove
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
requires :destination_storage_name, type: String, desc: 'The destination storage shard'
|
optional :destination_storage_name, type: String, desc: 'The destination storage shard'
|
||||||
end
|
end
|
||||||
post ':id/repository_storage_moves' do
|
post ':id/repository_storage_moves' do
|
||||||
storage_move = user_project.repository_storage_moves.build(
|
storage_move = user_project.repository_storage_moves.build(
|
||||||
|
|
|
@ -45,14 +45,6 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_zip_file!(file_path)
|
def read_zip_file!(file_path)
|
||||||
if ::Gitlab::Ci::Features.new_artifact_file_reader_enabled?(job.project)
|
|
||||||
read_with_new_artifact_file_reader(file_path)
|
|
||||||
else
|
|
||||||
read_with_legacy_artifact_file_reader(file_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_with_new_artifact_file_reader(file_path)
|
|
||||||
job.artifacts_file.use_open_file do |file|
|
job.artifacts_file.use_open_file do |file|
|
||||||
zip_file = Zip::File.new(file, false, true)
|
zip_file = Zip::File.new(file, false, true)
|
||||||
entry = zip_file.find_entry(file_path)
|
entry = zip_file.find_entry(file_path)
|
||||||
|
@ -69,25 +61,6 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_with_legacy_artifact_file_reader(file_path)
|
|
||||||
job.artifacts_file.use_file do |archive_path|
|
|
||||||
Zip::File.open(archive_path) do |zip_file|
|
|
||||||
entry = zip_file.find_entry(file_path)
|
|
||||||
unless entry
|
|
||||||
raise Error, "Path `#{file_path}` does not exist inside the `#{job.name}` artifacts archive!"
|
|
||||||
end
|
|
||||||
|
|
||||||
if entry.name_is_directory?
|
|
||||||
raise Error, "Path `#{file_path}` was expected to be a file but it was a directory!"
|
|
||||||
end
|
|
||||||
|
|
||||||
zip_file.get_input_stream(entry) do |is|
|
|
||||||
is.read
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def max_archive_size_in_mb
|
def max_archive_size_in_mb
|
||||||
ActiveSupport::NumberHelper.number_to_human_size(MAX_ARCHIVE_SIZE)
|
ActiveSupport::NumberHelper.number_to_human_size(MAX_ARCHIVE_SIZE)
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,10 +59,6 @@ module Gitlab
|
||||||
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
|
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.new_artifact_file_reader_enabled?(project)
|
|
||||||
::Feature.enabled?(:ci_new_artifact_file_reader, project, default_enabled: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.one_dimensional_matrix_enabled?
|
def self.one_dimensional_matrix_enabled?
|
||||||
::Feature.enabled?(:one_dimensional_matrix, default_enabled: true)
|
::Feature.enabled?(:one_dimensional_matrix, default_enabled: true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,10 @@ module Gitlab
|
||||||
self.class.trace_bytes.increment({}, size.to_i)
|
self.class.trace_bytes.increment({}, size.to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def observe_migration_duration(seconds)
|
||||||
|
self.class.finalize_histogram.observe({}, seconds.to_f)
|
||||||
|
end
|
||||||
|
|
||||||
def self.trace_operations
|
def self.trace_operations
|
||||||
strong_memoize(:trace_operations) do
|
strong_memoize(:trace_operations) do
|
||||||
name = :gitlab_ci_trace_operations_total
|
name = :gitlab_ci_trace_operations_total
|
||||||
|
@ -50,6 +54,17 @@ module Gitlab
|
||||||
Gitlab::Metrics.counter(name, comment)
|
Gitlab::Metrics.counter(name, comment)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.finalize_histogram
|
||||||
|
strong_memoize(:finalize_histogram) do
|
||||||
|
name = :gitlab_ci_trace_finalize_duration_seconds
|
||||||
|
comment = 'Duration of build trace chunks migration to object storage'
|
||||||
|
buckets = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 10.0, 30.0, 60.0, 300.0]
|
||||||
|
labels = {}
|
||||||
|
|
||||||
|
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -250,6 +250,12 @@ module Gitlab
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.system_id
|
||||||
|
row = connection.execute('SELECT system_identifier FROM pg_control_system()').first
|
||||||
|
|
||||||
|
row['system_identifier']
|
||||||
|
end
|
||||||
|
|
||||||
def self.get_write_location(ar_connection)
|
def self.get_write_location(ar_connection)
|
||||||
row = ar_connection
|
row = ar_connection
|
||||||
.select_all("SELECT pg_current_wal_insert_lsn()::text AS location")
|
.select_all("SELECT pg_current_wal_insert_lsn()::text AS location")
|
||||||
|
|
|
@ -53,7 +53,7 @@ module Gitlab
|
||||||
ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'),
|
ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'),
|
||||||
ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'),
|
ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'),
|
||||||
ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'),
|
ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'),
|
||||||
ProjectTemplate.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman'),
|
ProjectTemplate.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman', 'illustrations/logos/middleman.svg'),
|
||||||
ProjectTemplate.new('gitpod_spring_petclinic', 'Gitpod/Spring Petclinic', _('A Gitpod configured Webapplication in Spring and Java'), 'https://gitlab.com/gitlab-org/project-templates/gitpod-spring-petclinic', 'illustrations/logos/gitpod.svg'),
|
ProjectTemplate.new('gitpod_spring_petclinic', 'Gitpod/Spring Petclinic', _('A Gitpod configured Webapplication in Spring and Java'), 'https://gitlab.com/gitlab-org/project-templates/gitpod-spring-petclinic', 'illustrations/logos/gitpod.svg'),
|
||||||
ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
|
ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
|
||||||
ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
|
ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
# Centralized class for repository size related calculations.
|
# Centralized class for repository size related calculations.
|
||||||
class RepositorySizeChecker
|
class RepositorySizeChecker
|
||||||
attr_reader :limit, :total_repository_size_excess, :additional_purchased_storage
|
attr_reader :limit
|
||||||
|
|
||||||
# @param current_size_proc [Proc] returns repository size in bytes
|
# @param current_size_proc [Proc] returns repository size in bytes
|
||||||
def initialize(current_size_proc:, limit:, total_repository_size_excess:, additional_purchased_storage:, enabled: true)
|
def initialize(current_size_proc:, limit:, namespace:, enabled: true)
|
||||||
@current_size_proc = current_size_proc
|
@current_size_proc = current_size_proc
|
||||||
@limit = limit
|
@limit = limit
|
||||||
@total_repository_size_excess = total_repository_size_excess.to_i
|
@namespace = namespace
|
||||||
@additional_purchased_storage = additional_purchased_storage.to_i
|
|
||||||
@enabled = enabled && limit != 0
|
@enabled = enabled && limit != 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,6 +43,10 @@ module Gitlab
|
||||||
def error_message
|
def error_message
|
||||||
@error_message_object ||= ::Gitlab::RepositorySizeErrorMessage.new(self)
|
@error_message_object ||= ::Gitlab::RepositorySizeErrorMessage.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :namespace
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ module Gitlab
|
||||||
class RepositorySizeErrorMessage
|
class RepositorySizeErrorMessage
|
||||||
include ActiveSupport::NumberHelper
|
include ActiveSupport::NumberHelper
|
||||||
|
|
||||||
delegate :current_size, :limit, :total_repository_size_excess, :additional_purchased_storage, :exceeded_size, to: :@checker
|
delegate :current_size, :limit, :exceeded_size, to: :@checker
|
||||||
|
|
||||||
# @param checher [RepositorySizeChecker]
|
# @param checher [RepositorySizeChecker]
|
||||||
def initialize(checker)
|
def initialize(checker)
|
||||||
|
|
|
@ -303,7 +303,8 @@ module Gitlab
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
adapter: alt_usage_data { Gitlab::Database.adapter_name },
|
adapter: alt_usage_data { Gitlab::Database.adapter_name },
|
||||||
version: alt_usage_data { Gitlab::Database.version }
|
version: alt_usage_data { Gitlab::Database.version },
|
||||||
|
pg_system_id: alt_usage_data { Gitlab::Database.system_id }
|
||||||
},
|
},
|
||||||
mail: {
|
mail: {
|
||||||
smtp_server: alt_usage_data { ActionMailer::Base.smtp_settings[:address] }
|
smtp_server: alt_usage_data { ActionMailer::Base.smtp_settings[:address] }
|
||||||
|
|
|
@ -6,6 +6,10 @@ module RuboCop
|
||||||
module Cop
|
module Cop
|
||||||
module Migration
|
module Migration
|
||||||
# Cop that enforces always adding a limit on text columns
|
# Cop that enforces always adding a limit on text columns
|
||||||
|
#
|
||||||
|
# Text columns starting with `encrypted_` are very likely used
|
||||||
|
# by `attr_encrypted` which controls the text length. Those columns
|
||||||
|
# should not add a text limit.
|
||||||
class AddLimitToTextColumns < RuboCop::Cop::Cop
|
class AddLimitToTextColumns < RuboCop::Cop::Cop
|
||||||
include MigrationHelpers
|
include MigrationHelpers
|
||||||
|
|
||||||
|
@ -102,6 +106,8 @@ module RuboCop
|
||||||
# Check if there is an `add_text_limit` call for the provided
|
# Check if there is an `add_text_limit` call for the provided
|
||||||
# table and attribute name
|
# table and attribute name
|
||||||
def text_limit_missing?(node, table_name, attribute_name)
|
def text_limit_missing?(node, table_name, attribute_name)
|
||||||
|
return false if encrypted_attribute_name?(attribute_name)
|
||||||
|
|
||||||
limit_found = false
|
limit_found = false
|
||||||
|
|
||||||
node.each_descendant(:send) do |send_node|
|
node.each_descendant(:send) do |send_node|
|
||||||
|
@ -118,6 +124,10 @@ module RuboCop
|
||||||
|
|
||||||
!limit_found
|
!limit_found
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def encrypted_attribute_name?(attribute_name)
|
||||||
|
attribute_name.to_s.start_with?('encrypted_')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module RSpec
|
||||||
|
module FactoryBot
|
||||||
|
# This cop encourages the use of inline associations in FactoryBot.
|
||||||
|
# The explicit use of `create` and `build` is discouraged.
|
||||||
|
#
|
||||||
|
# See https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#inline-definition
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
#
|
||||||
|
# Context:
|
||||||
|
#
|
||||||
|
# Factory.define do
|
||||||
|
# factory :project, class: 'Project'
|
||||||
|
# # EXAMPLE below
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # bad
|
||||||
|
# creator { create(:user) }
|
||||||
|
# creator { create(:user, :admin) }
|
||||||
|
# creator { build(:user) }
|
||||||
|
# creator { FactoryBot.build(:user) }
|
||||||
|
# creator { ::FactoryBot.build(:user) }
|
||||||
|
# add_attribute(:creator) { build(:user) }
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# creator { association(:user) }
|
||||||
|
# creator { association(:user, :admin) }
|
||||||
|
# add_attribute(:creator) { association(:user) }
|
||||||
|
#
|
||||||
|
# # Accepted
|
||||||
|
# after(:build) do |instance|
|
||||||
|
# instance.creator = create(:user)
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# initialize_with do
|
||||||
|
# create(:project)
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# creator_id { create(:user).id }
|
||||||
|
#
|
||||||
|
class InlineAssociation < RuboCop::Cop::Cop
|
||||||
|
MSG = 'Prefer inline `association` over `%{type}`. ' \
|
||||||
|
'See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#factories'
|
||||||
|
|
||||||
|
REPLACEMENT = 'association'
|
||||||
|
|
||||||
|
def_node_matcher :create_or_build, <<~PATTERN
|
||||||
|
(
|
||||||
|
send
|
||||||
|
${ nil? (const { nil? (cbase) } :FactoryBot) }
|
||||||
|
${ :create :build }
|
||||||
|
(sym _)
|
||||||
|
...
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_matcher :association_definition, <<~PATTERN
|
||||||
|
(block
|
||||||
|
{
|
||||||
|
(send nil? $_)
|
||||||
|
(send nil? :add_attribute (sym $_))
|
||||||
|
}
|
||||||
|
...
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_matcher :chained_call?, <<~PATTERN
|
||||||
|
(send _ _)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
SKIP_NAMES = %i[initialize_with].to_set.freeze
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
_receiver, type = create_or_build(node)
|
||||||
|
return unless type
|
||||||
|
return if chained_call?(node.parent)
|
||||||
|
return unless inside_assocation_definition?(node)
|
||||||
|
|
||||||
|
add_offense(node, message: format(MSG, type: type))
|
||||||
|
end
|
||||||
|
|
||||||
|
def autocorrect(node)
|
||||||
|
lambda do |corrector|
|
||||||
|
receiver, type = create_or_build(node)
|
||||||
|
receiver = "#{receiver.source}." if receiver
|
||||||
|
expression = "#{receiver}#{type}"
|
||||||
|
replacement = node.source.sub(expression, REPLACEMENT)
|
||||||
|
corrector.replace(node.source_range, replacement)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def inside_assocation_definition?(node)
|
||||||
|
node.each_ancestor(:block).any? do |parent|
|
||||||
|
name = association_definition(parent)
|
||||||
|
name && !SKIP_NAMES.include?(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,6 @@ FactoryBot.define do
|
||||||
project
|
project
|
||||||
|
|
||||||
source_storage_name { 'default' }
|
source_storage_name { 'default' }
|
||||||
destination_storage_name { 'default' }
|
|
||||||
|
|
||||||
trait :scheduled do
|
trait :scheduled do
|
||||||
state { ProjectRepositoryStorageMove.state_machines[:state].states[:scheduled].value }
|
state { ProjectRepositoryStorageMove.state_machines[:state].states[:scheduled].value }
|
||||||
|
|
|
@ -13,7 +13,7 @@ import actions, { gqlClient } from '~/boards/stores/actions';
|
||||||
import * as types from '~/boards/stores/mutation_types';
|
import * as types from '~/boards/stores/mutation_types';
|
||||||
import { inactiveId, ListType } from '~/boards/constants';
|
import { inactiveId, ListType } from '~/boards/constants';
|
||||||
import issueMoveListMutation from '~/boards/queries/issue_move_list.mutation.graphql';
|
import issueMoveListMutation from '~/boards/queries/issue_move_list.mutation.graphql';
|
||||||
import { fullBoardId, formatListIssues } from '~/boards/boards_util';
|
import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util';
|
||||||
|
|
||||||
const expectNotImplemented = action => {
|
const expectNotImplemented = action => {
|
||||||
it('is not implemented', () => {
|
it('is not implemented', () => {
|
||||||
|
@ -78,6 +78,80 @@ describe('setActiveId', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('fetchLists', () => {
|
||||||
|
const state = {
|
||||||
|
endpoints: {
|
||||||
|
fullPath: 'gitlab-org',
|
||||||
|
boardId: 1,
|
||||||
|
},
|
||||||
|
filterParams: {},
|
||||||
|
boardType: 'group',
|
||||||
|
};
|
||||||
|
|
||||||
|
let queryResponse = {
|
||||||
|
data: {
|
||||||
|
group: {
|
||||||
|
board: {
|
||||||
|
hideBacklogList: true,
|
||||||
|
lists: {
|
||||||
|
nodes: [mockLists[1]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const formattedLists = formatBoardLists(queryResponse.data.group.board.lists);
|
||||||
|
|
||||||
|
it('should commit mutations RECEIVE_BOARD_LISTS_SUCCESS on success', done => {
|
||||||
|
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
|
||||||
|
|
||||||
|
testAction(
|
||||||
|
actions.fetchLists,
|
||||||
|
{},
|
||||||
|
state,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: types.RECEIVE_BOARD_LISTS_SUCCESS,
|
||||||
|
payload: formattedLists,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[{ type: 'showWelcomeList' }],
|
||||||
|
done,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatch createList action when backlog list does not exist and is not hidden', done => {
|
||||||
|
queryResponse = {
|
||||||
|
data: {
|
||||||
|
group: {
|
||||||
|
board: {
|
||||||
|
hideBacklogList: false,
|
||||||
|
lists: {
|
||||||
|
nodes: [mockLists[1]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
|
||||||
|
|
||||||
|
testAction(
|
||||||
|
actions.fetchLists,
|
||||||
|
{},
|
||||||
|
state,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: types.RECEIVE_BOARD_LISTS_SUCCESS,
|
||||||
|
payload: formattedLists,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[{ type: 'createList', payload: { backlog: true } }, { type: 'showWelcomeList' }],
|
||||||
|
done,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('showWelcomeList', () => {
|
describe('showWelcomeList', () => {
|
||||||
it('should dispatch addList action', done => {
|
it('should dispatch addList action', done => {
|
||||||
const state = {
|
const state = {
|
||||||
|
|
|
@ -18,17 +18,6 @@ RSpec.describe Gitlab::Ci::ArtifactFileReader do
|
||||||
expect(YAML.safe_load(subject).keys).to contain_exactly('rspec', 'time', 'custom')
|
expect(YAML.safe_load(subject).keys).to contain_exactly('rspec', 'time', 'custom')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when FF ci_new_artifact_file_reader is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(ci_new_artifact_file_reader: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns the content at the path' do
|
|
||||||
is_expected.to be_present
|
|
||||||
expect(YAML.safe_load(subject).keys).to contain_exactly('rspec', 'time', 'custom')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when path does not exist' do
|
context 'when path does not exist' do
|
||||||
let(:path) { 'file/does/not/exist.txt' }
|
let(:path) { 'file/does/not/exist.txt' }
|
||||||
let(:expected_error) do
|
let(:expected_error) do
|
||||||
|
|
|
@ -39,6 +39,12 @@ RSpec.describe Gitlab::Database do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.system_id' do
|
||||||
|
it 'returns the PostgreSQL system identifier' do
|
||||||
|
expect(described_class.system_id).to be_an_instance_of(Integer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.postgresql?' do
|
describe '.postgresql?' do
|
||||||
subject { described_class.postgresql? }
|
subject { described_class.postgresql? }
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::RepositorySizeChecker do
|
RSpec.describe Gitlab::RepositorySizeChecker do
|
||||||
|
let_it_be(:namespace) { nil }
|
||||||
let(:current_size) { 0 }
|
let(:current_size) { 0 }
|
||||||
let(:limit) { 50 }
|
let(:limit) { 50 }
|
||||||
let(:enabled) { true }
|
let(:enabled) { true }
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
described_class.new(
|
described_class.new(
|
||||||
current_size_proc: -> { current_size },
|
current_size_proc: -> { current_size.megabytes },
|
||||||
limit: limit,
|
limit: limit.megabytes,
|
||||||
total_repository_size_excess: 0,
|
namespace: namespace,
|
||||||
additional_purchased_storage: 0,
|
|
||||||
enabled: enabled
|
enabled: enabled
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
||||||
describe '#enabled?' do
|
describe '#enabled?' do
|
||||||
context 'when enabled' do
|
context 'when enabled' do
|
||||||
it 'returns true' do
|
it 'returns true' do
|
||||||
expect(subject.enabled?).to be_truthy
|
expect(subject.enabled?).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
||||||
let(:limit) { 0 }
|
let(:limit) { 0 }
|
||||||
|
|
||||||
it 'returns false' do
|
it 'returns false' do
|
||||||
expect(subject.enabled?).to be_falsey
|
expect(subject.enabled?).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,11 +37,11 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
||||||
let(:current_size) { 49 }
|
let(:current_size) { 49 }
|
||||||
|
|
||||||
it 'returns true when changes go over' do
|
it 'returns true when changes go over' do
|
||||||
expect(subject.changes_will_exceed_size_limit?(2)).to be_truthy
|
expect(subject.changes_will_exceed_size_limit?(2.megabytes)).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false when changes do not go over' do
|
it 'returns false when changes do not go over' do
|
||||||
expect(subject.changes_will_exceed_size_limit?(1)).to be_falsey
|
expect(subject.changes_will_exceed_size_limit?(1.megabytes)).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::RepositorySizeErrorMessage do
|
RSpec.describe Gitlab::RepositorySizeErrorMessage do
|
||||||
|
let_it_be(:namespace) { build(:namespace) }
|
||||||
let(:checker) do
|
let(:checker) do
|
||||||
Gitlab::RepositorySizeChecker.new(
|
Gitlab::RepositorySizeChecker.new(
|
||||||
current_size_proc: -> { 15.megabytes },
|
current_size_proc: -> { 15.megabytes },
|
||||||
total_repository_size_excess: 0,
|
namespace: namespace,
|
||||||
additional_purchased_storage: 0,
|
|
||||||
limit: 10.megabytes
|
limit: 10.megabytes
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,10 @@ RSpec.describe Gitlab::RepositorySizeErrorMessage do
|
||||||
let(:message) { checker.error_message }
|
let(:message) { checker.error_message }
|
||||||
let(:base_message) { 'because this repository has exceeded its size limit of 10 MB by 5 MB' }
|
let(:base_message) { 'because this repository has exceeded its size limit of 10 MB by 5 MB' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(namespace).to receive(:total_repository_size_excess).and_return(0)
|
||||||
|
end
|
||||||
|
|
||||||
describe 'error messages' do
|
describe 'error messages' do
|
||||||
describe '#commit_error' do
|
describe '#commit_error' do
|
||||||
it 'returns the correct message' do
|
it 'returns the correct message' do
|
||||||
|
|
|
@ -723,6 +723,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
||||||
expect(subject[:git][:version]).to eq(Gitlab::Git.version)
|
expect(subject[:git][:version]).to eq(Gitlab::Git.version)
|
||||||
expect(subject[:database][:adapter]).to eq(Gitlab::Database.adapter_name)
|
expect(subject[:database][:adapter]).to eq(Gitlab::Database.adapter_name)
|
||||||
expect(subject[:database][:version]).to eq(Gitlab::Database.version)
|
expect(subject[:database][:version]).to eq(Gitlab::Database.version)
|
||||||
|
expect(subject[:database][:pg_system_id]).to eq(Gitlab::Database.system_id)
|
||||||
expect(subject[:mail][:smtp_server]).to eq(ActionMailer::Base.smtp_settings[:address])
|
expect(subject[:mail][:smtp_server]).to eq(ActionMailer::Base.smtp_settings[:address])
|
||||||
expect(subject[:gitaly][:version]).to be_present
|
expect(subject[:gitaly][:version]).to be_present
|
||||||
expect(subject[:gitaly][:servers]).to be >= 1
|
expect(subject[:gitaly][:servers]).to be >= 1
|
||||||
|
|
|
@ -43,6 +43,18 @@ RSpec.describe ProjectRepositoryStorageMove, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'defaults' do
|
||||||
|
context 'destination_storage_name' do
|
||||||
|
subject { build(:project_repository_storage_move) }
|
||||||
|
|
||||||
|
it 'picks storage from ApplicationSetting' do
|
||||||
|
expect(Gitlab::CurrentSettings).to receive(:pick_repository_storage).and_return('picked').at_least(:once)
|
||||||
|
|
||||||
|
expect(subject.destination_storage_name).to eq('picked')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'state transitions' do
|
describe 'state transitions' do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
|
|
||||||
|
|
|
@ -666,8 +666,7 @@ RSpec.describe Snippet do
|
||||||
|
|
||||||
let(:checker) { subject.repository_size_checker }
|
let(:checker) { subject.repository_size_checker }
|
||||||
let(:current_size) { 60 }
|
let(:current_size) { 60 }
|
||||||
let(:total_repository_size_excess) { 0 }
|
let(:namespace) { nil }
|
||||||
let(:additional_purchased_storage) { 0 }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(subject.repository).to receive(:size).and_return(current_size)
|
allow(subject.repository).to receive(:size).and_return(current_size)
|
||||||
|
|
|
@ -145,10 +145,17 @@ RSpec.describe API::ProjectRepositoryStorageMoves do
|
||||||
context 'destination_storage_name is missing' do
|
context 'destination_storage_name is missing' do
|
||||||
let(:destination_storage_name) { nil }
|
let(:destination_storage_name) { nil }
|
||||||
|
|
||||||
it 'returns a validation error' do
|
it 'schedules a project repository storage move' do
|
||||||
create_project_repository_storage_move
|
create_project_repository_storage_move
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
storage_move = project.repository_storage_moves.last
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
expect(response).to match_response_schema('public_api/v4/project_repository_storage_move')
|
||||||
|
expect(json_response['id']).to eq(storage_move.id)
|
||||||
|
expect(json_response['state']).to eq('scheduled')
|
||||||
|
expect(json_response['source_storage_name']).to eq('default')
|
||||||
|
expect(json_response['destination_storage_name']).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -129,6 +129,28 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns, type: :rubocop do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when text columns are used for encryption' do
|
||||||
|
it 'registers no offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class TestTextLimits < ActiveRecord::Migration[6.0]
|
||||||
|
DOWNTIME = false
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
create_table :test_text_limits, id: false do |t|
|
||||||
|
t.integer :test_id, null: false
|
||||||
|
t.text :encrypted_name
|
||||||
|
end
|
||||||
|
|
||||||
|
add_column :encrypted_test_text_limits, :encrypted_email, :text
|
||||||
|
add_column_with_default :encrypted_test_text_limits, :encrypted_role, :text, default: 'default'
|
||||||
|
change_column_type_concurrently :encrypted_test_text_limits, :encrypted_test_id, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'on down' do
|
context 'on down' do
|
||||||
it 'registers no offense' do
|
it 'registers no offense' do
|
||||||
expect_no_offenses(<<~RUBY)
|
expect_no_offenses(<<~RUBY)
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
require 'rspec-parameterized'
|
||||||
|
require 'rubocop'
|
||||||
|
|
||||||
|
require_relative '../../../../../rubocop/cop/rspec/factory_bot/inline_association'
|
||||||
|
|
||||||
|
RSpec.describe RuboCop::Cop::RSpec::FactoryBot::InlineAssociation, type: :rubocop do
|
||||||
|
include CopHelper
|
||||||
|
|
||||||
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
|
shared_examples 'offense' do |code_snippet, autocorrected|
|
||||||
|
# We allow `create` or `FactoryBot.create` or `::FactoryBot.create`
|
||||||
|
let(:type) { code_snippet[/^(?:::)?(?:FactoryBot\.)?(\w+)/, 1] }
|
||||||
|
let(:offense_marker) { '^' * code_snippet.size }
|
||||||
|
let(:offense_msg) { msg(type) }
|
||||||
|
let(:offense) { "#{offense_marker} #{offense_msg}" }
|
||||||
|
let(:pristine_source) { source.sub(offense, '') }
|
||||||
|
let(:source) do
|
||||||
|
<<~RUBY
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :project do
|
||||||
|
attribute { #{code_snippet} }
|
||||||
|
#{offense}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'registers an offense' do
|
||||||
|
expect_offense(source)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'autocorrects the source' do
|
||||||
|
corrected = autocorrect_source(pristine_source)
|
||||||
|
|
||||||
|
expect(corrected).not_to include(code_snippet)
|
||||||
|
expect(corrected).to include(autocorrected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'no offense' do |code_snippet|
|
||||||
|
first_line = code_snippet.lines.first.chomp
|
||||||
|
|
||||||
|
context "for `#{first_line}`" do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses <<~RUBY
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :project do
|
||||||
|
#{code_snippet}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'offenses' do
|
||||||
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
|
where(:code_snippet, :autocorrected) do
|
||||||
|
# create
|
||||||
|
'create(:user)' | 'association(:user)'
|
||||||
|
'FactoryBot.create(:user)' | 'association(:user)'
|
||||||
|
'::FactoryBot.create(:user)' | 'association(:user)'
|
||||||
|
'create(:user, :admin)' | 'association(:user, :admin)'
|
||||||
|
'create(:user, name: "any")' | 'association(:user, name: "any")'
|
||||||
|
# build
|
||||||
|
'build(:user)' | 'association(:user)'
|
||||||
|
'FactoryBot.build(:user)' | 'association(:user)'
|
||||||
|
'::FactoryBot.build(:user)' | 'association(:user)'
|
||||||
|
'build(:user, :admin)' | 'association(:user, :admin)'
|
||||||
|
'build(:user, name: "any")' | 'association(:user, name: "any")'
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
|
include_examples 'offense', params[:code_snippet], params[:autocorrected]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'recognizes `add_attribute`' do
|
||||||
|
expect_offense <<~RUBY
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :project, class: 'Project' do
|
||||||
|
add_attribute(:method) { create(:user) }
|
||||||
|
^^^^^^^^^^^^^ #{msg(:create)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'recognizes `transient` attributes' do
|
||||||
|
expect_offense <<~RUBY
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :project, class: 'Project' do
|
||||||
|
transient do
|
||||||
|
creator { create(:user) }
|
||||||
|
^^^^^^^^^^^^^ #{msg(:create)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'no offenses' do
|
||||||
|
include_examples 'no offense', 'association(:user)'
|
||||||
|
include_examples 'no offense', 'association(:user, :admin)'
|
||||||
|
include_examples 'no offense', 'association(:user, name: "any")'
|
||||||
|
|
||||||
|
include_examples 'no offense', <<~RUBY
|
||||||
|
after(:build) do |object|
|
||||||
|
object.user = create(:user)
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
include_examples 'no offense', <<~RUBY
|
||||||
|
initialize_with do
|
||||||
|
create(:user)
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
include_examples 'no offense', <<~RUBY
|
||||||
|
user_id { create(:user).id }
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
def msg(type)
|
||||||
|
format(described_class::MSG, type: type)
|
||||||
|
end
|
||||||
|
end
|
|
@ -131,6 +131,18 @@ RSpec.describe Ci::UpdateBuildStateService do
|
||||||
.with(operation: :finalized)
|
.with(operation: :finalized)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'records migration duration in a histogram' do
|
||||||
|
freeze_time do
|
||||||
|
create(:ci_build_pending_state, build: build, created_at: 0.5.seconds.ago)
|
||||||
|
|
||||||
|
execute_with_stubbed_metrics!
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(metrics)
|
||||||
|
.to have_received(:observe_migration_duration)
|
||||||
|
.with(0.5)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when trace checksum is not valid' do
|
context 'when trace checksum is not valid' do
|
||||||
it 'increments invalid trace metric' do
|
it 'increments invalid trace metric' do
|
||||||
execute_with_stubbed_metrics!
|
execute_with_stubbed_metrics!
|
||||||
|
|
|
@ -29,7 +29,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
||||||
let(:current_size) { 51 }
|
let(:current_size) { 51 }
|
||||||
|
|
||||||
it 'returns zero' do
|
it 'returns zero' do
|
||||||
expect(subject.exceeded_size).to eq(1)
|
expect(subject.exceeded_size).to eq(1.megabytes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
||||||
let(:current_size) { 50 }
|
let(:current_size) { 50 }
|
||||||
|
|
||||||
it 'returns zero' do
|
it 'returns zero' do
|
||||||
expect(subject.exceeded_size(1)).to eq(1)
|
expect(subject.exceeded_size(1.megabytes)).to eq(1.megabytes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
||||||
let(:current_size) { 49 }
|
let(:current_size) { 49 }
|
||||||
|
|
||||||
it 'returns zero' do
|
it 'returns zero' do
|
||||||
expect(subject.exceeded_size(1)).to eq(0)
|
expect(subject.exceeded_size(1.megabytes)).to eq(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,7 @@ RSpec.shared_examples 'size checker for snippet' do |action|
|
||||||
it 'sets up size checker', :aggregate_failures do
|
it 'sets up size checker', :aggregate_failures do
|
||||||
expect(checker.current_size).to eq(current_size.megabytes)
|
expect(checker.current_size).to eq(current_size.megabytes)
|
||||||
expect(checker.limit).to eq(Gitlab::CurrentSettings.snippet_size_limit)
|
expect(checker.limit).to eq(Gitlab::CurrentSettings.snippet_size_limit)
|
||||||
expect(checker.total_repository_size_excess).to eq(total_repository_size_excess)
|
|
||||||
expect(checker.additional_purchased_storage).to eq(additional_purchased_storage)
|
|
||||||
expect(checker.enabled?).to eq(true)
|
expect(checker.enabled?).to eq(true)
|
||||||
|
expect(checker.instance_variable_get(:@namespace)).to eq(namespace)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue