Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-11-07 15:19:19 +00:00
parent dfa6eac075
commit d4fcd1794e
210 changed files with 2709 additions and 963 deletions

View File

@ -105,6 +105,11 @@ workflow:
<<: [*default-ruby-variables, *default-branch-pipeline-failure-variables]
GITLAB_DEPENDENCY_PROXY_ADDRESS: ""
PIPELINE_NAME: 'Ruby $RUBY_VERSION $CI_COMMIT_BRANCH branch pipeline (triggered by a project token)'
# For `$CI_DEFAULT_BRANCH` from wider community contributors, we don't want to run any pipelines on pushes,
# because normally we want to run merge request pipelines and scheduled pipelines, not for repository synchronization.
# This can avoid accidentally using up pipeline minutes quota while synchronizing the repository for wider community contributors.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_NAMESPACE !~ /^gitlab(-org|-cn)?($|\/)/'
when: never
# For `$CI_DEFAULT_BRANCH` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
variables:

View File

@ -84,10 +84,10 @@ include:
- echo -e "\e[0Ksection_start:`date +%s`:report_results_section[collapsed=true]\r\e[0KReport results"
- |
if [ "$CREATE_RAILS_TEST_FAILURE_ISSUES" == "true" ]; then
bundle exec relate-failure-issue --input-files "rspec/rspec-*.json" --system-log-files "log" --project "gitlab-org/gitlab" --token "${TEST_FAILURES_PROJECT_TOKEN}";
bundle exec relate-failure-issue --input-files "rspec/rspec-*.json" --system-log-files "log" --project "gitlab-org/gitlab" --token "${TEST_FAILURES_PROJECT_TOKEN}" --related-issues-file "rspec/${CI_JOB_ID}-failed-test-issues.json";
fi
if [ "$CREATE_RAILS_SLOW_TEST_ISSUES" == "true" ]; then
bundle exec slow-test-issues --input-files "rspec/rspec-*.json" --project "gitlab-org/gitlab" --token "${TEST_FAILURES_PROJECT_TOKEN}";
bundle exec slow-test-issues --input-files "rspec/rspec-*.json" --project "gitlab-org/gitlab" --token "${TEST_FAILURES_PROJECT_TOKEN}" --related-issues-file "rspec/${CI_JOB_ID}-slow-test-issues.json";
fi
if [ "$ADD_SLOW_TEST_NOTE_TO_MERGE_REQUEST" == "true" ]; then
bundle exec slow-test-merge-request-report-note --input-files "rspec/rspec-*.json" --project "gitlab-org/gitlab" --merge_request_iid "$CI_MERGE_REQUEST_IID" --token "${TEST_SLOW_NOTE_PROJECT_TOKEN}";

View File

@ -60,7 +60,6 @@ Capybara/TestidFinders:
- 'spec/features/merge_request/user_views_open_merge_request_spec.rb'
- 'spec/features/milestone_spec.rb'
- 'spec/features/nav/new_nav_callout_spec.rb'
- 'spec/features/nav/new_nav_toggle_spec.rb'
- 'spec/features/nav/pinned_nav_items_spec.rb'
- 'spec/features/populate_new_pipeline_vars_with_params_spec.rb'
- 'spec/features/profile_spec.rb'

View File

@ -212,7 +212,6 @@ Layout/ArgumentAlignment:
- 'app/graphql/resolvers/group_releases_resolver.rb'
- 'app/graphql/resolvers/groups_resolver.rb'
- 'app/graphql/resolvers/incident_management/timeline_events_resolver.rb'
- 'app/graphql/resolvers/issues/base_parent_resolver.rb'
- 'app/graphql/resolvers/issues/base_resolver.rb'
- 'app/graphql/resolvers/issues_resolver.rb'
- 'app/graphql/resolvers/labels_resolver.rb'

View File

@ -2846,7 +2846,6 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/background_migration/backfill_user_details_fields_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb'
- 'spec/lib/gitlab/background_migration/base_job_spec.rb'
- 'spec/lib/gitlab/background_migration/batched_migration_job_spec.rb'
- 'spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb'
- 'spec/lib/gitlab/background_migration/batching_strategies/base_strategy_spec.rb'
- 'spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb'
@ -3262,7 +3261,6 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb'
- 'spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb'
- 'spec/lib/gitlab/database/count_spec.rb'
- 'spec/lib/gitlab/database/dynamic_model_helpers_spec.rb'
- 'spec/lib/gitlab/database/each_database_spec.rb'
- 'spec/lib/gitlab/database/grant_spec.rb'
- 'spec/lib/gitlab/database/load_balancing/configuration_spec.rb'
@ -3285,7 +3283,6 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/database/migration_spec.rb'
- 'spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/migrations/base_background_runner_spec.rb'
- 'spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/migrations/constraints_helpers_spec.rb'
- 'spec/lib/gitlab/database/migrations/extension_helpers_spec.rb'
- 'spec/lib/gitlab/database/migrations/instrumentation_spec.rb'

View File

@ -1 +1 @@
14.29.0
14.30.0

View File

