Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions __tests__/integration/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,26 @@ describe('integration tests', () => {
}
)
})

test('can deploy a release and wait for completion', async () => {
const config: ClientConfiguration = {
userAgentApp: 'Test',
instanceURL: apiClientConfig.instanceURL,
apiKey: apiClientConfig.apiKey
}

const client = await Client.create(config)

await createReleaseForTest(client)

const result = await createDeploymentFromInputs(client, {
...standardInputParameters,
releaseNumber: localReleaseNumber,
environments: ['Dev'],
waitForDeployment: true,
deploymentTimeoutMinutes: 2
})

expect(result.length).toBe(1)
})
})
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ inputs:
use_guided_failure:
default: "false"
description: 'Whether to use guided failure mode if errors occur during the deployment.'
wait_for_deployment:
default: "false"
description: 'Whether to wait for the deployment(s) to complete before finishing the action. If true, the action will fail if any deployment fails.'
deployment_timeout:
default: "60"
description: 'Maximum time (in minutes) to wait for deployment(s) to complete. Only used when wait_for_deployment is true. Defaults to 60 minutes.'
variables:
description: 'A multi-line list of prompted variable values. Format: name:value'
deploy_at:
Expand Down
28 changes: 27 additions & 1 deletion src/api-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
DeploymentRepository,
DeploymentEnvironmentV2,
DeploymentEnvironment,
ResourceCollection
ResourceCollection,
ServerTask,
ServerTaskWaiter,
TaskState
} from '@octopusdeploy/api-client'

export interface DeploymentResult {
Expand Down Expand Up @@ -73,6 +76,29 @@ export async function createDeploymentFromInputs(
}
})

if (parameters.waitForDeployment) {
client.info(`⏳ Waiting for deployment${response.DeploymentServerTasks.length > 1 ? 's' : '' } to complete...`)
const waiter = new ServerTaskWaiter(client, parameters.space)
const completedTasks = await waiter.waitForServerTasksToComplete(
results.map(r => r.serverTaskId),
5000,
parameters.deploymentTimeoutMinutes * 60 * 1000,
(serverTask: ServerTask): void => {
client.info(`Waiting for task ${serverTask.Id}. Current status: ${serverTask.State}`)
}
)

const failedTasks = completedTasks.filter(t => t.State !== TaskState.Success)
if (failedTasks.length > 0) {
const summary = failedTasks.map(t => `${t.Id} (${t.State})`).join(', ')
throw new Error(`One or more deployments did not complete successfully: ${summary}`)
}

client.info(`✅ The deployment${
response.DeploymentServerTasks.length > 1 ? 's' : ''
} completed successfully!`)
}

return results
}

Expand Down
11 changes: 10 additions & 1 deletion src/input-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface InputParameters {
variables?: PromptedVariableValues
runAt?: Date
noRunAfter?: Date
waitForDeployment?: boolean
deploymentTimeoutMinutes: number
}

export function getInputParameters(): InputParameters {
Expand All @@ -51,7 +53,9 @@ export function getInputParameters(): InputParameters {
useGuidedFailure: getBooleanInput('use_guided_failure') || undefined,
variables: variablesMap,
runAt: getInput('deploy_at') ? new Date(getInput('deploy_at')) : undefined,
noRunAfter: getInput('deploy_at_expiry') ? new Date(getInput('deploy_at_expiry')) : undefined
noRunAfter: getInput('deploy_at_expiry') ? new Date(getInput('deploy_at_expiry')) : undefined,
waitForDeployment: getBooleanInput('wait_for_deployment') || undefined,
deploymentTimeoutMinutes: parseInt(getInput('deployment_timeout') || '60', 10)
}

const errors: string[] = []
Expand All @@ -73,6 +77,11 @@ export function getInputParameters(): InputParameters {
)
}

const deploymentTimeout = parseInt(getInput('deployment_timeout') || '60', 10)
if (isNaN(deploymentTimeout) || deploymentTimeout <= 0) {
errors.push(`deployment_timeout '${getInput('deployment_timeout')}' must be a positive integer (minutes).`)
}

const deployAt = getInput('deploy_at')
if (deployAt && isNaN(new Date(deployAt).getTime())) {
errors.push(`deploy_at '${deployAt}' is not a valid ISO 8601 date-time string.`)
Expand Down