Skip to content

Commit 9d30f86

Browse files
authored
feat(scaffolder): add new plugin for extending scaffolder actions (#67)
1 parent 7708f12 commit 9d30f86

File tree

12 files changed

+679
-8
lines changed

12 files changed

+679
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ This SeatGeek Backstage Plugins Collection offers the following plugins:
1616
- [plugins/aws-catalog-backend](plugins/aws-catalog-backend)
1717
- **Gitlab Catalog**: the Gitlab Catalog Plugin offers catalog integrations with the Gitlab API.
1818
- [plugins/gitlab-catalog-backend](plugins/gitlab-catalog-backend/)
19+
- **HCL Scaffolder Actions**: the HCL Scaffolder Actions plugin includes custom actions for working with HCL in your Backstage Software Templates.
20+
- [plugins/scaffolder-backend-module-hcl](plugins/scaffolder-backend-module-hcl/)
1921

2022
Each of the plugins contain instructions for installation and development within
2123
their respective locations.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* Copyright SeatGeek
3+
* Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms.
4+
*/
5+
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
testdir/
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# scaffolder-backend-module-hcl
2+
3+
This contains a collection of actions to use in scaffolder templates for working with HCL (Hashicorp Configuration Language). See https://github.com/hashicorp/hcl to learn more about HCL.
4+
5+
## Getting started
6+
7+
### From your Backstage root directory
8+
9+
```bash
10+
# From your Backstage root directory
11+
yarn add --cwd packages/backend @seatgeek/backstage-plugin-scaffolder-backend-module-hcl
12+
```
13+
14+
Then ensure that both the scaffolder and this module are added to your backend:
15+
16+
```typescript
17+
// In packages/backend/src/index.ts
18+
const backend = createBackend();
19+
// ...
20+
backend.add(import('@seatgeek/backstage-plugin-scaffolder-backend-module-hcl'));
21+
```
22+
23+
After that you can use the actions in your template.
24+
25+
## Actions
26+
27+
- `hcl:merge` Merge HCL strings
28+
- `hcl:merge:write` Merge HCL strings and write to a file
29+
- `hcl:merge:files` Merge HCL files
30+
- `hcl:merge:files:write` Merge HCL files and write to a file
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@seatgeek/backstage-plugin-scaffolder-backend-module-hcl",
3+
"version": "0.0.0-semantically-released",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.cjs.js",
10+
"types": "dist/index.d.ts"
11+
},
12+
"backstage": {
13+
"role": "backend-plugin-module",
14+
"pluginId": "scaffolder",
15+
"pluginPackages": [
16+
"@seatgeek/backstage-plugin-scaffolder-backend-module-hcl"
17+
],
18+
"pluginPackage": "@backstage/plugin-scaffolder-backend"
19+
},
20+
"scripts": {
21+
"start": "backstage-cli package start",
22+
"build": "backstage-cli package build",
23+
"lint": "backstage-cli package lint",
24+
"test": "backstage-cli package test --no-cache",
25+
"clean": "backstage-cli package clean",
26+
"prepack": "backstage-cli package prepack",
27+
"postpack": "backstage-cli package postpack"
28+
},
29+
"dependencies": {
30+
"@backstage/backend-common": "^0.24.1",
31+
"@backstage/backend-plugin-api": "^0.8.1",
32+
"@backstage/config": "^1.2.0",
33+
"@backstage/plugin-scaffolder-node": "^0.4.10",
34+
"@seatgeek/node-hcl": "^1.0.0",
35+
"fs-extra": "^11.2.0",
36+
"zod": "^3.23.8"
37+
},
38+
"devDependencies": {
39+
"@backstage/cli": "^0.27.0",
40+
"@testing-library/jest-dom": "^5.10.1",
41+
"@types/fs-extra": "^11.0.4",
42+
"msw": "^1.0.0",
43+
"stream": "^0.0.3"
44+
},
45+
"files": [
46+
"dist"
47+
]
48+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright SeatGeek
3+
* Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms.
4+
*/
5+
import { getVoidLogger } from '@backstage/backend-common';
6+
import { randomBytes } from 'crypto';
7+
import { writeFileSync } from 'fs-extra';
8+
import { tmpdir } from 'os';
9+
import { PassThrough } from 'stream';
10+
import { createHclMergeAction, createHclMergeFilesAction } from './hcl';
11+
12+
// Since we have to
13+
const TMP_DIR = tmpdir();
14+
15+
describe('createHclMergeAction', () => {
16+
const mockContext = {
17+
logger: getVoidLogger(),
18+
logStream: new PassThrough(),
19+
output: jest.fn(),
20+
createTemporaryDirectory: jest.fn(),
21+
checkpoint: jest.fn(),
22+
getInitiatorCredentials: jest.fn(),
23+
workspacePath: '.',
24+
};
25+
26+
it('should merge HCL files', async () => {
27+
const a = `
28+
variable "name" {
29+
description = "Name to be used on all the resources as identifier"
30+
type = string
31+
default = ""
32+
}`;
33+
34+
const b = `
35+
variable "name" {
36+
type = string
37+
default = "my-name"
38+
}`;
39+
40+
const expected = `variable "name" {
41+
default = "my-name"
42+
description = "Name to be used on all the resources as identifier"
43+
type = string
44+
}
45+
46+
`;
47+
48+
const mockCtx = {
49+
...mockContext,
50+
input: {
51+
aSourceContent: a,
52+
bSourceContent: b,
53+
},
54+
};
55+
56+
await createHclMergeAction().handler(mockCtx);
57+
58+
expect(mockCtx.output.mock.calls[0][0]).toEqual('hcl');
59+
expect(mockCtx.output.mock.calls[0][1]).toEqual(expected);
60+
});
61+
});
62+
63+
describe('createHclMergeFilesAction', () => {
64+
const mockContext = {
65+
logger: getVoidLogger(),
66+
logStream: new PassThrough(),
67+
output: jest.fn(),
68+
createTemporaryDirectory: jest.fn(),
69+
checkpoint: jest.fn(),
70+
getInitiatorCredentials: jest.fn(),
71+
workspacePath: TMP_DIR,
72+
};
73+
74+
it('should merge HCL files', async () => {
75+
const a = `
76+
variable "name" {
77+
description = "Name to be used on all the resources as identifier"
78+
type = string
79+
default = ""
80+
}`;
81+
82+
const b = `
83+
variable "name" {
84+
type = string
85+
default = "my-name"
86+
}`;
87+
88+
const expected = `variable "name" {
89+
default = "my-name"
90+
description = "Name to be used on all the resources as identifier"
91+
type = string
92+
}
93+
94+
`;
95+
96+
const aPath = `${mockContext.workspacePath}/${randomBytes(12).toString(
97+
'hex',
98+
)}.hcl`;
99+
await writeFileSync(aPath, a, 'utf8');
100+
101+
const bPath = `${mockContext.workspacePath}/${randomBytes(12).toString(
102+
'hex',
103+
)}.hcl`;
104+
await writeFileSync(bPath, b, 'utf8');
105+
106+
const mockCtx = {
107+
...mockContext,
108+
input: {
109+
aSourcePath: aPath,
110+
bSourcePath: bPath,
111+
},
112+
};
113+
114+
await createHclMergeFilesAction().handler(mockCtx);
115+
116+
expect(mockCtx.output.mock.calls[0][0]).toEqual('hcl');
117+
expect(mockCtx.output.mock.calls[0][1]).toEqual(expected);
118+
});
119+
});

0 commit comments

Comments
 (0)