From e24c458c99bc57e68d4b73ebcf4e22a5b7c58a69 Mon Sep 17 00:00:00 2001 From: Diego Figueroa Date: Wed, 6 Dec 2023 21:46:44 -0400 Subject: [PATCH 1/2] feat: add lifecycle workflow input --- .changeset/sharp-kings-fold.md | 5 +++ .github/workflows/ci.yaml | 1 + README.md | 14 ++++++++ src/core/access-key-collector.ts | 11 +++++-- src/core/multisigs-collector.ts | 46 +++++++++++++++++---------- src/core/rbac-collector.ts | 11 +++++-- src/helpers/backstage-export.ts | 29 +++++++++++++---- src/types.ts | 1 + test/helpers/backstage-export.test.ts | 12 ++++++- 9 files changed, 102 insertions(+), 28 deletions(-) create mode 100644 .changeset/sharp-kings-fold.md diff --git a/.changeset/sharp-kings-fold.md b/.changeset/sharp-kings-fold.md new file mode 100644 index 0000000..af62131 --- /dev/null +++ b/.changeset/sharp-kings-fold.md @@ -0,0 +1,5 @@ +--- +"backstage-blockchain-actions": patch +--- + +feat: add lifecycle workflow input diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f4374ae..82f1746 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,6 +24,7 @@ jobs: - name: Create output directories run: | mkdir -p tmp/backstage/scoped + mkdir -p tmp/backstage/lifecycle - run: pnpm test env: BACKSTAGE_URL: ${{ secrets.BACKSTAGE_URL }} diff --git a/README.md b/README.md index ab3e6f5..6e00eea 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This action is for [Blockchainradar](https://github.com/aurora-is-near/backstage - backstage_url - The backstage url to pull entities from. - backstage_entities_repo - The repo to use as fallback if url is unavailable. - scope - The scope name (usually a Group name) to delimit exported entities by ownership. +- lifecycle = The lifecycle name (usually production) to delimit exported entities by lifecycle. - template_path - The handlebars templates folder path used for exporting. - output_path - The output folder path used for compiled templates. @@ -59,4 +60,17 @@ jobs: template_path: templates/backstage scope: output_path: ./scoped/ + lifecycle-export: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: aurora-is-near/backstage-blockchain-actions@v1 + id: export + with: + helper: backstage-export + backstage_url: https://example.com + template_path: templates/backstage + lifecycle: production + output_path: ./scoped/ ``` diff --git a/src/core/access-key-collector.ts b/src/core/access-key-collector.ts index 8070b9a..f5d4593 100644 --- a/src/core/access-key-collector.ts +++ b/src/core/access-key-collector.ts @@ -22,7 +22,7 @@ export class AccessKeyCollector extends BaseCollector { return acc; } - const components = this.collectComponents(system); + const components = this.collectComponents(system, opts); if (components.some((c) => c.contracts?.length)) { return [ @@ -38,7 +38,10 @@ export class AccessKeyCollector extends BaseCollector { }, []); } - collectComponents(system: Entity): ComponentInfo[] { + collectComponents( + system: Entity, + opts: CollectorOptions = {}, + ): ComponentInfo[] { const componentRefs = system.relations!.filter( (r) => r.type === RELATION_HAS_PART && @@ -47,6 +50,10 @@ export class AccessKeyCollector extends BaseCollector { return componentRefs .reduce((acc, componentRef) => { const component = this.entityCatalog[componentRef.targetRef]; + if (opts.lifecycle && component.spec?.lifecycle !== opts.lifecycle) { + return acc; + } + const contracts = this.collectContracts(componentRef).filter( (c) => c.keys && c.keys.length > 0, ); diff --git a/src/core/multisigs-collector.ts b/src/core/multisigs-collector.ts index aa89bcd..07d0df3 100644 --- a/src/core/multisigs-collector.ts +++ b/src/core/multisigs-collector.ts @@ -37,37 +37,49 @@ export class MultisigsCollector extends BaseCollector { if (opts.scope && system.spec?.owner !== opts.scope) { return acc; } - const components = this.collectComponents(system); + const components = this.collectComponents(system, opts); - return [ - ...acc, - { - title: system.metadata.title || system.metadata.name, - system, - components, - }, - ]; + if (components.some((c) => c.multisigs?.length)) { + return [ + ...acc, + { + title: system.metadata.title || system.metadata.name, + system, + components, + }, + ]; + } + return acc; }, []) .sort((a, b) => a.system.metadata.name.localeCompare(b.system.metadata.name), ); } - collectComponents(system: Entity): ComponentInfo[] { + collectComponents( + system: Entity, + opts: CollectorOptions = {}, + ): ComponentInfo[] { const componentRefs = system.relations!.filter( (r) => r.type === RELATION_HAS_PART && parseEntityRef(r.targetRef).kind === "component", ); return componentRefs - .map((componentRef) => { + .reduce((acc, componentRef) => { const component = this.entityCatalog[componentRef.targetRef]; - return { - title: component.metadata.title || component.metadata.name, - component, - multisigs: this.collectMultisigs(componentRef), - }; - }) + if (opts.lifecycle && component.spec?.lifecycle !== opts.lifecycle) { + return acc; + } + return [ + ...acc, + { + title: component.metadata.title || component.metadata.name, + component, + multisigs: this.collectMultisigs(componentRef), + }, + ]; + }, []) .sort((a, b) => a.component.metadata.name.localeCompare(b.component.metadata.name), ); diff --git a/src/core/rbac-collector.ts b/src/core/rbac-collector.ts index ee33b49..f44ce1b 100644 --- a/src/core/rbac-collector.ts +++ b/src/core/rbac-collector.ts @@ -25,7 +25,7 @@ export class RbacCollector extends BaseCollector { return acc; } - const components = this.collectComponents(system); + const components = this.collectComponents(system, opts); if (components.some((c) => c.contracts?.length)) { return [ @@ -41,7 +41,10 @@ export class RbacCollector extends BaseCollector { }, []); } - collectComponents(system: Entity): ComponentInfo[] { + collectComponents( + system: Entity, + opts: CollectorOptions = {}, + ): ComponentInfo[] { const componentRefs = system.relations!.filter( (r) => r.type === RELATION_HAS_PART && @@ -50,6 +53,10 @@ export class RbacCollector extends BaseCollector { return componentRefs .reduce((acc, componentRef) => { const component = this.entityCatalog[componentRef.targetRef]; + if (opts.lifecycle && component.spec?.lifecycle !== opts.lifecycle) { + return acc; + } + const contracts = this.collectContracts(componentRef); if (contracts.length) { return [ diff --git a/src/helpers/backstage-export.ts b/src/helpers/backstage-export.ts index 295e794..b6c53f4 100644 --- a/src/helpers/backstage-export.ts +++ b/src/helpers/backstage-export.ts @@ -19,6 +19,7 @@ export class BackstageExport { template_path = ""; output_path = ""; scope?: string; + lifecycle?: string; testing?: boolean; } @@ -28,6 +29,7 @@ export const backstageExport = async ({ template_path, output_path, scope, + lifecycle, testing, }: BackstageExport) => { if (!template_path || !output_path) { @@ -41,7 +43,10 @@ export const backstageExport = async ({ backstage_entities_repo, }); - const filteredCollector = new FilteredCollector(entities, { scope }); + const filteredCollector = new FilteredCollector(entities, { + scope, + lifecycle, + }); const multisigsCollector = new MultisigsCollector(entities); const rbacCollector = new RbacCollector(entities); const accessKeyCollector = new AccessKeyCollector(entities); @@ -51,11 +56,23 @@ export const backstageExport = async ({ const changedFiles = sync(`${template_path}**/*.hbs`).reduce( (acc, templatePath) => { const templateData = { - multisigSystemComponents: multisigsCollector.collectSystems({ scope }), - contractSystemComponents: rbacCollector.collectSystems({ scope }), - accessKeySystemComponents: accessKeyCollector.collectSystems({ scope }), - unknownSystemComponents: unknownCollector.collectEntities({ scope }), - addresses: addressCollector.collectAddresses({ scope }), + multisigSystemComponents: multisigsCollector.collectSystems({ + scope, + lifecycle, + }), + contractSystemComponents: rbacCollector.collectSystems({ + scope, + lifecycle, + }), + accessKeySystemComponents: accessKeyCollector.collectSystems({ + scope, + lifecycle, + }), + unknownSystemComponents: unknownCollector.collectEntities({ + scope, + lifecycle, + }), + addresses: addressCollector.collectAddresses({ scope, lifecycle }), filteredEntities: JSON.stringify(filteredCollector.entities, null, 2), testing, }; diff --git a/src/types.ts b/src/types.ts index 7aac6b4..9ce2cbf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import { Entity } from "@backstage/catalog-model"; export type CollectorOptions = { scope?: string; + lifecycle?: string; }; export type EntityCatalog = { diff --git a/test/helpers/backstage-export.test.ts b/test/helpers/backstage-export.test.ts index 25d270c..553c69b 100644 --- a/test/helpers/backstage-export.test.ts +++ b/test/helpers/backstage-export.test.ts @@ -12,7 +12,7 @@ describe("backstage-export", () => { }); expect(result).toBeTruthy(); }, 10000); - it("generates reports with scoped ownership", async () => { + it("generates reports filtered by ownership", async () => { const result = await backstageExport({ backstage_url: process.env.BACKSTAGE_URL, template_path: "templates/backstage", @@ -22,4 +22,14 @@ describe("backstage-export", () => { }); expect(result).toBeTruthy(); }, 10000); + it("generates reports filtered by lifecycle", async () => { + const result = await backstageExport({ + backstage_url: process.env.BACKSTAGE_URL, + template_path: "templates/backstage", + output_path: "tmp/backstage/lifecycle", + lifecycle: "production", + testing: true, + }); + expect(result).toBeTruthy(); + }, 10000); }); From d5b4242108ef3f50ea9d62abd01219f603cb6d32 Mon Sep 17 00:00:00 2001 From: Diego Figueroa Date: Thu, 7 Dec 2023 09:19:48 -0400 Subject: [PATCH 2/2] docs: add lifecycle to action metadata --- README.md | 2 +- action.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e00eea..52f3291 100644 --- a/README.md +++ b/README.md @@ -72,5 +72,5 @@ jobs: backstage_url: https://example.com template_path: templates/backstage lifecycle: production - output_path: ./scoped/ + output_path: ./lifecycle/ ``` diff --git a/action.yml b/action.yml index f049112..e6844f0 100644 --- a/action.yml +++ b/action.yml @@ -12,6 +12,8 @@ inputs: required: false scope: description: "Group or User entity name to filter by ownership of entities" + lifecycle: + description: "Spec lifecycle name to filter entities by" template_path: description: "Handlebars Template Path" required: false