Skip to content

Commit 44c8ccb

Browse files
committed
feat(scaffolder): create custom action for adding timestamp to templates
1 parent 67051c1 commit 44c8ccb

File tree

10 files changed

+243
-0
lines changed

10 files changed

+243
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Timestamp custom action for Scaffolder Backstage
2+
3+
The timestamp module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend).
4+
5+
## For administrators
6+
7+
### Installation
8+
9+
#### Procedure
10+
11+
1. Install the Timestamp custom action plugin using the following command:
12+
13+
```console
14+
yarn workspace backend add @janus-idp/backstage-scaffolder-backend-module-timestamp
15+
```
16+
17+
2. Integrate the custom action in the `packages/backend/src/plugins/scaffolder.ts`:
18+
19+
- Install the `@backstage/integration` package using the following command:
20+
21+
```console
22+
yarn workspace backend add @backstage/integration
23+
```
24+
25+
- ```ts title="packages/backend/src/plugins/scaffolder.ts"
26+
27+
import { CatalogClient } from '@backstage/catalog-client';
28+
/* highlight-add-start */
29+
import { ScmIntegrations } from '@backstage/integration';
30+
import {
31+
createBuiltinActions,
32+
createRouter,
33+
} from '@backstage/plugin-scaffolder-backend';
34+
import { createTimestampAction } from '@janus-idp/backstage-scaffolder-backend-module-timestamp';
35+
/* highlight-add-end */
36+
...
37+
38+
export default async function createPlugin(
39+
const catalogClient = new CatalogClient({
40+
discoveryApi: env.discovery,
41+
});
42+
43+
/* highlight-add-start */
44+
const integrations = ScmIntegrations.fromConfig(env.config);
45+
46+
const builtInActions = createBuiltinActions({
47+
integrations: integrations as any,
48+
catalogClient,
49+
config: env.config,
50+
reader: env.reader,
51+
});
52+
const actions = [...builtInActions, createTimestampAction()];
53+
/* highlight-add-end */
54+
55+
56+
return await createRouter({
57+
/* highlight-add-next-line */
58+
actions,
59+
logger: env.logger,
60+
...
61+
});
62+
```
63+
64+
3. Add the **catalog:timestamping** custom action in your template yaml after the `Fetch Skeleton + Template` step to annotate the catalog-info.yaml skeleton with the current timestamp:
65+
66+
```tsx title="template.yaml"
67+
- id: template
68+
name: Fetch Skeleton + Template
69+
action: fetch:template
70+
...
71+
72+
/* highlight-add-start */
73+
- id: timestamp
74+
name: Add Timestamp to catalog-info.yaml
75+
action: catalog:timestamping
76+
/* highlight-add-end */
77+
78+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "@janus-idp/backstage-scaffolder-backend-module-timestamp",
3+
"description": "The timestamp module for @backstage/plugin-scaffolder-backend",
4+
"version": "0.1.0",
5+
"main": "src/index.ts",
6+
"types": "src/index.ts",
7+
"license": "Apache-2.0",
8+
"private": true,
9+
"publishConfig": {
10+
"access": "public",
11+
"main": "dist/index.cjs.js",
12+
"types": "dist/index.d.ts"
13+
},
14+
"backstage": {
15+
"role": "backend-plugin-module"
16+
},
17+
"scripts": {
18+
"start": "backstage-cli package start",
19+
"build": "backstage-cli package build",
20+
"lint": "backstage-cli package lint",
21+
"test": "backstage-cli package test",
22+
"clean": "backstage-cli package clean",
23+
"prepack": "backstage-cli package prepack",
24+
"postpack": "backstage-cli package postpack"
25+
},
26+
"dependencies": {
27+
"@backstage/plugin-scaffolder-node": "^0.4.2"
28+
},
29+
"devDependencies": {
30+
"@backstage/backend-common": "^0.21.6",
31+
"@backstage/cli": "0.26.2"
32+
},
33+
"files": [
34+
"dist"
35+
]
36+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './timestamp';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const getCurrentTimestamp = () => {
2+
const dateObj = new Date(Date.now());
3+
4+
const time =
5+
dateObj.getHours() > 12
6+
? `${dateObj.getHours() - 12}:${
7+
dateObj.getMinutes() < 10
8+
? '0' + dateObj.getMinutes()
9+
: dateObj.getMinutes()
10+
}:${
11+
dateObj.getSeconds() < 10
12+
? '0' + dateObj.getSeconds()
13+
: dateObj.getSeconds()
14+
} PM`
15+
: `${dateObj.getHours()}:${
16+
dateObj.getMinutes() < 10
17+
? '0' + dateObj.getMinutes()
18+
: dateObj.getMinutes()
19+
}:${
20+
dateObj.getSeconds() < 10
21+
? '0' + dateObj.getSeconds()
22+
: dateObj.getSeconds()
23+
} AM`;
24+
return `${dateObj.toLocaleDateString()}, ${time}`;
25+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './timestamp';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: backstage.io/v1alpha1
2+
kind: Component
3+
metadata:
4+
name: ${{values.component_id | dump}}
5+
description: ${{values.description | dump}}
6+
annotations:
7+
github.com/project-slug: ${{values.destination.owner + "/" + values.destination.repo}}
8+
backstage.io/techdocs-ref: dir:.
9+
backstage.io/createdAt: 4/26/2024, 3:22:25 PM
10+
spec:
11+
type: website
12+
lifecycle: experimental
13+
owner: ${{values.owner | dump}}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getVoidLogger } from '@backstage/backend-common';
2+
3+
import { PassThrough } from 'stream';
4+
5+
import { createTimestampAction } from './timestamp';
6+
7+
describe('catalog:timestamping', () => {
8+
afterEach(() => {
9+
jest.resetAllMocks();
10+
});
11+
12+
it('should call action', async () => {
13+
const action = createTimestampAction();
14+
15+
const logger = getVoidLogger();
16+
jest.spyOn(logger, 'info');
17+
18+
await action.handler({
19+
workspacePath: 'src/actions/timestamp/mocks',
20+
logger,
21+
logStream: new PassThrough(),
22+
output: jest.fn(),
23+
createTemporaryDirectory() {
24+
// Usage of createMockDirectory is recommended for testing of filesystem operations
25+
throw new Error('Not implemented');
26+
},
27+
} as any);
28+
29+
expect(logger.info).toHaveBeenCalledWith(
30+
'Annotating catalog-info.yaml with current timestamp',
31+
);
32+
});
33+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { resolveSafeChildPath } from '@backstage/backend-common';
2+
import { Entity } from '@backstage/catalog-model';
3+
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
4+
5+
import * as fs from 'fs-extra';
6+
import * as yaml from 'yaml';
7+
8+
import { getCurrentTimestamp } from './getCurrentTimestamp';
9+
10+
/**
11+
* Creates a new `catalog:timestamping` Scaffolder action to annotate catalog-info.yaml with creation timestamp.
12+
*
13+
*/
14+
15+
export const createTimestampAction = () => {
16+
return createTemplateAction({
17+
id: 'catalog:timestamping',
18+
description:
19+
'Creates a new `catalog:timestamping` Scaffolder action to annotate scaffolded entities with creation timestamp.',
20+
21+
async handler(ctx) {
22+
const entitySkeleton = await fs.readFile(
23+
resolveSafeChildPath(ctx.workspacePath, './catalog-info.yaml'),
24+
'utf8',
25+
);
26+
27+
const entity: Entity = yaml.parse(entitySkeleton);
28+
29+
var result = yaml.stringify({
30+
...entity,
31+
metadata: {
32+
...entity.metadata,
33+
annotations: {
34+
...entity.metadata.annotations,
35+
'backstage.io/createdAt': getCurrentTimestamp(),
36+
},
37+
},
38+
});
39+
ctx.logger.info(`Annotating catalog-info.yaml with current timestamp`);
40+
await fs.writeFile(
41+
resolveSafeChildPath(ctx.workspacePath, './catalog-info.yaml'),
42+
result,
43+
'utf8',
44+
);
45+
},
46+
});
47+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/***/
2+
/**
3+
* The timestamp module for @backstage/plugin-scaffolder-backend.
4+
*
5+
* @packageDocumentation
6+
*/
7+
8+
export * from './actions';

0 commit comments

Comments
 (0)