mirror of
https://github.com/actions/deploy-pages.git
synced 2025-12-08 16:16:16 +00:00
Merge pull request #288 from actions/use-artifacts-client
Use the Actions artifacts client to avoid needing the `actions: read` permission
This commit is contained in:
@@ -30,7 +30,6 @@ jobs:
|
||||
permissions:
|
||||
pages: write # to deploy to Pages
|
||||
id-token: write # to verify the deployment originates from an appropriate source
|
||||
actions: read # to download an artifact uploaded by `actions/upload-pages-artifact@v3`
|
||||
|
||||
# Deploy to the github-pages environment
|
||||
environment:
|
||||
@@ -77,7 +76,6 @@ There are a few important considerations to be aware of:
|
||||
2. The job that executes the deployment must at minimum have the following permissions:
|
||||
- `pages: write`
|
||||
- `id-token: write`
|
||||
- `actions: read`
|
||||
|
||||
3. The deployment should target the `github-pages` environment (you may use a different environment name if needed, but this is not recommended.)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 81.39%"><title>Coverage: 81.39%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">81.39%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">81.39%</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="116" height="20" role="img" aria-label="Coverage: 81.13%"><title>Coverage: 81.13%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">81.13%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">81.13%</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
97523
dist/index.js
generated
vendored
97523
dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
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
2766
dist/licenses.txt
generated
vendored
2766
dist/licenses.txt
generated
vendored
File diff suppressed because it is too large
Load Diff
1186
package-lock.json
generated
1186
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,11 @@
|
||||
"description": "Deploy an actions artifact to GitHub Pages",
|
||||
"main": "./dist/index.js",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "^2.0.0",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.0"
|
||||
"@actions/github": "^6.0.0",
|
||||
"@octokit/request-error": "^5.0.1",
|
||||
"http-status-messages": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
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)
|
||||
const { MockAgent, setGlobalDispatcher } = require('undici')
|
||||
|
||||
@@ -7,6 +9,8 @@ const { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION } = requir
|
||||
const fakeJwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
|
||||
|
||||
const LIST_ARTIFACTS_TWIRP_PATH = '/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts'
|
||||
|
||||
describe('Deployment', () => {
|
||||
let mockPool
|
||||
|
||||
@@ -19,6 +23,10 @@ describe('Deployment', () => {
|
||||
process.env.GITHUB_ACTOR = 'monalisa'
|
||||
process.env.GITHUB_ACTION = '__monalisa/octocat'
|
||||
process.env.GITHUB_ACTION_PATH = 'something'
|
||||
// A valid actions token must have an 'scp' field whose value is a space-delimited list of strings
|
||||
process.env.ACTIONS_RUNTIME_TOKEN =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY3AiOiJBY3Rpb25zLkV4YW1wbGVTY29wZSBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCJ9.l-VcBU1PeNk_lWpOhjWehQlYyjCcY2dp_EMt7Rf06io'
|
||||
process.env.ACTIONS_RESULTS_URL = 'https://actions-results-url.biz'
|
||||
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
@@ -48,7 +56,7 @@ describe('Deployment', () => {
|
||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
||||
|
||||
// Set up Fetch mocking
|
||||
const mockAgent = new MockAgent()
|
||||
let mockAgent = new MockAgent()
|
||||
mockAgent.disableNetConnect()
|
||||
setGlobalDispatcher(mockAgent)
|
||||
mockPool = mockAgent.get('https://api.github.com')
|
||||
@@ -63,16 +71,12 @@ describe('Deployment', () => {
|
||||
it('can successfully create a deployment', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -114,21 +118,18 @@ describe('Deployment', () => {
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('can successfully create a preview deployment', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -176,39 +177,42 @@ describe('Deployment', () => {
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed artifact metadata exchange', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
|
||||
mockPool
|
||||
.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' } })
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(400, { msg: 'yikes!' }, { 'content-type': 'application/json' })
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await expect(deployment.create()).rejects.toEqual(
|
||||
new Error(
|
||||
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
|
||||
)
|
||||
await expect(deployment.create()).rejects.toThrow(
|
||||
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}.`
|
||||
)
|
||||
expect(core.error).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'Listing artifact metadata failed',
|
||||
new Error('Failed to ListArtifacts: Received non-retryable error: Failed request: (400) null: yikes!')
|
||||
)
|
||||
expect(core.error).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'Fetching artifact metadata failed. Is githubstatus.com reporting issues with API requests, Pages, or Actions? Please re-run the deployment at a later time.',
|
||||
expect.any(Error)
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with a failed 500 in a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||
method: 'GET'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -238,20 +242,17 @@ 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.`
|
||||
)
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with an unexpected 403 during deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||
method: 'GET'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -281,20 +282,17 @@ describe('Deployment', () => {
|
||||
`Failed to create deployment (status: 403) with build version ${process.env.GITHUB_SHA}. Ensure GITHUB_TOKEN has permission "pages: write".`
|
||||
)
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with an unexpected 404 during deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||
method: 'GET'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -324,20 +322,17 @@ 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`
|
||||
)
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed deployments', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||
method: 'GET'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -367,31 +362,20 @@ describe('Deployment', () => {
|
||||
`Failed to create deployment (status: 400) with build version ${process.env.GITHUB_SHA}. Responded with: Bad request`
|
||||
)
|
||||
)
|
||||
twirpScope.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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 2,
|
||||
artifacts: [
|
||||
{
|
||||
id: 13,
|
||||
name: `github-pages`,
|
||||
size_in_bytes: 1400
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: `github-pages`,
|
||||
size_in_bytes: 1620
|
||||
}
|
||||
{ databaseId: 13, name: 'github-pages', size: 1400 },
|
||||
{ databaseId: 14, name: 'github-pages', size: 1620 }
|
||||
]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
@@ -399,22 +383,19 @@ describe('Deployment', () => {
|
||||
|
||||
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.`
|
||||
`Multiple artifacts named "github-pages" were unexpectedly found for this workflow run. Artifact count is 2.`
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 0,
|
||||
artifacts: []
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
@@ -422,40 +403,46 @@ describe('Deployment', () => {
|
||||
|
||||
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.`
|
||||
`No artifacts named "github-pages" were found for this workflow run. Ensure artifacts are uploaded with actions/artifact@v4 or later.`
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('fails with error message if list artifact endpoint returns 500', async () => {
|
||||
it('fails with error message if list artifact endpoint returns 501', 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 twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(501, { msg: '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.`
|
||||
`Failed to create deployment (status: 501) 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.`
|
||||
)
|
||||
expect(core.error).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'Listing artifact metadata failed',
|
||||
new Error('Failed to ListArtifacts: Received non-retryable error: Failed request: (501) null: oh no')
|
||||
)
|
||||
expect(core.error).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'Fetching artifact metadata failed. Is githubstatus.com reporting issues with API requests, Pages, or Actions? Please re-run the deployment at a later time.',
|
||||
expect.any(Error)
|
||||
)
|
||||
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('warns if the artifact size is bigger than maximum', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
const artifactSize = ONE_GIGABYTE + 1
|
||||
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}/artifacts?name=github-pages`,
|
||||
method: 'GET'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 12, name: `github-pages`, size_in_bytes: artifactSize }]
|
||||
artifacts: [{ databaseId: 12, name: 'github-pages', size: artifactSize }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -497,21 +484,18 @@ describe('Deployment', () => {
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('warns when the timeout is greater than the maximum allowed', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -567,6 +551,7 @@ describe('Deployment', () => {
|
||||
expect(core.warning).toBeCalledWith(
|
||||
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
|
||||
)
|
||||
twirpScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -574,16 +559,12 @@ describe('Deployment', () => {
|
||||
it('sets output to success when deployment is successful', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -631,6 +612,7 @@ describe('Deployment', () => {
|
||||
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('fails check when no deployment is found', async () => {
|
||||
@@ -643,16 +625,12 @@ describe('Deployment', () => {
|
||||
it('exits early when deployment is not in progress', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -691,21 +669,18 @@ describe('Deployment', () => {
|
||||
deployment.deploymentInfo.pending = false
|
||||
await deployment.check()
|
||||
expect(core.setFailed).toBeCalledWith('Unable to get deployment status.')
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('enforces max timeout', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -790,21 +765,18 @@ describe('Deployment', () => {
|
||||
expect(deployment.timeout).toEqual(MAX_TIMEOUT)
|
||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('sets timeout to user timeout if user timeout is less than max timeout', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -878,21 +850,18 @@ describe('Deployment', () => {
|
||||
expect(deployment.timeout).toEqual(42)
|
||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('sets output to success when timeout is set but not reached', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -967,6 +936,7 @@ describe('Deployment', () => {
|
||||
expect(core.error).not.toBeCalled()
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||
twirpScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -974,16 +944,12 @@ describe('Deployment', () => {
|
||||
it('can successfully cancel a deployment', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -1032,6 +998,7 @@ describe('Deployment', () => {
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
|
||||
twirpScope.done()
|
||||
})
|
||||
|
||||
it('can exit if a pages deployment was not created and none need to be cancelled', async () => {
|
||||
@@ -1050,16 +1017,12 @@ describe('Deployment', () => {
|
||||
it('catches an error when trying to cancel a deployment', 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'
|
||||
})
|
||||
const twirpScope = nock(process.env.ACTIONS_RESULTS_URL)
|
||||
.post(LIST_ARTIFACTS_TWIRP_PATH)
|
||||
.reply(
|
||||
200,
|
||||
{
|
||||
total_count: 1,
|
||||
artifacts: [{ id: 11, name: `github-pages`, size_in_bytes: 221 }]
|
||||
artifacts: [{ databaseId: 11, name: 'github-pages', size: 221 }]
|
||||
},
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
@@ -1108,6 +1071,7 @@ describe('Deployment', () => {
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.error).toHaveBeenCalledWith(`Canceling Pages deployment failed`, expect.anything())
|
||||
twirpScope.done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,50 +1,107 @@
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const { DefaultArtifactClient } = require('@actions/artifact')
|
||||
const { RequestError } = require('@octokit/request-error')
|
||||
const HttpStatusMessages = require('http-status-messages')
|
||||
|
||||
async function getArtifactMetadata({ githubToken, runId, artifactName }) {
|
||||
const octokit = github.getOctokit(githubToken)
|
||||
function wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions) {
|
||||
// Specific response shape aligned with Octokit
|
||||
const response = {
|
||||
url: requestOptions.url,
|
||||
status: 200,
|
||||
headers: {
|
||||
...requestOptions.headers
|
||||
},
|
||||
data: twirpResponse
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// Mimic the errors thrown by Octokit for consistency.
|
||||
function wrapTwirpErrorLikeOctokit(twirpError, requestOptions) {
|
||||
const rawErrorMsg = twirpError?.message || twirpError?.toString() || ''
|
||||
const statusCodeMatch = rawErrorMsg.match(/Failed request: \((?<statusCode>\d+)\)/)
|
||||
const statusCode = statusCodeMatch?.groups?.statusCode ?? 500
|
||||
|
||||
// Try to provide the best error message
|
||||
const errorMsg =
|
||||
rawErrorMsg ||
|
||||
// Fallback to the HTTP status message based on the status code
|
||||
HttpStatusMessages[statusCode] ||
|
||||
// Or if the status code is unexpected...
|
||||
`Unknown error (${statusCode})`
|
||||
|
||||
// RequestError is an Octokit-specific class
|
||||
return new RequestError(errorMsg, statusCode, {
|
||||
response: {
|
||||
url: requestOptions.url,
|
||||
status: statusCode,
|
||||
headers: {
|
||||
...requestOptions.headers
|
||||
},
|
||||
data: rawErrorMsg ? { message: rawErrorMsg } : ''
|
||||
},
|
||||
request: requestOptions
|
||||
})
|
||||
}
|
||||
|
||||
function getArtifactsServiceOrigin() {
|
||||
const resultsUrl = process.env.ACTIONS_RESULTS_URL
|
||||
return resultsUrl ? new URL(resultsUrl).origin : ''
|
||||
}
|
||||
|
||||
async function getArtifactMetadata({ artifactName }) {
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
|
||||
// Primarily for debugging purposes, accuracy is not critical
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
url: `${getArtifactsServiceOrigin()}/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts`,
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: {}
|
||||
}
|
||||
|
||||
try {
|
||||
core.info(`Fetching artifact metadata for ${artifactName} in run ${runId}`)
|
||||
core.info(`Fetching artifact metadata for "${artifactName}" in this workflow run`)
|
||||
|
||||
const response = await octokit.request(
|
||||
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts?name={artifactName}',
|
||||
{
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
run_id: runId,
|
||||
artifactName: artifactName
|
||||
}
|
||||
)
|
||||
let response
|
||||
try {
|
||||
const twirpResponse = await artifactClient.listArtifacts()
|
||||
response = wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions)
|
||||
} catch (twirpError) {
|
||||
core.error('Listing artifact metadata failed', twirpError)
|
||||
const octokitError = wrapTwirpErrorLikeOctokit(twirpError, requestOptions)
|
||||
throw octokitError
|
||||
}
|
||||
|
||||
const artifactCount = response.data.total_count
|
||||
const filteredArtifacts = response.data.artifacts.filter(artifact => artifact.name === artifactName)
|
||||
|
||||
const artifactCount = filteredArtifacts.length
|
||||
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.`
|
||||
`No artifacts named "${artifactName}" were found for this workflow run. 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}.`
|
||||
`Multiple artifacts named "${artifactName}" were unexpectedly found for this workflow run. Artifact count is ${artifactCount}.`
|
||||
)
|
||||
}
|
||||
|
||||
const artifact = response.data.artifacts[0]
|
||||
const artifact = filteredArtifacts[0]
|
||||
core.debug(`Artifact: ${JSON.stringify(artifact)}`)
|
||||
|
||||
const artifactSize = artifact.size_in_bytes
|
||||
if (!artifactSize) {
|
||||
if (!artifact.size) {
|
||||
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
||||
}
|
||||
|
||||
return {
|
||||
id: artifact.id,
|
||||
size: artifactSize
|
||||
}
|
||||
return artifact
|
||||
} catch (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.',
|
||||
'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
|
||||
|
||||
@@ -63,11 +63,7 @@ class Deployment {
|
||||
core.debug(`Action ID: ${this.actionsId}`)
|
||||
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
||||
|
||||
const artifactData = await getArtifactMetadata({
|
||||
githubToken: this.githubToken,
|
||||
runId: this.workflowRun,
|
||||
artifactName: this.artifactName
|
||||
})
|
||||
const artifactData = await getArtifactMetadata({ artifactName: this.artifactName })
|
||||
|
||||
if (artifactData?.size > ONE_GIGABYTE) {
|
||||
core.warning(
|
||||
@@ -102,14 +98,14 @@ class Deployment {
|
||||
|
||||
// build customized error message based on server response
|
||||
if (error.response) {
|
||||
let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}. `
|
||||
let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}.`
|
||||
if (error.status === 400) {
|
||||
errorMessage += `Responded with: ${error.message}`
|
||||
errorMessage += ` Responded with: ${error.message}`
|
||||
} else if (error.status === 403) {
|
||||
errorMessage += 'Ensure GITHUB_TOKEN has permission "pages: write".'
|
||||
errorMessage += ' Ensure GITHUB_TOKEN has permission "pages: write".'
|
||||
} else if (error.status === 404) {
|
||||
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
||||
errorMessage += `Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
|
||||
errorMessage += ` Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
|
||||
// If using GHES, add a special note about compatibility
|
||||
if (new URL(this.githubServerUrl).hostname.toLowerCase() !== 'github.com') {
|
||||
errorMessage +=
|
||||
@@ -117,7 +113,7 @@ class Deployment {
|
||||
}
|
||||
} else if (error.status >= 500) {
|
||||
errorMessage +=
|
||||
'Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.'
|
||||
' Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.'
|
||||
}
|
||||
throw new Error(errorMessage)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user