Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
93e4425400
commit
6559f0ee67
65 changed files with 609 additions and 149 deletions
|
@ -571,3 +571,9 @@ Gitlab/RailsLogger:
|
|||
Exclude:
|
||||
- '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/user_merge_requests_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 ListLabel from './label';
|
||||
import ListAssignee from './assignee';
|
||||
|
@ -34,7 +34,6 @@ const TYPES = {
|
|||
class List {
|
||||
constructor(obj) {
|
||||
this.id = obj.id;
|
||||
this._uid = this.guid();
|
||||
this.position = obj.position;
|
||||
this.title = (obj.list_type || obj.listType) === 'backlog' ? __('Open') : obj.title;
|
||||
this.type = obj.list_type || obj.listType;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Cookies from 'js-cookie';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import boardListsQuery from 'ee_else_ce/boards/queries/board_lists.query.graphql';
|
||||
import { __ } from '~/locale';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
||||
|
@ -15,7 +17,6 @@ import {
|
|||
import boardStore from '~/boards/stores/boards_store';
|
||||
|
||||
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 updateBoardListMutation from '../queries/board_list_update.mutation.graphql';
|
||||
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
|
||||
|
@ -76,10 +77,10 @@ export default {
|
|||
variables,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const { lists } = data[boardType]?.board;
|
||||
const { lists, hideBacklogList } = data[boardType]?.board;
|
||||
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, formatBoardLists(lists));
|
||||
// Backlog list needs to be created if it doesn't exist
|
||||
if (!lists.nodes.find(l => l.listType === ListType.backlog)) {
|
||||
// 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) && !hideBacklogList) {
|
||||
dispatch('createList', { backlog: true });
|
||||
}
|
||||
dispatch('showWelcomeList');
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import Dropdown from './dropdown.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dropdown,
|
||||
GlButton,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['activeFile']),
|
||||
|
@ -65,9 +67,9 @@ export default {
|
|||
@click="selectTemplate"
|
||||
/>
|
||||
<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') }}
|
||||
</button>
|
||||
</gl-button>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -73,11 +73,9 @@ export function initIde(el, options = {}) {
|
|||
* @param {Objects} options - Extra options for the IDE (Used by EE).
|
||||
*/
|
||||
export function startIde(options) {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const ideElement = document.getElementById('ide');
|
||||
if (ideElement) {
|
||||
resetServiceWorkersPublicPath();
|
||||
initIde(ideElement, options);
|
||||
}
|
||||
});
|
||||
const ideElement = document.getElementById('ide');
|
||||
if (ideElement) {
|
||||
resetServiceWorkersPublicPath();
|
||||
initIde(ideElement, options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import initNotes from '~/init_notes';
|
||||
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';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
SnippetShowInit();
|
||||
initNotes();
|
||||
loadAwardsHandler();
|
||||
SnippetsAppFactory(document.getElementById('js-snippet-view'), SnippetsShow);
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new ZenMode();
|
||||
});
|
||||
initNotes();
|
||||
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(Translate);
|
||||
|
||||
function appFactory(el, Component) {
|
||||
export default function appFactory(el, Component) {
|
||||
if (!el) {
|
||||
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 = () => {
|
||||
import('./components/edit.vue')
|
||||
.then(({ default: SnippetsEdit }) => {
|
||||
|
|
|
@ -20,6 +20,10 @@ class ProjectRepositoryStorageMove < ApplicationRecord
|
|||
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
||||
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
|
||||
event :schedule do
|
||||
transition initial: :scheduled
|
||||
|
@ -77,6 +81,12 @@ class ProjectRepositoryStorageMove < ApplicationRecord
|
|||
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
||||
scope :with_projects, -> { includes(project: :route) }
|
||||
|
||||
class << self
|
||||
def pick_repository_storage
|
||||
Project.pick_repository_storage
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project_repository_writable
|
||||
|
|
|
@ -283,8 +283,7 @@ class Snippet < ApplicationRecord
|
|||
::Gitlab::RepositorySizeChecker.new(
|
||||
current_size_proc: -> { repository.size.megabytes },
|
||||
limit: Gitlab::CurrentSettings.snippet_size_limit,
|
||||
total_repository_size_excess: nil,
|
||||
additional_purchased_storage: nil
|
||||
namespace: nil
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -75,6 +75,7 @@ module Ci
|
|||
|
||||
unless live_chunks_pending?
|
||||
metrics.increment_trace_operation(operation: :finalized)
|
||||
metrics.observe_migration_duration(pending_state_seconds)
|
||||
end
|
||||
|
||||
::Gitlab::Ci::Trace::Checksum.new(build).then do |checksum|
|
||||
|
@ -130,7 +131,15 @@ module Ci
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def build_state
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
%p
|
||||
#{_('Status')}: #{current_user.two_factor_enabled? ? _('Enabled') : _('Disabled')}
|
||||
- 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
|
||||
.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
|
||||
- if display_providers_on_profile?
|
||||
|
|
|
@ -30,6 +30,6 @@
|
|||
= 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.') },
|
||||
method: :delete,
|
||||
class: "btn btn-danger gl-ml-3" }) do
|
||||
class: "gl-button btn btn-danger gl-ml-3" }) do
|
||||
%span.sr-only= _('Revoke')
|
||||
= _('Revoke')
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
= _('Never')
|
||||
|
||||
%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
|
||||
= form_tag profile_chat_names_path, method: :post do
|
||||
= 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
|
||||
= 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.text_field :email, class: 'form-control', data: { qa_selector: 'email_address_field' }
|
||||
.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
|
||||
%h4.gl-mt-0
|
||||
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
|
||||
|
@ -56,8 +56,8 @@
|
|||
%span.badge.badge-info= s_('Profiles|Notification email')
|
||||
- unless email.confirmed?
|
||||
- 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')
|
||||
= 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-----'.")
|
||||
|
||||
.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
|
||||
%span.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')
|
||||
= 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')
|
||||
= _('Revoke')
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
%button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
|
||||
|
||||
.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.password_field :password_confirmation, required: true, class: 'form-control', data: { qa_selector: 'confirm_password_field' }
|
||||
.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?
|
||||
= link_to _('I forgot my password'), reset_profile_password_path, method: :put
|
||||
|
|
|
@ -28,4 +28,4 @@
|
|||
.col-sm-10
|
||||
= f.password_field :password_confirmation, required: true, class: 'form-control'
|
||||
.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-8
|
||||
.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.")
|
||||
- if @user.avatar?
|
||||
%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
|
||||
.row
|
||||
|
@ -46,7 +46,7 @@
|
|||
.col-lg-8
|
||||
= f.fields_for :status, @user.status do |status_form|
|
||||
- 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
|
||||
- if @user.status
|
||||
= emoji_icon @user.status.emoji
|
||||
|
@ -56,7 +56,7 @@
|
|||
= sprite_icon('smile', css_class: 'award-control-icon-super-positive')
|
||||
- reset_message_button = button_tag type: :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
|
||||
= sprite_icon("close")
|
||||
|
||||
|
@ -78,7 +78,7 @@
|
|||
-# TODO: might need an entry in user/profile.md to describe some of these settings
|
||||
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/60070
|
||||
%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 }
|
||||
|
||||
%hr
|
||||
|
@ -119,8 +119,8 @@
|
|||
.help-block
|
||||
= 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
|
||||
= f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
|
||||
= link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'
|
||||
= f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-success'
|
||||
= 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-dialog
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
%span.monospace{ data: { qa_selector: 'code_content' } }= code
|
||||
|
||||
.d-flex
|
||||
= link_to _('Proceed'), profile_account_path, class: '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 _('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: 'gl-button btn btn-default'
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
= link_to _('Disable two-factor authentication'), profile_two_factor_auth_path,
|
||||
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.') },
|
||||
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|
|
||||
= submit_tag _('Regenerate recovery codes'), class: 'btn'
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
|||
= 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' }
|
||||
.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
|
||||
|
||||
|
@ -109,7 +109,7 @@
|
|||
%span.gl-text-gray-500
|
||||
= _("no name set")
|
||||
%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
|
||||
.settings-message.text-center
|
||||
|
|
|
@ -22,4 +22,4 @@
|
|||
- if partial_exists? "registrations/welcome/button"
|
||||
= render "registrations/welcome/button"
|
||||
- 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' }
|
||||
|
|
5
changelogs/unreleased/223236_update_middleman_logo.yml
Normal file
5
changelogs/unreleased/223236_update_middleman_logo.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Middleman Logo for Project Templates
|
||||
merge_request: 44617
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/automatic_move_storage.yml
Normal file
5
changelogs/unreleased/automatic_move_storage.yml
Normal file
|
@ -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
|
5
changelogs/unreleased/sh-usage-data-pg-system-id.yml
Normal file
5
changelogs/unreleased/sh-usage-data-pg-system-id.yml
Normal file
|
@ -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 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `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:
|
||||
|
||||
|
|
|
@ -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
|
||||
dependency should be argued in the merge request, as per our [Approval
|
||||
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
|
||||
Scanning](../../user/application_security/dependency_scanning/index.md)
|
||||
**(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": {
|
||||
"adapter": "postgresql",
|
||||
"version": "9.6.15"
|
||||
"version": "9.6.15",
|
||||
"pg_system_id": 6842684531675334351
|
||||
},
|
||||
"avg_cycle_analytics": {
|
||||
"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
|
||||
|
||||
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)**
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
#### 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 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 Cycle Analytics - Product Page](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/)
|
||||
1. [GitLab Value Stream Analytics Overview (as of 2016)](https://about.gitlab.com/blog/2016/09/21/cycle-analytics-feature-highlight/)
|
||||
1. [GitLab Value Stream Analytics - Product Page](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/)
|
||||
|
||||
### 3.9. Integrations
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
BIN
doc/user/project/wiki/img/wiki_sidebar_v13_5.png
Normal file
BIN
doc/user/project/wiki/img/wiki_sidebar_v13_5.png
Normal file
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
|
||||
---
|
||||
|
||||
# Wiki
|
||||
# Wiki **(CORE)**
|
||||
|
||||
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
|
||||
|
@ -130,10 +130,12 @@ be preceded by the slash (`/`) character.
|
|||
|
||||
## 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
|
||||
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
|
||||
**View All Pages** to see all of them.
|
||||
|
|
|
@ -69,7 +69,7 @@ module API
|
|||
success Entities::ProjectRepositoryStorageMove
|
||||
end
|
||||
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
|
||||
post ':id/repository_storage_moves' do
|
||||
storage_move = user_project.repository_storage_moves.build(
|
||||
|
|
|
@ -45,14 +45,6 @@ module Gitlab
|
|||
end
|
||||
|
||||
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|
|
||||
zip_file = Zip::File.new(file, false, true)
|
||||
entry = zip_file.find_entry(file_path)
|
||||
|
@ -69,25 +61,6 @@ module Gitlab
|
|||
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
|
||||
ActiveSupport::NumberHelper.number_to_human_size(MAX_ARCHIVE_SIZE)
|
||||
end
|
||||
|
|
|
@ -59,10 +59,6 @@ module Gitlab
|
|||
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
|
||||
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?
|
||||
::Feature.enabled?(:one_dimensional_matrix, default_enabled: true)
|
||||
end
|
||||
|
|
|
@ -33,6 +33,10 @@ module Gitlab
|
|||
self.class.trace_bytes.increment({}, size.to_i)
|
||||
end
|
||||
|
||||
def observe_migration_duration(seconds)
|
||||
self.class.finalize_histogram.observe({}, seconds.to_f)
|
||||
end
|
||||
|
||||
def self.trace_operations
|
||||
strong_memoize(:trace_operations) do
|
||||
name = :gitlab_ci_trace_operations_total
|
||||
|
@ -50,6 +54,17 @@ module Gitlab
|
|||
Gitlab::Metrics.counter(name, comment)
|
||||
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
|
||||
|
|
|
@ -250,6 +250,12 @@ module Gitlab
|
|||
false
|
||||
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)
|
||||
row = ar_connection
|
||||
.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('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('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('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'),
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
module Gitlab
|
||||
# Centralized class for repository size related calculations.
|
||||
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
|
||||
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
|
||||
@limit = limit
|
||||
@total_repository_size_excess = total_repository_size_excess.to_i
|
||||
@additional_purchased_storage = additional_purchased_storage.to_i
|
||||
@namespace = namespace
|
||||
@enabled = enabled && limit != 0
|
||||
end
|
||||
|
||||
|
@ -44,6 +43,10 @@ module Gitlab
|
|||
def error_message
|
||||
@error_message_object ||= ::Gitlab::RepositorySizeErrorMessage.new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :namespace
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Gitlab
|
|||
class RepositorySizeErrorMessage
|
||||
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]
|
||||
def initialize(checker)
|
||||
|
|
|
@ -303,7 +303,8 @@ module Gitlab
|
|||
},
|
||||
database: {
|
||||
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: {
|
||||
smtp_server: alt_usage_data { ActionMailer::Base.smtp_settings[:address] }
|
||||
|
|
|
@ -6,6 +6,10 @@ module RuboCop
|
|||
module Cop
|
||||
module Migration
|
||||
# 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
|
||||
include MigrationHelpers
|
||||
|
||||
|
@ -102,6 +106,8 @@ module RuboCop
|
|||
# Check if there is an `add_text_limit` call for the provided
|
||||
# table and attribute name
|
||||
def text_limit_missing?(node, table_name, attribute_name)
|
||||
return false if encrypted_attribute_name?(attribute_name)
|
||||
|
||||
limit_found = false
|
||||
|
||||
node.each_descendant(:send) do |send_node|
|
||||
|
@ -118,6 +124,10 @@ module RuboCop
|
|||
|
||||
!limit_found
|
||||
end
|
||||
|
||||
def encrypted_attribute_name?(attribute_name)
|
||||
attribute_name.to_s.start_with?('encrypted_')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
109
rubocop/cop/rspec/factory_bot/inline_association.rb
Normal file
109
rubocop/cop/rspec/factory_bot/inline_association.rb
Normal file
|
@ -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
|
||||
|
||||
source_storage_name { 'default' }
|
||||
destination_storage_name { 'default' }
|
||||
|
||||
trait :scheduled do
|
||||
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 { inactiveId, ListType } from '~/boards/constants';
|
||||
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 => {
|
||||
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', () => {
|
||||
it('should dispatch addList action', done => {
|
||||
const state = {
|
||||
|
|
|
@ -18,17 +18,6 @@ RSpec.describe Gitlab::Ci::ArtifactFileReader do
|
|||
expect(YAML.safe_load(subject).keys).to contain_exactly('rspec', 'time', 'custom')
|
||||
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
|
||||
let(:path) { 'file/does/not/exist.txt' }
|
||||
let(:expected_error) do
|
||||
|
|
|
@ -39,6 +39,12 @@ RSpec.describe Gitlab::Database do
|
|||
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
|
||||
subject { described_class.postgresql? }
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::RepositorySizeChecker do
|
||||
let_it_be(:namespace) { nil }
|
||||
let(:current_size) { 0 }
|
||||
let(:limit) { 50 }
|
||||
let(:enabled) { true }
|
||||
|
||||
subject do
|
||||
described_class.new(
|
||||
current_size_proc: -> { current_size },
|
||||
limit: limit,
|
||||
total_repository_size_excess: 0,
|
||||
additional_purchased_storage: 0,
|
||||
current_size_proc: -> { current_size.megabytes },
|
||||
limit: limit.megabytes,
|
||||
namespace: namespace,
|
||||
enabled: enabled
|
||||
)
|
||||
end
|
||||
|
@ -20,7 +20,7 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
|||
describe '#enabled?' do
|
||||
context 'when enabled' do
|
||||
it 'returns true' do
|
||||
expect(subject.enabled?).to be_truthy
|
||||
expect(subject.enabled?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,7 +28,7 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
|||
let(:limit) { 0 }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject.enabled?).to be_falsey
|
||||
expect(subject.enabled?).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -37,11 +37,11 @@ RSpec.describe Gitlab::RepositorySizeChecker do
|
|||
let(:current_size) { 49 }
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::RepositorySizeErrorMessage do
|
||||
let_it_be(:namespace) { build(:namespace) }
|
||||
let(:checker) do
|
||||
Gitlab::RepositorySizeChecker.new(
|
||||
current_size_proc: -> { 15.megabytes },
|
||||
total_repository_size_excess: 0,
|
||||
additional_purchased_storage: 0,
|
||||
namespace: namespace,
|
||||
limit: 10.megabytes
|
||||
)
|
||||
end
|
||||
|
@ -15,6 +15,10 @@ RSpec.describe Gitlab::RepositorySizeErrorMessage do
|
|||
let(:message) { checker.error_message }
|
||||
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 '#commit_error' 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[:database][:adapter]).to eq(Gitlab::Database.adapter_name)
|
||||
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[:gitaly][:version]).to be_present
|
||||
expect(subject[:gitaly][:servers]).to be >= 1
|
||||
|
|
|
@ -43,6 +43,18 @@ RSpec.describe ProjectRepositoryStorageMove, type: :model do
|
|||
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
|
||||
let(:project) { create(:project) }
|
||||
|
||||
|
|
|
@ -666,8 +666,7 @@ RSpec.describe Snippet do
|
|||
|
||||
let(:checker) { subject.repository_size_checker }
|
||||
let(:current_size) { 60 }
|
||||
let(:total_repository_size_excess) { 0 }
|
||||
let(:additional_purchased_storage) { 0 }
|
||||
let(:namespace) { nil }
|
||||
|
||||
before do
|
||||
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
|
||||
let(:destination_storage_name) { nil }
|
||||
|
||||
it 'returns a validation error' do
|
||||
it 'schedules a project repository storage move' do
|
||||
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
|
||||
|
|
|
@ -129,6 +129,28 @@ RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns, type: :rubocop do
|
|||
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
|
||||
it 'registers no offense' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
|
|
132
spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
Normal file
132
spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
Normal file
|
@ -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)
|
||||
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
|
||||
it 'increments invalid trace metric' do
|
||||
execute_with_stubbed_metrics!
|
||||
|
|
|
@ -29,7 +29,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
|||
let(:current_size) { 51 }
|
||||
|
||||
it 'returns zero' do
|
||||
expect(subject.exceeded_size).to eq(1)
|
||||
expect(subject.exceeded_size).to eq(1.megabytes)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,7 +37,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
|||
let(:current_size) { 50 }
|
||||
|
||||
it 'returns zero' do
|
||||
expect(subject.exceeded_size(1)).to eq(1)
|
||||
expect(subject.exceeded_size(1.megabytes)).to eq(1.megabytes)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -45,7 +45,7 @@ RSpec.shared_examples 'checker size exceeded' do
|
|||
let(:current_size) { 49 }
|
||||
|
||||
it 'returns zero' do
|
||||
expect(subject.exceeded_size(1)).to eq(0)
|
||||
expect(subject.exceeded_size(1.megabytes)).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,7 @@ RSpec.shared_examples 'size checker for snippet' do |action|
|
|||
it 'sets up size checker', :aggregate_failures do
|
||||
expect(checker.current_size).to eq(current_size.megabytes)
|
||||
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.instance_variable_get(:@namespace)).to eq(namespace)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue