@backstage/plugin-scaffolder-backend 1.3.0-next.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/dist/index.cjs.js +122 -31
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# @backstage/plugin-scaffolder-backend
|
|
2
2
|
|
|
3
|
+
## 1.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Don't resolve symlinks, treat them as binary files and copy them as-is
|
|
8
|
+
|
|
9
|
+
## 1.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 35a26131b3: **DEPRECATION**: The `projectid` input parameters to the `publish:gitlab:merge-request`, it's no longer required as it can be decoded from the `repoUrl` input parameter.
|
|
14
|
+
**DEPRECATION**: The `projectid` output of the action in favour of `projectPath`
|
|
15
|
+
- 72dfcbc8bf: A new scaffolder action has been added: `gerrit:publish`
|
|
16
|
+
- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators.
|
|
17
|
+
This fix required changing the way parameters are passed to the action.
|
|
18
|
+
In order to add a team as collaborator, now you must use the `team` field instead of `username`.
|
|
19
|
+
In order to add a user as collaborator, you must use the `user` field.
|
|
20
|
+
|
|
21
|
+
It's still possible to use the field `username` but is deprecated in favor of `team`.
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
- id: publish
|
|
25
|
+
name: Publish
|
|
26
|
+
action: publish:github
|
|
27
|
+
input:
|
|
28
|
+
repoUrl: ...
|
|
29
|
+
collaborators:
|
|
30
|
+
- access: ...
|
|
31
|
+
team: my_team
|
|
32
|
+
- access: ...
|
|
33
|
+
user: my_username
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef`
|
|
37
|
+
- Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef`
|
|
38
|
+
- Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter
|
|
39
|
+
- c042c5eaff: Add an option to not protect the default branch.
|
|
40
|
+
- f93af969cd: Added the ability to support running of templates that are not in the `default` namespace
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- 8f7b1835df: Updated dependency `msw` to `^0.41.0`.
|
|
45
|
+
- 6901f6be4a: Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository.
|
|
46
|
+
- Updated dependencies
|
|
47
|
+
- @backstage/plugin-catalog-backend@1.2.0
|
|
48
|
+
- @backstage/backend-common@0.14.0
|
|
49
|
+
- @backstage/integration@1.2.1
|
|
50
|
+
- @backstage/catalog-client@1.0.3
|
|
51
|
+
- @backstage/catalog-model@1.0.3
|
|
52
|
+
- @backstage/plugin-scaffolder-common@1.1.1
|
|
53
|
+
|
|
54
|
+
## 1.3.0-next.2
|
|
55
|
+
|
|
56
|
+
### Minor Changes
|
|
57
|
+
|
|
58
|
+
- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators.
|
|
59
|
+
This fix required changing the way parameters are passed to the action.
|
|
60
|
+
In order to add a team as collaborator, now you must use the `team` field instead of `username`.
|
|
61
|
+
In order to add a user as collaborator, you must use the `user` field.
|
|
62
|
+
|
|
63
|
+
It's still possible to use the field `username` but is deprecated in favor of `team`.
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
- id: publish
|
|
67
|
+
name: Publish
|
|
68
|
+
action: publish:github
|
|
69
|
+
input:
|
|
70
|
+
repoUrl: ...
|
|
71
|
+
collaborators:
|
|
72
|
+
- access: ...
|
|
73
|
+
team: my_team
|
|
74
|
+
- access: ...
|
|
75
|
+
user: my_username
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef`
|
|
79
|
+
- Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef`
|
|
80
|
+
- Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter
|
|
81
|
+
|
|
82
|
+
### Patch Changes
|
|
83
|
+
|
|
84
|
+
- Updated dependencies
|
|
85
|
+
- @backstage/backend-common@0.14.0-next.2
|
|
86
|
+
- @backstage/integration@1.2.1-next.2
|
|
87
|
+
- @backstage/plugin-catalog-backend@1.2.0-next.2
|
|
88
|
+
|
|
3
89
|
## 1.3.0-next.1
|
|
4
90
|
|
|
5
91
|
### Minor Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -571,8 +571,9 @@ function createFetchTemplateAction(options) {
|
|
|
571
571
|
await fs__default["default"].ensureDir(outputPath);
|
|
572
572
|
} else {
|
|
573
573
|
const inputFilePath = backendCommon.resolveSafeChildPath(templateDir, location);
|
|
574
|
-
|
|
575
|
-
|
|
574
|
+
const stats = await fs__default["default"].promises.lstat(inputFilePath);
|
|
575
|
+
if (stats.isSymbolicLink() || await isbinaryfile.isBinaryFile(inputFilePath)) {
|
|
576
|
+
ctx.logger.info(`Copying file binary or symbolic link at ${location}, to template output path.`);
|
|
576
577
|
await fs__default["default"].copy(inputFilePath, outputPath);
|
|
577
578
|
} else {
|
|
578
579
|
const statsObj = await fs__default["default"].stat(inputFilePath);
|
|
@@ -1862,22 +1863,36 @@ function createPublishGithubAction(options) {
|
|
|
1862
1863
|
},
|
|
1863
1864
|
collaborators: {
|
|
1864
1865
|
title: "Collaborators",
|
|
1865
|
-
description: "Provide additional users with permissions",
|
|
1866
|
+
description: "Provide additional users or teams with permissions",
|
|
1866
1867
|
type: "array",
|
|
1867
1868
|
items: {
|
|
1868
1869
|
type: "object",
|
|
1869
|
-
|
|
1870
|
+
additionalProperties: false,
|
|
1871
|
+
required: ["access"],
|
|
1870
1872
|
properties: {
|
|
1871
1873
|
access: {
|
|
1872
1874
|
type: "string",
|
|
1873
1875
|
description: "The type of access for the user",
|
|
1874
1876
|
enum: ["push", "pull", "admin", "maintain", "triage"]
|
|
1875
1877
|
},
|
|
1878
|
+
user: {
|
|
1879
|
+
type: "string",
|
|
1880
|
+
description: "The name of the user that will be added as a collaborator"
|
|
1881
|
+
},
|
|
1876
1882
|
username: {
|
|
1877
1883
|
type: "string",
|
|
1878
|
-
description: "
|
|
1884
|
+
description: "Deprecated. Use the `team` or `user` field instead."
|
|
1885
|
+
},
|
|
1886
|
+
team: {
|
|
1887
|
+
type: "string",
|
|
1888
|
+
description: "The name of the team that will be added as a collaborator"
|
|
1879
1889
|
}
|
|
1880
|
-
}
|
|
1890
|
+
},
|
|
1891
|
+
oneOf: [
|
|
1892
|
+
{ required: ["user"] },
|
|
1893
|
+
{ required: ["username"] },
|
|
1894
|
+
{ required: ["team"] }
|
|
1895
|
+
]
|
|
1881
1896
|
}
|
|
1882
1897
|
},
|
|
1883
1898
|
token: {
|
|
@@ -1990,21 +2005,37 @@ function createPublishGithubAction(options) {
|
|
|
1990
2005
|
});
|
|
1991
2006
|
}
|
|
1992
2007
|
if (collaborators) {
|
|
1993
|
-
for (const {
|
|
1994
|
-
access: permission,
|
|
1995
|
-
username: team_slug
|
|
1996
|
-
} of collaborators) {
|
|
2008
|
+
for (const collaborator of collaborators) {
|
|
1997
2009
|
try {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2010
|
+
if ("user" in collaborator) {
|
|
2011
|
+
await client.rest.repos.addCollaborator({
|
|
2012
|
+
owner,
|
|
2013
|
+
repo,
|
|
2014
|
+
username: collaborator.user,
|
|
2015
|
+
permission: collaborator.access
|
|
2016
|
+
});
|
|
2017
|
+
} else if ("username" in collaborator) {
|
|
2018
|
+
ctx.logger.warn("The field `username` is deprecated in favor of `team` and will be removed in the future.");
|
|
2019
|
+
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
2020
|
+
org: owner,
|
|
2021
|
+
team_slug: collaborator.username,
|
|
2022
|
+
owner,
|
|
2023
|
+
repo,
|
|
2024
|
+
permission: collaborator.access
|
|
2025
|
+
});
|
|
2026
|
+
} else if ("team" in collaborator) {
|
|
2027
|
+
await client.rest.teams.addOrUpdateRepoPermissionsInOrg({
|
|
2028
|
+
org: owner,
|
|
2029
|
+
team_slug: collaborator.team,
|
|
2030
|
+
owner,
|
|
2031
|
+
repo,
|
|
2032
|
+
permission: collaborator.access
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2005
2035
|
} catch (e) {
|
|
2006
2036
|
errors.assertError(e);
|
|
2007
|
-
|
|
2037
|
+
const name = extractCollaboratorName(collaborator);
|
|
2038
|
+
ctx.logger.warn(`Skipping ${collaborator.access} access for ${name}, ${e.message}`);
|
|
2008
2039
|
}
|
|
2009
2040
|
}
|
|
2010
2041
|
}
|
|
@@ -2059,6 +2090,13 @@ function createPublishGithubAction(options) {
|
|
|
2059
2090
|
}
|
|
2060
2091
|
});
|
|
2061
2092
|
}
|
|
2093
|
+
function extractCollaboratorName(collaborator) {
|
|
2094
|
+
if ("username" in collaborator)
|
|
2095
|
+
return collaborator.username;
|
|
2096
|
+
if ("user" in collaborator)
|
|
2097
|
+
return collaborator.user;
|
|
2098
|
+
return collaborator.team;
|
|
2099
|
+
}
|
|
2062
2100
|
|
|
2063
2101
|
const DEFAULT_GLOB_PATTERNS = ["./**", "!.git"];
|
|
2064
2102
|
const isExecutable = (fileMode) => {
|
|
@@ -2379,7 +2417,7 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2379
2417
|
id: "publish:gitlab:merge-request",
|
|
2380
2418
|
schema: {
|
|
2381
2419
|
input: {
|
|
2382
|
-
required: ["
|
|
2420
|
+
required: ["repoUrl", "targetPath", "branchName"],
|
|
2383
2421
|
type: "object",
|
|
2384
2422
|
properties: {
|
|
2385
2423
|
repoUrl: {
|
|
@@ -2426,6 +2464,10 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2426
2464
|
title: "Gitlab Project id/Name(slug)",
|
|
2427
2465
|
type: "string"
|
|
2428
2466
|
},
|
|
2467
|
+
projectPath: {
|
|
2468
|
+
title: "Gitlab Project path",
|
|
2469
|
+
type: "string"
|
|
2470
|
+
},
|
|
2429
2471
|
mergeRequestURL: {
|
|
2430
2472
|
title: "MergeRequest(MR) URL",
|
|
2431
2473
|
type: "string",
|
|
@@ -2437,7 +2479,13 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2437
2479
|
async handler(ctx) {
|
|
2438
2480
|
var _a;
|
|
2439
2481
|
const repoUrl = ctx.input.repoUrl;
|
|
2440
|
-
const { host } = parseRepoUrl(repoUrl, integrations);
|
|
2482
|
+
const { host, owner, repo } = parseRepoUrl(repoUrl, integrations);
|
|
2483
|
+
const projectPath = `${owner}/${repo}`;
|
|
2484
|
+
if (ctx.input.projectid) {
|
|
2485
|
+
const deprecationWarning = `Property "projectid" is deprecated and no longer to needed to create a MR`;
|
|
2486
|
+
ctx.logger.warn(deprecationWarning);
|
|
2487
|
+
console.warn(deprecationWarning);
|
|
2488
|
+
}
|
|
2441
2489
|
const integrationConfig = integrations.gitlab.byHost(host);
|
|
2442
2490
|
const destinationBranch = ctx.input.branchName;
|
|
2443
2491
|
if (!integrationConfig) {
|
|
@@ -2463,23 +2511,24 @@ const createPublishGitlabMergeRequestAction = (options) => {
|
|
|
2463
2511
|
content: file.content.toString("base64"),
|
|
2464
2512
|
execute_filemode: file.executable
|
|
2465
2513
|
}));
|
|
2466
|
-
const projects = await api.Projects.show(
|
|
2514
|
+
const projects = await api.Projects.show(projectPath);
|
|
2467
2515
|
const { default_branch: defaultBranch } = projects;
|
|
2468
2516
|
try {
|
|
2469
|
-
await api.Branches.create(
|
|
2517
|
+
await api.Branches.create(projectPath, destinationBranch, String(defaultBranch));
|
|
2470
2518
|
} catch (e) {
|
|
2471
2519
|
throw new errors.InputError(`The branch creation failed ${e}`);
|
|
2472
2520
|
}
|
|
2473
2521
|
try {
|
|
2474
|
-
await api.Commits.create(
|
|
2522
|
+
await api.Commits.create(projectPath, destinationBranch, ctx.input.title, actions);
|
|
2475
2523
|
} catch (e) {
|
|
2476
2524
|
throw new errors.InputError(`Committing the changes to ${destinationBranch} failed ${e}`);
|
|
2477
2525
|
}
|
|
2478
2526
|
try {
|
|
2479
|
-
const mergeRequestUrl = await api.MergeRequests.create(
|
|
2527
|
+
const mergeRequestUrl = await api.MergeRequests.create(projectPath, destinationBranch, String(defaultBranch), ctx.input.title, { description: ctx.input.description }).then((mergeRequest) => {
|
|
2480
2528
|
return mergeRequest.web_url;
|
|
2481
2529
|
});
|
|
2482
|
-
ctx.output("projectid",
|
|
2530
|
+
ctx.output("projectid", projectPath);
|
|
2531
|
+
ctx.output("projectPath", projectPath);
|
|
2483
2532
|
ctx.output("mergeRequestUrl", mergeRequestUrl);
|
|
2484
2533
|
} catch (e) {
|
|
2485
2534
|
throw new errors.InputError(`Merge request creation failed${e}`);
|
|
@@ -2834,6 +2883,12 @@ class TemplateActionRegistry {
|
|
|
2834
2883
|
}
|
|
2835
2884
|
|
|
2836
2885
|
const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-scaffolder-backend", "migrations");
|
|
2886
|
+
const parseSqlDateToIsoString = (input) => {
|
|
2887
|
+
if (typeof input === "string") {
|
|
2888
|
+
return luxon.DateTime.fromSQL(input, { zone: "UTC" }).toISO();
|
|
2889
|
+
}
|
|
2890
|
+
return input;
|
|
2891
|
+
};
|
|
2837
2892
|
class DatabaseTaskStore {
|
|
2838
2893
|
static async create(options) {
|
|
2839
2894
|
await options.database.migrate.latest({
|
|
@@ -2844,6 +2899,27 @@ class DatabaseTaskStore {
|
|
|
2844
2899
|
constructor(options) {
|
|
2845
2900
|
this.db = options.database;
|
|
2846
2901
|
}
|
|
2902
|
+
async list(options) {
|
|
2903
|
+
const queryBuilder = this.db("tasks");
|
|
2904
|
+
if (options.createdBy) {
|
|
2905
|
+
queryBuilder.where({
|
|
2906
|
+
created_by: options.createdBy
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
const results = await queryBuilder.orderBy("created_at", "desc").select();
|
|
2910
|
+
const tasks = results.map((result) => {
|
|
2911
|
+
var _a;
|
|
2912
|
+
return {
|
|
2913
|
+
id: result.id,
|
|
2914
|
+
spec: JSON.parse(result.spec),
|
|
2915
|
+
status: result.status,
|
|
2916
|
+
createdBy: (_a = result.created_by) != null ? _a : void 0,
|
|
2917
|
+
lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),
|
|
2918
|
+
createdAt: parseSqlDateToIsoString(result.created_at)
|
|
2919
|
+
};
|
|
2920
|
+
});
|
|
2921
|
+
return { tasks };
|
|
2922
|
+
}
|
|
2847
2923
|
async getTask(taskId) {
|
|
2848
2924
|
var _a;
|
|
2849
2925
|
const [result] = await this.db("tasks").where({ id: taskId }).select();
|
|
@@ -2857,8 +2933,8 @@ class DatabaseTaskStore {
|
|
|
2857
2933
|
id: result.id,
|
|
2858
2934
|
spec,
|
|
2859
2935
|
status: result.status,
|
|
2860
|
-
lastHeartbeatAt: result.last_heartbeat_at,
|
|
2861
|
-
createdAt: result.created_at,
|
|
2936
|
+
lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),
|
|
2937
|
+
createdAt: parseSqlDateToIsoString(result.created_at),
|
|
2862
2938
|
createdBy: (_a = result.created_by) != null ? _a : void 0,
|
|
2863
2939
|
secrets
|
|
2864
2940
|
};
|
|
@@ -2995,7 +3071,7 @@ class DatabaseTaskStore {
|
|
|
2995
3071
|
taskId,
|
|
2996
3072
|
body,
|
|
2997
3073
|
type: event.event_type,
|
|
2998
|
-
createdAt:
|
|
3074
|
+
createdAt: parseSqlDateToIsoString(event.created_at)
|
|
2999
3075
|
};
|
|
3000
3076
|
} catch (error) {
|
|
3001
3077
|
throw new Error(`Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`);
|
|
@@ -3078,6 +3154,12 @@ class StorageTaskBroker {
|
|
|
3078
3154
|
this.logger = logger;
|
|
3079
3155
|
this.deferredDispatch = defer();
|
|
3080
3156
|
}
|
|
3157
|
+
async list(options) {
|
|
3158
|
+
if (!this.storage.list) {
|
|
3159
|
+
throw new Error("TaskStore does not implement the list method. Please implement the list method to be able to list tasks");
|
|
3160
|
+
}
|
|
3161
|
+
return await this.storage.list({ createdBy: options == null ? void 0 : options.createdBy });
|
|
3162
|
+
}
|
|
3081
3163
|
async claim() {
|
|
3082
3164
|
for (; ; ) {
|
|
3083
3165
|
const pendingTask = await this.storage.claimTask();
|
|
@@ -3520,9 +3602,6 @@ function getEntityBaseUrl(entity) {
|
|
|
3520
3602
|
}
|
|
3521
3603
|
async function findTemplate(options) {
|
|
3522
3604
|
const { entityRef, token, catalogApi } = options;
|
|
3523
|
-
if (entityRef.namespace.toLocaleLowerCase("en-US") !== catalogModel.DEFAULT_NAMESPACE) {
|
|
3524
|
-
throw new errors.InputError(`Invalid namespace, only '${catalogModel.DEFAULT_NAMESPACE}' namespace is supported`);
|
|
3525
|
-
}
|
|
3526
3605
|
if (entityRef.kind.toLocaleLowerCase("en-US") !== "template") {
|
|
3527
3606
|
throw new errors.InputError(`Invalid kind, only 'Template' kind is supported`);
|
|
3528
3607
|
}
|
|
@@ -3682,6 +3761,18 @@ async function createRouter(options) {
|
|
|
3682
3761
|
}
|
|
3683
3762
|
});
|
|
3684
3763
|
res.status(201).json({ id: result.taskId });
|
|
3764
|
+
}).get("/v2/tasks", async (req, res) => {
|
|
3765
|
+
const [userEntityRef] = [req.query.createdBy].flat();
|
|
3766
|
+
if (typeof userEntityRef !== "string" && typeof userEntityRef !== "undefined") {
|
|
3767
|
+
throw new errors.InputError("createdBy query parameter must be a string");
|
|
3768
|
+
}
|
|
3769
|
+
if (!taskBroker.list) {
|
|
3770
|
+
throw new Error("TaskBroker does not support listing tasks, please implement the list method on the TaskBroker.");
|
|
3771
|
+
}
|
|
3772
|
+
const tasks = await taskBroker.list({
|
|
3773
|
+
createdBy: userEntityRef
|
|
3774
|
+
});
|
|
3775
|
+
res.status(200).json(tasks);
|
|
3685
3776
|
}).get("/v2/tasks/:taskId", async (req, res) => {
|
|
3686
3777
|
const { taskId } = req.params;
|
|
3687
3778
|
const task = await taskBroker.get(taskId);
|