Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
dfa6eac075
commit
d4fcd1794e
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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}";
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
14.29.0
|
||||
14.30.0
|
||||
|
|
|
|||
4
Gemfile
4
Gemfile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ query getOrganizationUsers($id: OrganizationsOrganizationID!) {
|
|||
id
|
||||
organizationUsers {
|
||||
nodes {
|
||||
badges
|
||||
badges {
|
||||
text
|
||||
variant
|
||||
}
|
||||
id
|
||||
user {
|
||||
id
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const initAccessDropdown = (el, options) => {
|
|||
data() {
|
||||
return { preselected };
|
||||
},
|
||||
disabled,
|
||||
methods: {
|
||||
setPreselectedItems(items) {
|
||||
this.preselected = items;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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' },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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')"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-blue {
|
||||
@include gitlab-theme(
|
||||
$theme-blue-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-gray {
|
||||
@include gitlab-theme(
|
||||
$gray-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-green {
|
||||
@include gitlab-theme(
|
||||
$theme-green-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-indigo {
|
||||
@include gitlab-theme(
|
||||
$indigo-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-light-blue {
|
||||
@include gitlab-theme(
|
||||
$theme-light-blue-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-light-gray {
|
||||
@include gitlab-theme(
|
||||
$gray-500,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-light-green {
|
||||
@include gitlab-theme(
|
||||
$theme-green-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-light-indigo {
|
||||
@include gitlab-theme(
|
||||
$indigo-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-light-red {
|
||||
@include gitlab-theme(
|
||||
$theme-light-red-200,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import './theme_helper';
|
||||
|
||||
body {
|
||||
:root {
|
||||
&.ui-red {
|
||||
@include gitlab-theme(
|
||||
$theme-red-200,
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
- organization
|
||||
- package_registry
|
||||
- pages
|
||||
- permissions
|
||||
- pipeline_composition
|
||||
- portfolio_management
|
||||
- product_analytics_data_management
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue