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