feat: reworking zod integration a little bit to reduce the amount of typing you have to do

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2023-02-14 14:31:17 +01:00
parent 7d724d8ef5
commit 10a64ff693
13 changed files with 556 additions and 425 deletions
@@ -16,12 +16,15 @@ export function createFetchCookiecutterAction(options: {
reader: UrlReader;
integrations: ScmIntegrations;
containerRunner: ContainerRunner;
}): TemplateAction<{
url: string;
targetPath?: string | undefined;
values: JsonObject;
copyWithoutRender?: string[] | undefined;
extensions?: string[] | undefined;
imageName?: string | undefined;
}>;
}): TemplateAction<
{
url: string;
targetPath?: string | undefined;
values: JsonObject;
copyWithoutRender?: string[] | undefined;
extensions?: string[] | undefined;
imageName?: string | undefined;
},
{}
>;
```
@@ -15,10 +15,13 @@ export function createFetchRailsAction(options: {
integrations: ScmIntegrations;
containerRunner: ContainerRunner;
allowedImageNames?: string[];
}): TemplateAction<{
url: string;
targetPath?: string | undefined;
values: JsonObject;
imageName?: string | undefined;
}>;
}): TemplateAction<
{
url: string;
targetPath?: string | undefined;
values: JsonObject;
imageName?: string | undefined;
},
{}
>;
```
@@ -9,13 +9,16 @@ import { TemplateAction } from '@backstage/plugin-scaffolder-node';
// @public
export function createSentryCreateProjectAction(options: {
config: Config;
}): TemplateAction<{
organizationSlug: string;
teamSlug: string;
name: string;
slug?: string | undefined;
authToken?: string | undefined;
}>;
}): TemplateAction<
{
organizationSlug: string;
teamSlug: string;
name: string;
slug?: string | undefined;
authToken?: string | undefined;
},
{}
>;
// (No @packageDocumentation comment for this package)
```
@@ -7,9 +7,12 @@ import { JsonObject } from '@backstage/types';
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
// @public
export function createRunYeomanAction(): TemplateAction<{
namespace: string;
args?: string[] | undefined;
options?: JsonObject | undefined;
}>;
export function createRunYeomanAction(): TemplateAction<
{
namespace: string;
args?: string[] | undefined;
options?: JsonObject | undefined;
},
{}
>;
```
+412 -327
View File
@@ -24,6 +24,7 @@ import { Observable } from '@backstage/types';
import { Octokit } from 'octokit';
import { PluginDatabaseManager } from '@backstage/backend-common';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
import { Schema } from 'jsonschema';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { ScmIntegrations } from '@backstage/integration';
import { SpawnOptionsWithoutStdio } from 'child_process';
@@ -33,6 +34,9 @@ import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common';
import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node';
import { UrlReader } from '@backstage/backend-common';
import { Writable } from 'stream';
import { z } from 'zod';
import { ZodType } from 'zod';
import { ZodTypeDef } from 'zod';
// @public @deprecated (undocumented)
export type ActionContext<TInput extends JsonObject> = ActionContext_2<TInput>;
@@ -66,39 +70,63 @@ export function createCatalogRegisterAction(options: {
repoContentsUrl: string;
catalogInfoPath?: string | undefined;
optional?: boolean | undefined;
}
},
{}
>;
// Warning: (ae-missing-release-tag) "createCatalogWriteAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function createCatalogWriteAction(): TemplateAction<{
filePath?: string | undefined;
entity: Entity;
}>;
// @public
export function createDebugLogAction(): TemplateAction_2<{
message?: string | undefined;
listWorkspace?: boolean | undefined;
}>;
export function createCatalogWriteAction(): TemplateAction_2<
unknown,
z.ZodObject<
{
filePath: z.ZodOptional<z.ZodString>;
entity: z.ZodOptional<z.ZodObject<{}, 'strip', z.ZodTypeAny, {}, {}>>;
},
'strip',
z.ZodTypeAny,
{
filePath?: string | undefined;
entity?: {} | undefined;
},
{
filePath?: string | undefined;
entity?: {} | undefined;
}
>
>;
// @public
export function createDebugLogAction(): TemplateAction_2<
{
message?: string | undefined;
listWorkspace?: boolean | undefined;
},
{}
>;
// @public
export function createFetchCatalogEntityAction(options: {
catalogClient: CatalogApi;
}): TemplateAction_2<{
entityRef: string;
optional?: boolean | undefined;
}>;
}): TemplateAction_2<
{
entityRef: string;
optional?: boolean | undefined;
},
{}
>;
// @public
export function createFetchPlainAction(options: {
reader: UrlReader;
integrations: ScmIntegrations;
}): TemplateAction_2<{
url: string;
targetPath?: string | undefined;
}>;
}): TemplateAction_2<
{
url: string;
targetPath?: string | undefined;
},
{}
>;
// @public
export function createFetchTemplateAction(options: {
@@ -106,57 +134,72 @@ export function createFetchTemplateAction(options: {
integrations: ScmIntegrations;
additionalTemplateFilters?: Record<string, TemplateFilter>;
additionalTemplateGlobals?: Record<string, TemplateGlobal>;
}): TemplateAction_2<{
url: string;
targetPath?: string | undefined;
values: any;
templateFileExtension?: string | boolean | undefined;
copyWithoutRender?: string[] | undefined;
copyWithoutTemplating?: string[] | undefined;
cookiecutterCompat?: boolean | undefined;
replace?: boolean | undefined;
}>;
}): TemplateAction_2<
{
url: string;
targetPath?: string | undefined;
values: any;
templateFileExtension?: string | boolean | undefined;
copyWithoutRender?: string[] | undefined;
copyWithoutTemplating?: string[] | undefined;
cookiecutterCompat?: boolean | undefined;
replace?: boolean | undefined;
},
{}
>;
// @public
export const createFilesystemDeleteAction: () => TemplateAction_2<{
files: string[];
}>;
export const createFilesystemDeleteAction: () => TemplateAction_2<
{
files: string[];
},
{}
>;
// @public
export const createFilesystemRenameAction: () => TemplateAction_2<{
files: Array<{
from: string;
to: string;
overwrite?: boolean;
}>;
}>;
export const createFilesystemRenameAction: () => TemplateAction_2<
{
files: Array<{
from: string;
to: string;
overwrite?: boolean;
}>;
},
{}
>;
// @public
export function createGithubActionsDispatchAction(options: {
integrations: ScmIntegrations;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
workflowId: string;
branchOrTagName: string;
workflowInputs?:
| {
[key: string]: string;
}
| undefined;
token?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
workflowId: string;
branchOrTagName: string;
workflowInputs?:
| {
[key: string]: string;
}
| undefined;
token?: string | undefined;
},
{}
>;
// @public
export function createGithubIssuesLabelAction(options: {
integrations: ScmIntegrationRegistry;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
number: number;
labels: string[];
token?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
number: number;
labels: string[];
token?: string | undefined;
},
{}
>;
// @public
export interface CreateGithubPullRequestActionOptions {
@@ -181,344 +224,386 @@ export type CreateGithubPullRequestClientFactoryInput = {
export function createGithubRepoCreateAction(options: {
integrations: ScmIntegrationRegistry;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
homepage?: string | undefined;
access?: string | undefined;
deleteBranchOnMerge?: boolean | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
allowRebaseMerge?: boolean | undefined;
allowSquashMerge?: boolean | undefined;
squashMergeCommitTitle?: 'PR_TITLE' | 'COMMIT_OR_PR_TITLE' | undefined;
squashMergeCommitMessage?:
| 'PR_BODY'
| 'COMMIT_MESSAGES'
| 'BLANK'
| undefined;
allowMergeCommit?: boolean | undefined;
allowAutoMerge?: boolean | undefined;
requireCodeOwnerReviews?: boolean | undefined;
bypassPullRequestAllowances?:
| {
users?: string[] | undefined;
teams?: string[] | undefined;
apps?: string[] | undefined;
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[] | undefined;
}
| undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
| {
user: string;
access: string;
}
| {
team: string;
access: string;
}
| {
username: string;
access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage';
}
)[]
| undefined;
hasProjects?: boolean | undefined;
hasWiki?: boolean | undefined;
hasIssues?: boolean | undefined;
token?: string | undefined;
topics?: string[] | undefined;
requireCommitSigning?: boolean | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
homepage?: string | undefined;
access?: string | undefined;
deleteBranchOnMerge?: boolean | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
allowRebaseMerge?: boolean | undefined;
allowSquashMerge?: boolean | undefined;
squashMergeCommitTitle?: 'PR_TITLE' | 'COMMIT_OR_PR_TITLE' | undefined;
squashMergeCommitMessage?:
| 'PR_BODY'
| 'COMMIT_MESSAGES'
| 'BLANK'
| undefined;
allowMergeCommit?: boolean | undefined;
allowAutoMerge?: boolean | undefined;
requireCodeOwnerReviews?: boolean | undefined;
bypassPullRequestAllowances?:
| {
users?: string[] | undefined;
teams?: string[] | undefined;
apps?: string[] | undefined;
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[] | undefined;
}
| undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
| {
user: string;
access: string;
}
| {
team: string;
access: string;
}
| {
username: string;
access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage';
}
)[]
| undefined;
hasProjects?: boolean | undefined;
hasWiki?: boolean | undefined;
hasIssues?: boolean | undefined;
token?: string | undefined;
topics?: string[] | undefined;
requireCommitSigning?: boolean | undefined;
},
{}
>;
// @public
export function createGithubRepoPushAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
protectDefaultBranch?: boolean | undefined;
protectEnforceAdmins?: boolean | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
requireCodeOwnerReviews?: boolean | undefined;
dismissStaleReviews?: boolean | undefined;
bypassPullRequestAllowances?:
| {
users?: string[];
teams?: string[];
apps?: string[];
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[];
}
| undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
requiredCommitSigning?: boolean | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
protectDefaultBranch?: boolean | undefined;
protectEnforceAdmins?: boolean | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
requireCodeOwnerReviews?: boolean | undefined;
dismissStaleReviews?: boolean | undefined;
bypassPullRequestAllowances?:
| {
users?: string[];
teams?: string[];
apps?: string[];
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[];
}
| undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
requiredCommitSigning?: boolean | undefined;
},
{}
>;
// @public
export function createGithubWebhookAction(options: {
integrations: ScmIntegrationRegistry;
defaultWebhookSecret?: string;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
webhookUrl: string;
webhookSecret?: string | undefined;
events?: string[] | undefined;
active?: boolean | undefined;
contentType?: 'form' | 'json' | undefined;
insecureSsl?: boolean | undefined;
token?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
webhookUrl: string;
webhookSecret?: string | undefined;
events?: string[] | undefined;
active?: boolean | undefined;
contentType?: 'form' | 'json' | undefined;
insecureSsl?: boolean | undefined;
token?: string | undefined;
},
{}
>;
// @public
export function createPublishAzureAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
},
{}
>;
// @public @deprecated
export function createPublishBitbucketAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
enableLFS?: boolean | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
enableLFS?: boolean | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
},
{}
>;
// @public
export function createPublishBitbucketCloudAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
},
{}
>;
// @public
export function createPublishBitbucketServerAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
enableLFS?: boolean | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
sourcePath?: string | undefined;
enableLFS?: boolean | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
},
{}
>;
// @public
export function createPublishGerritAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
description: string;
defaultBranch?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
sourcePath?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description: string;
defaultBranch?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
sourcePath?: string | undefined;
},
{}
>;
// @public
export function createPublishGerritReviewAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
branch?: string | undefined;
sourcePath?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
branch?: string | undefined;
sourcePath?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
},
{}
>;
// @public
export function createPublishGithubAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
githubCredentialsProvider?: GithubCredentialsProvider;
}): TemplateAction_2<{
repoUrl: string;
description?: string | undefined;
homepage?: string | undefined;
access?: string | undefined;
defaultBranch?: string | undefined;
protectDefaultBranch?: boolean | undefined;
protectEnforceAdmins?: boolean | undefined;
deleteBranchOnMerge?: boolean | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
allowRebaseMerge?: boolean | undefined;
allowSquashMerge?: boolean | undefined;
squashMergeCommitTitle?: 'PR_TITLE' | 'COMMIT_OR_PR_TITLE' | undefined;
squashMergeCommitMessage?:
| 'PR_BODY'
| 'COMMIT_MESSAGES'
| 'BLANK'
| undefined;
allowMergeCommit?: boolean | undefined;
allowAutoMerge?: boolean | undefined;
sourcePath?: string | undefined;
bypassPullRequestAllowances?:
| {
users?: string[];
teams?: string[];
apps?: string[];
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[];
}
| undefined;
requireCodeOwnerReviews?: boolean | undefined;
dismissStaleReviews?: boolean | undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
| {
user: string;
access: string;
}
| {
team: string;
access: string;
}
| {
username: string;
access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage';
}
)[]
| undefined;
hasProjects?: boolean | undefined;
hasWiki?: boolean | undefined;
hasIssues?: boolean | undefined;
token?: string | undefined;
topics?: string[] | undefined;
requiredCommitSigning?: boolean | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
description?: string | undefined;
homepage?: string | undefined;
access?: string | undefined;
defaultBranch?: string | undefined;
protectDefaultBranch?: boolean | undefined;
protectEnforceAdmins?: boolean | undefined;
deleteBranchOnMerge?: boolean | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
allowRebaseMerge?: boolean | undefined;
allowSquashMerge?: boolean | undefined;
squashMergeCommitTitle?: 'PR_TITLE' | 'COMMIT_OR_PR_TITLE' | undefined;
squashMergeCommitMessage?:
| 'PR_BODY'
| 'COMMIT_MESSAGES'
| 'BLANK'
| undefined;
allowMergeCommit?: boolean | undefined;
allowAutoMerge?: boolean | undefined;
sourcePath?: string | undefined;
bypassPullRequestAllowances?:
| {
users?: string[];
teams?: string[];
apps?: string[];
}
| undefined;
requiredApprovingReviewCount?: number | undefined;
restrictions?:
| {
users: string[];
teams: string[];
apps?: string[];
}
| undefined;
requireCodeOwnerReviews?: boolean | undefined;
dismissStaleReviews?: boolean | undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
requiredConversationResolution?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
| {
user: string;
access: string;
}
| {
team: string;
access: string;
}
| {
username: string;
access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage';
}
)[]
| undefined;
hasProjects?: boolean | undefined;
hasWiki?: boolean | undefined;
hasIssues?: boolean | undefined;
token?: string | undefined;
topics?: string[] | undefined;
requiredCommitSigning?: boolean | undefined;
},
{}
>;
// @public
export const createPublishGithubPullRequestAction: ({
integrations,
githubCredentialsProvider,
clientFactory,
}: CreateGithubPullRequestActionOptions) => TemplateAction_2<{
title: string;
branchName: string;
description: string;
repoUrl: string;
draft?: boolean | undefined;
targetPath?: string | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
reviewers?: string[] | undefined;
teamReviewers?: string[] | undefined;
}>;
}: CreateGithubPullRequestActionOptions) => TemplateAction_2<
{
title: string;
branchName: string;
description: string;
repoUrl: string;
draft?: boolean | undefined;
targetPath?: string | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
reviewers?: string[] | undefined;
teamReviewers?: string[] | undefined;
},
{}
>;
// @public
export function createPublishGitlabAction(options: {
integrations: ScmIntegrationRegistry;
config: Config;
}): TemplateAction_2<{
repoUrl: string;
defaultBranch?: string | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
setUserAsOwner?: boolean | undefined;
topics?: string[] | undefined;
}>;
}): TemplateAction_2<
{
repoUrl: string;
defaultBranch?: string | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
setUserAsOwner?: boolean | undefined;
topics?: string[] | undefined;
},
{}
>;
// @public
export const createPublishGitlabMergeRequestAction: (options: {
integrations: ScmIntegrationRegistry;
}) => TemplateAction_2<{
repoUrl: string;
title: string;
description: string;
branchName: string;
sourcePath?: string | undefined;
targetPath?: string | undefined;
token?: string | undefined;
commitAction?: 'update' | 'delete' | 'create' | undefined;
projectid?: string | undefined;
removeSourceBranch?: boolean | undefined;
assignee?: string | undefined;
}>;
}) => TemplateAction_2<
{
repoUrl: string;
title: string;
description: string;
branchName: string;
sourcePath?: string | undefined;
targetPath?: string | undefined;
token?: string | undefined;
commitAction?: 'update' | 'delete' | 'create' | undefined;
projectid?: string | undefined;
removeSourceBranch?: boolean | undefined;
assignee?: string | undefined;
},
{}
>;
// @public
export function createRouter(options: RouterOptions): Promise<express.Router>;
// @public @deprecated (undocumented)
export const createTemplateAction: <TInput extends JsonObject>(
templateAction: TemplateAction_2<TInput>,
) => TemplateAction_2<TInput>;
export const createTemplateAction: <
TParams,
TInputSchema extends ZodType<any, ZodTypeDef, any> | Schema = {},
>(
templateAction: TemplateAction_2<TParams, TInputSchema>,
) => TemplateAction_2<TParams, TInputSchema>;
// @public
export type CreateWorkerOptions = {
@@ -17,10 +17,8 @@
import fs from 'fs-extra';
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
import * as yaml from 'yaml';
import { Entity } from '@backstage/catalog-model';
import { resolveSafeChildPath } from '@backstage/backend-common';
import { z } from 'zod';
import { convertZodtoJson } from '../../../tasks';
const id = 'catalog:write';
@@ -60,23 +58,24 @@ const examples = [
*/
export function createCatalogWriteAction() {
const inputSchema = convertZodtoJson(
z.object({
filePath: z.string().optional().describe('Defaults to catalog-info.yaml'),
entity: z
.string()
.optional()
.describe('You can provide the same values used in the Entity schema.'),
}),
);
return createTemplateAction<{ filePath?: string; entity: Entity }>({
id,
return createTemplateAction({
id: 'catalog:write',
description: 'Writes the catalog-info.yaml for your template',
examples,
schema: {
input: inputSchema,
input: z.object({
filePath: z
.string()
.optional()
.describe('Defaults to catalog-info.yaml'),
entity: z
.object({})
.optional()
.describe(
'You can provide the same values used in the Entity schema.',
),
}),
},
examples,
supportsDryRun: true,
async handler(ctx) {
ctx.logStream.write(`Writing catalog-info.yaml`);
@@ -1,41 +0,0 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
import { Schema } from 'jsonschema';
interface TemplateActionSchema {
readonly schema: {
input?: Schema;
output?: Schema;
};
}
/** @public */
export function convertZodtoJson<
TInputSchema extends z.ZodType = z.ZodType<any, any, {}>,
TOutputSchema extends z.ZodType = z.ZodType<any, any, {}>,
>(
zodInputSchema?: TInputSchema,
zodOutputSchema?: TOutputSchema,
): TemplateActionSchema {
return {
schema: {
input: zodInputSchema ? zodToJsonSchema(zodInputSchema) : undefined,
output: zodOutputSchema ? zodToJsonSchema(zodOutputSchema) : undefined,
},
};
}
@@ -26,6 +26,7 @@ import { TaskContext } from './types';
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
import { TaskSecrets } from '@backstage/plugin-scaffolder-node';
import { UserEntity } from '@backstage/catalog-model';
import { z } from 'zod';
// The Stream module is lazy loaded, so make sure it's in the module cache before mocking fs
void winston.transports.Stream;
@@ -103,6 +104,18 @@ describe('DefaultWorkflowRunner', () => {
},
});
actionRegistry.register({
id: 'jest-zod-validated-action',
description: 'Mock action for testing',
supportsDryRun: true,
handler: fakeActionHandler,
schema: {
input: z.object({
foo: z.number(),
}),
},
});
actionRegistry.register({
id: 'output-action',
description: 'Mock action for testing',
@@ -151,6 +164,41 @@ describe('DefaultWorkflowRunner', () => {
);
});
it('should throw an error if the action has a zod schema and the input does not match', async () => {
const task = createMockTaskWithSpec({
apiVersion: 'scaffolder.backstage.io/v1beta3',
parameters: {},
output: {},
steps: [
{ id: 'test', name: 'name', action: 'jest-zod-validated-action' },
],
});
await expect(runner.execute(task)).rejects.toThrow(
/Invalid input passed to action jest-validated-action, instance requires property \"foo\"/,
);
});
it('should run the action when the zod validation passes', async () => {
const task = createMockTaskWithSpec({
apiVersion: 'scaffolder.backstage.io/v1beta3',
parameters: {},
output: {},
steps: [
{
id: 'test',
name: 'name',
action: 'jest-zod-validated-action',
input: { foo: 1 },
},
],
});
await runner.execute(task);
expect(fakeActionHandler).toHaveBeenCalledTimes(1);
});
it('should run the action when the validation passes', async () => {
const task = createMockTaskWithSpec({
apiVersion: 'scaffolder.backstage.io/v1beta3',
+19 -6
View File
@@ -13,6 +13,7 @@ import { Schema } from 'jsonschema';
import { TemplateInfo } from '@backstage/plugin-scaffolder-common';
import { UserEntity } from '@backstage/catalog-model';
import { Writable } from 'stream';
import { z } from 'zod';
// @public
export type ActionContext<TInput extends JsonObject> = {
@@ -32,9 +33,12 @@ export type ActionContext<TInput extends JsonObject> = {
};
// @public
export const createTemplateAction: <TInput extends JsonObject>(
templateAction: TemplateAction<TInput>,
) => TemplateAction<TInput>;
export const createTemplateAction: <
TParams,
TInputSchema extends z.ZodType<any, z.ZodTypeDef, any> | Schema = {},
>(
templateAction: TemplateAction<TParams, TInputSchema>,
) => TemplateAction<TParams, TInputSchema>;
// @alpha
export interface ScaffolderActionsExtensionPoint {
@@ -51,7 +55,10 @@ export type TaskSecrets = Record<string, string> & {
};
// @public (undocumented)
export type TemplateAction<TInput extends JsonObject> = {
export type TemplateAction<
TParams,
TInputSchema extends Schema | z.ZodType = {},
> = {
id: string;
description?: string;
examples?: {
@@ -60,9 +67,15 @@ export type TemplateAction<TInput extends JsonObject> = {
}[];
supportsDryRun?: boolean;
schema?: {
input?: Schema;
input?: TInputSchema;
output?: Schema;
};
handler: (ctx: ActionContext<TInput>) => Promise<void>;
handler: (
ctx: ActionContext<
TInputSchema extends z.ZodType<any, any, infer IReturn>
? IReturn
: TParams
>,
) => Promise<void>;
};
```
+2 -1
View File
@@ -29,7 +29,8 @@
"@backstage/plugin-scaffolder-common": "workspace:^",
"@backstage/types": "workspace:^",
"jsonschema": "^1.2.6",
"winston": "^3.2.1"
"winston": "^3.2.1",
"zod": "~3.18.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
@@ -14,17 +14,21 @@
* limitations under the License.
*/
import { JsonObject } from '@backstage/types';
import { TemplateAction } from './types';
import { z } from 'zod';
import { Schema } from 'jsonschema';
/**
* This function is used to create new template actions to get type safety.
*
* @public
*/
export const createTemplateAction = <TInput extends JsonObject>(
templateAction: TemplateAction<TInput>,
): TemplateAction<TInput> => {
export const createTemplateAction = <
TParams,
TInputSchema extends Schema | z.ZodType = {},
>(
templateAction: TemplateAction<TParams, TInputSchema>,
): TemplateAction<TParams, TInputSchema> => {
// TODO(blam): Can add some more validation here to validate the action later on
return templateAction;
};
+13 -4
View File
@@ -21,7 +21,7 @@ import { Schema } from 'jsonschema';
import { TaskSecrets } from '../tasks/types';
import { TemplateInfo } from '@backstage/plugin-scaffolder-common';
import { UserEntity } from '@backstage/catalog-model';
import { z } from 'zod';
/**
* ActionContext is passed into scaffolder actions.
* @public
@@ -63,14 +63,23 @@ export type ActionContext<TInput extends JsonObject> = {
};
/** @public */
export type TemplateAction<TInput extends JsonObject> = {
export type TemplateAction<
TParams,
TInputSchema extends Schema | z.ZodType = {},
> = {
id: string;
description?: string;
examples?: { description: string; example: string }[];
supportsDryRun?: boolean;
schema?: {
input?: Schema;
input?: TInputSchema;
output?: Schema;
};
handler: (ctx: ActionContext<TInput>) => Promise<void>;
handler: (
ctx: ActionContext<
TInputSchema extends z.ZodType<any, any, infer IReturn>
? IReturn
: TParams
>,
) => Promise<void>;
};