mirror of
https://github.com/actions/deploy-pages.git
synced 2026-03-28 08:54:53 +00:00
Compare commits
2 Commits
release/v2
...
v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f27bcc1584 | ||
|
|
eb68e76f70 |
@@ -13,5 +13,5 @@
|
||||
"rules": {
|
||||
"semi": ["error", "never"]
|
||||
},
|
||||
"ignorePatterns": ["/dist/"]
|
||||
"ignorePatterns": ["/dist/", "/pre/"]
|
||||
}
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
dist/** -diff linguist-generated=true
|
||||
4
.github/release-drafter.yml
vendored
4
.github/release-drafter.yml
vendored
@@ -6,11 +6,7 @@ template: |
|
||||
|
||||
$CHANGES
|
||||
|
||||
---
|
||||
|
||||
See details of [all code changes](https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION) since previous release.
|
||||
|
||||
:warning: For use with products other than GitHub.com, such as GitHub Enterprise Server, please consult the [compatibility table](https://github.com/$OWNER/$REPOSITORY/#compatibilty).
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
|
||||
17
.github/workflows/check-dist.yml
vendored
17
.github/workflows/check-dist.yml
vendored
@@ -21,13 +21,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
- name: Set Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
node-version: 16.x
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -37,17 +36,9 @@ jobs:
|
||||
run: npm run prepare
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
id: diff
|
||||
run: |
|
||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after build in dist folder. See status below:"
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
10
.github/workflows/check-formatting.yml
vendored
10
.github/workflows/check-formatting.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Check formatting
|
||||
name: Checking formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,13 +19,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
10
.github/workflows/check-linter.yml
vendored
10
.github/workflows/check-linter.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Check linting
|
||||
name: Check linter
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,13 +19,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -9,7 +9,7 @@
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: CodeQL
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -37,8 +37,8 @@ jobs:
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
3
.github/workflows/draft-release.yml
vendored
3
.github/workflows/draft-release.yml
vendored
@@ -11,6 +11,7 @@ jobs:
|
||||
draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@09c613e259eb8d4e7c81c2cb00618eb5fc4575a7 # v5.25.0
|
||||
- uses: actions/checkout@v3
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
8
.github/workflows/rebuild-dependabot-prs.yml
vendored
8
.github/workflows/rebuild-dependabot-prs.yml
vendored
@@ -22,15 +22,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.PAGES_AUTOMATION_PAT }}
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Run tests
|
||||
name: Run Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -14,13 +14,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
18.9.0
|
||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
* @actions/pages
|
||||
@@ -1,29 +0,0 @@
|
||||
# Contributing 💻
|
||||
|
||||
All contributions are welcome and greatly appreciated!
|
||||
|
||||
## Steps to Contribute 💡
|
||||
|
||||
> Check the `.node-version` file in the root of this repo so see what version of Node.js is required for local development - note, this can be different from the version of Node.js which runs the Action on GitHub runners. It is suggested to download [nodenv](https://github.com/nodenv/nodenv) which uses this file and manages your Node.js versions for you
|
||||
|
||||
1. Fork this repository
|
||||
2. Make your changes
|
||||
3. [Test](#testing-) your changes locally
|
||||
4. Before opening a pull request, please run `npm run all` to verify formatting, linting, tests, generated files, etc.
|
||||
5. Commit and push your changes to your fork
|
||||
6. Open a pull request back to this repository
|
||||
7. Wait for an approval or changes requested from the maintainers of this repository
|
||||
|
||||
After merging the pull request, the maintainers of this repository will create a new release with those changes included. After that, everyone can utilize the newly integrated changes in their own Actions workflows and enjoy your awesome improvements!
|
||||
|
||||
## Testing 🧪
|
||||
|
||||
### Running the test suite (required)
|
||||
|
||||
Simply run the following command to execute the entire test suite:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
> Note: This requires that you have already run `npm install`.
|
||||
55
README.md
55
README.md
@@ -1,8 +1,10 @@
|
||||
# deploy-pages 🚀
|
||||
# deploy-pages
|
||||
|
||||
[](https://github.com/actions/deploy-pages/releases/latest) [](https://github.com/actions/deploy-pages/actions/workflows/check-linter.yml) [](https://github.com/actions/deploy-pages/actions/workflows/check-formatting.yml) [](https://github.com/actions/deploy-pages/actions/workflows/test.yml)  [](https://github.com/actions/deploy-pages/actions/workflows/check-dist.yml) [](https://github.com/actions/deploy-pages/actions/workflows/codeql-analysis.yml)
|
||||
This action is used to deploy [Actions artifacts][artifacts] to GitHub Pages.
|
||||
|
||||
This action is used to deploy [Actions artifacts][artifacts] to [GitHub Pages](https://pages.github.com/).
|
||||
## Scope
|
||||
|
||||
⚠️ Official support for building Pages with Actions is in public beta at the moment.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -10,7 +12,7 @@ See [action.yml](action.yml) for the various `inputs` this action supports.
|
||||
|
||||
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 expects an artifact named `github-pages` to have been created prior to execution. This is done automatically using [`actions/upload-pages-artifact`][upload-pages-artifact].
|
||||
|
||||
We recommend this action to be used in a dedicated job:
|
||||
|
||||
@@ -41,33 +43,10 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2 # or the latest "vX.X.X" version tag for this action
|
||||
uses: actions/deploy-pages@v1
|
||||
```
|
||||
|
||||
### Inputs 📥
|
||||
|
||||
| Input | Required? | Default | Description |
|
||||
| ----- | --------- | ------- | ----------- |
|
||||
| `token` | `true` | `${{ github.token }}` | The GitHub token used to create an authenticated client - Provided for you by default! |
|
||||
| `timeout` | `false` | `"600000"` | Time in milliseconds after which to timeout and cancel the deployment (default: 10 minutes) |
|
||||
| `error_count` | `false` | `"10"` | Maximum number of status report errors before cancelling a deployment (default: 10) |
|
||||
| `reporting_interval` | `false` | `"5000"` | Time in milliseconds between two deployment status report (default: 5 seconds) |
|
||||
| `artifact_name` | `false` | `"github-pages"` | The name of the artifact to deploy |
|
||||
| `preview` | `false` | `"false"` | Is this attempting to deploy a pull request as a GitHub Pages preview site? (NOTE: This feature is only in alpha currently and is not available to the public!) |
|
||||
|
||||
### Outputs 📤
|
||||
|
||||
| Output | Description |
|
||||
| ------ | ----------- |
|
||||
| `page_url` | The URL of the deployed Pages site |
|
||||
|
||||
### Environment Variables 🌎
|
||||
|
||||
| Variable | Description |
|
||||
| -------- | ----------- |
|
||||
| `GITHUB_PAGES` | This environment variable is created and set to the string value `"true"` so that framework build tools may choose to differentiate their output based on the intended target hosting platform. |
|
||||
|
||||
## Security Considerations
|
||||
## Security considerations
|
||||
|
||||
There are a few important considerations to be aware of:
|
||||
|
||||
@@ -81,23 +60,9 @@ There are a few important considerations to be aware of:
|
||||
|
||||
4. If your Pages site is using a source branch, the deployment must originate from this source branch unless [your environment is protected][environment-protection] in which case the environment protection rules take precedence over the source branch rule
|
||||
|
||||
5. If your Pages site is using GitHub Actions as the source, while not required we highly recommend you also [protect your environment][environment-protection] (we will configure it by default for you).
|
||||
5. If your Pages site is using GitHub Actions as the source, while not required we highly recommend you also [protect your environment][environment-protection] (we do it by default for you)
|
||||
|
||||
## Compatibility
|
||||
|
||||
This action is primarily design for use with GitHub.com's Actions workflows and Pages deployments. However, certain releases should also be compatible with GitHub Enterprise Server (GHES) `3.7` and above.
|
||||
|
||||
| Release | GHES Compatibility |
|
||||
|:---|:---|
|
||||
| [`v2`](https://github.com/actions/deploy-pages/releases/tag/v2) | `>= 3.9` |
|
||||
| `v2.x.x` | `>= 3.9` |
|
||||
| [`v1`](https://github.com/actions/deploy-pages/releases/tag/v1) | `>= 3.7` |
|
||||
| [`v1.2.8`](https://github.com/actions/deploy-pages/releases/tag/v1.2.8) | `>= 3.7` |
|
||||
| [`v1.2.7`](https://github.com/actions/deploy-pages/releases/tag/v1.2.7) | :warning: `>= 3.9` [Incompatible with prior versions!](https://github.com/actions/deploy-pages/issues/137) |
|
||||
| [`v1.2.6`](https://github.com/actions/deploy-pages/releases/tag/v1.2.6) | `>= 3.7` |
|
||||
| `v1.x.x` | `>= 3.7` |
|
||||
|
||||
## Release Instructions
|
||||
# Release instructions
|
||||
|
||||
In order to release a new version of this Action:
|
||||
|
||||
|
||||
@@ -5,6 +5,15 @@ runs:
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
inputs:
|
||||
emit_telemetry:
|
||||
deprecationMessage: 'Use of this input causes the action to do nothing. You should remove this build step from your workflow.'
|
||||
description: 'Should this action only emit build telemetry instead of deploying the build artifact?'
|
||||
required: false
|
||||
default: 'false'
|
||||
conclusion:
|
||||
deprecationMessage: 'Use of this input does nothing as it was part of the deprecated `emit_telemetry` flow. You should remove this parameter from your workflow.'
|
||||
description: 'The status of the previous build.'
|
||||
required: false
|
||||
token:
|
||||
description: 'GitHub token'
|
||||
default: ${{ github.token }}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<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: 79.53%"><title>Coverage: 79.53%</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="#e05d44"/><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">79.53%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">79.53%</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
24094
dist/index.js
vendored
24094
dist/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
47
dist/licenses.txt
vendored
47
dist/licenses.txt
vendored
@@ -47,28 +47,6 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@fastify/busboy
|
||||
MIT
|
||||
Copyright Brian White. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
@octokit/auth-token
|
||||
MIT
|
||||
The MIT License
|
||||
@@ -575,31 +553,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
undici
|
||||
MIT
|
||||
MIT License
|
||||
|
||||
Copyright (c) Matteo Collina and Undici contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
universal-user-agent
|
||||
ISC
|
||||
# [ISC License](https://spdx.org/licenses/ISC)
|
||||
|
||||
2
dist/sourcemap-register.js
vendored
2
dist/sourcemap-register.js
vendored
File diff suppressed because one or more lines are too long
5995
package-lock.json
generated
5995
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@@ -1,28 +1,26 @@
|
||||
{
|
||||
"name": "deploy-pages",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"description": "Deploy an actions artifact to GitHub Pages",
|
||||
"main": "./dist/index.js",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.1.1",
|
||||
"@actions/http-client": "^2.2.0",
|
||||
"@octokit/request-error": "^5.0.1",
|
||||
"@actions/http-client": "^2.1.0",
|
||||
"@octokit/request-error": "^3.0.3",
|
||||
"http-status-messages": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-github": "^4.10.1",
|
||||
"jest": "^29.7.0",
|
||||
"nock": "^13.3.8",
|
||||
"prettier": "^3.1.0",
|
||||
"make-coverage-badge": "^1.2.0"
|
||||
"@vercel/ncc": "^0.31.1",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.7.0",
|
||||
"eslint-plugin-github": "^4.6.1",
|
||||
"jest": "^29.5.0",
|
||||
"nock": "^13.3.0",
|
||||
"prettier": "^2.8.4"
|
||||
},
|
||||
"scripts": {
|
||||
"all": "npm run format && npm run lint && npm run prepare && npm run test && npm run coverage-badge",
|
||||
"coverage-badge": "make-coverage-badge --output-path ./coverage_badge.svg",
|
||||
"all": "npm run format && npm run lint && npm run prepare && npm run test",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"lint": "DEBUG=eslint:cli-engine eslint --fix .",
|
||||
@@ -43,22 +41,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/actions/deploy-pages/issues"
|
||||
},
|
||||
"homepage": "https://github.com/actions/deploy-pages#readme",
|
||||
"jest": {
|
||||
"coverageReporters": [
|
||||
"json-summary",
|
||||
"text",
|
||||
"lcov"
|
||||
],
|
||||
"collectCoverage": true,
|
||||
"collectCoverageFrom": [
|
||||
"./src/**"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"lines": 70,
|
||||
"statements": 70
|
||||
}
|
||||
}
|
||||
}
|
||||
"homepage": "https://github.com/actions/deploy-pages#readme"
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
const process = require('process')
|
||||
const cp = require('child_process')
|
||||
const path = require('path')
|
||||
|
||||
describe('with all environment variables set', () => {
|
||||
beforeEach(() => {
|
||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url'
|
||||
process.env.GITHUB_RUN_ID = '123'
|
||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||
process.env.GITHUB_TOKEN = 'gha-token'
|
||||
process.env.GITHUB_SHA = '123abc'
|
||||
process.env.GITHUB_ACTOR = 'monalisa'
|
||||
process.env.GITHUB_ACTION = '__monalisa/octocat'
|
||||
process.env.GITHUB_ACTION_PATH = 'something'
|
||||
})
|
||||
|
||||
it('executes cleanly', done => {
|
||||
const ip = path.join(__dirname, '../index.js')
|
||||
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
||||
expect(stdout).toMatch(/::debug::all variables are set/)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with variables missing', () => {
|
||||
it('execution fails if there are missing variables', done => {
|
||||
delete process.env.ACTIONS_RUNTIME_URL
|
||||
const ip = path.join(__dirname, '../index.js')
|
||||
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
||||
expect(stdout).toBe('')
|
||||
expect(err).toBeTruthy()
|
||||
expect(err.code).toBe(1)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,730 +0,0 @@
|
||||
const core = require('@actions/core')
|
||||
const nock = require('nock')
|
||||
|
||||
const { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION } = require('../../internal/deployment')
|
||||
|
||||
const fakeJwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
|
||||
|
||||
describe('Deployment', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url/'
|
||||
process.env.GITHUB_RUN_ID = '123'
|
||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||
process.env.GITHUB_TOKEN = 'gha-token'
|
||||
process.env.GITHUB_SHA = '123abc'
|
||||
process.env.GITHUB_ACTOR = 'monalisa'
|
||||
process.env.GITHUB_ACTION = '__monalisa/octocat'
|
||||
process.env.GITHUB_ACTION_PATH = 'something'
|
||||
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
jest.spyOn(core, 'setOutput').mockImplementation(param => {
|
||||
return param
|
||||
})
|
||||
|
||||
jest.spyOn(core, 'setFailed').mockImplementation(param => {
|
||||
return param
|
||||
})
|
||||
// Mock error/warning/info/debug
|
||||
jest.spyOn(core, 'error').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'info').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
||||
})
|
||||
|
||||
describe('#create', () => {
|
||||
afterEach(() => {
|
||||
// Remove mock for `core.getInput('preview')`
|
||||
delete process.env.INPUT_PREVIEW
|
||||
})
|
||||
|
||||
it('can successfully create a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('can successfully create a preview deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt,
|
||||
preview: true
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome',
|
||||
preview_url: 'https://actions.drafts.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Return `"true"` for `core.getInput("preview")`
|
||||
process.env.INPUT_PREVIEW = 'true'
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed artifact exchange', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(400, {})
|
||||
|
||||
// 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`
|
||||
)
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with a failed 500 in a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA
|
||||
})
|
||||
.reply(500, { message: 'oh no' })
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await expect(deployment.create()).rejects.toEqual(
|
||||
new Error(
|
||||
`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()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with an unexpected 403 during deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA
|
||||
})
|
||||
.reply(403, { message: 'You are forbidden' })
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await expect(deployment.create()).rejects.toEqual(
|
||||
new Error(
|
||||
`Failed to create deployment (status: 403) with build version ${process.env.GITHUB_SHA}. Ensure GITHUB_TOKEN has permission "pages: write".`
|
||||
)
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with an unexpected 404 during deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA
|
||||
})
|
||||
.reply(404, { message: 'Not found' })
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await expect(deployment.create()).rejects.toEqual(
|
||||
new Error(
|
||||
`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()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed deployments', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA
|
||||
})
|
||||
.reply(400, { message: 'Bad request' })
|
||||
|
||||
// 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`
|
||||
)
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('warns if the artifact size is bigger than maximum', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
const artifactSize = ONE_GIGABYTE + 1
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages', size: `${artifactSize}` },
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.warning).toBeCalledWith(
|
||||
`Uploaded artifact size of ${artifactSize} bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}. Deployment might fail.`
|
||||
)
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('warns when the timeout is greater than the maximum allowed', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
case 'timeout':
|
||||
return MAX_TIMEOUT + 1
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.warning).toBeCalledWith(
|
||||
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#check', () => {
|
||||
it('sets output to success when deployment is successful', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const deploymentStatusScope = nock('https://api.github.com')
|
||||
.get(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`)
|
||||
.reply(200, {
|
||||
status: 'succeed'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
await deployment.check()
|
||||
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
deploymentStatusScope.done()
|
||||
})
|
||||
|
||||
it('fails check when no deployment is found', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
const deployment = new Deployment()
|
||||
await deployment.check()
|
||||
expect(core.setFailed).toBeCalledWith('Deployment not found.')
|
||||
})
|
||||
|
||||
it('exits early when deployment is not in progress', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
deployment.deploymentInfo.pending = false
|
||||
await deployment.check()
|
||||
expect(core.setFailed).toBeCalledWith('Unable to get deployment status.')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('enforces max timeout', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const cancelDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`)
|
||||
.reply(200, {})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Set timeout to great than max
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
case 'error_count':
|
||||
return 10
|
||||
case 'timeout':
|
||||
return MAX_TIMEOUT + 1
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
const now = Date.now()
|
||||
const mockStartTime = now - MAX_TIMEOUT
|
||||
jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementationOnce(() => mockStartTime)
|
||||
.mockImplementationOnce(() => now)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
await deployment.check()
|
||||
|
||||
expect(deployment.timeout).toEqual(MAX_TIMEOUT)
|
||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
cancelDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('sets timeout to user timeout if user timeout is less than max timeout', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const cancelDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`)
|
||||
.reply(200, {})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Set timeout to great than max
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
case 'error_count':
|
||||
return 10
|
||||
case 'timeout':
|
||||
return 42
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
const now = Date.now()
|
||||
const mockStartTime = now - 42
|
||||
jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementationOnce(() => mockStartTime)
|
||||
.mockImplementationOnce(() => now)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
await deployment.check()
|
||||
|
||||
expect(deployment.timeout).toEqual(42)
|
||||
expect(core.error).toBeCalledWith('Timeout reached, aborting!')
|
||||
expect(core.setFailed).toBeCalledWith('Timeout reached, aborting!')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
cancelDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('sets output to success when timeout is set but not reached', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const deploymentStatusScope = nock('https://api.github.com')
|
||||
.get(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`)
|
||||
.reply(200, {
|
||||
status: 'succeed'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Set timeout to great than max
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
case 'error_count':
|
||||
return 10
|
||||
case 'timeout':
|
||||
return 42
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
const now = Date.now()
|
||||
const mockStartTime = now
|
||||
jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementationOnce(() => mockStartTime)
|
||||
.mockImplementationOnce(() => now)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
await deployment.check()
|
||||
|
||||
expect(deployment.timeout).toEqual(42)
|
||||
expect(core.error).not.toBeCalled()
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
deploymentStatusScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#cancel', () => {
|
||||
it('can successfully cancel a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const cancelDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`)
|
||||
.reply(200, {})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
// Cancel it
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
cancelDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('can exit if a pages deployment was not created and none need to be cancelled', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
|
||||
// Cancel it
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.debug).toHaveBeenCalledWith('all variables are set')
|
||||
expect(core.debug).toHaveBeenCalledWith(`No deployment to cancel`)
|
||||
})
|
||||
|
||||
it('catches an error when trying to cancel a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
// nock will throw an error every time it tries to cancel the deployment
|
||||
const cancelDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployments/${process.env.GITHUB_SHA}/cancel`)
|
||||
.reply(500, {})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
// Cancel it
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.error).toHaveBeenCalledWith(`Canceling Pages deployment failed`, expect.anything())
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
cancelDeploymentScope.done()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -54,7 +54,7 @@ async function processRuntimeResponse(res, requestOptions) {
|
||||
return response
|
||||
}
|
||||
|
||||
async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifactName }) {
|
||||
async function getSignedArtifactUrl({ runtimeToken, workflowRunId, artifactName }) {
|
||||
const { runTimeUrl: RUNTIME_URL } = getContext()
|
||||
const artifactExchangeUrl = `${RUNTIME_URL}_apis/pipelines/workflows/${workflowRunId}/artifacts?api-version=6.0-preview`
|
||||
|
||||
@@ -88,8 +88,7 @@ async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifact
|
||||
throw error
|
||||
}
|
||||
|
||||
const artifact = data?.value?.find(artifact => artifact.name === artifactName)
|
||||
const artifactRawUrl = artifact?.url
|
||||
const artifactRawUrl = data?.value?.find(artifact => artifact.name === artifactName)?.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.'
|
||||
@@ -97,16 +96,7 @@ async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifact
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return signedArtifactUrl
|
||||
}
|
||||
|
||||
async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, idToken, isPreview = false }) {
|
||||
@@ -123,7 +113,7 @@ async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, i
|
||||
core.info(`Creating Pages deployment with payload:\n${JSON.stringify(payload, null, '\t')}`)
|
||||
|
||||
try {
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployments', {
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployment', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
...payload
|
||||
@@ -141,7 +131,7 @@ async function getPagesDeploymentStatus({ githubToken, deploymentId }) {
|
||||
|
||||
core.info('Getting Pages deployment status...')
|
||||
try {
|
||||
const response = await octokit.request('GET /repos/{owner}/{repo}/pages/deployments/{deploymentId}', {
|
||||
const response = await octokit.request('GET /repos/{owner}/{repo}/pages/deployment/status/{deploymentId}', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
deploymentId
|
||||
@@ -159,7 +149,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
||||
|
||||
core.info('Canceling Pages deployment...')
|
||||
try {
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployments/{deploymentId}/cancel', {
|
||||
const response = await octokit.request('PUT /repos/{owner}/{repo}/pages/deployment/cancel/{deploymentId}', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
deploymentId
|
||||
@@ -173,7 +163,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSignedArtifactMetadata,
|
||||
getSignedArtifactUrl,
|
||||
createPagesDeployment,
|
||||
getPagesDeploymentStatus,
|
||||
cancelPagesDeployment
|
||||
@@ -3,7 +3,7 @@ const core = require('@actions/core')
|
||||
// All variables we need from the runtime are loaded here
|
||||
const getContext = require('./context')
|
||||
const {
|
||||
getSignedArtifactMetadata,
|
||||
getSignedArtifactUrl,
|
||||
createPagesDeployment,
|
||||
getPagesDeploymentStatus,
|
||||
cancelPagesDeployment
|
||||
@@ -24,10 +24,6 @@ const finalErrorStatus = {
|
||||
deployment_lost: 'Deployment failed to report final status.'
|
||||
}
|
||||
|
||||
const MAX_TIMEOUT = 600000
|
||||
const ONE_GIGABYTE = 1073741824
|
||||
const SIZE_LIMIT_DESCRIPTION = '1 GB'
|
||||
|
||||
class Deployment {
|
||||
constructor() {
|
||||
const context = getContext()
|
||||
@@ -44,42 +40,25 @@ class Deployment {
|
||||
this.githubServerUrl = context.githubServerUrl
|
||||
this.artifactName = context.artifactName
|
||||
this.isPreview = context.isPreview === true
|
||||
this.timeout = MAX_TIMEOUT
|
||||
this.startTime = null
|
||||
}
|
||||
|
||||
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
|
||||
// by creating a deployment with that artifact
|
||||
async create(idToken) {
|
||||
if (Number(core.getInput('timeout')) > MAX_TIMEOUT) {
|
||||
core.warning(
|
||||
`Warning: timeout value is greater than the allowed maximum - timeout set to the maximum of ${MAX_TIMEOUT} milliseconds.`
|
||||
)
|
||||
}
|
||||
|
||||
const timeoutInput = Number(core.getInput('timeout'))
|
||||
this.timeout = !timeoutInput || timeoutInput <= 0 ? MAX_TIMEOUT : Math.min(timeoutInput, MAX_TIMEOUT)
|
||||
|
||||
try {
|
||||
core.debug(`Actor: ${this.buildActor}`)
|
||||
core.debug(`Action ID: ${this.actionsId}`)
|
||||
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
||||
|
||||
const artifactData = await getSignedArtifactMetadata({
|
||||
const artifactUrl = await getSignedArtifactUrl({
|
||||
runtimeToken: this.runTimeToken,
|
||||
workflowRunId: this.workflowRun,
|
||||
artifactName: this.artifactName
|
||||
})
|
||||
|
||||
if (artifactData?.size > ONE_GIGABYTE) {
|
||||
core.warning(
|
||||
`Uploaded artifact size of ${artifactData?.size} bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}. Deployment might fail.`
|
||||
)
|
||||
}
|
||||
|
||||
const deployment = await createPagesDeployment({
|
||||
githubToken: this.githubToken,
|
||||
artifactUrl: artifactData.url,
|
||||
artifactUrl,
|
||||
buildVersion: this.buildVersion,
|
||||
idToken,
|
||||
isPreview: this.isPreview
|
||||
@@ -91,7 +70,6 @@ class Deployment {
|
||||
id: deployment.id || deployment.status_url?.split('/')?.pop() || this.buildVersion,
|
||||
pending: true
|
||||
}
|
||||
this.startTime = Date.now()
|
||||
}
|
||||
|
||||
core.info(`Created deployment for ${this.buildVersion}, ID: ${this.deploymentInfo?.id}`)
|
||||
@@ -112,18 +90,12 @@ class Deployment {
|
||||
} else if (error.status === 404) {
|
||||
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
||||
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 +=
|
||||
'\nNote: This action version may not yet support GitHub Enterprise Server, please check the compatibility table.'
|
||||
}
|
||||
} else if (error.status >= 500) {
|
||||
errorMessage +=
|
||||
'Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.'
|
||||
}
|
||||
throw new Error(errorMessage)
|
||||
} else {
|
||||
// istanbul ignore next
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -142,9 +114,11 @@ class Deployment {
|
||||
}
|
||||
|
||||
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
||||
const timeout = Number(core.getInput('timeout'))
|
||||
const reportingInterval = Number(core.getInput('reporting_interval'))
|
||||
const maxErrorCount = Number(core.getInput('error_count'))
|
||||
|
||||
let startTime = Date.now()
|
||||
let errorCount = 0
|
||||
|
||||
// Time in milliseconds between two deployment status report when status errored, default 0.
|
||||
@@ -209,7 +183,7 @@ class Deployment {
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
if (Date.now() - this.startTime >= this.timeout) {
|
||||
if (Date.now() - startTime >= timeout) {
|
||||
core.error('Timeout reached, aborting!')
|
||||
core.setFailed('Timeout reached, aborting!')
|
||||
|
||||
@@ -223,7 +197,6 @@ class Deployment {
|
||||
async cancel() {
|
||||
// Don't attempt to cancel if no deployment was created
|
||||
if (!this.deploymentInfo || this.deploymentInfo.pending !== true) {
|
||||
core.debug('No deployment to cancel')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -246,4 +219,4 @@ class Deployment {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION }
|
||||
module.exports = { Deployment }
|
||||
11
src/index.js
11
src/index.js
@@ -4,8 +4,8 @@
|
||||
|
||||
const core = require('@actions/core')
|
||||
|
||||
const { Deployment } = require('./internal/deployment')
|
||||
const getContext = require('./internal/context')
|
||||
const { Deployment } = require('./deployment')
|
||||
const getContext = require('./context')
|
||||
|
||||
const deployment = new Deployment()
|
||||
|
||||
@@ -48,4 +48,9 @@ process.on('SIGINT', cancelHandler)
|
||||
process.on('SIGTERM', cancelHandler)
|
||||
|
||||
// Main
|
||||
main()
|
||||
const emitTelemetry = core.getInput('emit_telemetry')
|
||||
if (emitTelemetry === 'true') {
|
||||
// For compatibility, treat the use of this deprecated input as a no-op
|
||||
} else {
|
||||
main()
|
||||
}
|
||||
|
||||
312
src/index.test.js
Normal file
312
src/index.test.js
Normal file
@@ -0,0 +1,312 @@
|
||||
const core = require('@actions/core')
|
||||
const process = require('process')
|
||||
const cp = require('child_process')
|
||||
const path = require('path')
|
||||
const nock = require('nock')
|
||||
|
||||
const { Deployment } = require('./deployment')
|
||||
|
||||
const fakeJwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
|
||||
|
||||
describe('with all environment variables set', () => {
|
||||
beforeEach(() => {
|
||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url'
|
||||
process.env.GITHUB_RUN_ID = '123'
|
||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||
process.env.GITHUB_TOKEN = 'gha-token'
|
||||
process.env.GITHUB_SHA = '123abc'
|
||||
process.env.GITHUB_ACTOR = 'monalisa'
|
||||
process.env.GITHUB_ACTION = '__monalisa/octocat'
|
||||
process.env.GITHUB_ACTION_PATH = 'something'
|
||||
})
|
||||
|
||||
it('executes cleanly', done => {
|
||||
const ip = path.join(__dirname, './index.js')
|
||||
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
||||
expect(stdout).toMatch(/::debug::all variables are set/)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with variables missing', () => {
|
||||
it('execution fails if there are missing variables', done => {
|
||||
delete process.env.ACTIONS_RUNTIME_URL
|
||||
const ip = path.join(__dirname, './index.js')
|
||||
cp.exec(`node ${ip}`, { env: process.env }, (err, stdout) => {
|
||||
expect(stdout).toBe('')
|
||||
expect(err).toBeTruthy()
|
||||
expect(err.code).toBe(1)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Deployment', () => {
|
||||
beforeEach(() => {
|
||||
process.env.ACTIONS_RUNTIME_URL = 'http://my-url/'
|
||||
process.env.GITHUB_RUN_ID = '123'
|
||||
process.env.ACTIONS_RUNTIME_TOKEN = 'a-token'
|
||||
process.env.GITHUB_REPOSITORY = 'actions/is-awesome'
|
||||
process.env.GITHUB_TOKEN = 'gha-token'
|
||||
process.env.GITHUB_SHA = '123abc'
|
||||
process.env.GITHUB_ACTOR = 'monalisa'
|
||||
process.env.GITHUB_ACTION = '__monalisa/octocat'
|
||||
process.env.GITHUB_ACTION_PATH = 'something'
|
||||
|
||||
jest.spyOn(core, 'getInput').mockImplementation(param => {
|
||||
switch (param) {
|
||||
case 'artifact_name':
|
||||
return 'github-pages'
|
||||
case 'token':
|
||||
return process.env.GITHUB_TOKEN
|
||||
default:
|
||||
return process.env[`INPUT_${param.toUpperCase()}`] || ''
|
||||
}
|
||||
})
|
||||
|
||||
jest.spyOn(core, 'setOutput').mockImplementation(param => {
|
||||
return param
|
||||
})
|
||||
|
||||
jest.spyOn(core, 'setFailed').mockImplementation(param => {
|
||||
return param
|
||||
})
|
||||
// Mock error/warning/info/debug
|
||||
jest.spyOn(core, 'error').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'info').mockImplementation(jest.fn())
|
||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
||||
})
|
||||
|
||||
describe('#create', () => {
|
||||
afterEach(() => {
|
||||
// Remove mock for `core.getInput('preview')`
|
||||
delete process.env.INPUT_PREVIEW
|
||||
})
|
||||
|
||||
it('can successfully create a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/status/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('can successfully create a preview deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt,
|
||||
preview: true
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/status/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome',
|
||||
preview_url: 'https://actions.drafts.github.io/is-awesome'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Return `"true"` for `core.getInput("preview")`
|
||||
process.env.INPUT_PREVIEW = 'true'
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(new RegExp(`^Created deployment for ${process.env.GITHUB_SHA}`))
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed artifact exchange', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(400, {})
|
||||
|
||||
// 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`
|
||||
)
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
})
|
||||
|
||||
it('reports errors with failed deployments', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, { value: [{ url: 'https://invalid-artifact.com', name: 'github-pages' }] })
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment`, {
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA
|
||||
})
|
||||
.reply(400, { message: 'Bad request' })
|
||||
|
||||
// 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`
|
||||
)
|
||||
)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#check', () => {
|
||||
it('sets output to success when deployment is successful', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/status/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const deploymentStatusScope = nock('https://api.github.com')
|
||||
.get(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/status/${process.env.GITHUB_SHA}`)
|
||||
.reply(200, {
|
||||
status: 'succeed'
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
core.GetInput = jest.fn(input => {
|
||||
switch (input) {
|
||||
case 'timeout':
|
||||
return 10 * 1000
|
||||
case 'reporting_interval':
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
await deployment.check()
|
||||
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
expect(core.info).toHaveBeenLastCalledWith('Reported success!')
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
deploymentStatusScope.done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#cancel', () => {
|
||||
it('can successfully cancel a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
|
||||
const artifactExchangeScope = nock(`http://my-url`)
|
||||
.get('/_apis/pipelines/workflows/123/artifacts?api-version=6.0-preview')
|
||||
.reply(200, {
|
||||
value: [
|
||||
{ url: 'https://another-artifact.com', name: 'another-artifact' },
|
||||
{ url: 'https://fake-artifact.com', name: 'github-pages' }
|
||||
]
|
||||
})
|
||||
|
||||
const createDeploymentScope = nock('https://api.github.com')
|
||||
.post(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment`, {
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: process.env.GITHUB_SHA,
|
||||
oidc_token: fakeJwt
|
||||
})
|
||||
.reply(200, {
|
||||
status_url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/status/${process.env.GITHUB_SHA}`,
|
||||
page_url: 'https://actions.github.io/is-awesome'
|
||||
})
|
||||
|
||||
const cancelDeploymentScope = nock('https://api.github.com')
|
||||
.put(`/repos/${process.env.GITHUB_REPOSITORY}/pages/deployment/cancel/${process.env.GITHUB_SHA}`)
|
||||
.reply(200, {})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
// Cancel it
|
||||
await deployment.cancel()
|
||||
|
||||
expect(core.info).toHaveBeenLastCalledWith(`Canceled deployment with ID ${process.env.GITHUB_SHA}`)
|
||||
|
||||
artifactExchangeScope.done()
|
||||
createDeploymentScope.done()
|
||||
cancelDeploymentScope.done()
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user