Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-23 21:09:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-23 21:09:48 +0300
commit8ca26a8db290ec5eca22f8eecbd0b25d106cb89f (patch)
treed3d6ac96070f0bd9cb2fc71ee49cc2dffd394300 /app
parent61cb988554d7d554f0e9727fc73acc9acba5ea8f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/ci/job_details/components/sidebar/sidebar_job_details_container.vue38
-rw-r--r--app/assets/javascripts/ci/job_details/index.js5
-rw-r--r--app/assets/javascripts/ci/job_details/store/actions.js24
-rw-r--r--app/assets/javascripts/ci/job_details/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/ci/job_details/store/mutations.js7
-rw-r--r--app/assets/javascripts/ci/job_details/store/state.js3
-rw-r--r--app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue15
-rw-r--r--app/assets/javascripts/environments/components/kubernetes_pods.vue38
-rw-r--r--app/assets/javascripts/environments/constants.js5
-rw-r--r--app/assets/javascripts/environments/graphql/resolvers/kubernetes.js65
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue4
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/components/workload_stats.vue27
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/constants.js13
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/graphql/client.js25
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js68
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/graphql/queries/k8s_dashboard_pods.query.graphql7
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/graphql/resolvers.js7
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/graphql/resolvers/kubernetes.js9
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/init_kubernetes_dashboard.js23
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/pages/app.vue1
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/pages/pods_page.vue81
-rw-r--r--app/assets/javascripts/kubernetes_dashboard/router/routes.js2
-rw-r--r--app/assets/javascripts/repository/mixins/highlight_mixin.js4
-rw-r--r--app/assets/javascripts/super_sidebar/components/help_center.vue5
-rw-r--r--app/finders/autocomplete/group_users_finder.rb15
-rw-r--r--app/finders/autocomplete/users_finder.rb50
-rw-r--r--app/helpers/ci/jobs_helper.rb4
-rw-r--r--app/models/ci/build_need.rb3
-rw-r--r--app/models/ci/build_pending_state.rb3
-rw-r--r--app/models/ci/build_report_result.rb3
-rw-r--r--app/models/ci/build_runner_session.rb3
-rw-r--r--app/models/ci/build_trace_chunk.rb3
-rw-r--r--app/models/ci/build_trace_metadata.rb3
-rw-r--r--app/models/ci/job_artifact.rb3
-rw-r--r--app/models/ci/job_variable.rb3
-rw-r--r--app/models/ci/pending_build.rb3
-rw-r--r--app/models/ci/pipeline.rb3
-rw-r--r--app/models/ci/pipeline_variable.rb2
-rw-r--r--app/models/ci/running_build.rb3
-rw-r--r--app/models/ci/sources/pipeline.rb3
-rw-r--r--app/models/ci/stage.rb3
-rw-r--r--app/models/ci/unit_test_failure.rb3
-rw-r--r--app/models/repository.rb11
-rw-r--r--app/models/user.rb2
-rw-r--r--app/views/clusters/agents/dashboard/show.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml13
46 files changed, 452 insertions, 168 deletions
diff --git a/app/assets/javascripts/ci/job_details/components/sidebar/sidebar_job_details_container.vue b/app/assets/javascripts/ci/job_details/components/sidebar/sidebar_job_details_container.vue
index f04987a87b5..a8b29e7c581 100644
--- a/app/assets/javascripts/ci/job_details/components/sidebar/sidebar_job_details_container.vue
+++ b/app/assets/javascripts/ci/job_details/components/sidebar/sidebar_job_details_container.vue
@@ -4,6 +4,7 @@ import { mapState } from 'vuex';
import { GlBadge } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import DetailRow from './sidebar_detail_row.vue';
@@ -15,8 +16,9 @@ export default {
GlBadge,
},
mixins: [timeagoMixin],
+ inject: ['pipelineTestReportUrl'],
computed: {
- ...mapState(['job']),
+ ...mapState(['job', 'testSummary']),
coverage() {
return `${this.job.coverage}%`;
},
@@ -74,6 +76,32 @@ export default {
runnerAdminPath() {
return this.job?.runner?.admin_path || '';
},
+ hasTestSummaryDetails() {
+ return Object.keys(this.testSummary).length > 0;
+ },
+ testSummaryDescription() {
+ let message;
+
+ if (this.testSummary?.total?.failed > 0) {
+ message = sprintf(__('%{failures} of %{total} failed'), {
+ failures: this.testSummary?.total?.failed,
+ total: this.testSummary?.total.count,
+ });
+ } else {
+ message = sprintf(__('%{total}'), {
+ total: this.testSummary?.total.count,
+ });
+ }
+
+ return message;
+ },
+ testReportUrlWithJobName() {
+ const urlParams = {
+ job_name: this.job.name,
+ };
+
+ return mergeUrlParams(urlParams, this.pipelineTestReportUrl);
+ },
},
i18n: {
COVERAGE: __('Coverage'),
@@ -82,6 +110,7 @@ export default {
QUEUED: __('Queued'),
RUNNER: __('Runner'),
TAGS: __('Tags'),
+ TEST_SUMMARY: __('Test summary'),
TIMEOUT: __('Timeout'),
},
TIMEOUT_HELP_URL: helpPagePath('/ci/pipelines/settings.md', {
@@ -115,6 +144,13 @@ export default {
:path="runnerAdminPath"
/>
<detail-row v-if="job.coverage" :value="coverage" :title="$options.i18n.COVERAGE" />
+ <detail-row
+ v-if="hasTestSummaryDetails"
+ :value="testSummaryDescription"
+ :title="$options.i18n.TEST_SUMMARY"
+ :path="testReportUrlWithJobName"
+ data-testid="test-summary"
+ />
<p v-if="hasTags" class="build-detail-row" data-testid="job-tags">
<span class="font-weight-bold">{{ $options.i18n.TAGS }}:</span>
diff --git a/app/assets/javascripts/ci/job_details/index.js b/app/assets/javascripts/ci/job_details/index.js
index 20235015ce6..dee77d404a8 100644
--- a/app/assets/javascripts/ci/job_details/index.js
+++ b/app/assets/javascripts/ci/job_details/index.js
@@ -30,11 +30,13 @@ export const initJobDetails = () => {
projectPath,
retryOutdatedJobDocsUrl,
aiRootCauseAnalysisAvailable,
+ testReportSummaryUrl,
+ pipelineTestReportUrl,
} = el.dataset;
// init store to start fetching log
const store = createStore();
- store.dispatch('init', { endpoint, pagePath });
+ store.dispatch('init', { endpoint, pagePath, testReportSummaryUrl });
return new Vue({
el,
@@ -44,6 +46,7 @@ export const initJobDetails = () => {
projectPath,
retryOutdatedJobDocsUrl,
aiRootCauseAnalysisAvailable: parseBoolean(aiRootCauseAnalysisAvailable),
+ pipelineTestReportUrl,
},
render(h) {
return h(JobApp, {
diff --git a/app/assets/javascripts/ci/job_details/store/actions.js b/app/assets/javascripts/ci/job_details/store/actions.js
index 6f538e3b3d4..adcba0f2409 100644
--- a/app/assets/javascripts/ci/job_details/store/actions.js
+++ b/app/assets/javascripts/ci/job_details/store/actions.js
@@ -15,10 +15,11 @@ import { __ } from '~/locale';
import { reportToSentry } from '~/ci/utils';
import * as types from './mutation_types';
-export const init = ({ dispatch }, { endpoint, pagePath }) => {
+export const init = ({ dispatch }, { endpoint, pagePath, testReportSummaryUrl }) => {
dispatch('setJobLogOptions', {
endpoint,
pagePath,
+ testReportSummaryUrl,
});
return dispatch('fetchJob');
@@ -170,6 +171,7 @@ export const fetchJobLog = ({ dispatch, state }) =>
if (data.complete) {
dispatch('stopPollingJobLog');
+ dispatch('requestTestSummary');
} else if (!state.jobLogTimeout) {
dispatch('startPollingJobLog');
}
@@ -273,3 +275,23 @@ export const triggerManualJob = ({ state }, variables) => {
}),
);
};
+
+export const requestTestSummary = ({ state, commit, dispatch }) => {
+ if (!state.testSummaryComplete) {
+ axios
+ .get(state.testReportSummaryUrl)
+ .then(({ data }) => {
+ dispatch('receiveTestSummarySuccess', data);
+ })
+ .catch((e) => {
+ reportToSentry('job_test_summary_report', e);
+ })
+ .finally(() => {
+ commit(types.RECEIVE_TEST_SUMMARY_COMPLETE);
+ });
+ }
+};
+
+export const receiveTestSummarySuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_TEST_SUMMARY_SUCCESS, data);
+};
diff --git a/app/assets/javascripts/ci/job_details/store/mutation_types.js b/app/assets/javascripts/ci/job_details/store/mutation_types.js
index e125538317d..86ebbe57714 100644
--- a/app/assets/javascripts/ci/job_details/store/mutation_types.js
+++ b/app/assets/javascripts/ci/job_details/store/mutation_types.js
@@ -28,3 +28,6 @@ export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
export const REQUEST_JOBS_FOR_STAGE = 'REQUEST_JOBS_FOR_STAGE';
export const RECEIVE_JOBS_FOR_STAGE_SUCCESS = 'RECEIVE_JOBS_FOR_STAGE_SUCCESS';
export const RECEIVE_JOBS_FOR_STAGE_ERROR = 'RECEIVE_JOBS_FOR_STAGE_ERROR';
+
+export const RECEIVE_TEST_SUMMARY_SUCCESS = 'RECEIVE_TEST_SUMMARY_SUCCESS';
+export const RECEIVE_TEST_SUMMARY_COMPLETE = 'RECEIVE_TEST_SUMMARY_COMPLETE';
diff --git a/app/assets/javascripts/ci/job_details/store/mutations.js b/app/assets/javascripts/ci/job_details/store/mutations.js
index fe6506bf8a5..ad0603c050e 100644
--- a/app/assets/javascripts/ci/job_details/store/mutations.js
+++ b/app/assets/javascripts/ci/job_details/store/mutations.js
@@ -6,6 +6,7 @@ export default {
[types.SET_JOB_LOG_OPTIONS](state, options = {}) {
state.jobLogEndpoint = options.pagePath;
state.jobEndpoint = options.endpoint;
+ state.testReportSummaryUrl = options.testReportSummaryUrl;
},
[types.HIDE_SIDEBAR](state) {
@@ -127,4 +128,10 @@ export default {
state.isLoadingJobs = false;
state.jobs = [];
},
+ [types.RECEIVE_TEST_SUMMARY_SUCCESS](state, testSummary) {
+ state.testSummary = testSummary;
+ },
+ [types.RECEIVE_TEST_SUMMARY_COMPLETE](state) {
+ state.testSummaryComplete = true;
+ },
};
diff --git a/app/assets/javascripts/ci/job_details/store/state.js b/app/assets/javascripts/ci/job_details/store/state.js
index dfff65c364d..aa4497d0e37 100644
--- a/app/assets/javascripts/ci/job_details/store/state.js
+++ b/app/assets/javascripts/ci/job_details/store/state.js
@@ -1,9 +1,12 @@
export default () => ({
jobEndpoint: null,
jobLogEndpoint: null,
+ testReportSummaryUrl: null,
// sidebar
isSidebarOpen: true,
+ testSummary: {},
+ testSummaryComplete: false,
isLoading: false,
hasError: false,
diff --git a/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue b/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
index a7737d33285..6e9a705c046 100644
--- a/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
+++ b/app/assets/javascripts/ci/pipeline_details/test_reports/test_reports.vue
@@ -2,6 +2,7 @@
import { GlLoadingIcon } from '@gitlab/ui';
// eslint-disable-next-line no-restricted-imports
import { mapActions, mapGetters, mapState } from 'vuex';
+import { getParameterValues } from '~/lib/utils/url_utility';
import EmptyState from './empty_state.vue';
import TestSuiteTable from './test_suite_table.vue';
import TestSummary from './test_summary.vue';
@@ -19,7 +20,7 @@ export default {
inject: ['blobPath', 'summaryEndpoint', 'suiteEndpoint'],
computed: {
...mapState('testReports', ['isLoading', 'selectedSuiteIndex', 'testReports']),
- ...mapGetters('testReports', ['getSelectedSuite']),
+ ...mapGetters('testReports', ['getSelectedSuite', 'getTestSuites']),
showSuite() {
return this.selectedSuiteIndex !== null;
},
@@ -28,8 +29,16 @@ export default {
return testSuites.length > 0;
},
},
- created() {
- this.fetchSummary();
+ async created() {
+ await this.fetchSummary();
+ const jobName = getParameterValues('job_name')[0] || '';
+ if (jobName.length > 0) {
+ // get the index from the job name
+ const indexToSelect = this.getTestSuites.findIndex((test) => test.name === jobName);
+
+ this.setSelectedSuiteIndex(indexToSelect);
+ this.fetchTestSuite(indexToSelect);
+ }
},
methods: {
...mapActions('testReports', [
diff --git a/app/assets/javascripts/environments/components/kubernetes_pods.vue b/app/assets/javascripts/environments/components/kubernetes_pods.vue
index 3f040f1f40a..743159d6256 100644
--- a/app/assets/javascripts/environments/components/kubernetes_pods.vue
+++ b/app/assets/javascripts/environments/components/kubernetes_pods.vue
@@ -1,14 +1,20 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { s__ } from '~/locale';
+import {
+ PHASE_RUNNING,
+ PHASE_PENDING,
+ PHASE_SUCCEEDED,
+ PHASE_FAILED,
+ STATUS_LABELS,
+} from '~/kubernetes_dashboard/constants';
+import WorkloadStats from '~/kubernetes_dashboard/components/workload_stats.vue';
import k8sPodsQuery from '../graphql/queries/k8s_pods.query.graphql';
-import { PHASE_RUNNING, PHASE_PENDING, PHASE_SUCCEEDED, PHASE_FAILED } from '../constants';
export default {
components: {
GlLoadingIcon,
- GlSingleStat,
+ WorkloadStats,
},
apollo: {
k8sPods: {
@@ -53,19 +59,19 @@ export default {
return [
{
value: this.countPodsByPhase(PHASE_RUNNING),
- title: this.$options.i18n.runningPods,
+ title: STATUS_LABELS[PHASE_RUNNING],
},
{
value: this.countPodsByPhase(PHASE_PENDING),
- title: this.$options.i18n.pendingPods,
+ title: STATUS_LABELS[PHASE_PENDING],
},
{
value: this.countPodsByPhase(PHASE_SUCCEEDED),
- title: this.$options.i18n.succeededPods,
+ title: STATUS_LABELS[PHASE_SUCCEEDED],
},
{
value: this.countPodsByPhase(PHASE_FAILED),
- title: this.$options.i18n.failedPods,
+ title: STATUS_LABELS[PHASE_FAILED],
},
];
},
@@ -84,10 +90,6 @@ export default {
},
i18n: {
podsTitle: s__('Environment|Pods'),
- runningPods: s__('Environment|Running'),
- pendingPods: s__('Environment|Pending'),
- succeededPods: s__('Environment|Succeeded'),
- failedPods: s__('Environment|Failed'),
},
};
</script>
@@ -96,18 +98,6 @@ export default {
<p class="gl-text-gray-500">{{ $options.i18n.podsTitle }}</p>
<gl-loading-icon v-if="loading" />
-
- <div
- v-else-if="podStats && !error"
- class="gl-display-flex gl-flex-wrap gl-sm-flex-nowrap gl-mx-n3 gl-mt-n3"
- >
- <gl-single-stat
- v-for="(stat, index) in podStats"
- :key="index"
- class="gl-w-full gl-flex-direction-column gl-align-items-center gl-justify-content-center gl-bg-white gl-border gl-border-gray-a-08 gl-mx-3 gl-p-3 gl-mt-3"
- :value="stat.value"
- :title="stat.title"
- />
- </div>
+ <workload-stats v-else-if="podStats && !error" :stats="podStats" />
</div>
</template>
diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js
index e97720312b0..4f0eabb26a4 100644
--- a/app/assets/javascripts/environments/constants.js
+++ b/app/assets/javascripts/environments/constants.js
@@ -155,11 +155,6 @@ export const SYNC_STATUS_BADGES = {
export const STATUS_TRUE = 'True';
export const STATUS_FALSE = 'False';
-export const PHASE_RUNNING = 'Running';
-export const PHASE_PENDING = 'Pending';
-export const PHASE_SUCCEEDED = 'Succeeded';
-export const PHASE_FAILED = 'Failed';
-
const ERROR_UNAUTHORIZED = 'unauthorized';
const ERROR_FORBIDDEN = 'forbidden';
const ERROR_NOT_FOUND = 'not found';
diff --git a/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js b/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js
index 1bc8706cfce..06a55158152 100644
--- a/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js
+++ b/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js
@@ -7,6 +7,10 @@ import {
EVENT_DATA,
} from '@gitlab/cluster-client';
import produce from 'immer';
+import {
+ getK8sPods,
+ handleClusterError,
+} from '~/kubernetes_dashboard/graphql/helpers/resolver_helpers';
import { humanizeClusterErrors } from '../../helpers/k8s_integration_helper';
import k8sPodsQuery from '../queries/k8s_pods.query.graphql';
import k8sWorkloadsQuery from '../queries/k8s_workloads.query.graphql';
@@ -54,42 +58,6 @@ const mapWorkloadItems = (items, kind) => {
});
};
-const handleClusterError = async (err) => {
- if (!err.response) {
- throw err;
- }
-
- const errorData = await err.response.json();
- throw errorData;
-};
-
-const watchPods = ({ configuration, namespace, client }) => {
- const path = namespace ? `/api/v1/namespaces/${namespace}/pods` : '/api/v1/pods';
- const config = new Configuration(configuration);
- const watcherApi = new WatchApi(config);
-
- watcherApi
- .subscribeToStream(path, { watch: true })
- .then((watcher) => {
- let result = [];
-
- watcher.on(EVENT_DATA, (data) => {
- result = data.map((item) => {
- return { status: { phase: item.status.phase } };
- });
-
- client.writeQuery({
- query: k8sPodsQuery,
- variables: { configuration, namespace },
- data: { k8sPods: result },
- });
- });
- })
- .catch((err) => {
- handleClusterError(err);
- });
-};
-
const watchWorkloadItems = ({ kind, apiVersion, configuration, namespace, client }) => {
const itemKind = kind.toLowerCase().replace('list', 's');
@@ -130,28 +98,9 @@ const watchWorkloadItems = ({ kind, apiVersion, configuration, namespace, client
export default {
k8sPods(_, { configuration, namespace }, { client }) {
- const config = new Configuration(configuration);
-
- const coreV1Api = new CoreV1Api(config);
- const podsApi = namespace
- ? coreV1Api.listCoreV1NamespacedPod({ namespace })
- : coreV1Api.listCoreV1PodForAllNamespaces();
-
- return podsApi
- .then((res) => {
- if (gon.features?.k8sWatchApi) {
- watchPods({ configuration, namespace, client });
- }
-
- return res?.items || [];
- })
- .catch(async (err) => {
- try {
- await handleClusterError(err);
- } catch (error) {
- throw new Error(error.message);
- }
- });
+ const query = k8sPodsQuery;
+ const enableWatch = gon.features?.k8sWatchApi;
+ return getK8sPods({ client, query, configuration, namespace, enableWatch });
},
k8sServices(_, { configuration, namespace }) {
const coreV1Api = new CoreV1Api(new Configuration(configuration));
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index 4d4bae12570..95ae5e5a92c 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -22,7 +22,7 @@ import { helpPagePath } from '~/helpers/help_page_helper';
import AccessorUtils from '~/lib/utils/accessor';
import { __ } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import { sanitizeUrl } from '~/lib/utils/url_utility';
+import { sanitizeUrl, joinPaths } from '~/lib/utils/url_utility';
import {
trackErrorListViewsOptions,
trackErrorStatusUpdateOptions,
@@ -225,7 +225,7 @@ export default {
if (!isValidErrorId(errorId)) {
return 'about:blank';
}
- return `error_tracking/${errorId}/details`;
+ return joinPaths(this.listPath, errorId, 'details');
},
goToNextPage() {
this.pageValue = this.$options.NEXT_PAGE;
diff --git a/app/assets/javascripts/kubernetes_dashboard/components/workload_stats.vue b/app/assets/javascripts/kubernetes_dashboard/components/workload_stats.vue
new file mode 100644
index 00000000000..31b931e1855
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/components/workload_stats.vue
@@ -0,0 +1,27 @@
+<script>
+import { GlSingleStat } from '@gitlab/ui/dist/charts';
+
+export default {
+ components: {
+ GlSingleStat,
+ },
+ props: {
+ stats: {
+ type: Array,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-flex-wrap gl-sm-flex-nowrap gl-mx-n3 gl-mt-n3">
+ <gl-single-stat
+ v-for="(stat, index) in stats"
+ :key="index"
+ class="gl-w-full gl-flex-direction-column gl-align-items-center gl-justify-content-center gl-bg-white gl-border gl-border-gray-a-08 gl-mx-3 gl-p-3 gl-mt-3"
+ :value="stat.value"
+ :title="stat.title"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/kubernetes_dashboard/constants.js b/app/assets/javascripts/kubernetes_dashboard/constants.js
new file mode 100644
index 00000000000..2aeb2a4c113
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/constants.js
@@ -0,0 +1,13 @@
+import { s__ } from '~/locale';
+
+export const PHASE_RUNNING = 'Running';
+export const PHASE_PENDING = 'Pending';
+export const PHASE_SUCCEEDED = 'Succeeded';
+export const PHASE_FAILED = 'Failed';
+
+export const STATUS_LABELS = {
+ [PHASE_RUNNING]: s__('KubernetesDashboard|Running'),
+ [PHASE_PENDING]: s__('KubernetesDashboard|Pending'),
+ [PHASE_SUCCEEDED]: s__('KubernetesDashboard|Succeeded'),
+ [PHASE_FAILED]: s__('KubernetesDashboard|Failed'),
+};
diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/client.js b/app/assets/javascripts/kubernetes_dashboard/graphql/client.js
new file mode 100644
index 00000000000..0b9de9c2a61
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/graphql/client.js
@@ -0,0 +1,25 @@
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import typeDefs from '~/environments/graphql/typedefs.graphql';
+import k8sPodsQuery from './queries/k8s_dashboard_pods.query.graphql';
+import { resolvers } from './resolvers';
+
+export const apolloProvider = () => {
+ const defaultClient = createDefaultClient(resolvers, {
+ typeDefs,
+ });
+ const { cache } = defaultClient;
+
+ cache.writeQuery({
+ query: k8sPodsQuery,
+ data: {
+ status: {
+ phase: null,
+ },
+ },
+ });
+
+ return new VueApollo({
+ defaultClient,
+ });
+};
diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js b/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js
new file mode 100644
index 00000000000..22ddd7c6a61
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js
@@ -0,0 +1,68 @@
+import { CoreV1Api, Configuration, WatchApi, EVENT_DATA } from '@gitlab/cluster-client';
+
+export const handleClusterError = async (err) => {
+ if (!err.response) {
+ throw err;
+ }
+
+ const errorData = await err.response.json();
+ throw errorData;
+};
+
+export const watchPods = ({ client, query, configuration, namespace }) => {
+ const path = namespace ? `/api/v1/namespaces/${namespace}/pods` : '/api/v1/pods';
+ const config = new Configuration(configuration);
+ const watcherApi = new WatchApi(config);
+
+ watcherApi
+ .subscribeToStream(path, { watch: true })
+ .then((watcher) => {
+ let result = [];
+
+ watcher.on(EVENT_DATA, (data) => {
+ result = data.map((item) => {
+ return { status: { phase: item.status.phase } };
+ });
+
+ client.writeQuery({
+ query,
+ variables: { configuration, namespace },
+ data: { k8sPods: result },
+ });
+ });
+ })
+ .catch((err) => {
+ handleClusterError(err);
+ });
+};
+
+export const getK8sPods = ({
+ client,
+ query,
+ configuration,
+ namespace = '',
+ enableWatch = false,
+}) => {
+ const config = new Configuration(configuration);
+
+ const coreV1Api = new CoreV1Api(config);
+ const podsApi = namespace
+ ? coreV1Api.listCoreV1NamespacedPod({ namespace })
+ : coreV1Api.listCoreV1PodForAllNamespaces();
+
+ return podsApi
+ .then((res) => {
+ if (enableWatch) {
+ watchPods({ client, query, configuration, namespace });
+ }
+
+ return res?.items || [];
+ })
+ .catch(async (err) => {
+ try {
+ await handleClusterError(err);
+ } catch (error) {
+ throw new Error(error.message);
+ }
+ });
+};
diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/queries/k8s_dashboard_pods.query.graphql b/app/assets/javascripts/kubernetes_dashboard/graphql/queries/k8s_dashboard_pods.query.graphql
new file mode 100644
index 00000000000..17d264d32ec
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/graphql/queries/k8s_dashboard_pods.query.graphql
@@ -0,0 +1,7 @@
+query getK8sDashboardPods($configuration: LocalConfiguration) {
+ k8sPods(configuration: $configuration) @client {
+ status {
+ phase
+ }
+ }
+}
diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers.js b/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers.js
new file mode 100644
index 00000000000..b99ffff5bd1
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers.js
@@ -0,0 +1,7 @@
+import kubernetesQueries from './resolvers/kubernetes';
+
+export const resolvers = {
+ Query: {
+ ...kubernetesQueries,
+ },
+};
diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers/kubernetes.js b/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers/kubernetes.js
new file mode 100644
index 00000000000..db289541105
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/graphql/resolvers/kubernetes.js
@@ -0,0 +1,9 @@
+import { getK8sPods } from '../helpers/resolver_helpers';
+import k8sDashboardPodsQuery from '../queries/k8s_dashboard_pods.query.graphql';
+
+export default {
+ k8sPods(_, { configuration }, { client }) {
+ const query = k8sDashboardPodsQuery;
+ return getK8sPods({ client, query, configuration });
+ },
+};
diff --git a/app/assets/javascripts/kubernetes_dashboard/init_kubernetes_dashboard.js b/app/assets/javascripts/kubernetes_dashboard/init_kubernetes_dashboard.js
index 2299207ce29..6c8e8a2eb31 100644
--- a/app/assets/javascripts/kubernetes_dashboard/init_kubernetes_dashboard.js
+++ b/app/assets/javascripts/kubernetes_dashboard/init_kubernetes_dashboard.js
@@ -1,7 +1,13 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { removeLastSlashInUrlPath } from '~/lib/utils/url_utility';
+import csrf from '~/lib/utils/csrf';
+import { apolloProvider as createApolloProvider } from './graphql/client';
import App from './pages/app.vue';
import createRouter from './router/index';
+Vue.use(VueApollo);
+
const initKubernetesDashboard = () => {
const el = document.querySelector('.js-kubernetes-app');
@@ -9,7 +15,18 @@ const initKubernetesDashboard = () => {
return null;
}
- const { basePath, agent } = el.dataset;
+ const { basePath, agent, kasTunnelUrl } = el.dataset;
+ const agentObject = JSON.parse(agent);
+
+ const configuration = {
+ basePath: removeLastSlashInUrlPath(kasTunnelUrl),
+ headers: {
+ 'GitLab-Agent-Id': agentObject.id,
+ 'Content-Type': 'application/json',
+ ...csrf.headers,
+ },
+ credentials: 'include',
+ };
const router = createRouter({
base: basePath,
@@ -19,8 +36,10 @@ const initKubernetesDashboard = () => {
el,
name: 'KubernetesDashboardRoot',
router,
+ apolloProvider: createApolloProvider(),
provide: {
- agent: JSON.parse(agent),
+ agent: agentObject,
+ configuration,
},
render: (createElement) => createElement(App),
});
diff --git a/app/assets/javascripts/kubernetes_dashboard/pages/app.vue b/app/assets/javascripts/kubernetes_dashboard/pages/app.vue
index d8cb9ccd5fa..e135afed9fe 100644
--- a/app/assets/javascripts/kubernetes_dashboard/pages/app.vue
+++ b/app/assets/javascripts/kubernetes_dashboard/pages/app.vue
@@ -8,5 +8,6 @@ export default {
<template>
<div class="gl-mt-5">
<page-title> {{ $route.meta.title }} </page-title>
+ <router-view />
</div>
</template>
diff --git a/app/assets/javascripts/kubernetes_dashboard/pages/pods_page.vue b/app/assets/javascripts/kubernetes_dashboard/pages/pods_page.vue
new file mode 100644
index 00000000000..5d2c3252c15
--- /dev/null
+++ b/app/assets/javascripts/kubernetes_dashboard/pages/pods_page.vue
@@ -0,0 +1,81 @@
+<script>
+import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
+import WorkloadStats from '../components/workload_stats.vue';
+import k8sPodsQuery from '../graphql/queries/k8s_dashboard_pods.query.graphql';
+import {
+ PHASE_RUNNING,
+ PHASE_PENDING,
+ PHASE_SUCCEEDED,
+ PHASE_FAILED,
+ STATUS_LABELS,
+} from '../constants';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ GlAlert,
+ WorkloadStats,
+ },
+ inject: ['configuration'],
+ apollo: {
+ k8sPods: {
+ query: k8sPodsQuery,
+ variables() {
+ return {
+ configuration: this.configuration,
+ };
+ },
+ update(data) {
+ return data?.k8sPods || [];
+ },
+ error(err) {
+ this.errorMessage = err?.message;
+ },
+ },
+ },
+ data() {
+ return {
+ errorMessage: '',
+ };
+ },
+ computed: {
+ podStats() {
+ return [
+ {
+ value: this.countPodsByPhase(PHASE_RUNNING),
+ title: STATUS_LABELS[PHASE_RUNNING],
+ },
+ {
+ value: this.countPodsByPhase(PHASE_PENDING),
+ title: STATUS_LABELS[PHASE_PENDING],
+ },
+ {
+ value: this.countPodsByPhase(PHASE_SUCCEEDED),
+ title: STATUS_LABELS[PHASE_SUCCEEDED],
+ },
+ {
+ value: this.countPodsByPhase(PHASE_FAILED),
+ title: STATUS_LABELS[PHASE_FAILED],
+ },
+ ];
+ },
+ loading() {
+ return this.$apollo?.queries?.k8sPods?.loading;
+ },
+ },
+ methods: {
+ countPodsByPhase(phase) {
+ const filteredPods = this.k8sPods?.filter((item) => item.status.phase === phase) || [];
+
+ return filteredPods.length;
+ },
+ },
+};
+</script>
+<template>
+ <gl-loading-icon v-if="loading" />
+ <gl-alert v-else-if="errorMessage" variant="danger" :dismissible="false" class="gl-mb-5">
+ {{ errorMessage }}
+ </gl-alert>
+ <workload-stats v-else :stats="podStats" />
+</template>
diff --git a/app/assets/javascripts/kubernetes_dashboard/router/routes.js b/app/assets/javascripts/kubernetes_dashboard/router/routes.js
index 4c2ff299912..33ae36716ff 100644
--- a/app/assets/javascripts/kubernetes_dashboard/router/routes.js
+++ b/app/assets/javascripts/kubernetes_dashboard/router/routes.js
@@ -1,10 +1,12 @@
import { s__ } from '~/locale';
+import PodsPage from '../pages/pods_page.vue';
import { PODS_ROUTE_NAME, PODS_ROUTE_PATH } from './constants';
export default [
{
name: PODS_ROUTE_NAME,
path: PODS_ROUTE_PATH,
+ component: PodsPage,
meta: {
title: s__('KubernetesDashboard|Pods'),
},
diff --git a/app/assets/javascripts/repository/mixins/highlight_mixin.js b/app/assets/javascripts/repository/mixins/highlight_mixin.js
index fa4f0f48512..422a84dff40 100644
--- a/app/assets/javascripts/repository/mixins/highlight_mixin.js
+++ b/app/assets/javascripts/repository/mixins/highlight_mixin.js
@@ -3,6 +3,7 @@ import {
EVENT_ACTION,
EVENT_LABEL_FALLBACK,
LINES_PER_CHUNK,
+ ROUGE_TO_HLJS_LANGUAGE_MAP,
} from '~/vue_shared/components/source_viewer/constants';
import { splitIntoChunks } from '~/vue_shared/components/source_viewer/workers/highlight_utils';
import LineHighlighter from '~/blob/line_highlighter';
@@ -29,8 +30,9 @@ export default {
this.track(EVENT_ACTION, { label, property: language });
},
isUnsupportedLanguage(language) {
+ const mappedLanguage = ROUGE_TO_HLJS_LANGUAGE_MAP[language];
const supportedLanguages = Object.keys(languageLoader);
- const isUnsupportedLanguage = !supportedLanguages.includes(language);
+ const isUnsupportedLanguage = !supportedLanguages.includes(mappedLanguage);
return LEGACY_FALLBACKS.includes(language) || isUnsupportedLanguage;
},
diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue
index 752f077ca02..e354d82b76a 100644
--- a/app/assets/javascripts/super_sidebar/components/help_center.vue
+++ b/app/assets/javascripts/super_sidebar/components/help_center.vue
@@ -14,9 +14,6 @@ import { STORAGE_KEY } from '~/whats_new/utils/notification';
import Tracking from '~/tracking';
import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS, helpCenterState } from '../constants';
-// Left offset required for the dropdown to be aligned with the super sidebar
-const DROPDOWN_X_OFFSET = -4;
-
export default {
components: {
GlBadge,
@@ -204,7 +201,7 @@ export default {
});
},
},
- dropdownOffset: { mainAxis: DROPDOWN_Y_OFFSET, crossAxis: DROPDOWN_X_OFFSET },
+ dropdownOffset: { mainAxis: DROPDOWN_Y_OFFSET },
};
</script>
diff --git a/app/finders/autocomplete/group_users_finder.rb b/app/finders/autocomplete/group_users_finder.rb
index b24f3f7f032..749d7821f44 100644
--- a/app/finders/autocomplete/group_users_finder.rb
+++ b/app/finders/autocomplete/group_users_finder.rb
@@ -12,8 +12,9 @@ module Autocomplete
class GroupUsersFinder
include Gitlab::Utils::StrongMemoize
- def initialize(group:)
+ def initialize(group:, members_relation: false)
@group = group
+ @members_relation = members_relation # If true, it will return Member relation instead
end
def execute
@@ -22,6 +23,8 @@ module Autocomplete
.with(descendant_projects_cte.to_arel) # rubocop:disable CodeReuse/ActiveRecord
.from_union(member_relations, remove_duplicates: false)
+ return members if @members_relation
+
User
.id_in(members.select(:user_id))
.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/420387")
@@ -31,11 +34,11 @@ module Autocomplete
def member_relations
[
- members_from_group_hierarchy.select(:user_id),
- members_from_hierarchy_group_shares.select(:user_id),
- members_from_descendant_projects.select(:user_id),
- members_from_descendant_project_shares.select(:user_id)
- ]
+ members_from_group_hierarchy,
+ members_from_hierarchy_group_shares,
+ members_from_descendant_projects,
+ members_from_descendant_project_shares
+ ].map { |relation| relation.select(:id, :user_id) }
end
def members_from_group_hierarchy
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index e7a24cde2bd..ddd68524a04 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -9,6 +9,7 @@ module Autocomplete
# consistent and removes the need for implementing keyset pagination to
# ensure good performance.
LIMIT = 20
+ BATCH_SIZE = 1000
attr_reader :current_user, :project, :group, :search,
:author_id, :todo_filter, :todo_state_filter,
@@ -62,7 +63,19 @@ module Autocomplete
# When changing the order of these method calls, make sure that
# reorder_by_name() is called _before_ optionally_search(), otherwise
# reorder_by_name will break the ORDER BY applied in optionally_search().
- find_users
+ if project
+ project.authorized_users.union_with_user(author_id).merge(users_relation)
+ elsif group
+ find_group_users(group)
+ elsif current_user
+ users_relation
+ else
+ User.none
+ end.to_a
+ end
+
+ def users_relation
+ User
.where(state: states)
.non_internal
.reorder_by_name
@@ -73,7 +86,6 @@ module Autocomplete
todo_state: todo_state_filter
)
.limit(LIMIT)
- .to_a
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -85,15 +97,33 @@ module Autocomplete
author_id.present? && current_user
end
- def find_users
- if project
- project.authorized_users.union_with_user(author_id)
- elsif group
- ::Autocomplete::GroupUsersFinder.new(group: group).execute # rubocop: disable CodeReuse/Finder
- elsif current_user
- User.all
+ def find_group_users(group)
+ if Feature.enabled?(:group_users_autocomplete_using_batch_reduction, current_user)
+ members_relation = ::Autocomplete::GroupUsersFinder.new(group: group, members_relation: true).execute # rubocop: disable CodeReuse/Finder
+
+ user_ids = Set.new
+ members_relation.each_batch(of: BATCH_SIZE) do |relation|
+ user_ids += relation.pluck_user_ids
+ end
+
+ user_relations = []
+ user_ids.to_a.in_groups_of(BATCH_SIZE, false) do |batch_user_ids|
+ user_relations << users_relation.id_in(batch_user_ids)
+ end
+
+ # When there is more than 1 batch, we need to apply users_relation again on
+ # the top results per-batch so that we return results in the correct order.
+ if user_relations.empty?
+ User.none
+ elsif user_relations.one?
+ user_relations.first
+ else
+ # We pluck the ids per batch so that we don't send an unbounded number of ids in one query
+ user_ids = user_relations.flat_map(&:pluck_primary_key)
+ users_relation.id_in(user_ids)
+ end
else
- User.none
+ ::Autocomplete::GroupUsersFinder.new(group: group).execute.merge(users_relation) # rubocop: disable CodeReuse/Finder
end
end
diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb
index 216a8bc8fa1..5e902ce7bc6 100644
--- a/app/helpers/ci/jobs_helper.rb
+++ b/app/helpers/ci/jobs_helper.rb
@@ -12,7 +12,9 @@ module Ci
"runner_settings_url" => project_runners_path(build.project, anchor: 'js-runners-settings'),
"build_status" => build.status,
"build_stage" => build.stage_name,
- "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs')
+ "retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs'),
+ "test_report_summary_url" => test_report_summary_project_job_path(project, build, format: :json),
+ "pipeline_test_report_url" => test_report_project_pipeline_path(project, build.pipeline)
}
end
diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb
index fb0a02ca56e..54a54c42fd1 100644
--- a/app/models/ci/build_need.rb
+++ b/app/models/ci/build_need.rb
@@ -4,13 +4,10 @@ module Ci
class BuildNeed < Ci::ApplicationRecord
include Ci::Partitionable
include IgnorableColumns
- include SafelyChangeColumnDefault
include BulkInsertSafe
MAX_JOB_NAME_LENGTH = 255
- columns_changing_default :partition_id
-
belongs_to :build,
->(need) { in_partition(need) },
class_name: 'Ci::Processable',
diff --git a/app/models/ci/build_pending_state.rb b/app/models/ci/build_pending_state.rb
index 0b88f745d78..966884ae158 100644
--- a/app/models/ci/build_pending_state.rb
+++ b/app/models/ci/build_pending_state.rb
@@ -2,9 +2,6 @@
class Ci::BuildPendingState < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
belongs_to :build, class_name: 'Ci::Build', foreign_key: :build_id, inverse_of: :pending_state
diff --git a/app/models/ci/build_report_result.rb b/app/models/ci/build_report_result.rb
index 1dabf05e03c..bf58ebfd568 100644
--- a/app/models/ci/build_report_result.rb
+++ b/app/models/ci/build_report_result.rb
@@ -3,9 +3,6 @@
module Ci
class BuildReportResult < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
self.primary_key = :build_id
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
index 223cb9c5d68..c102b95a6a4 100644
--- a/app/models/ci/build_runner_session.rb
+++ b/app/models/ci/build_runner_session.rb
@@ -5,9 +5,6 @@ module Ci
# Data will be removed after transitioning from running to any state.
class BuildRunnerSession < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
TERMINAL_SUBPROTOCOL = 'terminal.gitlab.com'
DEFAULT_SERVICE_NAME = 'build'
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 0a0f401c9d5..7ab89533d76 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -8,9 +8,6 @@ module Ci
include ::Checksummable
include ::Gitlab::ExclusiveLeaseHelpers
include ::Gitlab::OptimisticLocking
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id, inverse_of: :trace_chunks
diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb
index 859dad29a06..ac0d7ce4e76 100644
--- a/app/models/ci/build_trace_metadata.rb
+++ b/app/models/ci/build_trace_metadata.rb
@@ -3,9 +3,6 @@
module Ci
class BuildTraceMetadata < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
MAX_ATTEMPTS = 5
self.table_name = 'ci_build_trace_metadata'
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index fe4437a4ad6..d3705265d91 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -13,9 +13,6 @@ module Ci
include FileStoreMounter
include EachBatch
include Gitlab::Utils::StrongMemoize
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
enum accessibility: { public: 0, private: 1 }, _suffix: true
diff --git a/app/models/ci/job_variable.rb b/app/models/ci/job_variable.rb
index 21c9842399e..573999995bc 100644
--- a/app/models/ci/job_variable.rb
+++ b/app/models/ci/job_variable.rb
@@ -5,11 +5,8 @@ module Ci
include Ci::Partitionable
include Ci::NewHasVariable
include Ci::RawVariable
- include SafelyChangeColumnDefault
include BulkInsertSafe
- columns_changing_default :partition_id
-
belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id, inverse_of: :job_variables
partitionable scope: :job
diff --git a/app/models/ci/pending_build.rb b/app/models/ci/pending_build.rb
index 80cc74a9946..1e628381233 100644
--- a/app/models/ci/pending_build.rb
+++ b/app/models/ci/pending_build.rb
@@ -4,9 +4,6 @@ module Ci
class PendingBuild < Ci::ApplicationRecord
include EachBatch
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
belongs_to :project
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 0a910d6b125..f771c264651 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -17,9 +17,6 @@ module Ci
include UpdatedAtFilterable
include EachBatch
include FastDestroyAll::Helpers
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
include IgnorableColumns
ignore_column :id_convert_to_bigint, remove_with: '16.3', remove_after: '2023-08-22'
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index a422aaa7daa..8c248c77130 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -6,9 +6,7 @@ module Ci
include Ci::HasVariable
include Ci::RawVariable
include IgnorableColumns
- include SafelyChangeColumnDefault
- columns_changing_default :partition_id
ignore_column :pipeline_id_convert_to_bigint, remove_with: '16.5', remove_after: '2023-10-22'
belongs_to :pipeline
diff --git a/app/models/ci/running_build.rb b/app/models/ci/running_build.rb
index 22753930b3c..e70ba3c97c3 100644
--- a/app/models/ci/running_build.rb
+++ b/app/models/ci/running_build.rb
@@ -10,9 +10,6 @@ module Ci
# of the running builds there is worth the additional pressure.
class RunningBuild < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
partitionable scope: :build
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index 6c9d3b9c19f..51c8fb787c7 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -5,9 +5,6 @@ module Ci
class Pipeline < Ci::ApplicationRecord
include Ci::Partitionable
include Ci::NamespacedModelName
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id, :source_partition_id
self.table_name = "ci_sources_pipelines"
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index e413ed8a668..becb8f204bf 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -7,11 +7,8 @@ module Ci
include Ci::HasStatus
include Gitlab::OptimisticLocking
include Presentable
- include SafelyChangeColumnDefault
include IgnorableColumns
- columns_changing_default :partition_id
-
ignore_column :pipeline_id_convert_to_bigint, remove_with: '16.6', remove_after: '2023-10-22'
partitionable scope: :pipeline
diff --git a/app/models/ci/unit_test_failure.rb b/app/models/ci/unit_test_failure.rb
index bec74d8be20..97e07463921 100644
--- a/app/models/ci/unit_test_failure.rb
+++ b/app/models/ci/unit_test_failure.rb
@@ -3,9 +3,6 @@
module Ci
class UnitTestFailure < Ci::ApplicationRecord
include Ci::Partitionable
- include SafelyChangeColumnDefault
-
- columns_changing_default :partition_id
REPORT_WINDOW = 14.days
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a6160a4c3b4..72a40ce5f01 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1287,6 +1287,17 @@ class Repository
paths - generated_files
end
+ def object_format
+ return unless exists?
+
+ case raw.object_format
+ when :OBJECT_FORMAT_SHA1
+ FORMAT_SHA1
+ when :OBJECT_FORMAT_SHA256
+ FORMAT_SHA256
+ end
+ end
+
private
def ancestor_cache_key(ancestor_id, descendant_id)
diff --git a/app/models/user.rb b/app/models/user.rb
index bb3b0100591..a7c90765be1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1814,6 +1814,8 @@ class User < MainClusterwide::ApplicationRecord
end
def owns_runner?(runner)
+ runner = runner.__getobj__ if runner.is_a?(Ci::RunnerPresenter)
+
ci_owned_runners.include?(runner)
end
diff --git a/app/views/clusters/agents/dashboard/show.html.haml b/app/views/clusters/agents/dashboard/show.html.haml
index adf71863669..5b6cfdf8c03 100644
--- a/app/views/clusters/agents/dashboard/show.html.haml
+++ b/app/views/clusters/agents/dashboard/show.html.haml
@@ -2,5 +2,5 @@
- add_to_breadcrumbs s_('KubernetesDashboard|Agents'), kubernetes_dashboard_path
- page_title s_('KubernetesDashboard|Dashboard')
-.js-kubernetes-app{ data: { base_path: kubernetes_dashboard_path, agent: @agent.to_json } }
+.js-kubernetes-app{ data: { base_path: kubernetes_dashboard_path, agent: @agent.to_json, kas_tunnel_url: ::Gitlab::Kas.tunnel_url } }
= gl_loading_icon(css_class: 'gl-my-5', size: 'md')
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 0753a021f1f..bb29ea92704 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -1,4 +1,4 @@
-- action = current_action?(:edit) || current_action?(:update) ? 'edit' : 'create'
+- action = current_action?(:edit, :update) ? 'edit' : 'create'
- file_name = params[:id].split("/").last ||= ""
- is_markdown = Gitlab::MarkupHelper.gitlab_markdown?(file_name)
@@ -6,12 +6,17 @@
.js-file-title.file-title.gl-display-flex.gl-align-items-center.gl-rounded-top-base{ data: { current_action: action } }
.editor-ref.block-truncated.has-tooltip{ title: ref }
= sprite_icon('branch', size: 12)
- = ref
- - if current_action?(:edit) || current_action?(:update)
+ - if current_action?(:edit, :update)
+ %span#editor_ref
+ = ref
+ - if current_action?(:new, :create)
+ %span#editor_path
+ = params[:id]
+ - if current_action?(:edit, :update)
- input_options = { id: 'file_path', name: 'file_path', value: (params[:file_path] || @path), class: 'new-file-path js-file-path-name-input' }
= render 'filepath_form', input_options: input_options
- - if current_action?(:new) || current_action?(:create)
+ - if current_action?(:new, :create)
- input_options = { id: 'file_name', name: 'file_name', value: params[:file_name] || (should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : ''), required: true, placeholder: "Filename", testid: 'file-name-field', class: 'new-file-name js-file-path-name-input' }
= render 'filepath_form', input_options: input_options
- if should_suggest_gitlab_ci_yml?