From e69089b4030dc17671fe1311ee36eed115c1d588 Mon Sep 17 00:00:00 2001 From: Edgar Garcia Date: Wed, 19 Feb 2025 16:47:45 -0600 Subject: [PATCH 1/6] feat(pipelineRef): conditionally load childExecutions when pipelineRef is enabled --- halconfig/settings.js | 2 ++ packages/core/src/config/settings.ts | 2 ++ packages/core/src/domain/IExecutionTrigger.ts | 1 + .../details/SingleExecutionDetails.tsx | 22 ++++++++++++++----- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/halconfig/settings.js b/halconfig/settings.js index e0c57a07bdc..84463c2296d 100644 --- a/halconfig/settings.js +++ b/halconfig/settings.js @@ -16,6 +16,7 @@ var fiatEnabled = '{%features.fiat%}' === 'true'; var manualJudgmentParentPipelineEnabled = '{%features.manualJudgmentParentPipeline%}' === 'true'; var mineCanaryEnabled = '{%features.mineCanary%}' === 'true'; var pipelineTemplatesEnabled = '{%features.pipelineTemplates%}' === 'true'; +var pipelineRefEnabled = '{%features.pipelineRefEnabled%}' === 'true'; var reduxLoggerEnabled = '{%canary.reduxLogger%}' === 'true'; var showAllConfigsEnabled = '{%canary.showAllCanaryConfigs%}' === 'true'; var dynamicRollbackTimeoutEnabled = '{%feature.dynamicRollbackTimeout%}' === 'true'; @@ -123,6 +124,7 @@ window.spinnakerSettings = { pipelineTemplates: pipelineTemplatesEnabled, roscoMode: true, dynamicRollbackTimeout: dynamicRollbackTimeoutEnabled, + pipelineRefEnabled: pipelineRefEnabled, }, gateUrl: gateHost, notifications: { diff --git a/packages/core/src/config/settings.ts b/packages/core/src/config/settings.ts index 8cfaea047ba..e6df748075c 100644 --- a/packages/core/src/config/settings.ts +++ b/packages/core/src/config/settings.ts @@ -53,6 +53,8 @@ export interface IFeatures { functions?: boolean; kubernetesRawResources?: boolean; renderPipelineStageThreshold?: number; + deployManifestStageAdvancedConfiguration?: boolean; + pipelineRefEnabled?: boolean; } export interface IDockerInsightSettings { diff --git a/packages/core/src/domain/IExecutionTrigger.ts b/packages/core/src/domain/IExecutionTrigger.ts index 3d2382127cf..ddb816504fd 100644 --- a/packages/core/src/domain/IExecutionTrigger.ts +++ b/packages/core/src/domain/IExecutionTrigger.ts @@ -8,6 +8,7 @@ export interface IExecutionTrigger extends ITrigger { isPipeline?: boolean; parameters?: { [key: string]: string }; parentExecution?: IExecution; + parentExecutionId?: string; parentPipelineApplication?: string; parentPipelineId?: string; parentPipelineStageId?: string; diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.tsx index b56a1d2196d..e1fb5ba3503 100644 --- a/packages/core/src/pipeline/details/SingleExecutionDetails.tsx +++ b/packages/core/src/pipeline/details/SingleExecutionDetails.tsx @@ -3,6 +3,7 @@ import { set } from 'lodash'; import React, { useEffect, useState } from 'react'; import type { Application } from '../../application/application.model'; +import { SETTINGS } from '../../config'; import type { IExecution, IPipeline } from '../../domain'; import { Execution } from '../executions/execution/Execution'; import { ManualExecutionModal } from '../manualExecution'; @@ -30,11 +31,22 @@ export interface ISingleExecutionRouterStateChange extends IStateChange { toParams: ISingleExecutionStateParams; } -export function getAndTransformExecution(id: string, app: Application) { - return ReactInjector.executionService.getExecution(id, app.pipelineConfigs?.data).then((execution) => { - ExecutionsTransformer.transformExecution(app, execution); - return execution; - }); +export async function getAndTransformExecution(id: string, app: Application) { + // Fetch the execution data + const execution = await ReactInjector.executionService.getExecution(id, app.pipelineConfigs?.data); + + // Transform the execution + ExecutionsTransformer.transformExecution(app, execution); + + // Check if the execution has a trigger with a parentExecutionId + if (SETTINGS.feature.pipelineRefEnabled && execution?.trigger?.parentExecutionId) { + // Recursively get the parent execution and append it + const parentExecution = await getAndTransformExecution(execution.trigger.parentExecutionId, app); + execution.trigger.parentExecution = parentExecution; + } + + // Return the transformed execution + return execution; } // 3 generations is probably the most reasonable window to render? From 1a45fb3371e0b51229b6af7858bfc9dc488f1efc Mon Sep 17 00:00:00 2001 From: Edgar Garcia Date: Thu, 20 Feb 2025 13:12:12 -0600 Subject: [PATCH 2/6] feat(pipelineRef): add basic test to SingleExecutionDetails component --- .../details/SingleExecutionDetails.spec.tsx | 37 +++++++++++++++++++ .../details/SingleExecutionDetails.tsx | 5 --- 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx new file mode 100644 index 00000000000..757f1549afa --- /dev/null +++ b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx @@ -0,0 +1,37 @@ +import { shallow } from 'enzyme'; +import React from 'react'; + +import { SingleExecutionDetails } from './SingleExecutionDetails'; +import { Application } from '../../application/application.model'; +import type { IExecution } from '../../domain'; + +describe('', () => { + let application: Application; + let wrapper: any; + // let execution: IExecution; + + beforeEach(() => { + application = new Application('my-app', null, []); + // execution = { id: 'exec1', trigger: {}, stages: [] } as IExecution; + wrapper = shallow(); + }); + + it('renders without crashing', () => { + expect(wrapper.exists()).toBe(true); + }); + + it('renders execution details when provided', () => { + expect(wrapper.find('.execution-details').length).toBeGreaterThan(0); + }); + + it('updates state when new execution is received', () => { + const newExecution = { id: 'exec2', trigger: {}, stages: [] } as IExecution; + wrapper.setProps({ execution: newExecution }); + expect(wrapper.instance().props.execution.id).toEqual('exec2'); + }); + + it('handles missing execution gracefully', () => { + wrapper.setProps({ execution: null }); + expect(wrapper.instance().props.execution).toBeNull(); + }); +}); diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.tsx index e1fb5ba3503..3ae6ca03972 100644 --- a/packages/core/src/pipeline/details/SingleExecutionDetails.tsx +++ b/packages/core/src/pipeline/details/SingleExecutionDetails.tsx @@ -32,10 +32,7 @@ export interface ISingleExecutionRouterStateChange extends IStateChange { } export async function getAndTransformExecution(id: string, app: Application) { - // Fetch the execution data const execution = await ReactInjector.executionService.getExecution(id, app.pipelineConfigs?.data); - - // Transform the execution ExecutionsTransformer.transformExecution(app, execution); // Check if the execution has a trigger with a parentExecutionId @@ -44,8 +41,6 @@ export async function getAndTransformExecution(id: string, app: Application) { const parentExecution = await getAndTransformExecution(execution.trigger.parentExecutionId, app); execution.trigger.parentExecution = parentExecution; } - - // Return the transformed execution return execution; } From 04ad042decec77cec0374fe3f22ad6a5982b963c Mon Sep 17 00:00:00 2001 From: Edgar Garcia Date: Thu, 20 Feb 2025 13:46:34 -0600 Subject: [PATCH 3/6] test(pipelineRef): define UIRouter in tests --- .../details/SingleExecutionDetails.spec.tsx | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx index 757f1549afa..d05f0d44b07 100644 --- a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx +++ b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx @@ -1,4 +1,5 @@ -import { shallow } from 'enzyme'; +import { UIRouter, UIRouterReact } from '@uirouter/react'; +import { mount } from 'enzyme'; import React from 'react'; import { SingleExecutionDetails } from './SingleExecutionDetails'; @@ -8,12 +9,20 @@ import type { IExecution } from '../../domain'; describe('', () => { let application: Application; let wrapper: any; - // let execution: IExecution; + let router: UIRouterReact; beforeEach(() => { application = new Application('my-app', null, []); - // execution = { id: 'exec1', trigger: {}, stages: [] } as IExecution; - wrapper = shallow(); + router = new UIRouterReact(); + wrapper = mount( + + + , + ); + }); + + afterEach(() => { + wrapper.unmount(); }); it('renders without crashing', () => { @@ -26,12 +35,14 @@ describe('', () => { it('updates state when new execution is received', () => { const newExecution = { id: 'exec2', trigger: {}, stages: [] } as IExecution; - wrapper.setProps({ execution: newExecution }); - expect(wrapper.instance().props.execution.id).toEqual('exec2'); + wrapper.setProps({ params: { executionId: newExecution.id } }); + wrapper.update(); + expect(wrapper.props().params.executionId).toEqual('exec2'); }); it('handles missing execution gracefully', () => { - wrapper.setProps({ execution: null }); - expect(wrapper.instance().props.execution).toBeNull(); + wrapper.setProps({ params: { executionId: null } }); + wrapper.update(); + expect(wrapper.props().params.executionId).toBeNull(); }); }); From f71064cd07f690b3780650b142d7d33905013b0b Mon Sep 17 00:00:00 2001 From: Edgar Garcia Date: Thu, 20 Feb 2025 14:23:56 -0600 Subject: [PATCH 4/6] test(pipelineRef): mock UIRouter using jest --- .../src/pipeline/details/SingleExecutionDetails.spec.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx index d05f0d44b07..6af595c4ea4 100644 --- a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx +++ b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx @@ -6,6 +6,14 @@ import { SingleExecutionDetails } from './SingleExecutionDetails'; import { Application } from '../../application/application.model'; import type { IExecution } from '../../domain'; +jest.mock('@uirouter/react', () => ({ + UIRouter: ({ children }: { children: React.ReactNode }) =>
{children}
, + UIRouterReact: class { + start = jest.fn(); + locationService = { onChange: jest.fn() }; + }, +})); + describe('', () => { let application: Application; let wrapper: any; From 5ec9e367dabd7d3416bbb760463e0f7cf91285c3 Mon Sep 17 00:00:00 2001 From: Edgar Garcia Date: Thu, 20 Feb 2025 14:36:15 -0600 Subject: [PATCH 5/6] test(pipelineRef): remove test --- .../details/SingleExecutionDetails.spec.tsx | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx diff --git a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx b/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx deleted file mode 100644 index 6af595c4ea4..00000000000 --- a/packages/core/src/pipeline/details/SingleExecutionDetails.spec.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { UIRouter, UIRouterReact } from '@uirouter/react'; -import { mount } from 'enzyme'; -import React from 'react'; - -import { SingleExecutionDetails } from './SingleExecutionDetails'; -import { Application } from '../../application/application.model'; -import type { IExecution } from '../../domain'; - -jest.mock('@uirouter/react', () => ({ - UIRouter: ({ children }: { children: React.ReactNode }) =>
{children}
, - UIRouterReact: class { - start = jest.fn(); - locationService = { onChange: jest.fn() }; - }, -})); - -describe('', () => { - let application: Application; - let wrapper: any; - let router: UIRouterReact; - - beforeEach(() => { - application = new Application('my-app', null, []); - router = new UIRouterReact(); - wrapper = mount( - - - , - ); - }); - - afterEach(() => { - wrapper.unmount(); - }); - - it('renders without crashing', () => { - expect(wrapper.exists()).toBe(true); - }); - - it('renders execution details when provided', () => { - expect(wrapper.find('.execution-details').length).toBeGreaterThan(0); - }); - - it('updates state when new execution is received', () => { - const newExecution = { id: 'exec2', trigger: {}, stages: [] } as IExecution; - wrapper.setProps({ params: { executionId: newExecution.id } }); - wrapper.update(); - expect(wrapper.props().params.executionId).toEqual('exec2'); - }); - - it('handles missing execution gracefully', () => { - wrapper.setProps({ params: { executionId: null } }); - wrapper.update(); - expect(wrapper.props().params.executionId).toBeNull(); - }); -}); From 2f4bcf086dbad5ed33a3c45e38227f3201fb5704 Mon Sep 17 00:00:00 2001 From: christosarvanitis Date: Tue, 12 Aug 2025 11:44:41 +0300 Subject: [PATCH 6/6] fix(settings): Removing unused feature flag --- packages/core/src/config/settings.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/config/settings.ts b/packages/core/src/config/settings.ts index e6df748075c..0dfc7e53aac 100644 --- a/packages/core/src/config/settings.ts +++ b/packages/core/src/config/settings.ts @@ -53,7 +53,6 @@ export interface IFeatures { functions?: boolean; kubernetesRawResources?: boolean; renderPipelineStageThreshold?: number; - deployManifestStageAdvancedConfiguration?: boolean; pipelineRefEnabled?: boolean; }