mirror of
https://github.com/actions/deploy-pages.git
synced 2026-02-09 03:45:15 +00:00
Merge pull request #279 from actions/artifacts-next-ga
Use artifacts v4
This commit is contained in:
@@ -6,11 +6,11 @@ This action is used to deploy [Actions artifacts][artifacts] to [GitHub Pages](h
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
See [action.yml](action.yml) for the various `inputs` this action supports.
|
See [action.yml](action.yml) for the various `inputs` this action supports (or [below](#inputs-📥)).
|
||||||
|
|
||||||
For examples that make use of this action, check out our [starter-workflows][starter-workflows] in a variety of frameworks.
|
For examples that make use of this action, check out our [starter-workflows][starter-workflows] in a variety of frameworks.
|
||||||
|
|
||||||
This action expects an artifact named `github-pages` to have been created prior to execution. This is done automatically when using [`actions/upload-pages-artifact`][upload-pages-artifact].
|
This action deploys a Pages site previously uploaded as an artifact (e.g. using [`actions/upload-pages-artifact`][upload-pages-artifact]).
|
||||||
|
|
||||||
We recommend this action to be used in a dedicated job:
|
We recommend this action to be used in a dedicated job:
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
|
|
||||||
There are a few important considerations to be aware of:
|
There are a few important considerations to be aware of:
|
||||||
|
|
||||||
1. The artifact being deployed must have been uploaded in a previous step, either in the same job or a separate job that doesn't execute until the upload is complete.
|
1. The artifact being deployed must have been uploaded in a previous step, either in the same job or a separate job that doesn't execute until the upload is complete. See [`actions/upload-pages-artifact`][upload-pages-artifact] for more information about the format of the artifact we expect.
|
||||||
|
|
||||||
2. The job that executes the deployment must at minimum have the following permissions:
|
2. The job that executes the deployment must at minimum have the following permissions:
|
||||||
- `pages: write`
|
- `pages: write`
|
||||||
|
|||||||
351
dist/index.js
generated
vendored
351
dist/index.js
generated
vendored
@@ -24859,7 +24859,7 @@ __export(dist_src_exports, {
|
|||||||
module.exports = __toCommonJS(dist_src_exports);
|
module.exports = __toCommonJS(dist_src_exports);
|
||||||
|
|
||||||
// pkg/dist-src/version.js
|
// pkg/dist-src/version.js
|
||||||
var VERSION = "9.1.4";
|
var VERSION = "9.1.5";
|
||||||
|
|
||||||
// pkg/dist-src/normalize-paginated-list-response.js
|
// pkg/dist-src/normalize-paginated-list-response.js
|
||||||
function normalizePaginatedListResponse(response) {
|
function normalizePaginatedListResponse(response) {
|
||||||
@@ -27863,102 +27863,6 @@ class Deprecation extends Error {
|
|||||||
exports.Deprecation = Deprecation;
|
exports.Deprecation = Deprecation;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 3703:
|
|
||||||
/***/ ((module) => {
|
|
||||||
|
|
||||||
// Source: 2014-06-11: http://en.wikipedia.org/wiki/HTTP_status_codes
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
100: "Continue",
|
|
||||||
101: "Switching Protocols",
|
|
||||||
102: "Processing",
|
|
||||||
|
|
||||||
200: "OK",
|
|
||||||
201: "Created",
|
|
||||||
202: "Accepted",
|
|
||||||
203: "Non-Authoritative Information",
|
|
||||||
204: "No Content",
|
|
||||||
205: "Reset Content",
|
|
||||||
206: "Partial Content",
|
|
||||||
207: "Multi-Status",
|
|
||||||
208: "Already Reported",
|
|
||||||
226: "IM Used",
|
|
||||||
|
|
||||||
300: "Multiple Choices",
|
|
||||||
301: "Moved Permanently",
|
|
||||||
302: "Found",
|
|
||||||
303: "See Other",
|
|
||||||
304: "Not Modified",
|
|
||||||
305: "Use Proxy",
|
|
||||||
306: "Switch Proxy",
|
|
||||||
307: "Temporary Redirect",
|
|
||||||
308: "Permanent Redirect",
|
|
||||||
|
|
||||||
400: "Bad Request",
|
|
||||||
401: "Unauthorized",
|
|
||||||
402: "Payment Required",
|
|
||||||
403: "Forbidden",
|
|
||||||
404: "Not Found",
|
|
||||||
405: "Method Not Allowed",
|
|
||||||
406: "Not Acceptable",
|
|
||||||
407: "Proxy Authentication Required",
|
|
||||||
408: "Request Timeout",
|
|
||||||
409: "Conflict",
|
|
||||||
410: "Gone",
|
|
||||||
411: "Length Required",
|
|
||||||
412: "Precondition Failed",
|
|
||||||
413: "Request Entity Too Large",
|
|
||||||
414: "Request-URI Too Long",
|
|
||||||
415: "Unsupported Media Type",
|
|
||||||
416: "Requested Range Not Satisfiable",
|
|
||||||
417: "Expectation Failed",
|
|
||||||
418: "I'm a teapot",
|
|
||||||
419: "Authentication Timeout",
|
|
||||||
420: "Method Failure",
|
|
||||||
420: "Enhance Your Calm",
|
|
||||||
422: "Unprocessable Entity",
|
|
||||||
423: "Locked",
|
|
||||||
424: "Failed Dependency",
|
|
||||||
426: "Upgrade Required",
|
|
||||||
428: "Precondition Required",
|
|
||||||
429: "Too Many Requests",
|
|
||||||
431: "Request Header Fields Too Large",
|
|
||||||
440: "Login Timeout",
|
|
||||||
444: "No Response",
|
|
||||||
449: "Retry With",
|
|
||||||
450: "Blocked by Windows Parental Controls",
|
|
||||||
451: "Unavailable For Legal Reasons",
|
|
||||||
451: "Redirect",
|
|
||||||
494: "Request Header Too Large",
|
|
||||||
495: "Cert Error",
|
|
||||||
496: "No Cert",
|
|
||||||
497: "HTTP to HTTPS",
|
|
||||||
499: "Client Closed Request",
|
|
||||||
|
|
||||||
500: "Internal Server Error",
|
|
||||||
501: "Not Implemented",
|
|
||||||
502: "Bad Gateway",
|
|
||||||
503: "Service Unavailable",
|
|
||||||
504: "Gateway Timeout",
|
|
||||||
505: "HTTP Version Not Supported",
|
|
||||||
506: "Variant Also Negotiates",
|
|
||||||
507: "Insufficient Storage",
|
|
||||||
508: "Loop Detected",
|
|
||||||
509: "Bandwidth Limit Exceeded",
|
|
||||||
510: "Not Extended",
|
|
||||||
511: "Network Authentication Required",
|
|
||||||
520: "Origin Error",
|
|
||||||
521: "Web server is down",
|
|
||||||
522: "Connection timed out",
|
|
||||||
523: "Proxy Declined Request",
|
|
||||||
524: "A timeout occurred",
|
|
||||||
598: "Network read timeout error",
|
|
||||||
599: "Network connect timeout error"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 1223:
|
/***/ 1223:
|
||||||
@@ -29659,6 +29563,18 @@ module.exports = class BodyReadable extends Readable {
|
|||||||
return super.destroy(err)
|
return super.destroy(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_destroy (err, callback) {
|
||||||
|
// Workaround for Node "bug". If the stream is destroyed in same
|
||||||
|
// tick as it is created, then a user who is waiting for a
|
||||||
|
// promise (i.e micro tick) for installing a 'error' listener will
|
||||||
|
// never get a chance and will always encounter an unhandled exception.
|
||||||
|
// - tick => process.nextTick(fn)
|
||||||
|
// - micro tick => queueMicrotask(fn)
|
||||||
|
queueMicrotask(() => {
|
||||||
|
callback(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
emit (ev, ...args) {
|
emit (ev, ...args) {
|
||||||
if (ev === 'data') {
|
if (ev === 'data') {
|
||||||
// Node < 16.7
|
// Node < 16.7
|
||||||
@@ -29763,7 +29679,7 @@ module.exports = class BodyReadable extends Readable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.closed) {
|
if (this._readableState.closeEmitted) {
|
||||||
return Promise.resolve(null)
|
return Promise.resolve(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29807,33 +29723,44 @@ function isUnusable (self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function consume (stream, type) {
|
async function consume (stream, type) {
|
||||||
if (isUnusable(stream)) {
|
|
||||||
throw new TypeError('unusable')
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!stream[kConsume])
|
assert(!stream[kConsume])
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
stream[kConsume] = {
|
if (isUnusable(stream)) {
|
||||||
type,
|
const rState = stream._readableState
|
||||||
stream,
|
if (rState.destroyed && rState.closeEmitted === false) {
|
||||||
resolve,
|
stream
|
||||||
reject,
|
.on('error', err => {
|
||||||
length: 0,
|
reject(err)
|
||||||
body: []
|
})
|
||||||
|
.on('close', () => {
|
||||||
|
reject(new TypeError('unusable'))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reject(rState.errored ?? new TypeError('unusable'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stream[kConsume] = {
|
||||||
|
type,
|
||||||
|
stream,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
length: 0,
|
||||||
|
body: []
|
||||||
|
}
|
||||||
|
|
||||||
|
stream
|
||||||
|
.on('error', function (err) {
|
||||||
|
consumeFinish(this[kConsume], err)
|
||||||
|
})
|
||||||
|
.on('close', function () {
|
||||||
|
if (this[kConsume].body !== null) {
|
||||||
|
consumeFinish(this[kConsume], new RequestAbortedError())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
queueMicrotask(() => consumeStart(stream[kConsume]))
|
||||||
}
|
}
|
||||||
|
|
||||||
stream
|
|
||||||
.on('error', function (err) {
|
|
||||||
consumeFinish(this[kConsume], err)
|
|
||||||
})
|
|
||||||
.on('close', function () {
|
|
||||||
if (this[kConsume].body !== null) {
|
|
||||||
consumeFinish(this[kConsume], new RequestAbortedError())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
process.nextTick(consumeStart, stream[kConsume])
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33209,12 +33136,19 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|||||||
body.resume()
|
body.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onAbort = function () {
|
const onClose = function () {
|
||||||
if (finished) {
|
// 'close' might be emitted *before* 'error' for
|
||||||
return
|
// broken streams. Wait a tick to avoid this case.
|
||||||
|
queueMicrotask(() => {
|
||||||
|
// It's only safe to remove 'error' listener after
|
||||||
|
// 'close'.
|
||||||
|
body.removeListener('error', onFinished)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!finished) {
|
||||||
|
const err = new RequestAbortedError()
|
||||||
|
queueMicrotask(() => onFinished(err))
|
||||||
}
|
}
|
||||||
const err = new RequestAbortedError()
|
|
||||||
queueMicrotask(() => onFinished(err))
|
|
||||||
}
|
}
|
||||||
const onFinished = function (err) {
|
const onFinished = function (err) {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
@@ -33232,8 +33166,7 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|||||||
body
|
body
|
||||||
.removeListener('data', onData)
|
.removeListener('data', onData)
|
||||||
.removeListener('end', onFinished)
|
.removeListener('end', onFinished)
|
||||||
.removeListener('error', onFinished)
|
.removeListener('close', onClose)
|
||||||
.removeListener('close', onAbort)
|
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
try {
|
try {
|
||||||
@@ -33256,7 +33189,7 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
|||||||
.on('data', onData)
|
.on('data', onData)
|
||||||
.on('end', onFinished)
|
.on('end', onFinished)
|
||||||
.on('error', onFinished)
|
.on('error', onFinished)
|
||||||
.on('close', onAbort)
|
.on('close', onClose)
|
||||||
|
|
||||||
if (body.resume) {
|
if (body.resume) {
|
||||||
body.resume()
|
body.resume()
|
||||||
@@ -51320,120 +51253,62 @@ function wrappy (fn, cb) {
|
|||||||
|
|
||||||
const core = __nccwpck_require__(2186)
|
const core = __nccwpck_require__(2186)
|
||||||
const github = __nccwpck_require__(5438)
|
const github = __nccwpck_require__(5438)
|
||||||
const hc = __nccwpck_require__(6255)
|
|
||||||
const { RequestError } = __nccwpck_require__(537)
|
|
||||||
const HttpStatusMessages = __nccwpck_require__(3703)
|
|
||||||
|
|
||||||
// All variables we need from the runtime are loaded here
|
async function getArtifactMetadata({ githubToken, runId, artifactName }) {
|
||||||
const getContext = __nccwpck_require__(8454)
|
const octokit = github.getOctokit(githubToken)
|
||||||
|
|
||||||
async function processRuntimeResponse(res, requestOptions) {
|
|
||||||
// Parse the response body as JSON
|
|
||||||
let obj = null
|
|
||||||
try {
|
|
||||||
const contents = await res.readBody()
|
|
||||||
if (contents && contents.length > 0) {
|
|
||||||
obj = JSON.parse(contents)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Invalid resource (contents not json); leaving resulting obj as null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific response shape aligned with Octokit
|
|
||||||
const response = {
|
|
||||||
url: res.message?.url || requestOptions.url,
|
|
||||||
status: res.message?.statusCode || 0,
|
|
||||||
headers: {
|
|
||||||
...res.message?.headers
|
|
||||||
},
|
|
||||||
data: obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forcibly throw errors for negative HTTP status codes!
|
|
||||||
// @actions/http-client doesn't do this by default.
|
|
||||||
// Mimic the errors thrown by Octokit for consistency.
|
|
||||||
if (response.status >= 400) {
|
|
||||||
// Try to get an error message from the response body
|
|
||||||
const errorMsg =
|
|
||||||
(typeof response.data === 'string' && response.data) ||
|
|
||||||
response.data?.error ||
|
|
||||||
response.data?.message ||
|
|
||||||
// Try the Node HTTP IncomingMessage's statusMessage property
|
|
||||||
res.message?.statusMessage ||
|
|
||||||
// Fallback to the HTTP status message based on the status code
|
|
||||||
HttpStatusMessages[response.status] ||
|
|
||||||
// Or if the status code is unexpected...
|
|
||||||
`Unknown error (${response.status})`
|
|
||||||
|
|
||||||
throw new RequestError(errorMsg, response.status, {
|
|
||||||
response,
|
|
||||||
request: requestOptions
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifactName }) {
|
|
||||||
const { runTimeUrl: RUNTIME_URL } = getContext()
|
|
||||||
const artifactExchangeUrl = `${RUNTIME_URL}_apis/pipelines/workflows/${workflowRunId}/artifacts?api-version=6.0-preview`
|
|
||||||
|
|
||||||
const httpClient = new hc.HttpClient()
|
|
||||||
let data = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const requestHeaders = {
|
core.info(`Fetching artifact metadata for ${artifactName} in run ${runId}`)
|
||||||
accept: 'application/json',
|
|
||||||
authorization: `Bearer ${runtimeToken}`
|
const response = await octokit.request(
|
||||||
}
|
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts?name={artifactName}',
|
||||||
const requestOptions = {
|
{
|
||||||
method: 'GET',
|
owner: github.context.repo.owner,
|
||||||
url: artifactExchangeUrl,
|
repo: github.context.repo.repo,
|
||||||
headers: {
|
run_id: runId,
|
||||||
...requestHeaders
|
artifactName: artifactName
|
||||||
},
|
}
|
||||||
body: null
|
)
|
||||||
|
|
||||||
|
const artifactCount = response.data.total_count
|
||||||
|
core.debug(`List artifact count: ${artifactCount}`)
|
||||||
|
|
||||||
|
if (artifactCount === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`No artifacts found for workflow run ${runId}. Ensure artifacts are uploaded with actions/artifact@v4 or later.`
|
||||||
|
)
|
||||||
|
} else if (artifactCount > 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Multiple artifact unexpectedly found for workflow run ${runId}. Artifact count is ${artifactCount}.`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Artifact exchange URL: ${artifactExchangeUrl}`)
|
const artifact = response.data.artifacts[0]
|
||||||
const res = await httpClient.get(artifactExchangeUrl, requestHeaders)
|
core.debug(`Artifact: ${JSON.stringify(artifact)}`)
|
||||||
|
|
||||||
// May throw a RequestError (HttpError)
|
const artifactSize = artifact.size_in_bytes
|
||||||
const response = await processRuntimeResponse(res, requestOptions)
|
if (!artifactSize) {
|
||||||
|
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
||||||
|
}
|
||||||
|
|
||||||
data = response.data
|
return {
|
||||||
core.debug(JSON.stringify(data))
|
id: artifact.id,
|
||||||
|
size: artifactSize
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error('Getting signed artifact URL failed', error)
|
core.error(
|
||||||
|
'Fetching artifact metadata failed. Is githubstatus.com reporting issues with API requests, Pages or Actions? Please re-run the deployment at a later time.',
|
||||||
|
error
|
||||||
|
)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const artifact = data?.value?.find(artifact => artifact.name === artifactName)
|
|
||||||
const artifactRawUrl = artifact?.url
|
|
||||||
if (!artifactRawUrl) {
|
|
||||||
throw new Error(
|
|
||||||
'No uploaded artifact was found! Please check if there are any errors at build step, or uploaded artifact name is correct.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const signedArtifactUrl = `${artifactRawUrl}&%24expand=SignedContent`
|
|
||||||
|
|
||||||
const artifactSize = artifact?.size
|
|
||||||
if (!artifactSize) {
|
|
||||||
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: signedArtifactUrl,
|
|
||||||
size: artifactSize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, idToken, isPreview = false }) {
|
async function createPagesDeployment({ githubToken, artifactId, buildVersion, idToken, isPreview = false }) {
|
||||||
const octokit = github.getOctokit(githubToken)
|
const octokit = github.getOctokit(githubToken)
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
artifact_url: artifactUrl,
|
artifact_id: artifactId,
|
||||||
pages_build_version: buildVersion,
|
pages_build_version: buildVersion,
|
||||||
oidc_token: idToken
|
oidc_token: idToken
|
||||||
}
|
}
|
||||||
@@ -51493,7 +51368,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSignedArtifactMetadata,
|
getArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
@@ -51510,9 +51385,7 @@ const core = __nccwpck_require__(2186)
|
|||||||
// Load variables from Actions runtime
|
// Load variables from Actions runtime
|
||||||
function getRequiredVars() {
|
function getRequiredVars() {
|
||||||
return {
|
return {
|
||||||
runTimeUrl: process.env.ACTIONS_RUNTIME_URL,
|
|
||||||
workflowRun: process.env.GITHUB_RUN_ID,
|
workflowRun: process.env.GITHUB_RUN_ID,
|
||||||
runTimeToken: process.env.ACTIONS_RUNTIME_TOKEN,
|
|
||||||
repositoryNwo: process.env.GITHUB_REPOSITORY,
|
repositoryNwo: process.env.GITHUB_REPOSITORY,
|
||||||
buildVersion: process.env.GITHUB_SHA,
|
buildVersion: process.env.GITHUB_SHA,
|
||||||
buildActor: process.env.GITHUB_ACTOR,
|
buildActor: process.env.GITHUB_ACTOR,
|
||||||
@@ -51547,7 +51420,7 @@ const core = __nccwpck_require__(2186)
|
|||||||
// All variables we need from the runtime are loaded here
|
// All variables we need from the runtime are loaded here
|
||||||
const getContext = __nccwpck_require__(8454)
|
const getContext = __nccwpck_require__(8454)
|
||||||
const {
|
const {
|
||||||
getSignedArtifactMetadata,
|
getArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
@@ -51575,9 +51448,7 @@ const SIZE_LIMIT_DESCRIPTION = '1 GB'
|
|||||||
class Deployment {
|
class Deployment {
|
||||||
constructor() {
|
constructor() {
|
||||||
const context = getContext()
|
const context = getContext()
|
||||||
this.runTimeUrl = context.runTimeUrl
|
|
||||||
this.repositoryNwo = context.repositoryNwo
|
this.repositoryNwo = context.repositoryNwo
|
||||||
this.runTimeToken = context.runTimeToken
|
|
||||||
this.buildVersion = context.buildVersion
|
this.buildVersion = context.buildVersion
|
||||||
this.buildActor = context.buildActor
|
this.buildActor = context.buildActor
|
||||||
this.actionsId = context.actionsId
|
this.actionsId = context.actionsId
|
||||||
@@ -51592,8 +51463,8 @@ class Deployment {
|
|||||||
this.startTime = null
|
this.startTime = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
|
// Call GitHub api to fetch artifacts matching the provided name and deploy to GitHub Pages
|
||||||
// by creating a deployment with that artifact
|
// by creating a deployment with that artifact id
|
||||||
async create(idToken) {
|
async create(idToken) {
|
||||||
if (Number(core.getInput('timeout')) > MAX_TIMEOUT) {
|
if (Number(core.getInput('timeout')) > MAX_TIMEOUT) {
|
||||||
core.warning(
|
core.warning(
|
||||||
@@ -51609,9 +51480,9 @@ class Deployment {
|
|||||||
core.debug(`Action ID: ${this.actionsId}`)
|
core.debug(`Action ID: ${this.actionsId}`)
|
||||||
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
||||||
|
|
||||||
const artifactData = await getSignedArtifactMetadata({
|
const artifactData = await getArtifactMetadata({
|
||||||
runtimeToken: this.runTimeToken,
|
githubToken: this.githubToken,
|
||||||
workflowRunId: this.workflowRun,
|
runId: this.workflowRun,
|
||||||
artifactName: this.artifactName
|
artifactName: this.artifactName
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51623,7 +51494,7 @@ class Deployment {
|
|||||||
|
|
||||||
const deployment = await createPagesDeployment({
|
const deployment = await createPagesDeployment({
|
||||||
githubToken: this.githubToken,
|
githubToken: this.githubToken,
|
||||||
artifactUrl: artifactData.url,
|
artifactId: artifactData.id,
|
||||||
buildVersion: this.buildVersion,
|
buildVersion: this.buildVersion,
|
||||||
idToken,
|
idToken,
|
||||||
isPreview: this.isPreview
|
isPreview: this.isPreview
|
||||||
|
|||||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/licenses.txt
generated
vendored
2
dist/licenses.txt
generated
vendored
@@ -465,8 +465,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
http-status-messages
|
|
||||||
|
|
||||||
once
|
once
|
||||||
ISC
|
ISC
|
||||||
The ISC License
|
The ISC License
|
||||||
|
|||||||
5993
package-lock.json
generated
5993
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,7 @@
|
|||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.10.1",
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0"
|
||||||
"@actions/http-client": "^2.2.0",
|
|
||||||
"@octokit/request-error": "^5.0.1",
|
|
||||||
"http-status-messages": "^1.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vercel/ncc": "^0.38.1",
|
"@vercel/ncc": "^0.38.1",
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ const path = require('path')
|
|||||||
|
|
||||||
describe('with all environment variables set', () => {
|
describe('with all environment variables set', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url'
|
|
||||||
process.env.GITHUB_RUN_ID = '123'
|
process.env.GITHUB_RUN_ID = '123'
|
||||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
|
||||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||||
process.env.GITHUB_TOKEN = 'gha-token'
|
process.env.GITHUB_TOKEN = 'gha-token'
|
||||||
process.env.GITHUB_SHA = '123abc'
|
process.env.GITHUB_SHA = '123abc'
|
||||||
@@ -26,7 +24,7 @@ describe('with all environment variables set', () => {
|
|||||||
|
|
||||||
describe('with variables missing', () => {
|
describe('with variables missing', () => {
|
||||||
it('execution fails if there are missing variables', done => {
|
it('execution fails if there are missing variables', done => {
|
||||||
delete process.env.ACTIONS_RUNTIME_URL
|
delete process.env.GITHUB_RUN_ID
|
||||||
const ip = path.join(__dirname, '../index.js')
|
const ip = path.join(__dirname, '../index.js')
|
||||||
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
||||||
expect(stdout).toBe('')
|
expect(stdout).toBe('')
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
const core = require('@actions/core')
|
const core = require('@actions/core')
|
||||||
// For mocking network calls with core http (http-client)
|
|
||||||
const nock = require('nock')
|
|
||||||
// For mocking network calls with native Fetch (octokit)
|
// For mocking network calls with native Fetch (octokit)
|
||||||
const { MockAgent, setGlobalDispatcher } = require('undici')
|
const { MockAgent, setGlobalDispatcher } = require('undici')
|
||||||
|
|
||||||
@@ -14,9 +12,7 @@ describe('Deployment', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url/'
|
|
||||||
process.env.GITHUB_RUN_ID = '123'
|
process.env.GITHUB_RUN_ID = '123'
|
||||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
|
||||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||||
process.env.GITHUB_TOKEN = 'gha-token'
|
process.env.GITHUB_TOKEN = 'gha-token'
|
||||||
process.env.GITHUB_SHA = '123abc'
|
process.env.GITHUB_SHA = '123abc'
|
||||||
@@ -67,14 +63,19 @@ describe('Deployment', () => {
|
|||||||
it('can successfully create a deployment', async () => {
|
it('can successfully create a deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -85,10 +86,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA &&
|
body.pages_build_version === process.env.GITHUB_SHA &&
|
||||||
body.oidc_token === fakeJwt
|
body.oidc_token === fakeJwt
|
||||||
)
|
)
|
||||||
@@ -113,21 +114,24 @@ describe('Deployment', () => {
|
|||||||
expect(core.info).toHaveBeenLastCalledWith(
|
expect(core.info).toHaveBeenLastCalledWith(
|
||||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can successfully create a preview deployment', async () => {
|
it('can successfully create a preview deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -138,11 +142,11 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 4 &&
|
keys.length === 4 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
keys[3] === 'preview' &&
|
keys[3] === 'preview' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA &&
|
body.pages_build_version === process.env.GITHUB_SHA &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.preview === true
|
body.preview === true
|
||||||
@@ -172,32 +176,42 @@ describe('Deployment', () => {
|
|||||||
expect(core.info).toHaveBeenLastCalledWith(
|
expect(core.info).toHaveBeenLastCalledWith(
|
||||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reports errors with failed artifact exchange', async () => {
|
it('reports errors with failed artifact metadata exchange', async () => {
|
||||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
mockPool
|
||||||
.reply(400, {})
|
.intercept({
|
||||||
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(400, { message: 'Bad request' }, { headers: { 'content-type': 'application/json' } })
|
||||||
|
|
||||||
// Create the deployment
|
// Create the deployment
|
||||||
const deployment = new Deployment()
|
const deployment = new Deployment()
|
||||||
await expect(deployment.create()).rejects.toEqual(
|
await expect(deployment.create()).rejects.toEqual(
|
||||||
new Error(
|
new Error(
|
||||||
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad Request`
|
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reports errors with a failed 500 in a deployment', async () => {
|
it('reports errors with a failed 500 in a deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'build-version'
|
process.env.GITHUB_SHA = 'build-version'
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -208,9 +222,9 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 2 &&
|
keys.length === 2 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'pages_build_version' &&
|
keys[1] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://invalid-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -224,15 +238,23 @@ describe('Deployment', () => {
|
|||||||
`Failed to create deployment (status: 500) with build version ${process.env.GITHUB_SHA}. Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
|
`Failed to create deployment (status: 500) with build version ${process.env.GITHUB_SHA}. Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reports errors with an unexpected 403 during deployment', async () => {
|
it('reports errors with an unexpected 403 during deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'build-version'
|
process.env.GITHUB_SHA = 'build-version'
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -243,9 +265,9 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 2 &&
|
keys.length === 2 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'pages_build_version' &&
|
keys[1] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://invalid-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -259,15 +281,23 @@ describe('Deployment', () => {
|
|||||||
`Failed to create deployment (status: 403) with build version ${process.env.GITHUB_SHA}. Ensure GITHUB_TOKEN has permission "pages: write".`
|
`Failed to create deployment (status: 403) with build version ${process.env.GITHUB_SHA}. Ensure GITHUB_TOKEN has permission "pages: write".`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reports errors with an unexpected 404 during deployment', async () => {
|
it('reports errors with an unexpected 404 during deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'build-version'
|
process.env.GITHUB_SHA = 'build-version'
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -278,9 +308,9 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 2 &&
|
keys.length === 2 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'pages_build_version' &&
|
keys[1] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://invalid-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -294,15 +324,23 @@ describe('Deployment', () => {
|
|||||||
`Failed to create deployment (status: 404) with build version ${process.env.GITHUB_SHA}. Ensure GitHub Pages has been enabled: https://github.com/actions/is-awesome/settings/pages`
|
`Failed to create deployment (status: 404) with build version ${process.env.GITHUB_SHA}. Ensure GitHub Pages has been enabled: https://github.com/actions/is-awesome/settings/pages`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reports errors with failed deployments', async () => {
|
it('reports errors with failed deployments', async () => {
|
||||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -313,9 +351,9 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 2 &&
|
keys.length === 2 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'pages_build_version' &&
|
keys[1] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://invalid-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -329,22 +367,98 @@ describe('Deployment', () => {
|
|||||||
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
|
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
it('fails if there are multiple artifacts with the same name', async () => {
|
||||||
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 2,
|
||||||
|
artifacts: [
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
name: `github-pages`,
|
||||||
|
size_in_bytes: 1400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
name: `github-pages`,
|
||||||
|
size_in_bytes: 1620
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
|
const deployment = new Deployment()
|
||||||
|
await expect(deployment.create(fakeJwt)).rejects.toThrow(
|
||||||
|
`Multiple artifact unexpectedly found for workflow run ${process.env.GITHUB_RUN_ID}. Artifact count is 2.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails if there are no artifacts found', async () => {
|
||||||
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 0,
|
||||||
|
artifacts: []
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
|
const deployment = new Deployment()
|
||||||
|
await expect(deployment.create(fakeJwt)).rejects.toThrow(
|
||||||
|
`No artifacts found for workflow run ${process.env.GITHUB_RUN_ID}. Ensure artifacts are uploaded with actions/artifact@v4 or later.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails with error message if list artifact endpoint returns 500', async () => {
|
||||||
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.reply(500, { message: 'oh no' }, { headers: { 'content-type': 'application/json' } })
|
||||||
|
|
||||||
|
const deployment = new Deployment()
|
||||||
|
await expect(deployment.create(fakeJwt)).rejects.toThrow(
|
||||||
|
`Failed to create deployment (status: 500) with build version valid-build-version. Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('warns if the artifact size is bigger than maximum', async () => {
|
it('warns if the artifact size is bigger than maximum', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
const artifactSize = ONE_GIGABYTE + 1
|
const artifactSize = ONE_GIGABYTE + 1
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages', size: `${artifactSize}` },
|
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 12, name: `github-pages`, size_in_bytes: artifactSize }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -355,10 +469,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 12 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -383,21 +497,24 @@ describe('Deployment', () => {
|
|||||||
expect(core.info).toHaveBeenLastCalledWith(
|
expect(core.info).toHaveBeenLastCalledWith(
|
||||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('warns when the timeout is greater than the maximum allowed', async () => {
|
it('warns when the timeout is greater than the maximum allowed', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -408,10 +525,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -450,8 +567,6 @@ describe('Deployment', () => {
|
|||||||
expect(core.warning).toBeCalledWith(
|
expect(core.warning).toBeCalledWith(
|
||||||
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
|
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
|
||||||
)
|
)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -459,14 +574,19 @@ describe('Deployment', () => {
|
|||||||
it('sets output to success when deployment is successful', async () => {
|
it('sets output to success when deployment is successful', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -477,10 +597,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -511,8 +631,6 @@ describe('Deployment', () => {
|
|||||||
|
|
||||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fails check when no deployment is found', async () => {
|
it('fails check when no deployment is found', async () => {
|
||||||
@@ -525,14 +643,19 @@ describe('Deployment', () => {
|
|||||||
it('exits early when deployment is not in progress', async () => {
|
it('exits early when deployment is not in progress', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -543,10 +666,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -568,21 +691,24 @@ describe('Deployment', () => {
|
|||||||
deployment.deploymentInfo.pending = false
|
deployment.deploymentInfo.pending = false
|
||||||
await deployment.check()
|
await deployment.check()
|
||||||
expect(core.setFailed).toBeCalledWith('Unable to get deployment status.')
|
expect(core.setFailed).toBeCalledWith('Unable to get deployment status.')
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('enforces max timeout', async () => {
|
it('enforces max timeout', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -593,10 +719,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -664,21 +790,24 @@ describe('Deployment', () => {
|
|||||||
expect(deployment.timeout).toEqual(MAX_TIMEOUT)
|
expect(deployment.timeout).toEqual(MAX_TIMEOUT)
|
||||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets timeout to user timeout if user timeout is less than max timeout', async () => {
|
it('sets timeout to user timeout if user timeout is less than max timeout', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -689,10 +818,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -749,21 +878,24 @@ describe('Deployment', () => {
|
|||||||
expect(deployment.timeout).toEqual(42)
|
expect(deployment.timeout).toEqual(42)
|
||||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets output to success when timeout is set but not reached', async () => {
|
it('sets output to success when timeout is set but not reached', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -774,10 +906,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -835,8 +967,6 @@ describe('Deployment', () => {
|
|||||||
expect(core.error).not.toBeCalled()
|
expect(core.error).not.toBeCalled()
|
||||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -844,14 +974,19 @@ describe('Deployment', () => {
|
|||||||
it('can successfully cancel a deployment', async () => {
|
it('can successfully cancel a deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -862,10 +997,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -897,8 +1032,6 @@ describe('Deployment', () => {
|
|||||||
await deployment.cancel()
|
await deployment.cancel()
|
||||||
|
|
||||||
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
|
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can exit if a pages deployment was not created and none need to be cancelled', async () => {
|
it('can exit if a pages deployment was not created and none need to be cancelled', async () => {
|
||||||
@@ -917,14 +1050,19 @@ describe('Deployment', () => {
|
|||||||
it('catches an error when trying to cancel a deployment', async () => {
|
it('catches an error when trying to cancel a deployment', async () => {
|
||||||
process.env.GITHUB_SHA = 'valid-build-version'
|
process.env.GITHUB_SHA = 'valid-build-version'
|
||||||
|
|
||||||
const artifactExchangeScope = nock(`http://my-url`)
|
mockPool
|
||||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
.intercept({
|
||||||
.reply(200, {
|
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||||
value: [
|
method: 'GET'
|
||||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
|
||||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
{
|
||||||
|
total_count: 1,
|
||||||
|
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||||
|
},
|
||||||
|
{ headers: { 'content-type': 'application/json' } }
|
||||||
|
)
|
||||||
|
|
||||||
mockPool
|
mockPool
|
||||||
.intercept({
|
.intercept({
|
||||||
@@ -935,10 +1073,10 @@ describe('Deployment', () => {
|
|||||||
const keys = Object.keys(body).sort()
|
const keys = Object.keys(body).sort()
|
||||||
return (
|
return (
|
||||||
keys.length === 3 &&
|
keys.length === 3 &&
|
||||||
keys[0] === 'artifact_url' &&
|
keys[0] === 'artifact_id' &&
|
||||||
keys[1] === 'oidc_token' &&
|
keys[1] === 'oidc_token' &&
|
||||||
keys[2] === 'pages_build_version' &&
|
keys[2] === 'pages_build_version' &&
|
||||||
body.artifact_url === 'https://fake-artifact.com&%24expand=SignedContent' &&
|
body.artifact_id === 11 &&
|
||||||
body.oidc_token === fakeJwt &&
|
body.oidc_token === fakeJwt &&
|
||||||
body.pages_build_version === process.env.GITHUB_SHA
|
body.pages_build_version === process.env.GITHUB_SHA
|
||||||
)
|
)
|
||||||
@@ -970,8 +1108,6 @@ describe('Deployment', () => {
|
|||||||
await deployment.cancel()
|
await deployment.cancel()
|
||||||
|
|
||||||
expect(core.error).toHaveBeenCalledWith(`Canceling Pages deployment failed`, expect.anything())
|
expect(core.error).toHaveBeenCalledWith(`Canceling Pages deployment failed`, expect.anything())
|
||||||
|
|
||||||
artifactExchangeScope.done()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,119 +1,61 @@
|
|||||||
const core = require('@actions/core')
|
const core = require('@actions/core')
|
||||||
const github = require('@actions/github')
|
const github = require('@actions/github')
|
||||||
const hc = require('@actions/http-client')
|
|
||||||
const { RequestError } = require('@octokit/request-error')
|
|
||||||
const HttpStatusMessages = require('http-status-messages')
|
|
||||||
|
|
||||||
// All variables we need from the runtime are loaded here
|
async function getArtifactMetadata({ githubToken, runId, artifactName }) {
|
||||||
const getContext = require('./context')
|
const octokit = github.getOctokit(githubToken)
|
||||||
|
|
||||||
async function processRuntimeResponse(res, requestOptions) {
|
|
||||||
// Parse the response body as JSON
|
|
||||||
let obj = null
|
|
||||||
try {
|
|
||||||
const contents = await res.readBody()
|
|
||||||
if (contents && contents.length > 0) {
|
|
||||||
obj = JSON.parse(contents)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Invalid resource (contents not json); leaving resulting obj as null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific response shape aligned with Octokit
|
|
||||||
const response = {
|
|
||||||
url: res.message?.url || requestOptions.url,
|
|
||||||
status: res.message?.statusCode || 0,
|
|
||||||
headers: {
|
|
||||||
...res.message?.headers
|
|
||||||
},
|
|
||||||
data: obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forcibly throw errors for negative HTTP status codes!
|
|
||||||
// @actions/http-client doesn't do this by default.
|
|
||||||
// Mimic the errors thrown by Octokit for consistency.
|
|
||||||
if (response.status >= 400) {
|
|
||||||
// Try to get an error message from the response body
|
|
||||||
const errorMsg =
|
|
||||||
(typeof response.data === 'string' && response.data) ||
|
|
||||||
response.data?.error ||
|
|
||||||
response.data?.message ||
|
|
||||||
// Try the Node HTTP IncomingMessage's statusMessage property
|
|
||||||
res.message?.statusMessage ||
|
|
||||||
// Fallback to the HTTP status message based on the status code
|
|
||||||
HttpStatusMessages[response.status] ||
|
|
||||||
// Or if the status code is unexpected...
|
|
||||||
`Unknown error (${response.status})`
|
|
||||||
|
|
||||||
throw new RequestError(errorMsg, response.status, {
|
|
||||||
response,
|
|
||||||
request: requestOptions
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifactName }) {
|
|
||||||
const { runTimeUrl: RUNTIME_URL } = getContext()
|
|
||||||
const artifactExchangeUrl = `${RUNTIME_URL}_apis/pipelines/workflows/${workflowRunId}/artifacts?api-version=6.0-preview`
|
|
||||||
|
|
||||||
const httpClient = new hc.HttpClient()
|
|
||||||
let data = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const requestHeaders = {
|
core.info(`Fetching artifact metadata for ${artifactName} in run ${runId}`)
|
||||||
accept: 'application/json',
|
|
||||||
authorization: `Bearer ${runtimeToken}`
|
const response = await octokit.request(
|
||||||
}
|
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts?name={artifactName}',
|
||||||
const requestOptions = {
|
{
|
||||||
method: 'GET',
|
owner: github.context.repo.owner,
|
||||||
url: artifactExchangeUrl,
|
repo: github.context.repo.repo,
|
||||||
headers: {
|
run_id: runId,
|
||||||
...requestHeaders
|
artifactName: artifactName
|
||||||
},
|
}
|
||||||
body: null
|
)
|
||||||
|
|
||||||
|
const artifactCount = response.data.total_count
|
||||||
|
core.debug(`List artifact count: ${artifactCount}`)
|
||||||
|
|
||||||
|
if (artifactCount === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`No artifacts found for workflow run ${runId}. Ensure artifacts are uploaded with actions/artifact@v4 or later.`
|
||||||
|
)
|
||||||
|
} else if (artifactCount > 1) {
|
||||||
|
throw new Error(
|
||||||
|
`Multiple artifact unexpectedly found for workflow run ${runId}. Artifact count is ${artifactCount}.`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Artifact exchange URL: ${artifactExchangeUrl}`)
|
const artifact = response.data.artifacts[0]
|
||||||
const res = await httpClient.get(artifactExchangeUrl, requestHeaders)
|
core.debug(`Artifact: ${JSON.stringify(artifact)}`)
|
||||||
|
|
||||||
// May throw a RequestError (HttpError)
|
const artifactSize = artifact.size_in_bytes
|
||||||
const response = await processRuntimeResponse(res, requestOptions)
|
if (!artifactSize) {
|
||||||
|
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
||||||
|
}
|
||||||
|
|
||||||
data = response.data
|
return {
|
||||||
core.debug(JSON.stringify(data))
|
id: artifact.id,
|
||||||
|
size: artifactSize
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error('Getting signed artifact URL failed', error)
|
core.error(
|
||||||
|
'Fetching artifact metadata failed. Is githubstatus.com reporting issues with API requests, Pages or Actions? Please re-run the deployment at a later time.',
|
||||||
|
error
|
||||||
|
)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const artifact = data?.value?.find(artifact => artifact.name === artifactName)
|
|
||||||
const artifactRawUrl = artifact?.url
|
|
||||||
if (!artifactRawUrl) {
|
|
||||||
throw new Error(
|
|
||||||
'No uploaded artifact was found! Please check if there are any errors at build step, or uploaded artifact name is correct.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const signedArtifactUrl = `${artifactRawUrl}&%24expand=SignedContent`
|
|
||||||
|
|
||||||
const artifactSize = artifact?.size
|
|
||||||
if (!artifactSize) {
|
|
||||||
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
url: signedArtifactUrl,
|
|
||||||
size: artifactSize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, idToken, isPreview = false }) {
|
async function createPagesDeployment({ githubToken, artifactId, buildVersion, idToken, isPreview = false }) {
|
||||||
const octokit = github.getOctokit(githubToken)
|
const octokit = github.getOctokit(githubToken)
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
artifact_url: artifactUrl,
|
artifact_id: artifactId,
|
||||||
pages_build_version: buildVersion,
|
pages_build_version: buildVersion,
|
||||||
oidc_token: idToken
|
oidc_token: idToken
|
||||||
}
|
}
|
||||||
@@ -173,7 +115,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSignedArtifactMetadata,
|
getArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ const core = require('@actions/core')
|
|||||||
// Load variables from Actions runtime
|
// Load variables from Actions runtime
|
||||||
function getRequiredVars() {
|
function getRequiredVars() {
|
||||||
return {
|
return {
|
||||||
runTimeUrl: process.env.ACTIONS_RUNTIME_URL,
|
|
||||||
workflowRun: process.env.GITHUB_RUN_ID,
|
workflowRun: process.env.GITHUB_RUN_ID,
|
||||||
runTimeToken: process.env.ACTIONS_RUNTIME_TOKEN,
|
|
||||||
repositoryNwo: process.env.GITHUB_REPOSITORY,
|
repositoryNwo: process.env.GITHUB_REPOSITORY,
|
||||||
buildVersion: process.env.GITHUB_SHA,
|
buildVersion: process.env.GITHUB_SHA,
|
||||||
buildActor: process.env.GITHUB_ACTOR,
|
buildActor: process.env.GITHUB_ACTOR,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const core = require('@actions/core')
|
|||||||
// All variables we need from the runtime are loaded here
|
// All variables we need from the runtime are loaded here
|
||||||
const getContext = require('./context')
|
const getContext = require('./context')
|
||||||
const {
|
const {
|
||||||
getSignedArtifactMetadata,
|
getArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
@@ -31,9 +31,7 @@ const SIZE_LIMIT_DESCRIPTION = '1 GB'
|
|||||||
class Deployment {
|
class Deployment {
|
||||||
constructor() {
|
constructor() {
|
||||||
const context = getContext()
|
const context = getContext()
|
||||||
this.runTimeUrl = context.runTimeUrl
|
|
||||||
this.repositoryNwo = context.repositoryNwo
|
this.repositoryNwo = context.repositoryNwo
|
||||||
this.runTimeToken = context.runTimeToken
|
|
||||||
this.buildVersion = context.buildVersion
|
this.buildVersion = context.buildVersion
|
||||||
this.buildActor = context.buildActor
|
this.buildActor = context.buildActor
|
||||||
this.actionsId = context.actionsId
|
this.actionsId = context.actionsId
|
||||||
@@ -48,8 +46,8 @@ class Deployment {
|
|||||||
this.startTime = null
|
this.startTime = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
|
// Call GitHub api to fetch artifacts matching the provided name and deploy to GitHub Pages
|
||||||
// by creating a deployment with that artifact
|
// by creating a deployment with that artifact id
|
||||||
async create(idToken) {
|
async create(idToken) {
|
||||||
if (Number(core.getInput('timeout')) > MAX_TIMEOUT) {
|
if (Number(core.getInput('timeout')) > MAX_TIMEOUT) {
|
||||||
core.warning(
|
core.warning(
|
||||||
@@ -65,9 +63,9 @@ class Deployment {
|
|||||||
core.debug(`Action ID: ${this.actionsId}`)
|
core.debug(`Action ID: ${this.actionsId}`)
|
||||||
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
||||||
|
|
||||||
const artifactData = await getSignedArtifactMetadata({
|
const artifactData = await getArtifactMetadata({
|
||||||
runtimeToken: this.runTimeToken,
|
githubToken: this.githubToken,
|
||||||
workflowRunId: this.workflowRun,
|
runId: this.workflowRun,
|
||||||
artifactName: this.artifactName
|
artifactName: this.artifactName
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -79,7 +77,7 @@ class Deployment {
|
|||||||
|
|
||||||
const deployment = await createPagesDeployment({
|
const deployment = await createPagesDeployment({
|
||||||
githubToken: this.githubToken,
|
githubToken: this.githubToken,
|
||||||
artifactUrl: artifactData.url,
|
artifactId: artifactData.id,
|
||||||
buildVersion: this.buildVersion,
|
buildVersion: this.buildVersion,
|
||||||
idToken,
|
idToken,
|
||||||
isPreview: this.isPreview
|
isPreview: this.isPreview
|
||||||
|
|||||||
Reference in New Issue
Block a user