@ -63,7 +63,7 @@ gem 'marginalia', '~> 1.11.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'declarative_policy', '~> 1.1.0' # rubocop:todo Gemfile/MissingFeatureCategory
# Authentication libraries
gem 'devise', '~> 4.8.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'devise', '~> 4.9.3', feature_category: :system_access
gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-encryptable' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'bcrypt', '~> 3.1', '>= 3.1.14' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'doorkeeper', '~> 5.6', '>= 5.6.6' # rubocop:todo Gemfile/MissingFeatureCategory
@ -208,7 +208,7 @@ gem 'deckar01-task_list', '2.3.3' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'gitlab-markup', '~> 1.9.0', require: 'github/markup' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'commonmarker', '~> 0.23.10' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'kramdown', '~> 2.3.1' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'RedCloth', '~> 4.3.2' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'RedCloth', '~> 4.3.3' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'org-ruby', '~> 0.9.12' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'creole', '~> 0.5.0' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'wikicloth', '0.8.1' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -1,6 +1,6 @@
[
{"name":"CFPropertyList","version":"3.0.5","platform":"ruby","checksum":"a78551cd4768d78ebca98488c27e33652ef818be64697a54676d34e6434674a4"},
{"name":"RedCloth","version":"4.3.2","platform":"ruby","checksum":"1ee7bc55c8dcec92cf7741a2132a9a6cd19e4b884fbc1b3aca23e1a4fcd92d55"},
{"name":"RedCloth","version":"4.3.3","platform":"ruby","checksum":"d941b8ac96e2730d2d9326d97dda9fcf64cb73532b3f902d91c18970c5f4632d"},
{"name":"acme-client","version":"2.0.11","platform":"ruby","checksum":"edf6da9f3c5dbe3ab0c6738eb3b97978b7a60e3500445480d2a72fcc610089de"},
{"name":"actioncable","version":"7.0.8","platform":"ruby","checksum":"1f504ddb4ab6a34f7c52e9df924441a403e9f358bace330c36dcca6358ecfb84"},
{"name":"actionmailbox","version":"7.0.8","platform":"ruby","checksum":"9420037b801e44aa4e36cf113f4bd6eb25c17eb1b84d9c8865e8abf8846c14e5"},
@ -115,7 +115,7 @@
{"name":"devfile","version":"0.0.24.pre.alpha1","platform":"ruby","checksum":"72bbfc26edb519902d5c68e07188e0a3d699a1866392fa1497e5b7f3abb36600"},
{"name":"devfile","version":"0.0.24.pre.alpha1","platform":"x86_64-linux","checksum":"d121b1094aa3a24c29592a83c629ee640920e0196711dd06f27b6fa9b1ced609"},
{"name":"device_detector","version":"1.0.0","platform":"ruby","checksum":"b800fb3150b00c23e87b6768011808ac1771fffaae74c3238ebaf2b782947a7d"},
{"name":"devise","version":"4.8.1","platform":"ruby","checksum":"fdd48bbe79a89e7c1152236a70479842ede48bea4fa7f4f2d8da1f872559803e"},
{"name":"devise","version":"4.9.3","platform":"ruby","checksum":"480638d6c51b97f56da6e28d4f3e2a1b8e606681b316aa594b87c6ab94923488"},
{"name":"devise-two-factor","version":"4.1.1","platform":"ruby","checksum":"c95f5b07533e62217aaed3c386874d94e2d472fb5f2b6598afe8600fc17a8b95"},
{"name":"diff-lcs","version":"1.5.0","platform":"ruby","checksum":"49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67"},
{"name":"diff_match_patch","version":"0.1.0","platform":"ruby","checksum":"b36057bfcfeaedf19dcb7b2c28c19ee625bd6ec6d0d182717d3ef22b3879c40e"},

View File

@ -28,6 +28,7 @@ PATH
specs:
gitlab-http (0.1.0)
activesupport (~> 7)
concurrent-ruby (~> 1.2)
httparty (~> 0.21.0)
ipaddress (~> 0.8.3)
nokogiri (~> 1.15.4)
@ -161,7 +162,7 @@ GEM
specs:
CFPropertyList (3.0.5)
rexml
RedCloth (4.3.2)
RedCloth (4.3.3)
acme-client (2.0.11)
faraday (>= 1.0, < 3.0.0)
faraday-retry (~> 1.0)
@ -444,7 +445,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.1)
devfile (0.0.24.pre.alpha1)
device_detector (1.0.0)
devise (4.8.1)
devise (4.9.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@ -1743,7 +1744,7 @@ PLATFORMS
DEPENDENCIES
CFPropertyList (~> 3.0.0)
RedCloth (~> 4.3.2)
RedCloth (~> 4.3.3)
acme-client (~> 2.0)
activerecord-explain-analyze (~> 0.1)
activerecord-gitlab!
@ -1799,7 +1800,7 @@ DEPENDENCIES
derailed_benchmarks
devfile (~> 0.0.24.pre.alpha1)
device_detector
devise (~> 4.8.1)
devise (~> 4.9.3)
devise-pbkdf2-encryptable (~> 0.0.0)!
devise-two-factor (~> 4.1.1)
diff_match_patch (~> 0.1.0)

View File

@ -72,22 +72,20 @@ export const fetchDrafts = ({ commit, getters, state, dispatch }) =>
}),
);
export const publishSingleDraft = ({ commit, dispatch, getters }, draftId) => {
export const publishSingleDraft = ({ commit, getters }, draftId) => {
commit(types.REQUEST_PUBLISH_DRAFT, draftId);
service
.publishDraft(getters.getNotesData.draftsPublishPath, draftId)
.then(() => dispatch('updateDiscussionsAfterPublish'))
.then(() => commit(types.RECEIVE_PUBLISH_DRAFT_SUCCESS, draftId))
.catch(() => commit(types.RECEIVE_PUBLISH_DRAFT_ERROR, draftId));
};
export const publishReview = ({ commit, dispatch, getters }, noteData = {}) => {
export const publishReview = ({ commit, getters }, noteData = {}) => {
commit(types.REQUEST_PUBLISH_REVIEW);
return service
.publish(getters.getNotesData.draftsPublishPath, noteData)
.then(() => dispatch('updateDiscussionsAfterPublish'))
.then(() => commit(types.RECEIVE_PUBLISH_REVIEW_SUCCESS))
.catch((e) => {
commit(types.RECEIVE_PUBLISH_REVIEW_ERROR);
@ -96,18 +94,6 @@ export const publishReview = ({ commit, dispatch, getters }, noteData = {}) => {
});
};
export const updateDiscussionsAfterPublish = async ({ dispatch, getters, rootGetters }) => {
await dispatch(
'fetchDiscussions',
{ path: getters.getNotesData.discussionsPath },
{ root: true },
);
dispatch('diffs/assignDiscussionsToDiff', rootGetters.discussionsStructuredByLineCode, {
root: true,
});
};
export const updateDraft = (
{ commit, getters },
{ note, noteText, resolveDiscussion, position, flashContainer, callback, errorCallback },

View File

@ -248,7 +248,6 @@ export default {
<template #downstream>
<linked-pipelines-column
v-if="showDownstreamPipelines"
class="gl-mr-5"
:class="{ 'gl-sm-ml-3': isNewPipelineGraph }"
:config-paths="configPaths"
:linked-pipelines="downstreamPipelines"

View File

@ -11,7 +11,10 @@ export default {
};
</script>
<template>
<div class="gl-display-flex" :class="{ 'gl-flex-wrap gl-w-full': isNewPipelineGraph }">
<div
class="gl-display-flex"
:class="{ 'gl-flex-wrap gl-sm-flex-nowrap gl-w-full': isNewPipelineGraph }"
>
<slot name="upstream"></slot>
<slot name="main"></slot>
<slot name="downstream"></slot>

View File

@ -74,7 +74,7 @@ export default {
left: 'gl-mx-6',
};
const positionValues = {
right: 'gl-ml-5',
right: 'gl-mx-5',
left: 'gl-mx-4 gl-flex-basis-full',
};
const usePositionValues = this.isNewPipelineGraph ? positionValues : positionValuesOld;

View File

@ -179,7 +179,7 @@ export default {
<template #stages>
<div
data-testid="stage-column-title"
class="gl-display-flex gl-justify-content-space-between gl-relative"
class="stage-column-title gl-display-flex gl-justify-content-space-between gl-relative"
:class="titleClasses"
>
<span :title="name" class="gl-text-truncate gl-pr-3 gl-w-85p">

View File

@ -403,12 +403,7 @@ export default {
{{ commitTitle }}
</h3>
<div>
<ci-icon
:status="detailedStatus"
show-status-text
:show-link="false"
class="gl-display-inline-block gl-mb-3"
/>
<ci-icon :status="detailedStatus" show-status-text :show-link="false" class="gl-mb-3" />
<div class="gl-ml-2 gl-mb-3 gl-display-inline-block gl-h-6">
<gl-link
v-if="user"

View File

@ -366,22 +366,11 @@ export default {
handleLocationHash();
this.autoScrolled = true;
}, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
this.unwatchDiscussions = this.$watch(
() => `${this.flatBlobsList.length}:${this.$store.state.notes.discussions.length}`,
() => {
this.setDiscussions();
if (this.$store.state.notes.doneFetchingBatchDiscussions) {
this.unwatchDiscussions();
}
},
);
this.unwatchRetrievingBatches = this.$watch(
() => `${this.retrievingBatches}:${this.$store.state.notes.discussions.length}`,
() => {
if (!this.retrievingBatches && this.$store.state.notes.discussions.length) {
this.unwatchRetrievingBatches();
this.$watch(
() => this.$store.state.notes.discussions.length,
(newVal, prevVal) => {
if (newVal > prevVal) {
this.setDiscussions();
}
},
);

View File

@ -39,12 +39,6 @@ export default {
},
methods: {
...mapActions(['toggleDiscussion']),
...mapActions('diffs', ['removeDiscussionsFromDiff']),
deleteNoteHandler(discussion) {
if (discussion.notes.length <= 1) {
this.removeDiscussionsFromDiff(discussion);
}
},
isExpanded(discussion) {
return this.shouldCollapseDiscussions ? discussion.expanded : true;
},
@ -90,7 +84,6 @@ export default {
:line="line"
:help-page-path="helpPagePath"
:should-scroll-to-note="false"
@noteDeleted="deleteNoteHandler"
>
<template v-if="renderAvatarBadge" #avatar-badge>
<design-note-pin

View File

@ -419,7 +419,11 @@ export const assignDiscussionsToDiff = (
};
export const removeDiscussionsFromDiff = ({ commit }, removeDiscussion) => {
const { file_hash: fileHash, line_code: lineCode, id } = removeDiscussion;
const {
diff_file: { file_hash: fileHash },
line_code: lineCode,
id,
} = removeDiscussion;
commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash, lineCode, id });
};

View File

@ -198,9 +198,10 @@ export default {
return {
...line,
discussionsExpanded:
line.discussions && line.discussions.length
line.discussionsExpanded ||
(line.discussions && line.discussions.length
? line.discussions.some((disc) => !disc.resolved) || isLineNoteTargeted
: false,
: false),
};
};

View File

@ -338,7 +338,7 @@ function prepareLine(line, file) {
problems.brokenSymlink || problems.fileOnlyMoved || problems.brokenLineCode,
),
rich_text: cleanRichText(line.rich_text),
discussionsExpanded: true,
discussionsExpanded: false,
discussions: [],
hasForm: false,
text: undefined,

View File

@ -112,6 +112,9 @@ export default {
canSetupReviewApp() {
return this.environmentApp?.reviewApp?.canSetupReviewApp;
},
hasReviewApp() {
return this.environmentApp?.reviewApp?.hasReviewApp;
},
canCleanUpEnvs() {
return this.environmentApp?.canStopStaleEnvironments;
},
@ -157,7 +160,10 @@ export default {
};
},
openReviewAppModal() {
if (!this.canSetupReviewApp) {
// we don't show the Enable review apps button
// if a user cannot setup a review app or review
// apps are already configured
if (!this.canSetupReviewApp || this.hasReviewApp) {
return null;
}

View File

@ -194,7 +194,7 @@ export default {
<div
class="item-attributes-area gl-display-flex gl-align-items-center gl-flex-wrap gl-gap-3"
>
<span v-if="hasPipeline" class="mr-ci-status order-md-last">
<span v-if="hasPipeline" class="mr-ci-status order-md-last gl-md-ml-3 gl-mr-n2">
<a :href="pipelinePath">
<ci-icon :status="pipelineStatus" :title="pipelineStatusTooltip" />
</a>
@ -203,7 +203,7 @@ export default {
<issue-milestone
v-if="hasMilestone"
:milestone="milestone"
class="item-milestone gl-font-sm gl-display-flex gl-align-items-center order-md-first"
class="item-milestone gl-font-sm gl-display-flex gl-align-items-center order-md-first gl-ml-2"
/>
<!-- Flex order for slots is defined in the parent component: e.g. related_issues_block.vue -->

View File

@ -14,29 +14,29 @@ import {
const badgePropertiesMap = {
[TYPE_EPIC]: {
[STATUS_OPEN]: {
icon: 'epic',
icon: 'issue-open-m',
text: __('Open'),
variant: 'success',
},
[STATUS_CLOSED]: {
icon: 'epic-closed',
icon: 'issue-close',
text: __('Closed'),
variant: 'info',
},
},
[TYPE_ISSUE]: {
[STATUS_OPEN]: {
icon: 'issues',
icon: 'issue-open-m',
text: __('Open'),
variant: 'success',
},
[STATUS_CLOSED]: {
icon: 'issue-closed',
icon: 'issue-close',
text: __('Closed'),
variant: 'info',
},
[STATUS_LOCKED]: {
icon: 'issues',
icon: 'issue-open-m',
text: __('Open'),
variant: 'success',
},

View File

@ -82,7 +82,7 @@ export default {
return this.issuableState === STATUS_OPEN || this.issuableState === STATUS_REOPENED;
},
statusIcon() {
return this.isOpen ? 'issues' : 'issue-closed';
return this.isOpen ? 'issue-open-m' : 'issue-close';
},
statusText() {
if (this.isOpen) {

View File

@ -2,12 +2,7 @@
import { GlBadge, GlIcon, GlIntersectionObserver, GlLink } from '@gitlab/ui';
import HiddenBadge from '~/issuable/components/hidden_badge.vue';
import LockedBadge from '~/issuable/components/locked_badge.vue';
import {
issuableStatusText,
STATUS_CLOSED,
TYPE_EPIC,
WORKSPACE_PROJECT,
} from '~/issues/constants';
import { issuableStatusText, STATUS_CLOSED, WORKSPACE_PROJECT } from '~/issues/constants';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
export default {
@ -60,10 +55,7 @@ export default {
return this.issuableStatus === STATUS_CLOSED;
},
statusIcon() {
if (this.issuableType === TYPE_EPIC) {
return this.isClosed ? 'epic-closed' : 'epic';
}
return this.isClosed ? 'issue-closed' : 'issues';
return this.isClosed ? 'issue-close' : 'issue-open-m';
},
statusText() {
return issuableStatusText[this.issuableStatus];

View File

@ -46,5 +46,5 @@ export function darkModeEnabled() {
if (isWebIde) {
return ideDarkThemes.includes(window.gon?.user_color_scheme);
}
return document.body.classList.contains('gl-dark');
return document.documentElement.classList.contains('gl-dark');
}

View File

@ -290,9 +290,6 @@ export default {
parent: this.$el,
});
},
deleteNoteHandler(note) {
this.$emit('noteDeleted', this.discussion, note);
},
onStartReplying(discussionId) {
if (this.discussion.id === discussionId) {
this.showReplyForm();
@ -329,7 +326,6 @@ export default {
:is-overview-tab="isOverviewTab"
:should-scroll-to-note="shouldScrollToNote"
@startReplying="showReplyForm"
@deleteNote="deleteNoteHandler"
>
<template #avatar-badge>
<slot name="avatar-badge"></slot>

View File

@ -318,11 +318,6 @@ export default {
const note = noteData;
const selectedDiscussion = state.discussions.find((disc) => disc.id === note.id);
note.expanded = true; // override expand flag to prevent collapse
if (note.diff_file) {
Object.assign(note, {
file_hash: note.diff_file.file_hash,
});
}
Object.assign(selectedDiscussion, { ...note });
},

View File

@ -246,22 +246,18 @@ async function fetchOperations(operationsUrl, serviceName) {
}
}
async function fetchMetrics() {
// TODO replace mocks with API calls https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2469
/* eslint-disable @gitlab/require-i18n-strings */
return {
metrics: [
{ name: 'metric A', description: 'a counter metric called A', type: 'COUNTER' },
{ name: 'metric B', description: 'a gauge metric called B', type: 'GAUGE' },
{ name: 'metric C', description: 'a histogram metric called C', type: 'HISTOGRAM' },
{
name: 'metric D',
description: 'a exp histogram metric called D',
type: 'EXPONENTIAL HISTOGRAM',
},
],
};
/* eslint-enable @gitlab/require-i18n-strings */
async function fetchMetrics(metricsUrl) {
try {
const { data } = await axios.get(metricsUrl, {
withCredentials: true,
});
if (!Array.isArray(data.metrics)) {
throw new Error('metrics are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
return data;
} catch (e) {
return reportErrorAndThrow(e);
}
}
export function buildClient(options) {

View File

@ -3,7 +3,10 @@ query getOrganizationUsers($id: OrganizationsOrganizationID!) {
id
organizationUsers {
nodes {
badges
badges {
text
variant
}
id
user {
id

View File

@ -23,6 +23,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-geo-migrate-hashed-storage-callout',
'.js-unlimited-members-during-trial-alert',
'.js-branch-rules-info-callout',
'.js-new-nav-for-everyone-callout',
'.js-namespace-over-storage-users-combined-alert',
];

View File

@ -5,9 +5,9 @@ import { INTEGRATION_VIEW_CONFIGS, i18n } from '../constants';
import IntegrationView from './integration_view.vue';
function updateClasses(bodyClasses = '', applicationTheme, layout) {
// Remove body class for any previous theme, re-add current one
document.body.classList.remove(...bodyClasses.split(' '));
document.body.classList.add(applicationTheme);
// Remove documentElement class for any previous theme, re-add current one
document.documentElement.classList.remove(...bodyClasses.split(' '));
document.documentElement.classList.add(applicationTheme);
// Toggle container-fluid class
if (layout === 'fluid') {

View File

@ -22,6 +22,7 @@ export const initAccessDropdown = (el, options) => {
data() {
return { preselected };
},
disabled,
methods: {
setPreselectedItems(items) {
this.preselected = items;

View File

@ -6,6 +6,10 @@ import { initToggle } from '~/toggles';
import { initAccessDropdown } from '~/projects/settings/init_access_dropdown';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
const isDropdownDisabled = (dropdown) => {
return dropdown?.$options.disabled === '';
};
export default class ProtectedBranchEdit {
constructor(options) {
this.hasLicense = options.hasLicense;
@ -104,6 +108,9 @@ export default class ProtectedBranchEdit {
}
initSelectedItems(dropdown, accessLevel) {
if (isDropdownDisabled(dropdown)) {
return;
}
this.selectedItems[accessLevel] = dropdown.preselected.map((item) => {
if (item.type === LEVEL_TYPES.USER) return { id: item.id, user_id: item.user_id };
if (item.type === LEVEL_TYPES.ROLE) return { id: item.id, access_level: item.access_level };
@ -183,7 +190,10 @@ export default class ProtectedBranchEdit {
};
});
this.selectedItems[accessLevel] = itemsToAdd;
this[`${accessLevel}_dropdown`]?.setPreselectedItems(itemsToAdd);
const dropdown = this[`${accessLevel}_dropdown`];
if (!isDropdownDisabled(dropdown)) {
this.selectedItems[accessLevel] = itemsToAdd;
dropdown?.setPreselectedItems(itemsToAdd);
}
}
}

View File

@ -8,7 +8,6 @@ import {
} from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, __, sprintf } from '~/locale';
import NewNavToggle from '~/nav/components/new_nav_toggle.vue';
import Tracking from '~/tracking';
import PersistentUserCallout from '~/persistent_user_callout';
import { USER_MENU_TRACKING_DEFAULTS, DROPDOWN_Y_OFFSET, IMPERSONATING_OFFSET } from '../constants';
@ -39,14 +38,13 @@ export default {
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
GlButton,
NewNavToggle,
UserMenuProfileItem,
},
directives: {
SafeHtml,
},
mixins: [Tracking.mixin()],
inject: ['toggleNewNavEndpoint', 'isImpersonating'],
inject: ['isImpersonating'],
props: {
data: {
required: true,
@ -301,13 +299,6 @@ export default {
/>
</gl-disclosure-dropdown-group>
<gl-disclosure-dropdown-group bordered>
<template #group-label>
<span class="gl-font-sm">{{ $options.i18n.newNavigation.sectionTitle }}</span>
</template>
<new-nav-toggle :endpoint="toggleNewNavEndpoint" enabled new-navigation />
</gl-disclosure-dropdown-group>
<gl-disclosure-dropdown-group
v-if="data.can_sign_out"
bordered

View File

@ -66,13 +66,7 @@ export const initSuperSidebar = () => {
if (!el) return false;
const {
rootPath,
sidebar,
toggleNewNavEndpoint,
forceDesktopExpandedSidebar,
commandPalette,
} = el.dataset;
const { rootPath, sidebar, forceDesktopExpandedSidebar, commandPalette } = el.dataset;
bindSuperSidebarCollapsedEvents(forceDesktopExpandedSidebar);
initSuperSidebarCollapsedState(parseBoolean(forceDesktopExpandedSidebar));
@ -98,7 +92,6 @@ export const initSuperSidebar = () => {
name: 'SuperSidebarRoot',
provide: {
rootPath,
toggleNewNavEndpoint,
isImpersonating,
...getTrialStatusWidgetData(sidebarData),
commandPaletteCommands,

View File

@ -1,11 +1,5 @@
<script>
import {
GlDropdown,
GlDropdownItem,
GlDropdownText,
GlSearchBoxByType,
GlSprintf,
} from '@gitlab/ui';
import { GlDisclosureDropdown, GlIcon, GlSearchBoxByType, GlSprintf } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { __, n__, s__, sprintf } from '~/locale';
@ -16,12 +10,16 @@ export const i18n = {
searchFiles: __('Search files'),
};
const variantCssColorMap = {
success: 'gl-text-green-500',
danger: 'gl-text-red-500',
};
export default {
i18n,
components: {
GlDropdown,
GlDropdownItem,
GlDropdownText,
GlDisclosureDropdown,
GlIcon,
GlSearchBoxByType,
GlSprintf,
},
@ -54,6 +52,15 @@ export default {
? fuzzaldrinPlus.filter(this.files, this.search, { key: 'name' })
: this.files;
},
dropdownItems() {
return this.filteredFiles.map((file) => {
return {
...file,
text: file.name || this.$options.i18n.noFileNameAvailable,
iconColor: variantCssColorMap[file.iconColor],
};
});
},
messageChanged() {
return sprintf(
n__(
@ -64,21 +71,21 @@ export default {
{ count: this.changed },
);
},
additionsText() {
return n__('Diffs|%d addition', 'Diffs|%d additions', this.added);
},
deletionsText() {
return n__('Diffs|%d deletion', 'Diffs|%d deletions', this.deleted);
},
},
methods: {
jumpToFile(fileHash) {
window.location.hash = fileHash;
},
focusInput() {
this.$refs.search.focusInput();
},
focusFirstItem() {
if (!this.filteredFiles.length) return;
this.$el.querySelector('.gl-new-dropdown-item:first-child').focus();
},
additionsText(numberOfChanges = this.added) {
return n__('Diffs|%d addition', 'Diffs|%d additions', numberOfChanges);
},
deletionsText(numberOfChanges = this.deleted) {
return n__('Diffs|%d deletion', 'Diffs|%d deletions', numberOfChanges);
},
},
};
</script>
@ -87,15 +94,15 @@ export default {
<div>
<gl-sprintf :message="messageChanged">
<template #dropdown="{ content: dropdownText }">
<gl-dropdown
<gl-disclosure-dropdown
:toggle-text="dropdownText"
:items="dropdownItems"
category="tertiary"
variant="confirm"
:text="dropdownText"
data-testid="diff-stats-dropdown"
class="gl-vertical-align-baseline"
toggle-class="gl-px-0! gl-font-weight-bold!"
menu-class="gl-w-auto!"
no-flip
fluid-width
@shown="focusInput"
>
<template #header>
@ -103,35 +110,38 @@ export default {
ref="search"
v-model.trim="search"
:placeholder="$options.i18n.searchFiles"
class="gl-mx-3 gl-my-4"
@keydown.down="focusFirstItem"
/>
<span v-if="!filteredFiles.length" class="gl-mx-3">
{{ $options.i18n.noFilesFound }}
</span>
</template>
<gl-dropdown-item
v-for="file in filteredFiles"
:key="file.href"
:icon-name="file.icon"
:icon-color="file.iconColor"
@click="jumpToFile(file.href)"
>
<div class="gl-display-flex">
<span v-if="file.name" class="gl-font-weight-bold gl-mr-3 gl-text-truncate">{{
file.name
}}</span>
<span v-else class="gl-mr-3 gl-font-weight-bold gl-font-style-italic gl-gray-400">{{
$options.i18n.noFileNameAvailable
}}</span>
<span class="gl-ml-auto gl-white-space-nowrap">
<span class="gl-text-green-600">+{{ file.added }}</span>
<span class="gl-text-red-500">-{{ file.removed }}</span>
</span>
<template #list-item="{ item }">
<div class="gl-display-flex gl-gap-3 gl-align-items-center">
<gl-icon :name="item.icon" :class="item.iconColor" />
<div class="gl-flex-grow-1">
<div class="gl-display-flex">
<span
class="gl-font-weight-bold gl-mr-3 gl-flex-grow-1"
:class="item.name ? 'gl-text-truncate' : 'gl-font-style-italic gl-gray-400'"
>{{ item.text }}</span
>
<span class="gl-ml-auto gl-white-space-nowrap" aria-hidden="true">
<span class="gl-text-green-600">+{{ item.added }}</span>
<span class="gl-text-red-500">-{{ item.removed }}</span>
</span>
<span class="gl-sr-only"
>{{ additionsText(item.added) }}, {{ deletionsText(item.removed) }}</span
>
</div>
<div class="gl-text-gray-700 gl-overflow-hidden gl-text-overflow-ellipsis">
{{ item.path }}
</div>
</div>
</div>
<div class="gl-text-gray-700 gl-overflow-hidden gl-text-overflow-ellipsis">
{{ file.path }}
</div>
</gl-dropdown-item>
<gl-dropdown-text v-if="!filteredFiles.length">
{{ $options.i18n.noFilesFound }}
</gl-dropdown-text>
</gl-dropdown>
</template>
</gl-disclosure-dropdown>
</template>
</gl-sprintf>
<span
@ -140,12 +150,20 @@ export default {
>
<gl-sprintf :message="$options.i18n.messageAdditionsDeletions">
<template #additions>
<span class="gl-text-green-600 gl-font-weight-bold">{{ additionsText }}</span>
<span class="gl-text-green-600 gl-font-weight-bold">{{ additionsText() }}</span>
</template>
<template #deletions>
<span class="gl-text-red-500 gl-font-weight-bold">{{ deletionsText }}</span>
<span class="gl-text-red-500 gl-font-weight-bold">{{ deletionsText() }}</span>
</template>
</gl-sprintf>
</span>
</div>
</template>
<style scoped>
/* TODO: Use max-height prop when gitlab-ui got updated.
See https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2374 */
::v-deep .gl-new-dropdown-inner {
max-height: 310px;
}
</style>

View File

@ -1,6 +1,6 @@
import { __ } from '~/locale';
export const CONFIG = {
users: { title: __('Users'), icon: 'user', filterKey: 'username' },
users: { title: __('Users'), icon: 'user', filterKey: 'username', showNamespaceDropdown: true },
groups: { title: __('Groups'), icon: 'group', filterKey: 'name' },
};

View File

@ -1,13 +1,23 @@
<script>
import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
import usersAutocompleteQuery from '~/graphql_shared/queries/users_autocomplete.query.graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import { createAlert } from '~/alert';
import { __ } from '~/locale';
import groupsAutocompleteQuery from '~/graphql_shared/queries/groups_autocomplete.query.graphql';
import Api from '~/api';
import UserItem from './user_item.vue';
import GroupItem from './group_item.vue';
import { CONFIG } from './constants';
const I18N = {
allGroups: __('All groups'),
projectGroups: __('Project groups'),
apiErrorMessage: __('An error occurred while fetching. Please try again.'),
};
export default {
name: 'ListSelector',
i18n: I18N,
components: {
GlCard,
GlIcon,
@ -33,11 +43,16 @@ export default {
required: false,
default: null,
},
groupPath: {
type: String,
required: false,
default: null,
},
},
data() {
return {
searchValue: '',
isProject: true, // TODO: implement a way to distinguish between project/group
isProjectNamespace: 'true',
selected: [],
items: [],
};
@ -46,39 +61,44 @@ export default {
config() {
return CONFIG[this.type];
},
searchItems() {
return this.items;
},
isUserVariant() {
return this.type === 'users';
},
component() {
return this.isUserVariant ? UserItem : GroupItem;
},
namespaceDropdownText() {
return parseBoolean(this.isProjectNamespace)
? this.$options.i18n.projectGroups
: this.$options.i18n.allGroups;
},
},
methods: {
async handleSearchInput(search) {
this.$refs.results.open();
if (this.isUserVariant) {
this.items = await this.fetchUsersBySearchTerm(search);
} else {
this.items = await this.fetchGroupsBySearchTerm(search);
try {
if (this.isUserVariant) {
this.items = await this.fetchUsersBySearchTerm(search);
} else {
this.items = await this.fetchGroupsBySearchTerm(search);
}
} catch (e) {
createAlert({
message: this.$options.i18n.apiErrorMessage,
});
}
},
fetchUsersBySearchTerm(search) {
const namespace = this.isProject ? 'project' : 'group';
return this.$apollo
.query({
query: usersAutocompleteQuery,
variables: { fullPath: this.projectPath, search, isProject: this.isProject },
})
.then(({ data }) =>
data[namespace]?.autocompleteUsers.map((user) => ({
text: user.name,
value: user.username,
...user,
})),
);
async fetchUsersBySearchTerm(search) {
let users = [];
if (parseBoolean(this.isProjectNamespace)) {
users = await Api.projectUsers(this.projectPath, search);
} else {
const groupMembers = await Api.groupMembers(this.groupPath, { query: search });
users = groupMembers?.data || [];
}
return users?.map((user) => ({ text: user.name, value: user.username, ...user }));
},
fetchGroupsBySearchTerm(search) {
return this.$apollo
@ -95,7 +115,7 @@ export default {
);
},
getItemByKey(key) {
return this.searchItems.find((item) => item[this.config.filterKey] === key);
return this.items.find((item) => item[this.config.filterKey] === key);
},
handleSelectItem(key) {
this.$emit('select', this.getItemByKey(key));
@ -103,7 +123,15 @@ export default {
handleDeleteItem(key) {
this.$emit('delete', key);
},
handleSelectNamespace() {
this.items = [];
this.searchValue = '';
},
},
namespaceOptions: [
{ text: I18N.projectGroups, value: 'true' },
{ text: I18N.allGroups, value: 'false' },
],
};
</script>
@ -118,28 +146,38 @@ export default {
></template
>
<gl-collapsible-listbox
ref="results"
v-model="selected"
class="list-selector gl-mb-4 gl-display-block"
:items="searchItems"
multiple
@shown="$refs.search.focusInput()"
>
<template #toggle>
<gl-search-box-by-type
ref="search"
v-model="searchValue"
autofocus
debounce="500"
@input="handleSearchInput"
/>
</template>
<div class="gl-display-flex gl-gap-3" :class="{ 'gl-mb-4': selectedItems.length }">
<gl-collapsible-listbox
ref="results"
v-model="selected"
class="list-selector gl-display-block gl-flex-grow-1"
:items="items"
multiple
@shown="$refs.search.focusInput()"
>
<template #toggle>
<gl-search-box-by-type
ref="search"
v-model="searchValue"
autofocus
debounce="500"
@input="handleSearchInput"
/>
</template>
<template #list-item="{ item }">
<component :is="component" :data="item" @select="handleSelectItem" />
</template>
</gl-collapsible-listbox>
<template #list-item="{ item }">
<component :is="component" :data="item" @select="handleSelectItem" />
</template>
</gl-collapsible-listbox>
<gl-collapsible-listbox
v-if="config.showNamespaceDropdown"
v-model="isProjectNamespace"
:toggle-text="namespaceDropdownText"
:items="$options.namespaceOptions"
@select="handleSelectNamespace"
/>
</div>
<component
:is="component"

View File

@ -74,6 +74,11 @@ export default {
required: false,
default: 0,
},
workspaceType: {
type: String,
required: false,
default: '',
},
},
computed: {
isUpdated() {
@ -161,6 +166,7 @@ export default {
:issuable="issuable"
:status-icon="statusIcon"
:enable-edit="enableEdit"
:workspace-type="workspaceType"
@edit-issuable="$emit('edit-issuable', $event)"
>
<template #status-badge>

View File

@ -147,6 +147,7 @@ export default {
:description-help-path="descriptionHelpPath"
:task-list-update-path="taskListUpdatePath"
:task-list-lock-version="taskListLockVersion"
:workspace-type="workspaceType"
@edit-issuable="$emit('edit-issuable', $event)"
@task-list-update-success="$emit('task-list-update-success', $event)"
@task-list-update-failure="$emit('task-list-update-failure')"

View File

@ -1,5 +1,6 @@
<script>
import { GlIcon, GlBadge, GlButton, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { STATUS_OPEN } from '~/issues/constants';
import { __ } from '~/locale';
@ -13,6 +14,7 @@ export default {
GlBadge,
GlButton,
GlIntersectionObserver,
ConfidentialityBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -31,6 +33,11 @@ export default {
type: Boolean,
required: true,
},
workspaceType: {
type: String,
required: false,
default: '',
},
},
data() {
return {
@ -89,6 +96,12 @@ export default {
<slot name="status-badge"></slot>
</span>
</gl-badge>
<confidentiality-badge
v-if="issuable.confidential"
class="gl-white-space-nowrap gl-mr-3 gl-align-self-center"
:issuable-type="issuable.type"
:workspace-type="workspaceType"
/>
<p
class="gl-font-weight-bold gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis gl-my-0"
:title="issuable.title"

View File

@ -36,11 +36,6 @@ export default {
return this.workItemType.toUpperCase().split(' ').join('_');
},
iconName() {
// TODO Delete this conditional once we have an `issue-type-epic` icon
if (this.workItemIconName === 'issue-type-epic') {
return 'epic';
}
return (
this.workItemIconName ||
WORK_ITEMS_TYPE_MAP[this.workItemTypeUppercase]?.icon ||

View File

@ -35,6 +35,7 @@ export const WORK_ITEM_TYPE_ENUM_TEST_CASE = 'TEST_CASE';
export const WORK_ITEM_TYPE_ENUM_REQUIREMENTS = 'REQUIREMENTS';
export const WORK_ITEM_TYPE_ENUM_OBJECTIVE = 'OBJECTIVE';
export const WORK_ITEM_TYPE_ENUM_KEY_RESULT = 'KEY_RESULT';
export const WORK_ITEM_TYPE_ENUM_EPIC = 'EPIC';
export const WORK_ITEM_TYPE_VALUE_EPIC = 'Epic';
export const WORK_ITEM_TYPE_VALUE_INCIDENT = 'Incident';
@ -185,6 +186,11 @@ export const WORK_ITEMS_TYPE_MAP = {
name: s__('WorkItem|Key result'),
value: WORK_ITEM_TYPE_VALUE_KEY_RESULT,
},
[WORK_ITEM_TYPE_ENUM_EPIC]: {
icon: `epic`,
name: s__('WorkItem|Epic'),
value: WORK_ITEM_TYPE_VALUE_EPIC,
},
};
export const WORK_ITEMS_TREE_TEXT_MAP = {

View File

@ -247,6 +247,7 @@ span.idiff {
border-bottom: 1px solid $border-color;
padding: $gl-padding-8 $gl-padding;
margin: 0;
min-height: px-to-rem(42px);
border-radius: $border-radius-default $border-radius-default 0 0;
@include media-breakpoint-up(md) {

View File

@ -169,6 +169,10 @@
}
}
.stage-column-title .gl-ci-action-icon-container {
right: 11px;
}
.split-report-section {
border-bottom: 1px solid var(--gray-50, $gray-50);
@ -269,7 +273,12 @@
.stage-column,
.stage-column.is-stage-view {
min-width: 1px;
@media (min-width: $breakpoint-sm) {
min-width: inherit;
max-width: $gl-spacing-scale-48;
&:first-of-type {
margin-left: $gl-spacing-scale-6;
}

View File

@ -105,7 +105,7 @@
--svg-status-bg: #{$white};
}
body.gl-dark {
:root.gl-dark {
// redefine some colors and values to prevent sourcegraph conflicts
color-scheme: dark;
--gray-10: #{$gray-10};
@ -239,7 +239,7 @@ aside.right-sidebar:not(.right-sidebar-merge-requests) {
border-left-color: $gray-50;
}
body.gl-dark {
:root.gl-dark {
@include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $white);
.terms {

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-blue {
@include gitlab-theme(
$theme-blue-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-gray {
@include gitlab-theme(
$gray-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-green {
@include gitlab-theme(
$theme-green-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-indigo {
@include gitlab-theme(
$indigo-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-light-blue {
@include gitlab-theme(
$theme-light-blue-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-light-gray {
@include gitlab-theme(
$gray-500,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-light-green {
@include gitlab-theme(
$theme-green-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-light-indigo {
@include gitlab-theme(
$indigo-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-light-red {
@include gitlab-theme(
$theme-light-red-200,

View File

@ -1,6 +1,6 @@
@import './theme_helper';
body {
:root {
&.ui-red {
@include gitlab-theme(
$theme-red-200,

View File

@ -5,7 +5,7 @@ module Groups
class ApplicationsController < Groups::ApplicationController
include OauthApplications
prepend_before_action :authorize_admin_group!
before_action :authorize_admin_group!
before_action :set_application, only: [:show, :edit, :update, :renew, :destroy]
before_action :load_scopes, only: [:index, :create, :edit, :update]

View File

@ -62,7 +62,11 @@ class Projects::ArtifactsController < Projects::ApplicationController
conditionally_expand_blob(blob)
if blob.external_link?(build)
redirect_to external_file_project_job_artifacts_path(@project, @build, path: params[:path])
if Gitlab::CurrentSettings.enable_artifact_external_redirect_warning_page
redirect_to external_file_project_job_artifacts_path(@project, @build, path: params[:path])
else
redirect_to blob.external_url(build)
end
else
respond_to do |format|
format.html do

View File

@ -17,7 +17,12 @@ module WorkItems
argument :state,
Types::IssuableStateEnum,
required: false,
description: 'Current state of the work item.'
description: 'Current state of the work item.',
prepare: ->(state, _ctx) {
return state unless state == 'locked'
raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
}
argument :types,
[Types::IssueTypeEnum],
as: :issue_types,

View File

@ -7,8 +7,13 @@ module Resolvers
include ::Issues::SortArguments
argument :state, Types::IssuableStateEnum,
required: false,
description: 'Current state of this issue.'
required: false,
description: 'Current state of this issue.',
prepare: ->(state, _ctx) {
return state unless state == 'locked'
raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
}
# see app/graphql/types/issue_connection.rb
type 'Types::IssueConnection', null: true

View File

@ -14,7 +14,12 @@ module Resolvers
description: 'Whether to include issues from archived projects. Defaults to `false`.'
argument :state, Types::IssuableStateEnum,
required: false,
description: 'Current state of this issue.'
description: 'Current state of this issue.',
prepare: ->(state, _ctx) {
return state unless state == 'locked'
raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
}
# see app/graphql/types/issue_connection.rb
type 'Types::IssueConnection', null: true

View File

@ -1,10 +1,15 @@
# frozen_string_literal: true
# DO NOT use this ENUM with issues. We need to define a new enum in places where we
# need to filter by state. locked is not a valid state filter for issues. More info in
# https://gitlab.com/gitlab-org/gitlab/-/issues/420667#note_1605900474
module Types
class IssuableStateEnum < BaseEnum
graphql_name 'IssuableState'
description 'State of a GitLab issue or merge request'
INVALID_LOCKED_MESSAGE = 'locked is not a valid state filter for issues.'
value 'opened', description: 'In open state.'
value 'closed', description: 'In closed state.'
value 'locked', description: 'Discussion has been locked.'

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Types
module Organizations
# rubocop: disable Graphql/AuthorizeTypes -- Already authorized in parent OrganizationUserType.
class OrganizationUserBadgeType < BaseObject
graphql_name 'OrganizationUserBadge'
description 'An organization user badge.'
field :text,
GraphQL::Types::String,
null: false,
description: 'Badge text.'
field :variant,
GraphQL::Types::String,
null: false,
description: 'Badge variant.'
end
# rubocop: enable Graphql/AuthorizeTypes
end
end

View File

@ -13,7 +13,7 @@ module Types
alias_method :organization_user, :object
field :badges,
[GraphQL::Types::String],
[::Types::Organizations::OrganizationUserBadgeType],
null: true,
description: 'Badges describing the user within the organization.',
alpha: { milestone: '16.4' }
@ -29,7 +29,7 @@ module Types
alpha: { milestone: '16.4' }
def badges
user_badges_in_admin_section(organization_user.user).pluck(:text) # rubocop:disable CodeReuse/ActiveRecord
user_badges_in_admin_section(organization_user.user)
end
end
end

View File

@ -197,6 +197,11 @@ module Types
null: true,
description: 'Timestamp of when the user was created.'
field :last_activity_on,
type: Types::DateType,
null: true,
description: 'Date the user last performed any actions.'
field :pronouns,
type: ::GraphQL::Types::String,
null: true,

View File

@ -488,6 +488,7 @@ module ApplicationSettingsHelper
:sidekiq_job_limiter_compression_threshold_bytes,
:sidekiq_job_limiter_limit_bytes,
:suggest_pipeline_enabled,
:enable_artifact_external_redirect_warning_page,
:search_rate_limit,
:search_rate_limit_unauthenticated,
:search_rate_limit_allowlist_raw,

View File

@ -78,15 +78,11 @@ module NavHelper
%w[system_info background_migrations background_jobs health_check]
end
def show_super_sidebar?(user = current_user)
# The new sidebar is not enabled for anonymous use
return true unless user
# Users who get the new nav unless they explicitly
# opt-out via the toggle
return true if user.use_new_navigation.nil?
!!user.use_new_navigation
def show_super_sidebar?(_user = current_user)
# The new navigation is now enabled for everyone.
# We are working on cleaning up the use of this helper and other related code.
# See https://gitlab.com/groups/gitlab-org/-/epics/11875
true
end
private

View File

@ -263,21 +263,6 @@ module SortingHelper
sort_direction_button(url, reverse_sort, sort_value)
end
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
sort_value_oldest_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name,
sort_value_version_desc => sort_title_version,
sort_value_version_asc => sort_title_version,
sort_value_type_desc => sort_title_type,
sort_value_type_asc => sort_title_type,
sort_value_project_name_desc => sort_title_project_name,
sort_value_project_name_asc => sort_title_project_name
}
end
def packages_reverse_sort_order_hash
{
sort_value_recently_created => sort_value_oldest_created,

View File

@ -15,6 +15,7 @@ module Users
REGISTRATION_ENABLED_CALLOUT_ALLOWED_CONTROLLER_PATHS = [/^root/, /^dashboard\S*/, /^admin\S*/].freeze
WEB_HOOK_DISABLED = 'web_hook_disabled'
BRANCH_RULES_INFO_CALLOUT = 'branch_rules_info_callout'
NEW_NAV_FOR_EVERYONE_CALLOUT = 'new_nav_for_everyone_callout'
def show_gke_cluster_integration_callout?(project)
active_nav_link?(controller: sidebar_operations_paths) &&
@ -74,6 +75,14 @@ module Users
!user_dismissed?(BRANCH_RULES_INFO_CALLOUT)
end
def show_new_nav_for_everyone_callout?
# The use_new_navigation user preference was controlled by the now removed "New navigation" toggle in the UI.
# We want to show this banner only to signed-in users who chose to disable the new nav (`false`).
# We don't want to show it for users who never touched the toggle and already had the new nav by default (`nil`)
user_had_new_nav_off = current_user && current_user.use_new_navigation == false
user_had_new_nav_off && !user_dismissed?(NEW_NAV_FOR_EVERYONE_CALLOUT)
end
private
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil, object: nil)

View File

@ -57,6 +57,7 @@ module ApplicationSettingImplementation
default_artifacts_expire_in: '30 days',
default_branch_name: nil,
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_branch_protection_defaults: Settings.gitlab['default_branch_protection_defaults'],
default_ci_config_path: nil,
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_project_creation: Settings.gitlab['default_project_creation'],

View File

@ -9,7 +9,7 @@ module Ci
TEMPLATE_FILE = 'template.yml'
TEMPLATES_DIR = 'templates'
TEMPLATE_PATH_REGEX = '^templates\/\w+\-?\w+(?:\/template)?\.yml$'
TEMPLATE_PATH_REGEX = '^templates\/[\w-]+(?:\/template)?\.yml$'
COMPONENTS_LIMIT = 10
ComponentData = Struct.new(:content, :path, keyword_init: true)

View File

@ -13,7 +13,8 @@ module Ci
self.table_name = 'catalog_resources'
belongs_to :project
has_many :components, class_name: 'Ci::Catalog::Resources::Component', inverse_of: :catalog_resource
has_many :components, class_name: 'Ci::Catalog::Resources::Component', foreign_key: :catalog_resource_id,
inverse_of: :catalog_resource
has_many :versions, class_name: 'Ci::Catalog::Resources::Version', inverse_of: :catalog_resource
scope :for_projects, ->(project_ids) { where(project_id: project_ids) }
@ -44,6 +45,10 @@ module Ci
update!(state: :draft)
end
def publish!
update!(state: :published)
end
def sync_with_project!
sync_with_project
save!

View File

@ -6,6 +6,8 @@ module Ci
# This class represents a CI/CD Catalog resource component.
# The data will be used as metadata of a component.
class Component < ::ApplicationRecord
include BulkInsertSafe
self.table_name = 'catalog_resource_components'
belongs_to :project, inverse_of: :ci_components

View File

@ -6,6 +6,8 @@ module Ci
# This class represents a CI/CD Catalog resource version.
# Only versions which contain valid CI components are included in this table.
class Version < ::ApplicationRecord
include BulkInsertableAssociations
self.table_name = 'catalog_resource_versions'
belongs_to :release, inverse_of: :catalog_resource_version

View File

@ -15,7 +15,7 @@ class Deployment < ApplicationRecord
ARCHIVABLE_OFFSET = 50_000
ignore_column :cluster_id, remove_with: '16.8', remove_after: '2023-12-22'
ignore_column :cluster_id, remove_with: '16.8', remove_after: '2023-12-21'
belongs_to :project, optional: false
belongs_to :environment, optional: false

View File

@ -594,40 +594,13 @@ class Group < Namespace
end
def authorizable_members_with_parents
source_ids =
if has_parent?
self_and_ancestors.reorder(nil).select(:id)
else
id
end
group_hierarchy_members = GroupMember.where(source_id: source_ids).select(*GroupMember.cached_column_list)
GroupMember.from_union([group_hierarchy_members,
members_from_self_and_ancestor_group_shares]).authorizable
Members::MembersWithParents.new(self).all_members.authorizable
end
def members_with_parents(only_active_users: true)
# Avoids an unnecessary SELECT when the group has no parents
source_ids =
if has_parent?
self_and_ancestors.reorder(nil).select(:id)
else
id
end
group_hierarchy_members = GroupMember.non_minimal_access
.where(source_id: source_ids)
.select(*GroupMember.cached_column_list)
group_hierarchy_members = if only_active_users
group_hierarchy_members.active_without_invites_and_requests
else
group_hierarchy_members.without_invites_and_requests
end
GroupMember.from_union([group_hierarchy_members,
members_from_self_and_ancestor_group_shares])
Members::MembersWithParents
.new(self)
.members(active_users: only_active_users)
end
def members_from_self_and_ancestors_with_effective_access_level
@ -988,48 +961,6 @@ class Group < Namespace
errors.add(:require_two_factor_authentication, _('is forbidden by a top-level group'))
end
def members_from_self_and_ancestor_group_shares
group_group_link_table = GroupGroupLink.arel_table
group_member_table = GroupMember.arel_table
source_ids =
if has_parent?
self_and_ancestors.reorder(nil).select(:id)
else
id
end
group_group_links_query = GroupGroupLink.where(shared_group_id: source_ids)
cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query)
cte_alias = cte.table.alias(GroupGroupLink.table_name)
# Instead of members.access_level, we need to maximize that access_level at
# the respective group_group_links.group_access.
member_columns = GroupMember.attribute_names.map do |column_name|
if column_name == 'access_level'
smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]], 'access_level')
else
group_member_table[column_name]
end
end
GroupMember
.with(cte.to_arel)
.select(*member_columns)
.from([group_member_table, cte.alias_to(group_group_link_table)])
.where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
.where(group_member_table[:source_type].eq('Namespace'))
.where(group_member_table[:state].eq(::Member::STATE_ACTIVE))
.non_minimal_access
end
def smallest_value_arel(args, column_alias)
Arel::Nodes::As.new(
Arel::Nodes::NamedFunction.new('LEAST', args),
Arel::Nodes::SqlLiteral.new(column_alias))
end
def runners_token_prefix
RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX
end

View File

@ -135,11 +135,12 @@ class Member < ApplicationRecord
.reorder(nil)
end
scope :without_invites_and_requests, -> do
active_state
.non_request
.non_invite
.non_minimal_access
scope :without_invites_and_requests, ->(minimal_access: false) do
result = active_state.non_request.non_invite
result = result.non_minimal_access unless minimal_access
result
end
scope :invite, -> { where.not(invite_token: nil) }

View File

@ -0,0 +1,105 @@
# frozen_string_literal: true
module Members
class MembersWithParents
attr_reader :group
def initialize(group)
@group = group
end
# Returns all members for group and parents, with no filters
def all_members
GroupMember.from_union([
members_from_self_and_ancestors,
members_from_self_and_ancestor_group_shares
])
end
# Returns members based on filter options:
#
# - `active_users`. DEPRECATED. If true, returns only members for active users
# - `minimal_access`. Used only in EE (GitLab Premium). If true, returns
# members which has minimal access. If false (default), does not return
# members with minimal access
#
# NOTE : this method does not return pending invites, nor requests.
def members(active_users: false, minimal_access: false)
raise ArgumentError, 'active_users: is deprecated' if active_users && minimal_access
group_hierarchy_members = members_from_self_and_ancestors
group_hierarchy_members =
if active_users
group_hierarchy_members.active_without_invites_and_requests
else
filter_invites_and_requests(group_hierarchy_members, minimal_access)
end
GroupMember.from_union([
group_hierarchy_members,
members_from_self_and_ancestor_group_shares
])
end
private
# NOTE: minimal access is Premium, so in FOSS we will not include minimal access member
def filter_invites_and_requests(members, _minimal_access)
members.without_invites_and_requests(minimal_access: false)
end
def source_ids
# Avoids an unnecessary SELECT when the group has no parents
@source_ids ||=
if group.has_parent?
group.self_and_ancestors.reorder(nil).select(:id)
else
group.id
end
end
def members_from_self_and_ancestors
GroupMember
.with_source_id(source_ids)
.select(*GroupMember.cached_column_list)
end
def members_from_self_and_ancestor_group_shares
group_group_link_table = GroupGroupLink.arel_table
group_member_table = GroupMember.arel_table
group_group_links_query = GroupGroupLink.where(shared_group_id: source_ids)
cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query)
cte_alias = cte.table.alias(GroupGroupLink.table_name)
# Instead of members.access_level, we need to maximize that access_level at
# the respective group_group_links.group_access.
member_columns = GroupMember.attribute_names.map do |column_name|
if column_name == 'access_level'
smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]], 'access_level')
else
group_member_table[column_name]
end
end
GroupMember
.with(cte.to_arel)
.select(*member_columns)
.from([group_member_table, cte.alias_to(group_group_link_table)])
.where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
.where(group_member_table[:source_type].eq('Namespace'))
.where(group_member_table[:state].eq(::Member::STATE_ACTIVE))
.non_minimal_access
end
def smallest_value_arel(args, column_alias)
Arel::Nodes::As.new(
Arel::Nodes::NamedFunction.new('LEAST', args),
Arel::Nodes::SqlLiteral.new(column_alias))
end
end
end
Members::MembersWithParents.prepend_mod

View File

@ -301,6 +301,14 @@ class Namespace < ApplicationRecord
super || Gitlab::CurrentSettings.default_branch_protection
end
def default_branch_protection_settings
settings = default_branch_protection_defaults
return settings unless settings.blank?
Gitlab::CurrentSettings.default_branch_protection_defaults
end
def visibility_level_field
:visibility_level
end

View File

@ -19,19 +19,6 @@ class ProjectFeatureUsage < ApplicationRecord
end
end
def log_jira_dvcs_integration_usage(cloud: true)
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
integration_field = self.class.jira_dvcs_integration_field(cloud: cloud)
# The feature usage is used only once later to query the feature usage in a
# long date range. Therefore, we just need to update the timestamp once per
# day
break if persisted? && updated_today?(integration_field)
persist_jira_dvcs_usage(integration_field)
end
end
private
def updated_today?(integration_field)

View File

@ -76,7 +76,8 @@ module Users
# 74 removed in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132751
vsd_feedback_banner: 75, # EE-only
security_policy_protected_branch_modification: 76, # EE-only
vulnerability_report_grouping: 77 # EE-only
vulnerability_report_grouping: 77, # EE-only
new_nav_for_everyone_callout: 78
}
validates :feature_name,

View File

@ -436,6 +436,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
count_of_extra_topics_not_shown > 0
end
def has_review_app?
!project.environments_for_scope('review/*').empty?
end
def can_setup_review_app?
strong_memoize(:can_setup_review_app) do
(can_instantiate_cluster? && all_clusters_empty?) || cicd_missing?

View File

@ -13,6 +13,8 @@ class ReviewAppSetupEntity < Grape::Entity
YAML.safe_load(File.read(Rails.root.join('lib', 'gitlab', 'ci', 'snippets', 'review_app_default.yml'))).to_s
end
expose :has_review_app?, as: :has_review_app
private
def current_user

View File

@ -0,0 +1,111 @@
# frozen_string_literal: true
module Ci
module Catalog
module Resources
module Versions
class CreateService
def initialize(release)
@project = release.project
@release = release
@errors = []
@version = nil
@components_project = Ci::Catalog::ComponentsProject.new(project)
end
def execute
build_catalog_resource_version
fetch_and_build_components if Feature.enabled?(:ci_catalog_create_metadata, project)
publish_catalog_resource!
if errors.empty?
ServiceResponse.success
else
ServiceResponse.error(message: errors.flatten.first(10).join(', '))
end
end
private
attr_reader :project, :errors, :release, :components_project
def build_catalog_resource_version
return error('Project is not a catalog resource') unless project.catalog_resource
@version = Ci::Catalog::Resources::Version.new(
release: release,
catalog_resource: project.catalog_resource,
project: project
)
end
def fetch_and_build_components
return if errors.present?
max_components = Ci::Catalog::ComponentsProject::COMPONENTS_LIMIT
component_paths = components_project.fetch_component_paths(release.sha, limit: max_components + 1)
if component_paths.size > max_components
return error("Release cannot contain more than #{max_components} components")
end
build_components(component_paths)
end
def build_components(component_paths)
paths_with_oids = component_paths.map { |path| [release.sha, path] }
blobs = project.repository.blobs_at(paths_with_oids)
blobs.each do |blob|
metadata = extract_metadata(blob)
build_catalog_resource_component(metadata)
end
rescue ::Gitlab::Config::Loader::FormatError => e
error(e)
end
def extract_metadata(blob)
{
name: components_project.extract_component_name(blob.path),
inputs: components_project.extract_inputs(blob.data),
path: blob.path
}
end
def build_catalog_resource_component(metadata)
return if errors.present?
component = @version.components.build(
name: metadata[:name],
project: @version.project,
inputs: metadata[:inputs],
catalog_resource: @version.catalog_resource,
path: metadata[:path],
created_at: Time.current
)
return if component.valid?
error("Build component error: #{component.errors.full_messages.join(', ')}")
end
def publish_catalog_resource!
return if errors.present?
::Ci::Catalog::Resources::Version.transaction do
BulkInsertableAssociations.with_bulk_insert do
@version.save!
end
project.catalog_resource.publish!
end
end
def error(message)
errors << message
end
end
end
end
end
end

View File

@ -59,6 +59,8 @@
.form-group
= f.gitlab_ui_checkbox_component :suggest_pipeline_enabled, s_('AdminSettings|Enable pipeline suggestion banner'), help_text: s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.')
#js-runner-token-expiration-intervals{ data: runner_token_expiration_interval_attributes }
.form-group
= f.gitlab_ui_checkbox_component :enable_artifact_external_redirect_warning_page, s_('AdminSettings|Enable the external redirect warning page for job artifacts'), help_text: s_('AdminSettings|Show a redirect page that warns you about user-generated content in GitLab Pages.')
= f.submit _('Save changes'), pajamas_button: true

View File

@ -7,7 +7,7 @@
- sidebar_panel = super_sidebar_nav_panel(nav: nav, user: current_user, group: group, project: @project, current_ref: current_ref, ref_type: @ref_type, viewed_user: @user, organization: @organization)
- sidebar_data = super_sidebar_context(current_user, group: group, project: @project, panel: sidebar_panel, panel_type: nav).to_json
%aside.js-super-sidebar.super-sidebar.super-sidebar-loading{ data: { root_path: root_path, sidebar: sidebar_data, toggle_new_nav_endpoint: profile_preferences_path, force_desktop_expanded_sidebar: @force_desktop_expanded_sidebar.to_s, command_palette: command_palette_data(project: @project).to_json } }
%aside.js-super-sidebar.super-sidebar.super-sidebar-loading{ data: { root_path: root_path, sidebar: sidebar_data, force_desktop_expanded_sidebar: @force_desktop_expanded_sidebar.to_s, command_palette: command_palette_data(project: @project).to_json } }
- if display_whats_new?
#whats-new-app{ data: { version_digest: whats_new_version_digest } }
@ -20,6 +20,7 @@
.mobile-overlay
= dispensable_render_if_exists 'layouts/header/verification_reminder'
.alert-wrapper.gl-force-block-formatting-context
= dispensable_render 'shared/new_nav_for_everyone_announcement'
= dispensable_render 'shared/outdated_browser'
= dispensable_render_if_exists "layouts/header/licensed_user_count_threshold"
= dispensable_render_if_exists "layouts/header/token_expiry_notification"

View File

@ -1,6 +1,6 @@
- page_classes = page_class << @html_class
- page_classes = page_classes.flatten.compact
- body_classes = [user_application_theme, user_tab_width, @body_class, client_class_list, *custom_diff_color_classes]
- page_classes = [user_application_theme, page_classes.flatten.compact]
- body_classes = [user_tab_width, @body_class, client_class_list, *custom_diff_color_classes]
!!! 5
%html{ lang: I18n.locale, class: page_classes }

View File

@ -1,9 +1,9 @@
- add_page_specific_style 'page_bundles/login'
- custom_text = custom_sign_in_description
!!! 5
%html.html-devise-layout{ lang: I18n.locale }
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
= render "layouts/head", { startup_filename: 'signin' }
%body.gl-h-full.login-page.navless{ class: "#{system_message_class} #{user_application_theme} #{client_class_list}", data: { page: body_data_page, qa_selector: 'login_page' } }
%body.gl-h-full.login-page.navless{ class: "#{system_message_class} #{client_class_list}", data: { page: body_data_page, qa_selector: 'login_page' } }
= header_message
= render "layouts/init_client_detection_flags"
- if Feature.enabled?(:restyle_login_page, @project)

View File

@ -1,8 +1,8 @@
- add_page_specific_style 'page_bundles/login'
!!! 5
%html.html-devise-layout{ lang: I18n.locale }
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
= render "layouts/head"
%body.gl-h-full.login-page.navless{ class: "#{system_message_class} #{user_application_theme} #{client_class_list}" }
%body.gl-h-full.login-page.navless{ class: "#{system_message_class} #{client_class_list}" }
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/empty"

View File

@ -1,8 +1,8 @@
- minimal = local_assigns.fetch(:minimal, false)
!!! 5
%html{ lang: I18n.locale, class: page_class }
%html{ class: [user_application_theme, page_class], lang: I18n.locale }
= render "layouts/head"
%body{ class: "#{user_application_theme} #{user_tab_width} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
%body{ class: "#{user_tab_width} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
= render 'peek/bar'
= header_message
- unless minimal

View File

@ -1,9 +1,9 @@
- add_page_specific_style 'page_bundles/signup'
- add_page_specific_style 'page_bundles/login'
!!! 5
%html.html-devise-layout{ lang: I18n.locale }
%html.html-devise-layout{ class: user_application_theme, lang: I18n.locale }
= render "layouts/head"
%body.signup-page.navless{ class: "#{system_message_class} #{user_application_theme} #{client_class_list}", data: { page: body_data_page } }
%body.signup-page.navless{ class: "#{system_message_class} #{client_class_list}", data: { page: body_data_page } }
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/logo_with_title"

View File

@ -2,11 +2,10 @@
- add_page_specific_style 'page_bundles/terms'
- @hide_top_bar = true
- @hide_top_bar_padding = true
- body_classes = [user_application_theme]
%html{ lang: I18n.locale, class: page_class }
%html{ lang: I18n.locale, class: [user_application_theme, page_class] }
= render "layouts/head"
%body{ class: body_classes, data: { page: body_data_page } }
%body{ data: { page: body_data_page } }
.layout-page.terms{ class: page_class }
.content-wrapper.gl-pb-5
.mobile-overlay

View File

@ -1,6 +1,6 @@
- blob = file.blob
- external_link = blob.external_link?(@build)
- if external_link
- if external_link && Gitlab::CurrentSettings.enable_artifact_external_redirect_warning_page
- path_to_file = external_file_project_job_artifacts_path(@project, @build, path: file.path)
- else
- path_to_file = file_project_job_artifacts_path(@project, @build, path: file.path)

View File

@ -0,0 +1,18 @@
- return unless show_new_nav_for_everyone_callout?
- blog_url = 'https://about.gitlab.com/blog/2023/08/15/navigation-research-blog-post/'
- issues_url = 'https://about.gitlab.com/submit-feedback/#product-feedback'
- blog_link_tags = tag_pair(link_to('', blog_url, rel: 'noopener noreferrer', target: '_blank'), :blog_link_start, :link_end)
- issues_link_tags = tag_pair(link_to('', issues_url, rel: 'noopener noreferrer', target: '_blank'), :issues_link_start, :link_end)
- welcome_text = safe_format(_('GitLab has redesigned the left sidebar to address customer feedback. View details in %{blog_link_start}this blog post%{link_end}. Here\'s how to %{issues_link_start}file an issue%{link_end} with the GitLab product team.'), blog_link_tags, issues_link_tags)
= render Pajamas::AlertComponent.new(dismissible: true,
alert_options: { class: 'js-new-nav-for-everyone-callout', data: { feature_id: "new_nav_for_everyone_callout", dismiss_endpoint: callouts_path }}) do |c|
- c.with_body do
%p
= welcome_text
- c.with_actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: blog_url, target: '_blank', button_options: { class: 'gl-alert-action' }) do |c|
= _('Learn more')

View File

@ -36,7 +36,12 @@ module BulkImports
def perform_failure(exception, entity_id)
@entity = ::BulkImports::Entity.find(entity_id)
log_and_fail(exception)
Gitlab::ErrorTracking.track_exception(
exception,
log_params(message: "Request to export #{entity.source_type} failed")
)
entity.fail_op!
end
private
@ -99,14 +104,5 @@ module BulkImports
defaults.merge(extra)
end
def log_and_fail(exception)
Gitlab::ErrorTracking.track_exception(
exception,
log_params(message: "Request to export #{entity.source_type} failed")
)
entity.fail_op!
end
end
end

View File

@ -93,6 +93,7 @@
- organization
- package_registry
- pages
- permissions
- pipeline_composition
- portfolio_management
- product_analytics_data_management

View File

@ -0,0 +1,8 @@
---
name: ci_catalog_create_metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134148
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/430120
milestone: '16.6'
type: development
group: group::pipeline authoring
default_enabled: false

View File

@ -1,7 +1,7 @@
---
name: manage_project_access_tokens
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342
rollout_issue_url:
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/430353
milestone: '16.5'
type: development
group: group::authorization

View File

@ -183,6 +183,7 @@ Settings.gitlab['default_project_creation'] ||= ::Gitlab::Access::DEVELOPER_MAIN
Settings.gitlab['default_project_deletion_protection'] ||= false
Settings.gitlab['default_projects_limit'] ||= 100000
Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_branch_protection_defaults'] ||= ::Gitlab::Access::BranchProtection.protected_fully
# `default_can_create_group` is deprecated since GitLab 15.5 in favour of the `can_create_group` column on `ApplicationSetting`.
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?

Some files were not shown because too many files have changed in this diff Show More