import { GITHUB_DEPLOYMENT_CREATION_SCHEMA, GITHUB_DEPLOYMENT_SCHEMA, GITHUB_DEPLOYMENT_STATUS_SCHEMA, GITHUB_EMPTY_RESPONSE_SCHEMA, } from "@automate.ax/integration-contracts/github" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getGitHubApi } from "../lib/api" import { GITHUB_PAGE_INPUT_SCHEMA, GITHUB_REPOSITORY_INPUT_SCHEMA, } from "../lib/input-schemas" import { GITHUB_DEPLOYMENTS_READ_SCOPE, GITHUB_DEPLOYMENTS_WRITE_SCOPE, } from "../lib/scopes" /** Lists deployments in one GitHub repository. */ export const listGitHubDeployments = defineAction("List GitHub deployments") .describe("Lists and filters deployments in a GitHub repository.") .account("github", GITHUB_DEPLOYMENTS_READ_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ ...GITHUB_PAGE_INPUT_SCHEMA, /** Filter by environment name. */ environment: z.string().min(1).optional(), /** Filter by branch or tag ref. */ ref: z.string().min(1).optional(), /** Filter by exact commit SHA. */ sha: z.string().min(1).optional(), /** Filter by deployment task. */ task: z.string().min(1).optional(), }), ) .output(GITHUB_DEPLOYMENT_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.listDeployments({ owner: input.owner, page: input.page, per_page: input.perPage, repo: input.repository, ...(input.environment && { environment: input.environment }), ...(input.ref && { ref: input.ref }), ...(input.sha && { sha: input.sha }), ...(input.task && { task: input.task }), }) return data }) /** Gets one GitHub deployment by ID. */ export const getGitHubDeployment = defineAction("Get GitHub deployment") .describe("Gets one GitHub deployment by repository deployment ID.") .account("github", GITHUB_DEPLOYMENTS_READ_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ /** Repository-scoped deployment ID. */ deploymentId: z.number().int().positive(), }), ) .output(GITHUB_DEPLOYMENT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.getDeployment({ deployment_id: input.deploymentId, owner: input.owner, repo: input.repository, }) return data }) /** Creates a GitHub deployment. */ export const createGitHubDeployment = defineAction("Create GitHub deployment") .describe("Creates a deployment for a branch, tag, or commit.") .account("github", GITHUB_DEPLOYMENTS_WRITE_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ /** Automatically merge the default branch into the requested ref. */ autoMerge: z.boolean().prefault(true), /** Short deployment description. */ description: z.string().optional(), /** Deployment environment name. */ environment: z.string().min(1).optional(), /** JSON payload available to deployment consumers. */ payload: z.union([z.string(), z.record(z.string(), z.json())]).optional(), /** Whether the environment represents production. */ productionEnvironment: z.boolean().optional(), /** Branch, tag, or commit SHA to deploy. */ ref: z.string().min(1), /** Required commit-status contexts. */ requiredContexts: z.string().min(1).array().optional(), /** Deployment task. */ task: z.string().min(1).prefault("deploy"), /** Whether the environment is temporary. */ transientEnvironment: z.boolean().optional(), }), ) .output(GITHUB_DEPLOYMENT_CREATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.createDeployment({ auto_merge: input.autoMerge, owner: input.owner, ref: input.ref, repo: input.repository, task: input.task, ...(input.description !== undefined && { description: input.description, }), ...(input.environment && { environment: input.environment }), ...(input.payload !== undefined && { payload: input.payload }), ...(input.productionEnvironment !== undefined && { production_environment: input.productionEnvironment, }), ...(input.requiredContexts !== undefined && { required_contexts: input.requiredContexts, }), ...(input.transientEnvironment !== undefined && { transient_environment: input.transientEnvironment, }), }) return data }) /** Deletes a GitHub deployment allowed by the repository lifecycle. */ export const deleteGitHubDeployment = defineAction("Delete GitHub deployment") .describe("Deletes a deployment from a GitHub repository.") .account("github", GITHUB_DEPLOYMENTS_WRITE_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ /** Repository-scoped deployment ID. */ deploymentId: z.number().int().positive(), }), ) .output(GITHUB_EMPTY_RESPONSE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await getGitHubApi(account.secret).rest.repos.deleteDeployment({ deployment_id: input.deploymentId, owner: input.owner, repo: input.repository, }) return {} }) /** Lists statuses for one GitHub deployment. */ export const listGitHubDeploymentStatuses = defineAction( "List GitHub deployment statuses", ) .describe("Lists status history for one GitHub deployment.") .account("github", GITHUB_DEPLOYMENTS_READ_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ ...GITHUB_PAGE_INPUT_SCHEMA, /** Repository-scoped deployment ID. */ deploymentId: z.number().int().positive(), }), ) .output(GITHUB_DEPLOYMENT_STATUS_SCHEMA.array()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.listDeploymentStatuses({ deployment_id: input.deploymentId, owner: input.owner, page: input.page, per_page: input.perPage, repo: input.repository, }) return data }) /** Gets one GitHub deployment status by ID. */ export const getGitHubDeploymentStatus = defineAction( "Get GitHub deployment status", ) .describe("Gets one status from a GitHub deployment.") .account("github", GITHUB_DEPLOYMENTS_READ_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ /** Repository-scoped deployment ID. */ deploymentId: z.number().int().positive(), /** Repository-scoped deployment status ID. */ statusId: z.number().int().positive(), }), ) .output(GITHUB_DEPLOYMENT_STATUS_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.getDeploymentStatus({ deployment_id: input.deploymentId, owner: input.owner, repo: input.repository, status_id: input.statusId, }) return data }) /** Creates a status for one GitHub deployment. */ export const createGitHubDeploymentStatus = defineAction( "Create GitHub deployment status", ) .describe("Creates a lifecycle status for one GitHub deployment.") .account("github", GITHUB_DEPLOYMENTS_WRITE_SCOPE) .input( GITHUB_REPOSITORY_INPUT_SCHEMA.extend({ /** Automatically deactivate earlier successful deployments. */ autoInactive: z.boolean().prefault(true), /** Short status description. */ description: z.string().max(140).optional(), /** Repository-scoped deployment ID. */ deploymentId: z.number().int().positive(), /** Environment name for the status. */ environment: z.string().min(1).optional(), /** URL for the deployed environment. */ environmentUrl: z.url().optional(), /** URL with deployment logs. */ logUrl: z.url().optional(), /** Deployment state. */ state: z.enum([ "error", "failure", "inactive", "in_progress", "pending", "queued", "success", ]), /** Legacy URL with status details. */ targetUrl: z.url().optional(), }), ) .output(GITHUB_DEPLOYMENT_STATUS_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { data } = await getGitHubApi( account.secret, ).rest.repos.createDeploymentStatus({ auto_inactive: input.autoInactive, deployment_id: input.deploymentId, owner: input.owner, repo: input.repository, state: input.state, ...(input.description !== undefined && { description: input.description, }), ...(input.environment && { environment: input.environment }), ...(input.environmentUrl && { environment_url: input.environmentUrl, }), ...(input.logUrl && { log_url: input.logUrl }), ...(input.targetUrl && { target_url: input.targetUrl }), }) return data })