Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e14edb3ce5
commit
74da249f7e
|
|
@ -952,6 +952,7 @@ lib/gitlab/checks/**
|
|||
/doc/user/project/push_options.md @aqualls
|
||||
/doc/user/project/quick_actions.md @msedlakjakubowski
|
||||
/doc/user/project/releases/ @phillipwells
|
||||
/doc/user/project/releases/release_evidence.md @eread
|
||||
/doc/user/project/remote_development/ @ashrafkhamis
|
||||
/doc/user/project/repository/ @aqualls
|
||||
/doc/user/project/repository/file_finder.md @ashrafkhamis
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
Gettext/StaticIdentifier:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/graphql/types/project_type.rb'
|
||||
- 'app/models/integrations/apple_app_store.rb'
|
||||
- 'app/models/integrations/confluence.rb'
|
||||
- 'app/models/integrations/google_play.rb'
|
||||
- 'app/services/import/fogbugz_service.rb'
|
||||
- 'app/services/issuable_links/create_service.rb'
|
||||
- 'app/services/issues/set_crm_contacts_service.rb'
|
||||
- 'app/services/projects/create_from_template_service.rb'
|
||||
- 'app/services/security/ci_configuration/base_create_service.rb'
|
||||
- 'app/services/users/banned_user_base_service.rb'
|
||||
- 'app/services/work_items/widgets/hierarchy_service/base_service.rb'
|
||||
- 'ee/app/controllers/admin/licenses_controller.rb'
|
||||
- 'ee/app/controllers/subscriptions/groups_controller.rb'
|
||||
- 'ee/app/mailers/ee/emails/admin_notification.rb'
|
||||
- 'ee/app/mailers/emails/namespace_storage_usage_mailer.rb'
|
||||
- 'ee/app/models/ee/member.rb'
|
||||
- 'ee/app/models/integrations/github.rb'
|
||||
- 'ee/app/services/ee/projects/create_from_template_service.rb'
|
||||
- 'ee/app/services/security/security_orchestration_policies/policy_configuration_validation_service.rb'
|
||||
- 'ee/app/services/timebox/rollup_report_service.rb'
|
||||
- 'ee/app/services/timebox_report_service.rb'
|
||||
- 'ee/spec/controllers/groups/security/policies_controller_spec.rb'
|
||||
- 'ee/spec/features/registrations/identity_verification_spec.rb'
|
||||
- 'lib/gitlab/github_import/settings.rb'
|
||||
|
|
@ -1 +1 @@
|
|||
3da9f535e6e0c9194e2201ef389171c84ab8c0dc
|
||||
5db2d4b7c1b5f2cdb4dbf5c28b31b21e8d6f19e9
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
v15.11.0
|
||||
v16.0.0-rc1
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ export default {
|
|||
<board-list-header
|
||||
:list="list"
|
||||
:filter-params="filtersToUse"
|
||||
:board-id="boardId"
|
||||
@setActiveList="$emit('setActiveList', $event)"
|
||||
/>
|
||||
<board-list
|
||||
|
|
|
|||
|
|
@ -15,12 +15,20 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
|
|||
import { n__, s__ } from '~/locale';
|
||||
import sidebarEventHub from '~/sidebar/event_hub';
|
||||
import Tracking from '~/tracking';
|
||||
import { TYPE_ISSUE } from '~/issues/constants';
|
||||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
|
||||
import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql';
|
||||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import { inactiveId, LIST, ListType, toggleFormEventPrefix } from '../constants';
|
||||
import {
|
||||
inactiveId,
|
||||
LIST,
|
||||
ListType,
|
||||
toggleFormEventPrefix,
|
||||
updateListQueries,
|
||||
toggleCollapsedMutations,
|
||||
} from 'ee_else_ce/boards/constants';
|
||||
import eventHub from '../eventhub';
|
||||
import ItemCount from './item_count.vue';
|
||||
|
||||
|
|
@ -65,6 +73,9 @@ export default {
|
|||
disabled: {
|
||||
default: true,
|
||||
},
|
||||
issuableType: {
|
||||
default: TYPE_ISSUE,
|
||||
},
|
||||
isApolloBoard: {
|
||||
default: false,
|
||||
},
|
||||
|
|
@ -84,9 +95,13 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
boardId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activeId', 'boardId']),
|
||||
...mapState(['activeId']),
|
||||
isLoggedIn() {
|
||||
return Boolean(this.currentUserId);
|
||||
},
|
||||
|
|
@ -238,7 +253,7 @@ export default {
|
|||
created() {
|
||||
const localCollapsed = parseBoolean(localStorage.getItem(`${this.uniqueKey}.collapsed`));
|
||||
if ((!this.isLoggedIn || this.isEpicBoard) && localCollapsed) {
|
||||
this.toggleListCollapsed({ listId: this.list.id, collapsed: true });
|
||||
this.updateLocalCollapsedStatus(true);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -287,12 +302,12 @@ export default {
|
|||
},
|
||||
toggleExpanded() {
|
||||
const collapsed = !this.list.collapsed;
|
||||
this.toggleListCollapsed({ listId: this.list.id, collapsed });
|
||||
this.updateLocalCollapsedStatus(collapsed);
|
||||
|
||||
if (!this.isLoggedIn) {
|
||||
this.addToLocalStorage();
|
||||
this.addToLocalStorage(collapsed);
|
||||
} else {
|
||||
this.updateListFunction();
|
||||
this.updateListFunction(collapsed);
|
||||
}
|
||||
|
||||
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
|
||||
|
|
@ -304,13 +319,37 @@ export default {
|
|||
property: collapsed ? 'closed' : 'open',
|
||||
});
|
||||
},
|
||||
addToLocalStorage() {
|
||||
addToLocalStorage(collapsed) {
|
||||
if (AccessorUtilities.canUseLocalStorage()) {
|
||||
localStorage.setItem(`${this.uniqueKey}.collapsed`, this.list.collapsed);
|
||||
localStorage.setItem(`${this.uniqueKey}.collapsed`, collapsed);
|
||||
}
|
||||
},
|
||||
updateListFunction() {
|
||||
this.updateList({ listId: this.list.id, collapsed: this.list.collapsed });
|
||||
async updateListFunction(collapsed) {
|
||||
if (this.isApolloBoard) {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: updateListQueries[this.issuableType].mutation,
|
||||
variables: {
|
||||
listId: this.list.id,
|
||||
collapsed,
|
||||
},
|
||||
optimisticResponse: {
|
||||
updateBoardList: {
|
||||
__typename: 'UpdateBoardListPayload',
|
||||
errors: [],
|
||||
list: {
|
||||
...this.list,
|
||||
collapsed,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
this.$emit('error');
|
||||
}
|
||||
} else {
|
||||
this.updateList({ listId: this.list.id, collapsed });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
|
||||
|
|
@ -322,6 +361,19 @@ export default {
|
|||
const due = formatDate(dueDate, 'mmm d, yyyy', true);
|
||||
return `${start} - ${due}`;
|
||||
},
|
||||
updateLocalCollapsedStatus(collapsed) {
|
||||
if (this.isApolloBoard) {
|
||||
this.$apollo.mutate({
|
||||
mutation: toggleCollapsedMutations[this.issuableType].mutation,
|
||||
variables: {
|
||||
list: this.list,
|
||||
collapsed,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.toggleListCollapsed({ listId: this.list.id, collapsed });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutatio
|
|||
import destroyBoardListMutation from './graphql/board_list_destroy.mutation.graphql';
|
||||
import updateBoardListMutation from './graphql/board_list_update.mutation.graphql';
|
||||
|
||||
import toggleListCollapsedMutation from './graphql/client/board_toggle_collapsed.mutation.graphql';
|
||||
import issueSetSubscriptionMutation from './graphql/issue_set_subscription.mutation.graphql';
|
||||
import issueSetTitleMutation from './graphql/issue_set_title.mutation.graphql';
|
||||
import groupBoardQuery from './graphql/group_board.query.graphql';
|
||||
|
|
@ -76,6 +77,12 @@ export const updateListQueries = {
|
|||
},
|
||||
};
|
||||
|
||||
export const toggleCollapsedMutations = {
|
||||
[TYPE_ISSUE]: {
|
||||
mutation: toggleListCollapsedMutation,
|
||||
},
|
||||
};
|
||||
|
||||
export const deleteListQueries = {
|
||||
[TYPE_ISSUE]: {
|
||||
mutation: destroyBoardListMutation,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
|
||||
|
||||
mutation toggleListCollapsed($list: BoardList!, $collapsed: Boolean!) {
|
||||
clientToggleListCollapsed(list: $list, collapsed: $collapsed) @client {
|
||||
list {
|
||||
...BoardListFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,6 +217,22 @@ export const resolvers = {
|
|||
});
|
||||
return boardItem;
|
||||
},
|
||||
clientToggleListCollapsed(_, { list = {}, collapsed = false }) {
|
||||
return {
|
||||
list: {
|
||||
...list,
|
||||
collapsed,
|
||||
},
|
||||
};
|
||||
},
|
||||
clientToggleEpicListCollapsed(_, { list = {}, collapsed = false }) {
|
||||
return {
|
||||
list: {
|
||||
...list,
|
||||
collapsed,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -388,9 +388,10 @@ export const formatTimeAsSummary = ({ seconds, hours, days, minutes, weeks, mont
|
|||
};
|
||||
|
||||
export const durationTimeFormatted = (duration) => {
|
||||
const date = new Date(duration * 1000);
|
||||
const date = new Date(Math.abs(duration) * 1000);
|
||||
|
||||
let hh = date.getUTCHours();
|
||||
const days = date.getUTCDate() - 1;
|
||||
let hh = 24 * days + date.getUTCHours();
|
||||
let mm = date.getUTCMinutes();
|
||||
let ss = date.getSeconds();
|
||||
|
||||
|
|
@ -404,7 +405,7 @@ export const durationTimeFormatted = (duration) => {
|
|||
ss = `0${ss}`;
|
||||
}
|
||||
|
||||
return `${hh}:${mm}:${ss}`;
|
||||
return `${duration < 0 ? '-' : ''}${hh}:${mm}:${ss}`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export default {
|
|||
<template>
|
||||
<component :is="tag">
|
||||
<button
|
||||
class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-py-3 gl-px-0 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-text-decoration-none! gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left gl-w-full gl-focus--focus"
|
||||
class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-py-3 gl-px-0 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left gl-w-full gl-focus--focus"
|
||||
:class="computedLinkClasses"
|
||||
data-qa-selector="menu_section_button"
|
||||
:data-qa-section-name="item.title"
|
||||
|
|
@ -88,7 +88,7 @@ export default {
|
|||
{{ item.title }}
|
||||
</span>
|
||||
|
||||
<span class="gl-flex-grow-1 gl-text-right gl-mr-3">
|
||||
<span class="gl-flex-grow-1 gl-text-right gl-mr-3 gl-text-gray-400">
|
||||
<gl-icon :name="collapseIcon" />
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
// We do this because the flow of elements isn't affected by the rotate transform, so we must ensure that a
|
||||
// rotated element has square dimensions so it won't overlap with its siblings.
|
||||
margin: calc(50% - 8px) 0;
|
||||
max-width: 50vh;
|
||||
|
||||
transform-origin: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# rubocop:disable Style/ClassAndModuleChildren
|
||||
# frozen_string_literal: true
|
||||
|
||||
class MergeRequest::DiffLlmSummary < ApplicationRecord
|
||||
belongs_to :merge_request_diff
|
||||
belongs_to :user, optional: true
|
||||
|
||||
validates :provider, presence: true
|
||||
validates :content, presence: true, length: { maximum: 2056 }
|
||||
|
||||
enum provider: { openai: 0 }
|
||||
end
|
||||
# rubocop:enable Style/ClassAndModuleChildren
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: disallow_environment_name_update
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118146
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/408165
|
||||
milestone: '16.0'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
table_name: merge_request_diff_llm_summaries
|
||||
classes:
|
||||
- MergeRequest::DiffLlmSummary
|
||||
feature_categories:
|
||||
- code_review_workflow
|
||||
description: This is the table that stores information about the diff summaries produced
|
||||
from different LLM's.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118671
|
||||
milestone: '16.0'
|
||||
gitlab_schema: gitlab_main
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateMergeRequestDiffLlmSummary < Gitlab::Database::Migration[2.1]
|
||||
INDEX_NAME = "index_merge_request_diff_llm_summaries_on_mr_diff_id"
|
||||
|
||||
def change
|
||||
create_table :merge_request_diff_llm_summaries do |t|
|
||||
t.bigint :user_id, null: true, index: true
|
||||
t.bigint :merge_request_diff_id, null: false, index:
|
||||
{ name: INDEX_NAME }
|
||||
t.timestamps_with_timezone null: false
|
||||
t.integer :provider, null: false, limit: 2
|
||||
t.text :content, null: false, limit: 2056
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddUserForeignKeyToMergeRequestDiffLlmSummary < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :merge_request_diff_llm_summaries, :users, column: :user_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :merge_request_diff_llm_summaries, column: :user_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddMergeRequestDiffForeignKeyToMergeRequestDiffLlmSummary < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :merge_request_diff_llm_summaries, :merge_request_diffs, column: :merge_request_diff_id,
|
||||
on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :merge_request_diff_llm_summaries, column: :merge_request_diff_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
b1f6b1bbfdc4f2f5df1b360fdfbeffc99ca024d65a55c2a2d2fcebe1fdc90cfb
|
||||
|
|
@ -0,0 +1 @@
|
|||
176d8f13dc7743305a0637248aeb128e65d223d546a330869102c9d3c1714541
|
||||
|
|
@ -0,0 +1 @@
|
|||
9c9634937e59a27f4f3e48da2d4dc6964dee50d1b043cc9d668ec5934e7b6fff
|
||||
|
|
@ -18194,6 +18194,26 @@ CREATE TABLE merge_request_diff_files (
|
|||
external_diff_size integer
|
||||
);
|
||||
|
||||
CREATE TABLE merge_request_diff_llm_summaries (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint,
|
||||
merge_request_diff_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
provider smallint NOT NULL,
|
||||
content text NOT NULL,
|
||||
CONSTRAINT check_93955f22ad CHECK ((char_length(content) <= 2056))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE merge_request_diff_llm_summaries_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE merge_request_diff_llm_summaries_id_seq OWNED BY merge_request_diff_llm_summaries.id;
|
||||
|
||||
CREATE TABLE merge_request_diffs (
|
||||
id integer NOT NULL,
|
||||
state character varying,
|
||||
|
|
@ -25298,6 +25318,8 @@ ALTER TABLE ONLY merge_request_diff_commit_users ALTER COLUMN id SET DEFAULT nex
|
|||
|
||||
ALTER TABLE ONLY merge_request_diff_details ALTER COLUMN merge_request_diff_id SET DEFAULT nextval('merge_request_diff_details_merge_request_diff_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY merge_request_diff_llm_summaries ALTER COLUMN id SET DEFAULT nextval('merge_request_diff_llm_summaries_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_request_diffs_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY merge_request_metrics ALTER COLUMN id SET DEFAULT nextval('merge_request_metrics_id_seq'::regclass);
|
||||
|
|
@ -27458,6 +27480,9 @@ ALTER TABLE ONLY merge_request_diff_details
|
|||
ALTER TABLE ONLY merge_request_diff_files
|
||||
ADD CONSTRAINT merge_request_diff_files_pkey PRIMARY KEY (merge_request_diff_id, relative_order);
|
||||
|
||||
ALTER TABLE ONLY merge_request_diff_llm_summaries
|
||||
ADD CONSTRAINT merge_request_diff_llm_summaries_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY merge_request_diffs
|
||||
ADD CONSTRAINT merge_request_diffs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -31275,6 +31300,10 @@ CREATE INDEX index_merge_request_diff_details_on_verification_state ON merge_req
|
|||
|
||||
CREATE INDEX index_merge_request_diff_details_pending_verification ON merge_request_diff_details USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
|
||||
|
||||
CREATE INDEX index_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
|
||||
|
||||
CREATE INDEX index_merge_request_diff_llm_summaries_on_user_id ON merge_request_diff_llm_summaries USING btree (user_id);
|
||||
|
||||
CREATE INDEX index_merge_request_diffs_by_id_partial ON merge_request_diffs USING btree (id) WHERE ((files_count > 0) AND ((NOT stored_externally) OR (stored_externally IS NULL)));
|
||||
|
||||
CREATE INDEX index_merge_request_diffs_on_external_diff ON merge_request_diffs USING btree (external_diff);
|
||||
|
|
@ -34826,6 +34855,9 @@ ALTER TABLE ONLY protected_environment_approval_rules
|
|||
ALTER TABLE ONLY ci_pipeline_schedule_variables
|
||||
ADD CONSTRAINT fk_41c35fda51 FOREIGN KEY (pipeline_schedule_id) REFERENCES ci_pipeline_schedules(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY merge_request_diff_llm_summaries
|
||||
ADD CONSTRAINT fk_42551b9fea FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY namespace_bans
|
||||
ADD CONSTRAINT fk_4275fbb1d7 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -35474,6 +35506,9 @@ ALTER TABLE ONLY fork_networks
|
|||
ALTER TABLE ONLY integrations
|
||||
ADD CONSTRAINT fk_e8fe908a34 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY merge_request_diff_llm_summaries
|
||||
ADD CONSTRAINT fk_e98931c3cb FOREIGN KEY (merge_request_diff_id) REFERENCES merge_request_diffs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY pages_domains
|
||||
ADD CONSTRAINT fk_ea2f6dfc6f FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
WARNING:
|
||||
This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369117) in GitLab 15.3 and is planned for
|
||||
removal in 16.0.
|
||||
removal in 17.0.
|
||||
|
||||
Authenticate to GitLab using the Atlassian Crowd OmniAuth provider. Enabling
|
||||
this provider also allows Crowd authentication for Git-over-https requests.
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ deploy_review_app:
|
|||
> - Renaming an environment by using the UI was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68550) in GitLab 14.3.
|
||||
> - Renaming an environment by using the API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/338897) in GitLab 15.9 and is planned for removal in GitLab 16.0.
|
||||
|
||||
You cannot rename an environment by using the UI, and the API method was deprecated in GitLab 15.9.
|
||||
You cannot rename an environment by using the UI, and the API method was deprecated in GitLab 15.9 and to be removed in GitLab 16.0.
|
||||
|
||||
To achieve the same result as renaming an environment:
|
||||
|
||||
|
|
|
|||
|
|
@ -815,6 +815,25 @@ Make sure that you have relevant test data for your filter in the
|
|||
[`spec/fixtures/markdown.md.erb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/fixtures/markdown.md.erb)
|
||||
file.
|
||||
|
||||
### Benchmarking specific filters
|
||||
|
||||
A specific filter can be benchmarked by specifying the filter name as an environment variable.
|
||||
For example, to benchmark the `MarkdownFilter` use
|
||||
|
||||
```plaintext
|
||||
FILTER=MarkdownFilter bin/rake benchmark:banzai
|
||||
```
|
||||
|
||||
which generates the output
|
||||
|
||||
```plaintext
|
||||
--> Benchmarking MarkdownFilter for FullPipeline
|
||||
Warming up --------------------------------------
|
||||
Markdown 271.000 i/100ms
|
||||
Calculating -------------------------------------
|
||||
Markdown 2.584k (±16.5%) i/s - 23.848k in 10.042503s
|
||||
```
|
||||
|
||||
## Reading from files and other data sources
|
||||
|
||||
Ruby offers several convenience functions that deal with file contents specifically
|
||||
|
|
|
|||
|
|
@ -70,15 +70,11 @@ Prerequisite:
|
|||
|
||||
## Filter requests
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377371) in GitLab 15.10 [with a flag](../administration/feature_flags.md) named `deny_all_requests_except_allowed`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `deny_all_requests_except_allowed`.
|
||||
On GitLab.com, this feature is not available.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/377371) in GitLab 15.10.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have administrator access to the instance.
|
||||
- You must have administrator access to the GitLab instance.
|
||||
|
||||
To filter requests by blocking many requests:
|
||||
|
||||
|
|
|
|||
|
|
@ -163,3 +163,27 @@ error. The error response can include a HTML result of the GitLab URL `https://g
|
|||
This error is harmless and occurs because group provisioning was turned on but GitLab SCIM integration does not support
|
||||
it nor require it. To remove the error, follow the instructions in the Azure configuration guide to disable the option
|
||||
to [synchronize Azure Active Directory groups to AppName](scim_setup.md#configure-azure-active-directory).
|
||||
|
||||
## Okta
|
||||
|
||||
The following troubleshooting information is specifically for SCIM provisioned through Okta.
|
||||
|
||||
### `Error authenticating: null` message when testing API SCIM credentials
|
||||
|
||||
When testing the API credentials in your Okta SCIM application, you may encounter an error:
|
||||
|
||||
```plaintext
|
||||
Error authenticating: null
|
||||
```
|
||||
|
||||
Okta needs to be able to connect to your GitLab instance to provision or deprovision users.
|
||||
|
||||
In your Okta SCIM application, check that the SCIM **Base URL** is correct and pointing to a valid GitLab
|
||||
SCIM API endpoint URL. Check the following documentation to find information on this URL for:
|
||||
|
||||
- [GitLab.com groups](scim_setup.md#configure-gitlab).
|
||||
- [Self-managed GitLab instances](../../admin_area/settings/scim_setup.md#configure-gitlab).
|
||||
|
||||
For self-managed GitLab instances, ensure that GitLab is publicly available so Okta can connect to it. If needed,
|
||||
you can [allow access to Okta IP addresses](https://help.okta.com/en-us/Content/Topics/Security/ip-address-allow-listing.htm)
|
||||
on your firewall.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ When you [create a release](#create-a-release):
|
|||
|
||||
- GitLab automatically archives source code and associates it with the release.
|
||||
- GitLab automatically creates a JSON file that lists everything in the release,
|
||||
so you can compare and audit releases. This file is called [release evidence](#release-evidence).
|
||||
so you can compare and audit releases. This file is called [release evidence](release_evidence.md).
|
||||
|
||||
When you create a release, or after, you can:
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ You can create a release:
|
|||
- [In the Releases page](#create-a-release-in-the-releases-page).
|
||||
- Using the [Releases API](../../../api/releases/index.md#create-a-release).
|
||||
|
||||
We recommend creating a release as one of the last steps in your CI/CD pipeline.
|
||||
You should create a release as one of the last steps in your CI/CD pipeline.
|
||||
|
||||
### Create a release in the Releases page
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ release tag. When the `released_at` date and time has passed, the badge is autom
|
|||
You can create a release in the past using either the
|
||||
[Releases API](../../../api/releases/index.md#historical-releases) or the UI. When you set
|
||||
a past `released_at` date, an **Historical release** badge is displayed next to
|
||||
the release tag. Due to being released in the past, [release evidence](#release-evidence)
|
||||
the release tag. Due to being released in the past, [release evidence](release_evidence.md)
|
||||
is not available.
|
||||
|
||||
## Edit a release
|
||||
|
|
@ -254,7 +254,7 @@ Here is an example of milestones with no releases, one release, and two releases
|
|||

|
||||
|
||||
NOTE:
|
||||
A subgroup's project releases cannot be associated with a supergroup's milestone. To learn
|
||||
A subgroup's project releases cannot be associated with a parent group's milestone. To learn
|
||||
more, read issue #328054,
|
||||
[Releases cannot be associated with a supergroup milestone](https://gitlab.com/gitlab-org/gitlab/-/issues/328054).
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ Deploy freezes help reduce uncertainty and risk when automating deployments.
|
|||
A maintainer can set a deploy freeze window in the user interface or by using the [Freeze Periods API](../../../api/freeze_periods.md) to set a `freeze_start` and a `freeze_end`, which
|
||||
are defined as [crontab](https://crontab.guru/) entries.
|
||||
|
||||
If the job that's executing is within a freeze period, GitLab CI/CD creates an environment
|
||||
If the job that's executing is in a freeze period, GitLab CI/CD creates an environment
|
||||
variable named `$CI_DEPLOY_FREEZE`.
|
||||
|
||||
To prevent the deployment job from executing, create a `rules` entry in your
|
||||
|
|
@ -317,141 +317,6 @@ complete overlapping period.
|
|||
|
||||
For more information, see [Deployment safety](../../../ci/environments/deployment_safety.md).
|
||||
|
||||
## Release evidence
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26019) in GitLab 12.6.
|
||||
|
||||
Each time a release is created, GitLab takes a snapshot of data that's related to it.
|
||||
This data is saved in a JSON file and called *release evidence*. The feature
|
||||
includes test artifacts and linked milestones to facilitate
|
||||
internal processes, like external audits.
|
||||
|
||||
To access the release evidence, on the Releases page, select the link to the JSON file that's listed
|
||||
under the **Evidence collection** heading.
|
||||
|
||||
You can also [use the API](../../../api/releases/index.md#collect-release-evidence) to
|
||||
generate release evidence for an existing release. Because of this, each release
|
||||
can have multiple release evidence snapshots. You can view the release evidence and
|
||||
its details on the Releases page.
|
||||
|
||||
When the issue tracker is disabled, release evidence [can't be downloaded](https://gitlab.com/gitlab-org/gitlab/-/issues/208397).
|
||||
|
||||
Here is an example of a release evidence object:
|
||||
|
||||
```json
|
||||
{
|
||||
"release": {
|
||||
"id": 5,
|
||||
"tag_name": "v4.0",
|
||||
"name": "New release",
|
||||
"project": {
|
||||
"id": 20,
|
||||
"name": "Project name",
|
||||
"created_at": "2019-04-14T11:12:13.940Z",
|
||||
"description": "Project description"
|
||||
},
|
||||
"created_at": "2019-06-28 13:23:40 UTC",
|
||||
"description": "Release description",
|
||||
"milestones": [
|
||||
{
|
||||
"id": 11,
|
||||
"title": "v4.0-rc1",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-12 12:00:00 UTC",
|
||||
"created_at": "2019-04-17 15:45:12 UTC",
|
||||
"issues": [
|
||||
{
|
||||
"id": 82,
|
||||
"title": "The top-right popup is broken",
|
||||
"author_name": "John Doe",
|
||||
"author_email": "john@doe.com",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-10 12:00:00 UTC"
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"title": "The title of this page is misleading",
|
||||
"author_name": "Jane Smith",
|
||||
"author_email": "jane@smith.com",
|
||||
"state": "closed",
|
||||
"due_date": "nil"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"title": "v4.0-rc2",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-30 18:30:00 UTC",
|
||||
"created_at": "2019-04-17 15:45:12 UTC",
|
||||
"issues": []
|
||||
}
|
||||
],
|
||||
"report_artifacts": [
|
||||
{
|
||||
"url":"https://gitlab.example.com/root/project-name/-/jobs/111/artifacts/download"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Collect release evidence **(PREMIUM SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199065) in GitLab 12.10.
|
||||
|
||||
When a release is created, release evidence is automatically collected. To initiate evidence collection any other time, use an [API call](../../../api/releases/index.md#collect-release-evidence). You can collect release evidence multiple times for one release.
|
||||
|
||||
Evidence collection snapshots are visible on the Releases page, along with the timestamp the evidence was collected.
|
||||
|
||||
### Include report artifacts as release evidence **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32773) in GitLab 13.2.
|
||||
|
||||
When you create a release, if [job artifacts](../../../ci/yaml/index.md#artifactsreports) are included in the last pipeline that ran, they are automatically included in the release as release evidence.
|
||||
|
||||
Although job artifacts normally expire, artifacts included in release evidence do not expire.
|
||||
|
||||
To enable job artifact collection you must specify both:
|
||||
|
||||
1. [`artifacts:paths`](../../../ci/yaml/index.md#artifactspaths)
|
||||
1. [`artifacts:reports`](../../../ci/yaml/index.md#artifactsreports)
|
||||
|
||||
```yaml
|
||||
ruby:
|
||||
script:
|
||||
- gem install bundler
|
||||
- bundle install
|
||||
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
||||
artifacts:
|
||||
paths:
|
||||
- rspec.xml
|
||||
reports:
|
||||
junit: rspec.xml
|
||||
```
|
||||
|
||||
If the pipeline ran successfully, when you create your release, the `rspec.xml` file is saved as
|
||||
release evidence.
|
||||
|
||||
If you [schedule release evidence collection](#schedule-release-evidence-collection),
|
||||
some artifacts may already be expired by the time of evidence collection. To avoid this you can use
|
||||
the [`artifacts:expire_in`](../../../ci/yaml/index.md#artifactsexpire_in)
|
||||
keyword. For more information, see [issue 222351](https://gitlab.com/gitlab-org/gitlab/-/issues/222351).
|
||||
|
||||
### Schedule release evidence collection
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23697) in GitLab 12.8.
|
||||
|
||||
In the API:
|
||||
|
||||
- If you specify a future `released_at` date, the release becomes an **Upcoming release**
|
||||
and the evidence is collected on the date of the release. You cannot collect
|
||||
release evidence before then.
|
||||
- If you specify a past `released_at` date, the release becomes an **Historical
|
||||
release** and no evidence is collected.
|
||||
- If you do not specify a `released_at` date, release evidence is collected on the
|
||||
date the release is created.
|
||||
|
||||
## Release permissions
|
||||
|
||||
> Fixes to the permission model for create, update and delete actions [were introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327505) in GitLab 14.1.
|
||||
|
|
@ -465,14 +330,15 @@ In the API:
|
|||
- Users with the Guest role
|
||||
have read and download access to the project releases.
|
||||
This includes associated Git-tag-names, release description, author information of the releases.
|
||||
However, other repository-related information, such as [source code](release_fields.md#source-code), [release evidence](#release-evidence) are redacted.
|
||||
However, other repository-related information, such as [source code](release_fields.md#source-code) and
|
||||
[release evidence](release_evidence.md) are redacted.
|
||||
|
||||
### Publish releases without giving access to source code
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216485) in GitLab 15.6.
|
||||
|
||||
Releases can be made accessible to non-project members while keeping repository-related information such as
|
||||
[source code](release_fields.md#source-code) and [release evidence](#release-evidence) private. This is useful for
|
||||
[source code](release_fields.md#source-code) and [release evidence](release_evidence.md) private. Use this for
|
||||
projects that use releases as a way to give access to new versions of software but do not want the source code to
|
||||
be public.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
stage: Govern
|
||||
group: Compliance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Release evidence **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26019) in GitLab 12.6.
|
||||
|
||||
Each time a release is created, GitLab takes a snapshot of data that's related to it.
|
||||
This data is saved in a JSON file and called *release evidence*. The feature
|
||||
includes test artifacts and linked milestones to facilitate
|
||||
internal processes, like external audits.
|
||||
|
||||
To access the release evidence, on the Releases page, select the link to the JSON file that's listed
|
||||
under the **Evidence collection** heading.
|
||||
|
||||
You can also [use the API](../../../api/releases/index.md#collect-release-evidence) to
|
||||
generate release evidence for an existing release. Because of this, each release
|
||||
can have multiple release evidence snapshots. You can view the release evidence and
|
||||
its details on the Releases page.
|
||||
|
||||
When the issue tracker is disabled, release evidence [can't be downloaded](https://gitlab.com/gitlab-org/gitlab/-/issues/208397).
|
||||
|
||||
Here is an example of a release evidence object:
|
||||
|
||||
```json
|
||||
{
|
||||
"release": {
|
||||
"id": 5,
|
||||
"tag_name": "v4.0",
|
||||
"name": "New release",
|
||||
"project": {
|
||||
"id": 20,
|
||||
"name": "Project name",
|
||||
"created_at": "2019-04-14T11:12:13.940Z",
|
||||
"description": "Project description"
|
||||
},
|
||||
"created_at": "2019-06-28 13:23:40 UTC",
|
||||
"description": "Release description",
|
||||
"milestones": [
|
||||
{
|
||||
"id": 11,
|
||||
"title": "v4.0-rc1",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-12 12:00:00 UTC",
|
||||
"created_at": "2019-04-17 15:45:12 UTC",
|
||||
"issues": [
|
||||
{
|
||||
"id": 82,
|
||||
"title": "The top-right popup is broken",
|
||||
"author_name": "John Doe",
|
||||
"author_email": "john@doe.com",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-10 12:00:00 UTC"
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"title": "The title of this page is misleading",
|
||||
"author_name": "Jane Smith",
|
||||
"author_email": "jane@smith.com",
|
||||
"state": "closed",
|
||||
"due_date": "nil"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"title": "v4.0-rc2",
|
||||
"state": "closed",
|
||||
"due_date": "2019-05-30 18:30:00 UTC",
|
||||
"created_at": "2019-04-17 15:45:12 UTC",
|
||||
"issues": []
|
||||
}
|
||||
],
|
||||
"report_artifacts": [
|
||||
{
|
||||
"url":"https://gitlab.example.com/root/project-name/-/jobs/111/artifacts/download"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Collect release evidence **(PREMIUM SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199065) in GitLab 12.10.
|
||||
|
||||
When a release is created, release evidence is automatically collected. To initiate evidence collection any other time, use an [API call](../../../api/releases/index.md#collect-release-evidence). You can collect release evidence multiple times for one release.
|
||||
|
||||
Evidence collection snapshots are visible on the Releases page, along with the timestamp the evidence was collected.
|
||||
|
||||
## Include report artifacts as release evidence **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32773) in GitLab 13.2.
|
||||
|
||||
When you create a release, if [job artifacts](../../../ci/yaml/index.md#artifactsreports) are included in the last pipeline that ran, they are automatically included in the release as release evidence.
|
||||
|
||||
Although job artifacts normally expire, artifacts included in release evidence do not expire.
|
||||
|
||||
To enable job artifact collection you must specify both:
|
||||
|
||||
1. [`artifacts:paths`](../../../ci/yaml/index.md#artifactspaths)
|
||||
1. [`artifacts:reports`](../../../ci/yaml/index.md#artifactsreports)
|
||||
|
||||
```yaml
|
||||
ruby:
|
||||
script:
|
||||
- gem install bundler
|
||||
- bundle install
|
||||
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
||||
artifacts:
|
||||
paths:
|
||||
- rspec.xml
|
||||
reports:
|
||||
junit: rspec.xml
|
||||
```
|
||||
|
||||
If the pipeline ran successfully, when you create your release, the `rspec.xml` file is saved as
|
||||
release evidence.
|
||||
|
||||
If you [schedule release evidence collection](#schedule-release-evidence-collection),
|
||||
some artifacts may already be expired by the time of evidence collection. To avoid this you can use
|
||||
the [`artifacts:expire_in`](../../../ci/yaml/index.md#artifactsexpire_in)
|
||||
keyword. For more information, see [issue 222351](https://gitlab.com/gitlab-org/gitlab/-/issues/222351).
|
||||
|
||||
## Schedule release evidence collection
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23697) in GitLab 12.8.
|
||||
|
||||
In the API:
|
||||
|
||||
- If you specify a future `released_at` date, the release becomes an **Upcoming release**
|
||||
and the evidence is collected on the date of the release. You cannot collect
|
||||
release evidence before then.
|
||||
- If you specify a past `released_at` date, the release becomes an **Historical
|
||||
release** and no evidence is collected.
|
||||
- If you do not specify a `released_at` date, release evidence is collected on the
|
||||
date the release is created.
|
||||
|
|
@ -13,6 +13,13 @@ module API
|
|||
urgency :low
|
||||
|
||||
MIN_SEARCH_LENGTH = 3
|
||||
# rubocop:disable Gitlab/DocUrl
|
||||
ENVIRONMENT_NAME_UPDATE_ERROR = <<~DESC
|
||||
Updating environment name was deprecated in GitLab 15.9 and to be removed in GitLab 16.0.
|
||||
For workaround, see [the documentation](https://docs.gitlab.com/ee/ci/environments/#rename-an-environment).
|
||||
For more information, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/338897)
|
||||
DESC
|
||||
# rubocop:enable Gitlab/DocUrl
|
||||
|
||||
params do
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
|
||||
|
|
@ -90,8 +97,6 @@ module API
|
|||
end
|
||||
params do
|
||||
requires :environment_id, type: Integer, desc: 'The ID of the environment'
|
||||
# TODO: disallow renaming via the API https://gitlab.com/gitlab-org/gitlab/-/issues/338897
|
||||
optional :name, type: String, desc: 'DEPRECATED: Renaming environment can lead to errors, this will be removed in 15.0'
|
||||
optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable'
|
||||
optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true }
|
||||
optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`'
|
||||
|
|
@ -101,8 +106,19 @@ module API
|
|||
|
||||
environment = user_project.environments.find(params[:environment_id])
|
||||
|
||||
update_params = declared_params(include_missing: false).extract!(:name, :external_url, :tier)
|
||||
if environment.update(update_params)
|
||||
update_params = declared_params(include_missing: false).extract!(:external_url, :tier)
|
||||
|
||||
# For the transition period, we implicitly extract `:name` field.
|
||||
# This line should be removed when disallow_environment_name_update feature flag is removed.
|
||||
update_params[:name] = params[:name] if params[:name].present?
|
||||
|
||||
environment.assign_attributes(update_params)
|
||||
|
||||
if environment.name_changed? && ::Feature.enabled?(:disallow_environment_name_update, user_project)
|
||||
render_api_error!(ENVIRONMENT_NAME_UPDATE_ERROR, 400)
|
||||
end
|
||||
|
||||
if environment.save
|
||||
present environment, with: Entities::Environment, current_user: current_user
|
||||
else
|
||||
render_validation_error!(environment)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,15 @@
|
|||
return if Rails.env.production?
|
||||
|
||||
namespace :benchmark do
|
||||
desc 'Benchmark | Banzai pipeline/filters'
|
||||
desc 'Benchmark | Banzai pipeline/filters (optionally specify FILTER=xxxxxFilter)'
|
||||
RSpec::Core::RakeTask.new(:banzai) do |t|
|
||||
t.pattern = 'spec/benchmarks/banzai_benchmark.rb'
|
||||
t.rspec_opts = if ENV.key?('FILTER')
|
||||
['--tag specific_filter']
|
||||
else
|
||||
['--tag \~specific_filter']
|
||||
end
|
||||
|
||||
ENV['BENCHMARK'] = '1'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39885,7 +39885,10 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|After enabling a group-level policy, this policy automatically applies to all projects and sub-groups in this group."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|All policies"
|
||||
msgid "SecurityOrchestration|All sources"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|All types"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|An error occurred assigning your security policy project"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Gettext
|
||||
# Ensure that gettext identifiers are statically defined and not
|
||||
# interpolated, formatted, or concatenated.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# _('Hi #{name}')
|
||||
# _('Hi %{name}' % { name: 'Luki' })
|
||||
# _(format('Hi %{name}', name: 'Luki'))
|
||||
#
|
||||
# # good
|
||||
# _('Hi %{name}') % { name: 'Luki' }
|
||||
# format(_('Hi %{name}', name: 'Luki'))
|
||||
#
|
||||
# # also good
|
||||
# var = "Hi"
|
||||
# _(var)
|
||||
# _(some_method_call)
|
||||
# _(CONST)
|
||||
class StaticIdentifier < RuboCop::Cop::Base
|
||||
MSG = 'Ensure to pass static strings to translation method `%{method_name}(...)`.'
|
||||
|
||||
# Number of parameters to check for translation methods.
|
||||
PARAMETERS_TO_CHECK = {
|
||||
_: 1,
|
||||
s_: 1,
|
||||
N_: 1,
|
||||
n_: 2
|
||||
}.freeze
|
||||
|
||||
# RuboCop-specific optimization for `on_send`.
|
||||
RESTRICT_ON_SEND = PARAMETERS_TO_CHECK.keys.freeze
|
||||
|
||||
DENIED_METHOD_CALLS = %i[% format + concat].freeze
|
||||
|
||||
def on_send(node)
|
||||
method_name = node.method_name
|
||||
arguments = node.arguments
|
||||
|
||||
each_invalid_argument(method_name, arguments) do |argument_node|
|
||||
message = format(MSG, method_name: method_name)
|
||||
|
||||
add_offense(argument_node || node, message: message)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def each_invalid_argument(method_name, argument_nodes)
|
||||
number = PARAMETERS_TO_CHECK.fetch(method_name)
|
||||
|
||||
argument_nodes.take(number).each do |argument_node|
|
||||
yield argument_node unless valid_argument?(argument_node)
|
||||
end
|
||||
end
|
||||
|
||||
def valid_argument?(node)
|
||||
return false unless node
|
||||
|
||||
basic_type?(node) || multiline_string?(node) || allowed_method_call?(node)
|
||||
end
|
||||
|
||||
def basic_type?(node)
|
||||
node.str_type? || node.lvar_type? || node.const_type?
|
||||
end
|
||||
|
||||
def multiline_string?(node)
|
||||
node.dstr_type? && node.children.all?(&:str_type?)
|
||||
end
|
||||
|
||||
def allowed_method_call?(node)
|
||||
return false unless node.send_type?
|
||||
|
||||
!DENIED_METHOD_CALLS.include?(node.method_name) # rubocop:disable Rails/NegateInclude
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
class RubyInterpolationInTranslation < RuboCop::Cop::Base
|
||||
MSG = "Don't use ruby interpolation \#{} inside translated strings, instead use \%{}"
|
||||
|
||||
TRANSLATION_METHODS = ':_ :s_ :N_ :n_'
|
||||
|
||||
def_node_matcher :translation_method?, <<~PATTERN
|
||||
(send nil? {#{TRANSLATION_METHODS}} $dstr ...)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :plural_translation_method?, <<~PATTERN
|
||||
(send nil? :n_ str $dstr ...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
interpolation = translation_method?(node) || plural_translation_method?(node)
|
||||
return unless interpolation
|
||||
|
||||
interpolation.descendants.each do |possible_violation|
|
||||
add_offense(possible_violation, message: MSG) if possible_violation.type != :str
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,8 +16,15 @@ require 'benchmark/ips'
|
|||
# or
|
||||
# rake benchmark:banzai
|
||||
#
|
||||
# A specific filter can also be benchmarked by using the `FILTER`
|
||||
# environment variable.
|
||||
#
|
||||
# BENCHMARK=1 FILTER=MathFilter rspec spec/benchmarks/banzai_benchmark.rb --tag specific_filter
|
||||
# or
|
||||
# FILTER=MathFilter rake benchmark:banzai
|
||||
#
|
||||
# rubocop: disable RSpec/TopLevelDescribePath
|
||||
RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
|
||||
RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures, feature_category: :team_planning do
|
||||
include MarkupHelper
|
||||
|
||||
let_it_be(:feature) { MarkdownFeature.new }
|
||||
|
|
@ -83,9 +90,15 @@ RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
|
|||
benchmark_pipeline_filters(:plain_markdown)
|
||||
end
|
||||
|
||||
it 'benchmarks specified filters in the FullPipeline' do
|
||||
filter_klass_list = [Banzai::Filter::MathFilter]
|
||||
benchmark_pipeline_filters(:full, filter_klass_list)
|
||||
it 'benchmarks specified filters in the FullPipeline', :specific_filter do
|
||||
begin
|
||||
filter = ENV['FILTER'] || 'MarkdownFilter'
|
||||
filter_klass = "Banzai::Filter::#{filter}".constantize
|
||||
rescue NameError
|
||||
raise 'Incorrect filter specified. Correct example: FILTER=MathFilter'
|
||||
end
|
||||
|
||||
benchmark_pipeline_filters(:full, [filter_klass])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -114,7 +127,8 @@ RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
|
|||
pipeline = Banzai::Pipeline[pipeline_type]
|
||||
filter_source = build_filter_text(pipeline, markdown_text)
|
||||
|
||||
puts "\n--> Benchmarking #{pipeline.name.demodulize} filters\n"
|
||||
filter_msg = filter_klass_list ? filter_klass_list.first.name.demodulize : 'all filters'
|
||||
puts "\n--> Benchmarking #{filter_msg} for #{pipeline.name.demodulize}\n"
|
||||
|
||||
Benchmark.ips do |x|
|
||||
x.config(time: 10, warmup: 2)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :merge_request_diff_llm_summary, class: 'MergeRequest::DiffLlmSummary' do
|
||||
association :user, factory: :user
|
||||
association :merge_request_diff, factory: :merge_request_diff
|
||||
provider { 0 }
|
||||
content { 'test' }
|
||||
end
|
||||
end
|
||||
|
|
@ -4,8 +4,13 @@ import VueApollo from 'vue-apollo';
|
|||
import Vuex from 'vuex';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { boardListQueryResponse, mockLabelList } from 'jest/boards/mock_data';
|
||||
import {
|
||||
boardListQueryResponse,
|
||||
mockLabelList,
|
||||
updateBoardListResponse,
|
||||
} from 'jest/boards/mock_data';
|
||||
import BoardListHeader from '~/boards/components/board_list_header.vue';
|
||||
import updateBoardListMutation from '~/boards/graphql/board_list_update.mutation.graphql';
|
||||
import { ListType } from '~/boards/constants';
|
||||
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
|
||||
|
||||
|
|
@ -19,6 +24,8 @@ describe('Board List Header Component', () => {
|
|||
|
||||
const updateListSpy = jest.fn();
|
||||
const toggleListCollapsedSpy = jest.fn();
|
||||
const mockClientToggleListCollapsedResolver = jest.fn();
|
||||
const updateListHandler = jest.fn().mockResolvedValue(updateBoardListResponse);
|
||||
|
||||
afterEach(() => {
|
||||
fakeApollo = null;
|
||||
|
|
@ -34,7 +41,7 @@ describe('Board List Header Component', () => {
|
|||
listQueryHandler = jest.fn().mockResolvedValue(boardListQueryResponse()),
|
||||
injectedProps = {},
|
||||
} = {}) => {
|
||||
const boardId = '1';
|
||||
const boardId = 'gid://gitlab/Board/1';
|
||||
|
||||
const listMock = {
|
||||
...mockLabelList,
|
||||
|
|
@ -58,8 +65,17 @@ describe('Board List Header Component', () => {
|
|||
state: {},
|
||||
actions: { updateList: updateListSpy, toggleListCollapsed: toggleListCollapsedSpy },
|
||||
});
|
||||
|
||||
fakeApollo = createMockApollo([[listQuery, listQueryHandler]]);
|
||||
fakeApollo = createMockApollo(
|
||||
[
|
||||
[listQuery, listQueryHandler],
|
||||
[updateBoardListMutation, updateListHandler],
|
||||
],
|
||||
{
|
||||
Mutation: {
|
||||
clientToggleListCollapsed: mockClientToggleListCollapsedResolver,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
wrapper = shallowMountExtended(BoardListHeader, {
|
||||
apolloProvider: fakeApollo,
|
||||
|
|
@ -67,9 +83,9 @@ describe('Board List Header Component', () => {
|
|||
propsData: {
|
||||
list: listMock,
|
||||
filterParams: {},
|
||||
boardId,
|
||||
},
|
||||
provide: {
|
||||
boardId,
|
||||
weightFeatureAvailable: false,
|
||||
currentUserId,
|
||||
isEpicBoard: false,
|
||||
|
|
@ -191,7 +207,9 @@ describe('Board List Header Component', () => {
|
|||
await nextTick();
|
||||
|
||||
expect(updateListSpy).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(String(isCollapsed()));
|
||||
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(
|
||||
String(!isCollapsed()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -214,4 +232,44 @@ describe('Board List Header Component', () => {
|
|||
expect(findTitle().classes()).toContain('gl-cursor-grab');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Apollo boards', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ listType: ListType.label, injectedProps: { isApolloBoard: true } });
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
it('set active board item on client when clicking on card', async () => {
|
||||
findCaret().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(mockClientToggleListCollapsedResolver).toHaveBeenCalledWith(
|
||||
{},
|
||||
{
|
||||
list: mockLabelList,
|
||||
collapsed: true,
|
||||
},
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not call update list mutation when user is not logged in', async () => {
|
||||
createComponent({ currentUserId: null, injectedProps: { isApolloBoard: true } });
|
||||
|
||||
findCaret().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(updateListHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls update list mutation when user is logged in', async () => {
|
||||
createComponent({ currentUserId: 1, injectedProps: { isApolloBoard: true } });
|
||||
|
||||
findCaret().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(updateListHandler).toHaveBeenCalledWith({ listId: mockLabelList.id, collapsed: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -989,4 +989,12 @@ export const updateEpicTitleResponse = {
|
|||
},
|
||||
};
|
||||
|
||||
export const updateBoardListResponse = {
|
||||
data: {
|
||||
updateBoardList: {
|
||||
list: mockList,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_COLOR = '#1068bf';
|
||||
|
|
|
|||
|
|
@ -136,11 +136,16 @@ describe('formatTimeAsSummary', () => {
|
|||
|
||||
describe('durationTimeFormatted', () => {
|
||||
it.each`
|
||||
duration | expectedOutput
|
||||
${87} | ${'00:01:27'}
|
||||
${141} | ${'00:02:21'}
|
||||
${12} | ${'00:00:12'}
|
||||
${60} | ${'00:01:00'}
|
||||
duration | expectedOutput
|
||||
${0} | ${'00:00:00'}
|
||||
${12} | ${'00:00:12'}
|
||||
${60} | ${'00:01:00'}
|
||||
${60 + 27} | ${'00:01:27'}
|
||||
${120 + 21} | ${'00:02:21'}
|
||||
${4 * 60 * 60 + 25 * 60 + 37} | ${'04:25:37'}
|
||||
${35 * 60 * 60 + 3 * 60 + 7} | ${'35:03:07'}
|
||||
${-60} | ${'-00:01:00'}
|
||||
${-(35 * 60 * 60 + 3 * 60 + 7)} | ${'-35:03:07'}
|
||||
`('returns $expectedOutput when provided $duration', ({ duration, expectedOutput }) => {
|
||||
expect(utils.durationTimeFormatted(duration)).toBe(expectedOutput);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::MergeRequest::DiffLlmSummary, feature_category: :code_review_workflow do
|
||||
let_it_be_with_reload(:project) { create(:project, :repository) }
|
||||
|
||||
subject(:merge_request_diff_llm_summary) { build(:merge_request_diff_llm_summary) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:merge_request_diff) }
|
||||
it { is_expected.to belong_to(:user).optional }
|
||||
it { is_expected.to validate_presence_of(:content) }
|
||||
it { is_expected.to validate_length_of(:content).is_at_most(2056) }
|
||||
it { is_expected.to validate_presence_of(:provider) }
|
||||
end
|
||||
end
|
||||
|
|
@ -229,17 +229,24 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
|
|||
end
|
||||
|
||||
describe 'PUT /projects/:id/environments/:environment_id' do
|
||||
it 'returns a 200 if name and external_url are changed' do
|
||||
it 'returns a 200 if external_url is changed' do
|
||||
url = 'https://mepmep.whatever.ninja'
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { name: 'Mepmep', external_url: url }
|
||||
params: { external_url: url }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['name']).to eq('Mepmep')
|
||||
expect(json_response['external_url']).to eq(url)
|
||||
end
|
||||
|
||||
it 'returns a 400 if name is changed' do
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { name: 'Mepmep' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(described_class::ENVIRONMENT_NAME_UPDATE_ERROR)
|
||||
end
|
||||
|
||||
it 'returns a 200 if tier is changed' do
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { tier: 'production' }
|
||||
|
|
@ -258,21 +265,38 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
|
|||
expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
|
||||
end
|
||||
|
||||
it "won't update the external_url if only the name is passed" do
|
||||
url = environment.external_url
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { name: 'Mepmep' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['name']).to eq('Mepmep')
|
||||
expect(json_response['external_url']).to eq(url)
|
||||
end
|
||||
|
||||
it 'returns a 404 if the environment does not exist' do
|
||||
put api("/projects/#{project.id}/environments/#{non_existing_record_id}", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context 'when disallow_environment_name_update feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(disallow_environment_name_update: false)
|
||||
end
|
||||
|
||||
it 'returns a 200 if name and external_url are changed' do
|
||||
url = 'https://mepmep.whatever.ninja'
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { name: 'Mepmep', external_url: url }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['name']).to eq('Mepmep')
|
||||
expect(json_response['external_url']).to eq(url)
|
||||
end
|
||||
|
||||
it "won't update the external_url if only the name is passed" do
|
||||
url = environment.external_url
|
||||
put api("/projects/#{project.id}/environments/#{environment.id}", user),
|
||||
params: { name: 'Mepmep' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['name']).to eq('Mepmep')
|
||||
expect(json_response['external_url']).to eq(url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /projects/:id/environments/:environment_id' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gettext/static_identifier'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gettext::StaticIdentifier, feature_category: :internationalization do
|
||||
describe '#_()' do
|
||||
it 'does not flag correct use' do
|
||||
expect_no_offenses(<<~'RUBY')
|
||||
_('Hello')
|
||||
_('Hello #{name}')
|
||||
|
||||
_('Hello %{name}') % { name: name }
|
||||
format(_('Hello %{name}') % { name: name })
|
||||
|
||||
_('Hello' \
|
||||
'Multiline')
|
||||
_('Hello' \
|
||||
'Multiline %{name}') % { name: name }
|
||||
|
||||
var = "Hello"
|
||||
_(var)
|
||||
_(method_name)
|
||||
list.each { |item| _(item) }
|
||||
_(CONST)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags incorrect use' do
|
||||
expect_offense(<<~'RUBY')
|
||||
_('Hello' + ' concat')
|
||||
^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `_(...)`.
|
||||
_('Hello'.concat(' concat'))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `_(...)`.
|
||||
_("Hello #{name}")
|
||||
^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `_(...)`.
|
||||
_('Hello %{name}' % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `_(...)`.
|
||||
_(format('Hello %{name}') % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `_(...)`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
describe '#N_()' do
|
||||
it 'does not flag correct use' do
|
||||
expect_no_offenses(<<~'RUBY')
|
||||
N_('Hello')
|
||||
N_('Hello #{name}')
|
||||
N_('Hello %{name}') % { name: name }
|
||||
format(_('Hello %{name}') % { name: name })
|
||||
|
||||
N_('Hello' \
|
||||
'Multiline')
|
||||
|
||||
var = "Hello"
|
||||
N_(var)
|
||||
N_(method_name)
|
||||
list.each { |item| N_(item) }
|
||||
N_(CONST)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags incorrect use' do
|
||||
expect_offense(<<~'RUBY')
|
||||
N_('Hello' + ' concat')
|
||||
^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `N_(...)`.
|
||||
N_("Hello #{name}")
|
||||
^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `N_(...)`.
|
||||
N_('Hello %{name}' % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `N_(...)`.
|
||||
N_('Hello' \
|
||||
^^^^^^^^^ Ensure to pass static strings to translation method `N_(...)`.
|
||||
'Multiline %{name}' % { name: name })
|
||||
N_(format('Hello %{name}') % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `N_(...)`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
describe '#s_()' do
|
||||
it 'does not flag correct use' do
|
||||
expect_no_offenses(<<~'RUBY')
|
||||
s_('World|Hello')
|
||||
s_('World|Hello #{name}')
|
||||
s_('World|Hello %{name}') % { name: name }
|
||||
format(s_('World|Hello %{name}') % { name: name })
|
||||
|
||||
s_('World|Hello' \
|
||||
'Multiline')
|
||||
|
||||
var = "Hello"
|
||||
s_(var)
|
||||
s_(method_name)
|
||||
list.each { |item| s_(item) }
|
||||
s_(CONST)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags incorrect use' do
|
||||
expect_offense(<<~'RUBY')
|
||||
s_("World|Hello #{name}")
|
||||
^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `s_(...)`.
|
||||
s_('World|Hello' + ' concat')
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `s_(...)`.
|
||||
s_('World|Hello %{name}' % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `s_(...)`.
|
||||
s_('World|Hello' \
|
||||
^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `s_(...)`.
|
||||
'Multiline %{name}' % { name: name })
|
||||
s_(format('World|Hello %{name}') % { name: name })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `s_(...)`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
describe '#n_()' do
|
||||
it 'does not flag correct use' do
|
||||
expect_no_offenses(<<~'RUBY')
|
||||
n_('Hello', 'Hellos', 2)
|
||||
n_('Hello', 'Hellos', count)
|
||||
|
||||
n_('Hello' ' concat', 'Hellos', 2)
|
||||
n_('Hello', 'Hello' 's', 2)
|
||||
|
||||
n_('Hello %{name}', 'Hellos %{name}', 2) % { name: name }
|
||||
format(n_('Hello %{name}', 'Hellos %{name}', count) % { name: name })
|
||||
|
||||
n_('Hello', 'Hellos' \
|
||||
'Multiline', 2)
|
||||
|
||||
n_('Hello' \
|
||||
'Multiline', 'Hellos', 2)
|
||||
|
||||
n_('Hello' \
|
||||
'Multiline %{name}', 'Hellos %{name}', 2) % { name: name }
|
||||
|
||||
var = "Hello"
|
||||
n_(var, var, 1)
|
||||
n_(method_name, method_name, count)
|
||||
list.each { |item| n_(item, item, 2) }
|
||||
n_(CONST, CONST, 2)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'flags incorrect use' do
|
||||
expect_offense(<<~'RUBY')
|
||||
n_('Hello' + ' concat', 'Hellos', 2)
|
||||
^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
n_('Hello', 'Hello' + 's', 2)
|
||||
^^^^^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
n_("Hello #{name}", "Hellos", 2)
|
||||
^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
n_('Hello %{name}' % { name: name }, 'Hellos', 2)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
n_('Hello' \
|
||||
^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
'Multiline %{name}' % { name: name }, 'Hellos %{name}', 2)
|
||||
n_('Hello', format('Hellos %{name}') % { name: name }, count)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ensure to pass static strings to translation method `n_(...)`.
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
describe 'edge cases' do
|
||||
it 'does not flag' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
n_(s_('World|Hello'), s_('World|Hellos'), 2)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
|
||||
require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
|
||||
|
||||
# Disabling interpolation check as we deliberately want to have #{} in strings.
|
||||
# rubocop:disable Lint/InterpolationCheck
|
||||
RSpec.describe RuboCop::Cop::RubyInterpolationInTranslation do
|
||||
let(:msg) { "Don't use ruby interpolation \#{} inside translated strings, instead use %{}" }
|
||||
|
||||
it 'does not add an offense for a regular messages' do
|
||||
expect_no_offenses('_("Hello world")')
|
||||
end
|
||||
|
||||
it 'adds the correct offense when using interpolation in a string' do
|
||||
expect_offense(<<~CODE)
|
||||
_("Hello \#{world}")
|
||||
^^^^^ #{msg}
|
||||
^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'detects when using a ruby interpolation in the first argument of a pluralized string' do
|
||||
expect_offense(<<~CODE)
|
||||
n_("Hello \#{world}", "Hello world")
|
||||
^^^^^ #{msg}
|
||||
^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'detects when using a ruby interpolation in the second argument of a pluralized string' do
|
||||
expect_offense(<<~CODE)
|
||||
n_("Hello world", "Hello \#{world}")
|
||||
^^^^^ #{msg}
|
||||
^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'detects when using interpolation in a namespaced translation' do
|
||||
expect_offense(<<~CODE)
|
||||
s_("Hello|\#{world}")
|
||||
^^^^^ #{msg}
|
||||
^^^^^^^^ #{msg}
|
||||
CODE
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/InterpolationCheck
|
||||
Loading…
Reference in New Issue