diff --git a/package.json b/package.json index 21ce193..9c79207 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0-development", "homepage": "https://circleci-public.github.io/visual-config-editor/", "dependencies": { - "@circleci/circleci-config-sdk": "v0.9.0-alpha.7", + "@circleci/circleci-config-sdk": "v0.9.0-alpha.8", "@craco/craco": "^6.3.0", "@monaco-editor/react": "^4.3.1", "algoliasearch": "^4.13.1", diff --git a/src/components/menus/definitions/DefinitionsMenu.tsx b/src/components/menus/definitions/DefinitionsMenu.tsx index 42a3da6..d356549 100644 --- a/src/components/menus/definitions/DefinitionsMenu.tsx +++ b/src/components/menus/definitions/DefinitionsMenu.tsx @@ -1,6 +1,8 @@ import { parsers } from '@circleci/circleci-config-sdk'; +import { OrbImport } from '@circleci/circleci-config-sdk/dist/src/lib/Orb'; import { Form, Formik } from 'formik'; import { useRef } from 'react'; +import { parse } from 'yaml'; import WorkflowIcon from '../../../icons/components/WorkflowIcon'; import { dataMappings } from '../../../mappings/GenerableMapping'; import InspectableMapping from '../../../mappings/InspectableMapping'; @@ -13,6 +15,7 @@ import { WorkflowSelector } from '../../atoms/WorkflowSelector'; import DefinitionsContainer from '../../containers/DefinitionsContainer'; import OrbImportsContainer from '../../containers/OrbImportsContainer'; import TabbedMenu from '../TabbedMenu'; +import { loadOrb, OrbImportWithMeta } from './OrbDefinitionsMenu'; /** * The main menu for inspecting the app's contents. @@ -86,14 +89,60 @@ const DefinitionsMenu = (props: { expanded: boolean[] }) => { return; } - e.target.files[0].text().then((yml) => { + const setConfig = ( + yml: string, + orbImports?: Record, + ) => { let config; try { - config = parsers.parseConfig(yml); + config = parsers.parseConfig(yml, orbImports); } catch (e) { config = e as Error; } loadConfig(config); + }; + + e.target.files[0].text().then((yml) => { + const configBlob = parse(yml); + + if ('orbs' in configBlob) { + const orbs = parsers.parseOrbImports(configBlob.orbs); + + if (!orbs) { + setConfig(yml); + return; + } + + Promise.all( + // get a sneak of the orb imports so we can load the manifests + orbs.map((orb) => + loadOrb(`${orb.namespace}/${orb.name}@${orb.version}`, orb), + ), + ).then((manifests) => { + const orbImports = Object.assign( + {}, + ...manifests.map(({ orb, manifest }) => { + if (typeof orb === 'string') { + throw new Error(`Could not load orb ${orb}`); + } + + return { + [orb.alias]: new OrbImportWithMeta( + orb.alias, + orb.namespace, + orb.name, + manifest, + orb.version, + '', + orb.description, + ), + }; + }), + ); + + setConfig(yml, orbImports); + }); + } }); }} /> diff --git a/src/components/menus/definitions/OrbDefinitionsMenu.tsx b/src/components/menus/definitions/OrbDefinitionsMenu.tsx index 8d6b792..1eb2377 100644 --- a/src/components/menus/definitions/OrbDefinitionsMenu.tsx +++ b/src/components/menus/definitions/OrbDefinitionsMenu.tsx @@ -1,6 +1,9 @@ import { orb } from '@circleci/circleci-config-sdk'; import { AnyParameterLiteral } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters/types/CustomParameterLiterals.types'; -import { OrbRef } from '@circleci/circleci-config-sdk/dist/src/lib/Orb'; +import { + OrbImport, + OrbRef, +} from '@circleci/circleci-config-sdk/dist/src/lib/Orb'; import { OrbDisplayMeta, OrbImportManifest, @@ -31,7 +34,7 @@ export class OrbImportWithMeta extends orb.OrbImport { description?: string, display?: OrbDisplayMeta, ) { - super(alias, namespace, orb, manifest, version, description, display); + super(alias, namespace, orb, version, manifest, description, display); this.logo_url = logo_url; } @@ -47,13 +50,30 @@ export type OrbDefinitionProps = { url: string; }; -const loadOrb = (orb: string) => { +export const loadOrb = (orb: string, value?: OrbImport) => { const endpoint = process.env.NODE_ENV === 'development' ? 'http://localhost:3030' : 'https://temp-orb-manifest-endpoint.herokuapp.com'; - return fetch(`${endpoint}/orbs?orb=${orb}`).then((res) => res.json()); + return fetch(`${endpoint}/orbs?orb=${orb}`).then( + (resp) => + new Promise<{ orb: string | OrbImport; manifest: OrbImportManifest }>( + (res, rej) => { + resp + .json() + .then((manifest) => { + res({ + orb: value ?? orb, + manifest, + }); + }) + .catch((err) => { + rej(err); + }); + }, + ), + ); }; const orbDefinitions = ['jobs', 'commands', 'executors'] as Array< @@ -93,7 +113,7 @@ const OrbDefinitionsMenu = (props: OrbDefinitionProps) => { useEffect(() => { loadOrb(`${props.namespace}/${props.name}@${props.version}`).then( - (manifest) => { + ({ manifest }) => { setOrb( new OrbImportWithMeta( props.name, diff --git a/src/components/menus/stage/StagedJobMenu.tsx b/src/components/menus/stage/StagedJobMenu.tsx index 5f9ac8e..5ca97a4 100644 --- a/src/components/menus/stage/StagedJobMenu.tsx +++ b/src/components/menus/stage/StagedJobMenu.tsx @@ -1,4 +1,4 @@ -import { Job, orb, parsers, reusable } from '@circleci/circleci-config-sdk'; +import { orb, parsers, reusable } from '@circleci/circleci-config-sdk'; import { WorkflowJob } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Workflow'; import { Form, Formik } from 'formik'; import JobIcon from '../../../icons/components/JobIcon'; @@ -18,10 +18,9 @@ import { navSubTypeMenu } from '../SubTypeMenu'; import TabbedMenu from '../TabbedMenu'; type WorkflowJobMenuProps = { - source: Job; + source: WorkflowJob; values: any; id: string; - job: WorkflowJob; }; const AdjacentSteps = ({ @@ -87,8 +86,6 @@ const StagedJobMenu = ({ source, values, id }: WorkflowJobMenuProps) => { initialValues={{ parameters: { name: '', - 'pre-steps': [], - 'post-steps': [], ...values.parameters, }, }} @@ -103,6 +100,7 @@ const StagedJobMenu = ({ source, values, id }: WorkflowJobMenuProps) => { ), ), definitionsAsArray(definitions.jobs), + definitionsAsArray(definitions.orbs), ); updateWorkflowElement({ @@ -149,12 +147,12 @@ const StagedJobMenu = ({ source, values, id }: WorkflowJobMenuProps) => { Edit Filters */} - {(source instanceof reusable.ParameterizedJob || - source instanceof orb.OrbRef) && ( + {(source.job instanceof reusable.ParameterizedJob || + source.job instanceof orb.OrbRef) && ( <> )} diff --git a/src/mappings/components/JobMapping.tsx b/src/mappings/components/JobMapping.tsx index 3c979ff..b14adf6 100644 --- a/src/mappings/components/JobMapping.tsx +++ b/src/mappings/components/JobMapping.tsx @@ -70,6 +70,7 @@ export const JobMapping: InspectableMapping = { values, definitionsAsArray(definitions.commands), definitionsAsArray(definitions.executors), + definitionsAsArray(definitions.orbs), ); }, store: { diff --git a/src/state/Store.tsx b/src/state/Store.tsx index e8ffd42..f826fde 100644 --- a/src/state/Store.tsx +++ b/src/state/Store.tsx @@ -200,6 +200,7 @@ export type StoreActions = AllDefinitionActions & { setToast: Action; triggerToast: Action; triggerConfirmation: Action; + triggerConfigRefresh: ThunkOn; }; const Actions: StoreActions = { @@ -723,6 +724,12 @@ const Actions: StoreActions = { triggerConfirmation: action((state, payload) => { state.confirm = payload; }), + triggerConfigRefresh: thunkOn( + (actions) => actions.importOrb, + (actions) => { + actions.generateConfig(); + }, + ), }; const Store: StoreModel & StoreActions = { diff --git a/yarn.lock b/yarn.lock index b1dde21..c896a0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1197,10 +1197,10 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@circleci/circleci-config-sdk@v0.9.0-alpha.7": - version "0.9.0-alpha.7" - resolved "https://registry.yarnpkg.com/@circleci/circleci-config-sdk/-/circleci-config-sdk-0.9.0-alpha.7.tgz#864480bf8496af58ac7f9957ec3034cd318b8828" - integrity sha512-xBC1ftbUdxQzxGIfbYtXwZQ9wbTB0Qy3Ld5YYKG+7wcHp/RcJnBdf8bhHkC4Fq3h5QaCuM/TKkhg/NoJ+FI6xw== +"@circleci/circleci-config-sdk@v0.9.0-alpha.8": + version "0.9.0-alpha.8" + resolved "https://registry.yarnpkg.com/@circleci/circleci-config-sdk/-/circleci-config-sdk-0.9.0-alpha.8.tgz#4c05b7940fe76cafbdea75dc76e2a8f9bb838c6d" + integrity sha512-nUagt1s/DKLYk1wXbRyp3mCX4DQvMl2ZynpaWtaAI1fxvFX03gwwYQzYKd4vi7MlHVdDByFqovlVSdQnNis2gw== dependencies: ajv "^8.8.2" ajv-merge-patch "^5.0.1"