From a7eb36c6e3837926a6be28a9c6541752b47b13b2 Mon Sep 17 00:00:00 2001 From: Andreas Berger Date: Thu, 16 Mar 2023 18:25:52 +0100 Subject: [PATCH] Improve type-check for scaffolder output parameters If an action uses zod to define the actions output schema, it is now ensured that `ctx.output` is using only the defined properties Signed-off-by: Andreas Berger --- .changeset/good-papayas-lay.md | 6 + .../api-report.md | 19 +- .../api-report.md | 65 +- .../api-report.md | 15 +- .../api-report.md | 17 +- .../api-report.md | 13 +- plugins/scaffolder-backend/api-report.md | 734 ++++++++++-------- .../actions/builtin/catalog/fetch.ts | 68 +- plugins/scaffolder-node/api-report.md | 31 +- .../src/actions/createTemplateAction.ts | 18 +- plugins/scaffolder-node/src/actions/types.ts | 19 +- 11 files changed, 567 insertions(+), 438 deletions(-) create mode 100644 .changeset/good-papayas-lay.md diff --git a/.changeset/good-papayas-lay.md b/.changeset/good-papayas-lay.md new file mode 100644 index 0000000000..258397a14f --- /dev/null +++ b/.changeset/good-papayas-lay.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +Improve type-check for scaffolder output parameters diff --git a/plugins/scaffolder-backend-module-cookiecutter/api-report.md b/plugins/scaffolder-backend-module-cookiecutter/api-report.md index 2944f5240e..997e6b00e9 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/api-report.md +++ b/plugins/scaffolder-backend-module-cookiecutter/api-report.md @@ -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; + }, + undefined +>; ``` diff --git a/plugins/scaffolder-backend-module-gitlab/api-report.md b/plugins/scaffolder-backend-module-gitlab/api-report.md index 6db732fdcc..fcd5fada08 100644 --- a/plugins/scaffolder-backend-module-gitlab/api-report.md +++ b/plugins/scaffolder-backend-module-gitlab/api-report.md @@ -9,40 +9,49 @@ import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public export const createGitlabProjectAccessTokenAction: (options: { integrations: ScmIntegrationRegistry; -}) => TemplateAction<{ - repoUrl: string; - projectId: string | number; - name: string; - accessLevel: number; - scopes: string[]; - token?: string | undefined; -}>; +}) => TemplateAction< + { + repoUrl: string; + projectId: string | number; + name: string; + accessLevel: number; + scopes: string[]; + token?: string | undefined; + }, + undefined +>; // @public export const createGitlabProjectDeployTokenAction: (options: { integrations: ScmIntegrationRegistry; -}) => TemplateAction<{ - repoUrl: string; - projectId: string | number; - name: string; - username: string; - scopes: string[]; - token?: string | undefined; -}>; +}) => TemplateAction< + { + repoUrl: string; + projectId: string | number; + name: string; + username: string; + scopes: string[]; + token?: string | undefined; + }, + undefined +>; // @public export const createGitlabProjectVariableAction: (options: { integrations: ScmIntegrationRegistry; -}) => TemplateAction<{ - repoUrl: string; - projectId: string | number; - key: string; - value: string; - variableType: string; - variableProtected: boolean; - masked: boolean; - raw: boolean; - environmentScope: string; - token?: string | undefined; -}>; +}) => TemplateAction< + { + repoUrl: string; + projectId: string | number; + key: string; + value: string; + variableType: string; + variableProtected: boolean; + masked: boolean; + raw: boolean; + environmentScope: string; + token?: string | undefined; + }, + undefined +>; ``` diff --git a/plugins/scaffolder-backend-module-rails/api-report.md b/plugins/scaffolder-backend-module-rails/api-report.md index c91daa1344..5c27c8b957 100644 --- a/plugins/scaffolder-backend-module-rails/api-report.md +++ b/plugins/scaffolder-backend-module-rails/api-report.md @@ -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; + }, + undefined +>; ``` diff --git a/plugins/scaffolder-backend-module-sentry/api-report.md b/plugins/scaffolder-backend-module-sentry/api-report.md index ef09bdebc0..3583981b2d 100644 --- a/plugins/scaffolder-backend-module-sentry/api-report.md +++ b/plugins/scaffolder-backend-module-sentry/api-report.md @@ -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; + }, + undefined +>; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/scaffolder-backend-module-yeoman/api-report.md b/plugins/scaffolder-backend-module-yeoman/api-report.md index 562fe52ac2..62a6d62156 100644 --- a/plugins/scaffolder-backend-module-yeoman/api-report.md +++ b/plugins/scaffolder-backend-module-yeoman/api-report.md @@ -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; + }, + undefined +>; ``` diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index a50bb7a515..62626c551e 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -78,49 +78,66 @@ export function createCatalogRegisterAction(options: { repoContentsUrl: string; catalogInfoPath?: string | undefined; optional?: boolean | undefined; - } + }, + undefined >; // @public -export function createCatalogWriteAction(): TemplateAction_2<{ - entity: {} & { - [k: string]: unknown; - }; - filePath?: string | undefined; -}>; +export function createCatalogWriteAction(): TemplateAction_2< + { + filePath?: string | undefined; + entity: {}; + }, + undefined +>; // @public -export function createDebugLogAction(): TemplateAction_2<{ - message?: string | undefined; - listWorkspace?: boolean | undefined; -}>; +export function createDebugLogAction(): TemplateAction_2< + { + message?: string | undefined; + listWorkspace?: boolean | undefined; + }, + undefined +>; // @public export function createFetchCatalogEntityAction(options: { catalogClient: CatalogApi; -}): TemplateAction_2<{ - entityRef?: string | undefined; - entityRefs?: string[] | undefined; - optional?: boolean | undefined; -}>; +}): TemplateAction_2< + { + optional?: boolean | undefined; + entityRef?: string | undefined; + entityRefs?: string[] | undefined; + }, + { + entities?: any[] | undefined; + entity?: any; + } +>; // @public export function createFetchPlainAction(options: { reader: UrlReader; integrations: ScmIntegrations; -}): TemplateAction_2<{ - url: string; - targetPath?: string | undefined; -}>; +}): TemplateAction_2< + { + url: string; + targetPath?: string | undefined; + }, + undefined +>; // @public export function createFetchPlainFileAction(options: { reader: UrlReader; integrations: ScmIntegrations; -}): TemplateAction_2<{ - url: string; - targetPath: string; -}>; +}): TemplateAction_2< + { + url: string; + targetPath: string; + }, + undefined +>; // @public export function createFetchTemplateAction(options: { @@ -128,57 +145,72 @@ export function createFetchTemplateAction(options: { integrations: ScmIntegrations; additionalTemplateFilters?: Record; additionalTemplateGlobals?: Record; -}): 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; + }, + undefined +>; // @public -export const createFilesystemDeleteAction: () => TemplateAction_2<{ - files: string[]; -}>; +export const createFilesystemDeleteAction: () => TemplateAction_2< + { + files: string[]; + }, + undefined +>; // @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; + }>; + }, + undefined +>; // @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; + }, + 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; + }, + undefined +>; // @public export interface CreateGithubPullRequestActionOptions { @@ -203,334 +235,373 @@ 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + 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; + }, + undefined +>; // @public export const createPublishGithubPullRequestAction: ( options: 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; -}>; +) => 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; + }, + 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; + }, + 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; + }, + undefined +>; // @public export function createRouter(options: RouterOptions): Promise; @@ -543,14 +614,17 @@ export const createTemplateAction: < TActionInput = TInputSchema extends ZodType ? IReturn : TParams, + TActionOutput = TOutputSchema extends ZodType + ? IReturn_1 + : undefined, >( action: TemplateActionOptions, -) => TemplateAction_2; +) => TemplateAction_2; // @public export function createWaitAction(options?: { maxWaitTime?: Duration | HumanDuration; -}): TemplateAction_2; +}): TemplateAction_2; // @public export type CreateWorkerOptions = { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts index 1bcbfa0006..d6abeee792 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts @@ -17,6 +17,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import yaml from 'yaml'; +import { z } from 'zod'; const id = 'catalog:fetch'; @@ -63,55 +64,46 @@ export function createFetchCatalogEntityAction(options: { }) { const { catalogClient } = options; - return createTemplateAction<{ - entityRef?: string; - entityRefs?: string[]; - optional?: boolean; - }>({ + return createTemplateAction({ id, description: 'Returns entity or entities from the catalog by entity reference(s)', examples, schema: { - input: { - type: 'object', - properties: { - entityRef: { - type: 'string', - title: 'Entity reference', + input: z.object({ + entityRef: z + .string({ description: 'Entity reference of the entity to get', - }, - entityRefs: { - type: 'array', - title: 'Entity references', + }) + .optional(), + entityRefs: z + .array(z.string(), { description: 'Entity references of the entities to get', - }, - optional: { - title: 'Optional', + }) + .optional(), + optional: z + .boolean({ description: 'Allow the entity or entities to optionally exist. Default: false', - type: 'boolean', - }, - }, - }, - output: { - type: 'object', - properties: { - entity: { - title: 'Entity found by the entity reference', - type: 'object', + }) + .optional(), + }), + output: z.object({ + entity: z + .any({ description: 'Object containing same values used in the Entity schema. Only when used with `entityRef` parameter.', - }, - entities: { - title: 'Entities found by the entity references', - type: 'array', - items: { type: 'object' }, - description: - 'Array containing objects with same values used in the Entity schema. Only when used with `entityRefs` parameter.', - }, - }, - }, + }) + .optional(), + entities: z + .array( + z.any({ + description: + 'Array containing objects with same values used in the Entity schema. Only when used with `entityRefs` parameter.', + }), + ) + .optional(), + }), }, async handler(ctx) { const { entityRef, entityRefs, optional } = ctx.input; diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index d5b9b73767..6e865e0cd6 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -16,13 +16,19 @@ import { Writable } from 'stream'; import { z } from 'zod'; // @public -export type ActionContext = { +export type ActionContext< + TActionInput extends JsonObject, + TActionOutput extends JsonObject | undefined = undefined, +> = { logger: Logger; logStream: Writable; secrets?: TaskSecrets; workspacePath: string; input: TActionInput; - output(name: string, value: JsonValue): void; + output( + name: TActionOutput extends undefined ? string : KEY, + value: TActionOutput extends undefined ? JsonValue : TActionOutput[KEY], + ): void; createTemporaryDirectory(): Promise; templateInfo?: TemplateInfo; isDryRun?: boolean; @@ -41,9 +47,12 @@ export const createTemplateAction: < TActionInput = TInputSchema extends z.ZodType ? IReturn : TParams, + TActionOutput = TOutputSchema extends z.ZodType + ? IReturn_1 + : undefined, >( action: TemplateActionOptions, -) => TemplateAction; +) => TemplateAction; // @alpha export interface ScaffolderActionsExtensionPoint { @@ -60,7 +69,10 @@ export type TaskSecrets = Record & { }; // @public (undocumented) -export type TemplateAction = { +export type TemplateAction< + TActionInput = unknown, + TActionOutput = undefined, +> = { id: string; description?: string; examples?: { @@ -72,7 +84,7 @@ export type TemplateAction = { input?: Schema; output?: Schema; }; - handler: (ctx: ActionContext) => Promise; + handler: (ctx: ActionContext) => Promise; }; // @public (undocumented) @@ -92,6 +104,13 @@ export type TemplateActionOptions< input?: TInputSchema; output?: TOutputSchema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext< + TActionInput, + TOutputSchema extends z.ZodType + ? IReturn + : undefined + >, + ) => Promise; }; ``` diff --git a/plugins/scaffolder-node/src/actions/createTemplateAction.ts b/plugins/scaffolder-node/src/actions/createTemplateAction.ts index 8c98042d8c..3540e9220d 100644 --- a/plugins/scaffolder-node/src/actions/createTemplateAction.ts +++ b/plugins/scaffolder-node/src/actions/createTemplateAction.ts @@ -33,7 +33,14 @@ export type TemplateActionOptions< input?: TInputSchema; output?: TOutputSchema; }; - handler: (ctx: ActionContext) => Promise; + handler: ( + ctx: ActionContext< + TActionInput, + TOutputSchema extends z.ZodType + ? IReturn + : undefined + >, + ) => Promise; }; /** @@ -48,9 +55,12 @@ export const createTemplateAction = < TActionInput = TInputSchema extends z.ZodType ? IReturn : TParams, + TActionOutput = TOutputSchema extends z.ZodType + ? IReturn + : undefined, >( action: TemplateActionOptions, -): TemplateAction => { +): TemplateAction => { const inputSchema = action.schema?.input && 'safeParseAsync' in action.schema.input ? zodToJsonSchema(action.schema.input) @@ -61,7 +71,7 @@ export const createTemplateAction = < ? zodToJsonSchema(action.schema.output) : action.schema?.output; - const templateAction = { + return { ...action, schema: { ...action.schema, @@ -69,6 +79,4 @@ export const createTemplateAction = < output: outputSchema, }, }; - - return templateAction; }; diff --git a/plugins/scaffolder-node/src/actions/types.ts b/plugins/scaffolder-node/src/actions/types.ts index 8827aaf98c..c81fafd9ad 100644 --- a/plugins/scaffolder-node/src/actions/types.ts +++ b/plugins/scaffolder-node/src/actions/types.ts @@ -17,7 +17,7 @@ import { Logger } from 'winston'; import { Writable } from 'stream'; import { JsonObject, JsonValue } from '@backstage/types'; -import { TaskSecrets } from '../tasks/types'; +import { TaskSecrets } from '../tasks'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; import { UserEntity } from '@backstage/catalog-model'; import { Schema } from 'jsonschema'; @@ -26,13 +26,19 @@ import { Schema } from 'jsonschema'; * ActionContext is passed into scaffolder actions. * @public */ -export type ActionContext = { +export type ActionContext< + TActionInput extends JsonObject, + TActionOutput extends JsonObject | undefined = undefined, +> = { logger: Logger; logStream: Writable; secrets?: TaskSecrets; workspacePath: string; input: TActionInput; - output(name: string, value: JsonValue): void; + output( + name: TActionOutput extends undefined ? string : KEY, + value: TActionOutput extends undefined ? JsonValue : TActionOutput[KEY], + ): void; /** * Creates a temporary directory for use by the action, which is then cleaned up automatically. @@ -68,7 +74,10 @@ export type ActionContext = { }; /** @public */ -export type TemplateAction = { +export type TemplateAction< + TActionInput = unknown, + TActionOutput = undefined, +> = { id: string; description?: string; examples?: { description: string; example: string }[]; @@ -77,5 +86,5 @@ export type TemplateAction = { input?: Schema; output?: Schema; }; - handler: (ctx: ActionContext) => Promise; + handler: (ctx: ActionContext) => Promise; };