Skip to content

Commit

Permalink
feat(scaffolder): create custom action for scaffolder templates (#1567)
Browse files Browse the repository at this point in the history
* feat(scaffolder): create custom action for adding timestamp to templates

* create generic action for annotating objects

* chore: fix lint issues and dynamic plugin exporting

Signed-off-by: Frank Kong <frkong@redhat.com>

---------

Signed-off-by: Frank Kong <frkong@redhat.com>
Co-authored-by: Frank Kong <frkong@redhat.com>
  • Loading branch information
debsmita1 and Zaperex committed May 9, 2024
1 parent 5f73fdd commit e30701e
Show file tree
Hide file tree
Showing 26 changed files with 2,264 additions and 20 deletions.
1 change: 1 addition & 0 deletions plugins/scaffolder-annotator-action/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
150 changes: 150 additions & 0 deletions plugins/scaffolder-annotator-action/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Annotator custom action for Scaffolder Backstage

The annotator module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend).

This module enables users to generate custom actions for annotating their entity object(s).

## Getting started

### Installing on the new backend system

To install the module into the [new backend system](https://backstage.io/docs/backend-system/), add the following into the `packages/backend/src/index.ts` file:

```ts title="packages/backend/src/index.ts
const backend = createBackend();

// highlight-add-start
backend.add(
import('@janus-idp/backstage-scaffolder-backend-module-annotator/alpha'),
);
// highlight-add-end

backend.start();
```

### Installing on the legacy backend system

1. Install the Annotator custom action plugin using the following command:

```console
yarn workspace backend add @janus-idp/backstage-scaffolder-backend-module-annotator
```

2. Integrate the custom actions in the `scaffolder-backend` `createRouter` function:

- Install the `@backstage/integration` package using the following command:

```console
yarn workspace backend add @backstage/integration
```

- Add the `createTimestampAction` and `createScaffoldedFromAction` actions with the other built-in actions:

```ts title="packages/backend/src/plugins/scaffolder.ts"

import { CatalogClient } from '@backstage/catalog-client';
/* highlight-add-start */
import { ScmIntegrations } from '@backstage/integration';
import {
createBuiltinActions,
createRouter,
} from '@backstage/plugin-scaffolder-backend';
import { createTimestampAction, createScaffoldedFromAction } from '@janus-idp/backstage-scaffolder-backend-module-annotator';
/* highlight-add-end */
...

export default async function createPlugin(
const catalogClient = new CatalogClient({
discoveryApi: env.discovery,
});

/* highlight-add-start */
const integrations = ScmIntegrations.fromConfig(env.config);

const builtInActions = createBuiltinActions({
integrations: integrations as any,
catalogClient,
config: env.config,
reader: env.reader,
});
const actions = [...builtInActions, createTimestampAction(), createScaffoldedFromAction()];
/* highlight-add-end */


return await createRouter({
/* highlight-add-next-line */
actions,
logger: env.logger,
...
});
```

3. To annotate the catalog-info.yaml skeleton with the current timestamp, add the **catalog:timestamping** custom action in your template yaml after the `Fetch Skeleton + Template` step:

```yaml title="template.yaml"
steps:
- id: template
name: Fetch Skeleton + Template
action: fetch:template
...

# highlight-add-start
- id: timestamp
name: Add Timestamp to catalog-info.yaml
action: catalog:timestamping
# highlight-add-end
```

4. To annotate the catalog-info.yaml skeleton with the template entityRef, add the **catalog:scaffolded-from** custom action in your template yaml after the `Fetch Skeleton + Template` step:

```yaml "title=template.yaml"
steps:
- id: template
name: Fetch Skeleton + Template
action: fetch:template
...
# highlight-add-start
- id: append-templateRef
name: Append the entityRef of this template to the entityRef
action: catalog:scaffolded-from
# highlight-add-end

```

## Usage

To use the `createAnnotatorAction` to create a custom action

| Parameter Name | Type | Required | Description |
| ---------------------------------- | :------: | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actionId` | `string` | Yes | A unique id for the action |
| `actionDescription` | `string` | Yes | A description of what the action accomplishes |
| `loggerInfoMsg` | `string` | No | A message that will be logged upon the execution of the action |
| `annotateEntityObject.labels` | `object` | No | Key-value pairs to be added to the `metadata.labels` of the entity |
| `annotateEntityObject.annotations` | `object` | No | Key-value pairs to be added to the `metadata.annotations` of the entity |
| `annotateEntityObject.spec` | `object` | No | Key-value pairs to be added to the `metadata.spec` of the entity. The value for each key can either be a string or an object with the key `readFromContext`, enabling users to specify the path in the context from which the value should be retrieved. |

The annotator action accepts the following inputs

#### Input

| Parameter Name | Type | Required | Description |
| ---------------- | :------: | :------: | ----------------------------------------------------------------------------- |
| `labels` | `object` | No | Key-value pairs to be added to the `metadata.labels` of the entity |
| `annotations` | `object` | No | Key-value pairs to be added to the `metadata.annotations` of the entity |
| `entityFilePath` | `string` | No | The file path from which the YAML representation of the entity should be read |
| `objectYaml` | `object` | No | The YAML representation of the object/entity |
| `writeToFile` | `string` | No | The file path where the YAML representation of the entity should be stored |

#### Output

| Name | Type | Description |
| ----------------- | :------: | -------------------------------------------------------------------------------------------- |
| `annotatedObject` | `object` | The entity object marked with your specified annotation(s), label(s), and spec property(ies) |

Here are the custom actions currently available:

| Action | Description |
| ------------------------- | :-------------------------------------------: |
| `catalog:timestamping` | Adds current timestamp to your entity object |
| `catalog:scaffolded-from` | Adds template entityRef to your entity object |
63 changes: 63 additions & 0 deletions plugins/scaffolder-annotator-action/dist-dynamic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "@janus-idp/backstage-scaffolder-backend-module-annotator-dynamic",
"description": "The annotator module for @backstage/plugin-scaffolder-backend",
"version": "0.1.0",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "backend-plugin-module"
},
"exports": {
".": {
"require": "./dist/index.cjs.js",
"default": "./dist/index.cjs.js"
},
"./alpha": {
"require": "./dist/alpha.cjs.js",
"default": "./dist/alpha.cjs.js"
},
"./package.json": "./package.json"
},
"scripts": {},
"dependencies": {
"fs-extra": "^11.2.0",
"lodash": "^4.17.21",
"yaml": "^2.0.0"
},
"devDependencies": {},
"files": [
"dist",
"config.d.ts",
"app-config.janus-idp.yaml",
"alpha"
],
"repository": "github:janus-idp/backstage-plugins",
"keywords": [
"backstage",
"plugin"
],
"homepage": "https://janus-idp.io/",
"bugs": "https://github.com/janus-idp/backstage-plugins/issues",
"bundleDependencies": true,
"peerDependencies": {
"@backstage/backend-common": "^0.21.6",
"@backstage/backend-dynamic-feature-service": "^0.2.9",
"@backstage/backend-plugin-api": "^0.6.17",
"@backstage/catalog-model": "^1.4.5",
"@backstage/plugin-scaffolder-node": "^0.4.2"
},
"overrides": {
"@aws-sdk/util-utf8-browser": {
"@smithy/util-utf8": "^2.0.0"
}
},
"resolutions": {
"@aws-sdk/util-utf8-browser": "npm:@smithy/util-utf8@~2"
}
}
69 changes: 69 additions & 0 deletions plugins/scaffolder-annotator-action/dist-dynamic/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@aws-sdk/util-utf8-browser@npm:@smithy/util-utf8@~2":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5"
integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==
dependencies:
"@smithy/util-buffer-from" "^2.2.0"
tslib "^2.6.2"

"@smithy/is-array-buffer@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111"
integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==
dependencies:
tslib "^2.6.2"

"@smithy/util-buffer-from@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b"
integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==
dependencies:
"@smithy/is-array-buffer" "^2.2.0"
tslib "^2.6.2"

fs-extra@^11.2.0:
version "11.2.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"

graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==

jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"

lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==

universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==

yaml@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362"
integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: create-react-app-template-with-timestamp-entityRef
title: Create React App Template
description: Create a new CRA website project
tags:
- react
- cra
spec:
owner: web@example.com
type: website
parameters:
- title: Provide some simple information
required:
- component_id
- owner
properties:
component_id:
title: Name
type: string
description: Unique name of the component
ui:field: EntityNamePicker
description:
title: Description
type: string
description: Help others understand what this website is for.
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
steps:
- id: template
name: Fetch Skeleton + Template
action: fetch:template
input:
url: https://github.com/backstage/software-templates/tree/main/scaffolder-templates/create-react-app/skeleton
copyWithoutRender:
- .github/workflows/*
values:
component_id: ${{ parameters.component_id }}
description: ${{ parameters.description }}
destination: ${{ parameters.repoUrl | parseRepoUrl }}
owner: ${{ parameters.owner }}

- id: timestamp
name: Add Timestamp to catalog-info.yaml
action: catalog:timestamping

- id: append-templateRef
name: Append the entityRef of this template to the entityRef
action: catalog:scaffolded-from

- id: publish
name: Publish
action: publish:github
input:
allowedHosts:
- github.com
description: This is ${{ parameters.component_id }}
repoUrl: ${{ parameters.repoUrl }}

- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'

output:
links:
- title: Repository
url: ${{ steps.publish.output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps.register.output.entityRef }}

0 comments on commit e30701e

Please sign in to comment.