mirror of
https://github.com/actions/deploy-pages.git
synced 2026-03-28 08:54:53 +00:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77d7344265 | ||
|
|
84abb89dc5 | ||
|
|
de14547edc | ||
|
|
ced2b1a16b | ||
|
|
3a91181e1b | ||
|
|
6fa14b662d | ||
|
|
f9e640bce0 | ||
|
|
3eefb79441 | ||
|
|
971e36e6ad | ||
|
|
6abbee335a | ||
|
|
e149aa9f91 | ||
|
|
0077311596 | ||
|
|
03b62d1911 | ||
|
|
ada3f9d061 | ||
|
|
82b8ae3f91 | ||
|
|
d183a290bd | ||
|
|
8c6ef3d8d5 | ||
|
|
5a0bbc6e7d | ||
|
|
49af2606ad | ||
|
|
8b1057e1ce | ||
|
|
1f9a7ee304 | ||
|
|
3aff9369e6 | ||
|
|
a3969a1774 | ||
|
|
d9c5ae4728 | ||
|
|
94490d3099 | ||
|
|
b2f7f34415 | ||
|
|
dc61a05a4c | ||
|
|
57dcea9665 | ||
|
|
11d1068b0c | ||
|
|
641f4888c4 | ||
|
|
6c3eed1621 | ||
|
|
fa86ad3bc1 | ||
|
|
976abf18ca | ||
|
|
377be98d59 | ||
|
|
9dbe382482 | ||
|
|
96a5bb957b | ||
|
|
8458d4c873 | ||
|
|
0fd60c869b | ||
|
|
9f42854283 | ||
|
|
935c3f915d | ||
|
|
9c31b72d4a | ||
|
|
2b0ca4a2d6 | ||
|
|
f7b0e1891f | ||
|
|
8b4e85a223 | ||
|
|
cca68c9d4b | ||
|
|
73a7feb190 | ||
|
|
2457a7fc63 | ||
|
|
a562022ed9 | ||
|
|
b865ababeb | ||
|
|
12ab2b16cf | ||
|
|
157c090d5a | ||
|
|
39bfa7a819 | ||
|
|
cdd88557f6 | ||
|
|
9cf15d70b7 | ||
|
|
80d8f1cab8 | ||
|
|
ffdf102557 | ||
|
|
a1b3914731 | ||
|
|
ee48c7b82e | ||
|
|
d9731c4933 | ||
|
|
90eb49531e | ||
|
|
27cfbcb894 | ||
|
|
dc213a7f7d | ||
|
|
fdbbcdba1c | ||
|
|
2c56c1e0f7 | ||
|
|
2fb2bbeb02 | ||
|
|
b580d214b4 | ||
|
|
19e3eb4e0c | ||
|
|
e3fab2ac01 | ||
|
|
bc2634f423 | ||
|
|
f4c31b94b1 | ||
|
|
af578d2e40 | ||
|
|
8492390532 | ||
|
|
2905629ea4 | ||
|
|
c9a8d971f4 | ||
|
|
ff6c51e54c | ||
|
|
b26c4cb790 | ||
|
|
6b39486d2c | ||
|
|
69cf9b75ca | ||
|
|
668cc2675f | ||
|
|
edaafab71f | ||
|
|
fb67d78c18 | ||
|
|
21b085af5f | ||
|
|
ccca7e9d96 | ||
|
|
c02d64fe5c | ||
|
|
dcb366ca66 | ||
|
|
3e2c26d7d5 | ||
|
|
054faf7e6d | ||
|
|
bacaae7379 | ||
|
|
af48cf94a4 | ||
|
|
b39c421b98 | ||
|
|
176fcdbd0d | ||
|
|
b8a0d0233e | ||
|
|
31bb4568ea | ||
|
|
6657d340c9 | ||
|
|
efce2761be | ||
|
|
c77de140c8 | ||
|
|
0a6b5a09eb | ||
|
|
ddf1436b98 | ||
|
|
b463fdb52c | ||
|
|
26c37d6a64 | ||
|
|
16c932f680 | ||
|
|
e93f066622 | ||
|
|
0d1bd15af6 | ||
|
|
5aacbeac9b | ||
|
|
3a8a04e5ad | ||
|
|
324f764c2b | ||
|
|
5daf42835f | ||
|
|
dab0b0f83d | ||
|
|
a8b1dc44fa | ||
|
|
20bd69d98b | ||
|
|
f7c37e718b | ||
|
|
425b204f49 | ||
|
|
9c5eb6975b | ||
|
|
c667db85ec | ||
|
|
c05cd8b1e8 | ||
|
|
571bf8a87f | ||
|
|
36f9337a64 | ||
|
|
c38b3699e6 | ||
|
|
44c5eaa46e | ||
|
|
7b5216ce57 | ||
|
|
5f3aba0651 | ||
|
|
3194543231 | ||
|
|
515e39edbf | ||
|
|
dbac87a7c1 | ||
|
|
f0789de653 | ||
|
|
b98f9c9766 | ||
|
|
54f29598ff | ||
|
|
adb13af2ed | ||
|
|
68c51d2103 | ||
|
|
c21c0b1aa2 | ||
|
|
f28358b33b | ||
|
|
f5eb43ca84 | ||
|
|
ddc80bafdb | ||
|
|
f1d846dabc | ||
|
|
174365715e | ||
|
|
215f5a3008 | ||
|
|
ff86b6ce87 | ||
|
|
79773139e9 | ||
|
|
aaa3e0131c | ||
|
|
f81ad71d2e | ||
|
|
adbdac1229 | ||
|
|
c124166064 | ||
|
|
d4e8b127fc | ||
|
|
777703e175 | ||
|
|
34029d15d3 | ||
|
|
4cc630477b | ||
|
|
9f07c73fa5 | ||
|
|
6ff0b5a17e | ||
|
|
703ddcc7b8 | ||
|
|
ca55b66fad | ||
|
|
73e62e6511 | ||
|
|
b254707f5c |
@@ -13,5 +13,5 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"semi": ["error", "never"]
|
"semi": ["error", "never"]
|
||||||
},
|
},
|
||||||
"ignorePatterns": ["/dist/", "/pre/"]
|
"ignorePatterns": ["/dist/"]
|
||||||
}
|
}
|
||||||
|
|||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
dist/** -diff linguist-generated=true
|
||||||
4
.github/release-drafter.yml
vendored
4
.github/release-drafter.yml
vendored
@@ -6,7 +6,11 @@ template: |
|
|||||||
|
|
||||||
$CHANGES
|
$CHANGES
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
See details of [all code changes](https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION) since previous release.
|
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:
|
categories:
|
||||||
- title: '🚀 Features'
|
- title: '🚀 Features'
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
17
.github/workflows/check-dist.yml
vendored
17
.github/workflows/check-dist.yml
vendored
@@ -21,12 +21,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set Node.js 16.x
|
- name: Setup Node.JS
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version-file: '.node-version'
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -36,9 +37,17 @@ jobs:
|
|||||||
run: npm run prepare
|
run: npm run prepare
|
||||||
|
|
||||||
- name: Compare the expected and actual dist/ directories
|
- name: Compare the expected and actual dist/ directories
|
||||||
|
id: diff
|
||||||
run: |
|
run: |
|
||||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
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:"
|
echo "Detected uncommitted changes after build in dist folder. See status below:"
|
||||||
git diff
|
git diff
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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: Checking formatting
|
name: Check formatting
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -19,13 +19,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.JS
|
- name: Setup Node.JS
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version-file: '.node-version'
|
||||||
cache: 'npm'
|
cache: npm
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
10
.github/workflows/check-linter.yml
vendored
10
.github/workflows/check-linter.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Check linter
|
name: Check linting
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -19,13 +19,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.JS
|
- name: Setup Node.JS
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version-file: '.node-version'
|
||||||
cache: 'npm'
|
cache: npm
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
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
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: 'CodeQL'
|
name: CodeQL
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -37,8 +37,8 @@ jobs:
|
|||||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|||||||
3
.github/workflows/draft-release.yml
vendored
3
.github/workflows/draft-release.yml
vendored
@@ -11,7 +11,6 @@ jobs:
|
|||||||
draft-release:
|
draft-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: release-drafter/release-drafter@09c613e259eb8d4e7c81c2cb00618eb5fc4575a7 # v5.25.0
|
||||||
- uses: release-drafter/release-drafter@v5
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAGES_AUTOMATION_PAT }}
|
token: ${{ secrets.PAGES_AUTOMATION_PAT }}
|
||||||
|
|
||||||
- name: Setup Node.JS
|
- name: Setup Node.JS
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version-file: '.node-version'
|
||||||
cache: 'npm'
|
cache: npm
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
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:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -14,13 +14,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.JS
|
- name: Setup Node.JS
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version-file: '.node-version'
|
||||||
cache: 'npm'
|
cache: npm
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -64,4 +64,4 @@ typings/
|
|||||||
.env
|
.env
|
||||||
|
|
||||||
# next.js build output
|
# next.js build output
|
||||||
.next
|
.next
|
||||||
|
|||||||
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
18.9.0
|
||||||
@@ -1 +0,0 @@
|
|||||||
* @actions/pages
|
|
||||||
29
CONTRIBUTING.md
Normal file
29
CONTRIBUTING.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# 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,10 +1,8 @@
|
|||||||
# deploy-pages
|
# deploy-pages 🚀
|
||||||
|
|
||||||
This action is used to deploy [Actions artifacts][artifacts] to GitHub 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)
|
||||||
|
|
||||||
## Scope
|
This action is used to deploy [Actions artifacts][artifacts] to [GitHub Pages](https://pages.github.com/).
|
||||||
|
|
||||||
⚠️ Official support for building Pages with Actions is in public beta at the moment.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -12,7 +10,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.
|
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 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 when using [`actions/upload-pages-artifact`][upload-pages-artifact].
|
||||||
|
|
||||||
We recommend this action to be used in a dedicated job:
|
We recommend this action to be used in a dedicated job:
|
||||||
|
|
||||||
@@ -43,10 +41,33 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v1
|
uses: actions/deploy-pages@v2 # or the latest "vX.X.X" version tag for this action
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security considerations
|
### 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
|
||||||
|
|
||||||
There are a few important considerations to be aware of:
|
There are a few important considerations to be aware of:
|
||||||
|
|
||||||
@@ -60,9 +81,23 @@ 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
|
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 do 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 will configure it by default for you).
|
||||||
|
|
||||||
# Release instructions
|
## 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
|
||||||
|
|
||||||
In order to release a new version of this Action:
|
In order to release a new version of this Action:
|
||||||
|
|
||||||
|
|||||||
11
action.yml
11
action.yml
@@ -2,18 +2,9 @@ name: 'Deploy GitHub Pages site'
|
|||||||
description: 'A GitHub Action to deploy an artifact as a GitHub Pages site'
|
description: 'A GitHub Action to deploy an artifact as a GitHub Pages site'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
runs:
|
runs:
|
||||||
using: 'node16'
|
using: 'node20'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
inputs:
|
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:
|
token:
|
||||||
description: 'GitHub token'
|
description: 'GitHub token'
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
|
|||||||
1
coverage_badge.svg
Normal file
1
coverage_badge.svg
Normal file
@@ -0,0 +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: 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>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
24101
dist/index.js
generated
vendored
24101
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
47
dist/licenses.txt
generated
vendored
47
dist/licenses.txt
generated
vendored
@@ -47,6 +47,28 @@ 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.
|
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
|
@octokit/auth-token
|
||||||
MIT
|
MIT
|
||||||
The MIT License
|
The MIT License
|
||||||
@@ -553,6 +575,31 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||||||
THE SOFTWARE.
|
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
|
universal-user-agent
|
||||||
ISC
|
ISC
|
||||||
# [ISC License](https://spdx.org/licenses/ISC)
|
# [ISC License](https://spdx.org/licenses/ISC)
|
||||||
|
|||||||
2
dist/sourcemap-register.js
generated
vendored
2
dist/sourcemap-register.js
generated
vendored
File diff suppressed because one or more lines are too long
5975
package-lock.json
generated
5975
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
@@ -1,26 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "deploy-pages",
|
"name": "deploy-pages",
|
||||||
"version": "0.0.1",
|
"version": "0.0.0",
|
||||||
"description": "Deploy an actions artifact to GitHub Pages",
|
"description": "Deploy an actions artifact to GitHub Pages",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/github": "^5.1.1",
|
"@actions/github": "^5.1.1",
|
||||||
"@actions/http-client": "^2.1.0",
|
"@actions/http-client": "^2.2.0",
|
||||||
"@octokit/request-error": "^3.0.3",
|
"@octokit/request-error": "^5.0.1",
|
||||||
"http-status-messages": "^1.1.0"
|
"http-status-messages": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vercel/ncc": "^0.31.1",
|
"@vercel/ncc": "^0.38.1",
|
||||||
"eslint": "^8.36.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^8.7.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-github": "^4.6.1",
|
"eslint-plugin-github": "^4.10.1",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.7.0",
|
||||||
"nock": "^13.3.0",
|
"nock": "^13.3.8",
|
||||||
"prettier": "^2.8.4"
|
"prettier": "^3.1.0",
|
||||||
|
"make-coverage-badge": "^1.2.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"all": "npm run format && npm run lint && npm run prepare && npm run test",
|
"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",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check .",
|
"format:check": "prettier --check .",
|
||||||
"lint": "DEBUG=eslint:cli-engine eslint --fix .",
|
"lint": "DEBUG=eslint:cli-engine eslint --fix .",
|
||||||
@@ -41,5 +43,22 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/actions/deploy-pages/issues"
|
"url": "https://github.com/actions/deploy-pages/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/actions/deploy-pages#readme"
|
"homepage": "https://github.com/actions/deploy-pages#readme",
|
||||||
|
"jest": {
|
||||||
|
"coverageReporters": [
|
||||||
|
"json-summary",
|
||||||
|
"text",
|
||||||
|
"lcov"
|
||||||
|
],
|
||||||
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"./src/**"
|
||||||
|
],
|
||||||
|
"coverageThreshold": {
|
||||||
|
"global": {
|
||||||
|
"lines": 70,
|
||||||
|
"statements": 70
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/__tests__/index.test.js
Normal file
38
src/__tests__/index.test.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
730
src/__tests__/internal/deployment.test.js
Normal file
730
src/__tests__/internal/deployment.test.js
Normal file
@@ -0,0 +1,730 @@
|
|||||||
|
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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
11
src/index.js
11
src/index.js
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
const core = require('@actions/core')
|
const core = require('@actions/core')
|
||||||
|
|
||||||
const { Deployment } = require('./deployment')
|
const { Deployment } = require('./internal/deployment')
|
||||||
const getContext = require('./context')
|
const getContext = require('./internal/context')
|
||||||
|
|
||||||
const deployment = new Deployment()
|
const deployment = new Deployment()
|
||||||
|
|
||||||
@@ -48,9 +48,4 @@ process.on('SIGINT', cancelHandler)
|
|||||||
process.on('SIGTERM', cancelHandler)
|
process.on('SIGTERM', cancelHandler)
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
const emitTelemetry = core.getInput('emit_telemetry')
|
main()
|
||||||
if (emitTelemetry === 'true') {
|
|
||||||
// For compatibility, treat the use of this deprecated input as a no-op
|
|
||||||
} else {
|
|
||||||
main()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,312 +0,0 @@
|
|||||||
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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -54,7 +54,7 @@ async function processRuntimeResponse(res, requestOptions) {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSignedArtifactUrl({ runtimeToken, workflowRunId, artifactName }) {
|
async function getSignedArtifactMetadata({ runtimeToken, workflowRunId, artifactName }) {
|
||||||
const { runTimeUrl: RUNTIME_URL } = getContext()
|
const { runTimeUrl: RUNTIME_URL } = getContext()
|
||||||
const artifactExchangeUrl = `${RUNTIME_URL}_apis/pipelines/workflows/${workflowRunId}/artifacts?api-version=6.0-preview`
|
const artifactExchangeUrl = `${RUNTIME_URL}_apis/pipelines/workflows/${workflowRunId}/artifacts?api-version=6.0-preview`
|
||||||
|
|
||||||
@@ -88,7 +88,8 @@ async function getSignedArtifactUrl({ runtimeToken, workflowRunId, artifactName
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const artifactRawUrl = data?.value?.find(artifact => artifact.name === artifactName)?.url
|
const artifact = data?.value?.find(artifact => artifact.name === artifactName)
|
||||||
|
const artifactRawUrl = artifact?.url
|
||||||
if (!artifactRawUrl) {
|
if (!artifactRawUrl) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'No uploaded artifact was found! Please check if there are any errors at build step, or uploaded artifact name is correct.'
|
'No uploaded artifact was found! Please check if there are any errors at build step, or uploaded artifact name is correct.'
|
||||||
@@ -96,7 +97,16 @@ async function getSignedArtifactUrl({ runtimeToken, workflowRunId, artifactName
|
|||||||
}
|
}
|
||||||
|
|
||||||
const signedArtifactUrl = `${artifactRawUrl}&%24expand=SignedContent`
|
const signedArtifactUrl = `${artifactRawUrl}&%24expand=SignedContent`
|
||||||
return signedArtifactUrl
|
|
||||||
|
const artifactSize = artifact?.size
|
||||||
|
if (!artifactSize) {
|
||||||
|
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: signedArtifactUrl,
|
||||||
|
size: artifactSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, idToken, isPreview = false }) {
|
async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, idToken, isPreview = false }) {
|
||||||
@@ -113,7 +123,7 @@ async function createPagesDeployment({ githubToken, artifactUrl, buildVersion, i
|
|||||||
core.info(`Creating Pages deployment with payload:\n${JSON.stringify(payload, null, '\t')}`)
|
core.info(`Creating Pages deployment with payload:\n${JSON.stringify(payload, null, '\t')}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployment', {
|
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployments', {
|
||||||
owner: github.context.repo.owner,
|
owner: github.context.repo.owner,
|
||||||
repo: github.context.repo.repo,
|
repo: github.context.repo.repo,
|
||||||
...payload
|
...payload
|
||||||
@@ -131,7 +141,7 @@ async function getPagesDeploymentStatus({ githubToken, deploymentId }) {
|
|||||||
|
|
||||||
core.info('Getting Pages deployment status...')
|
core.info('Getting Pages deployment status...')
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request('GET /repos/{owner}/{repo}/pages/deployment/status/{deploymentId}', {
|
const response = await octokit.request('GET /repos/{owner}/{repo}/pages/deployments/{deploymentId}', {
|
||||||
owner: github.context.repo.owner,
|
owner: github.context.repo.owner,
|
||||||
repo: github.context.repo.repo,
|
repo: github.context.repo.repo,
|
||||||
deploymentId
|
deploymentId
|
||||||
@@ -149,7 +159,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
|||||||
|
|
||||||
core.info('Canceling Pages deployment...')
|
core.info('Canceling Pages deployment...')
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request('PUT /repos/{owner}/{repo}/pages/deployment/cancel/{deploymentId}', {
|
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployments/{deploymentId}/cancel', {
|
||||||
owner: github.context.repo.owner,
|
owner: github.context.repo.owner,
|
||||||
repo: github.context.repo.repo,
|
repo: github.context.repo.repo,
|
||||||
deploymentId
|
deploymentId
|
||||||
@@ -163,7 +173,7 @@ async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSignedArtifactUrl,
|
getSignedArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
@@ -3,7 +3,7 @@ const core = require('@actions/core')
|
|||||||
// All variables we need from the runtime are loaded here
|
// All variables we need from the runtime are loaded here
|
||||||
const getContext = require('./context')
|
const getContext = require('./context')
|
||||||
const {
|
const {
|
||||||
getSignedArtifactUrl,
|
getSignedArtifactMetadata,
|
||||||
createPagesDeployment,
|
createPagesDeployment,
|
||||||
getPagesDeploymentStatus,
|
getPagesDeploymentStatus,
|
||||||
cancelPagesDeployment
|
cancelPagesDeployment
|
||||||
@@ -17,12 +17,17 @@ const temporaryErrorStatus = {
|
|||||||
|
|
||||||
const finalErrorStatus = {
|
const finalErrorStatus = {
|
||||||
deployment_failed: 'Deployment failed, try again later.',
|
deployment_failed: 'Deployment failed, try again later.',
|
||||||
|
deployment_perms_error: 'Deployment failed. Please ensure that the file permissions are correct.',
|
||||||
deployment_content_failed:
|
deployment_content_failed:
|
||||||
'Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.',
|
'Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.',
|
||||||
deployment_cancelled: 'Deployment cancelled.',
|
deployment_cancelled: 'Deployment cancelled.',
|
||||||
deployment_lost: 'Deployment failed to report final status.'
|
deployment_lost: 'Deployment failed to report final status.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_TIMEOUT = 600000
|
||||||
|
const ONE_GIGABYTE = 1073741824
|
||||||
|
const SIZE_LIMIT_DESCRIPTION = '1 GB'
|
||||||
|
|
||||||
class Deployment {
|
class Deployment {
|
||||||
constructor() {
|
constructor() {
|
||||||
const context = getContext()
|
const context = getContext()
|
||||||
@@ -39,25 +44,42 @@ class Deployment {
|
|||||||
this.githubServerUrl = context.githubServerUrl
|
this.githubServerUrl = context.githubServerUrl
|
||||||
this.artifactName = context.artifactName
|
this.artifactName = context.artifactName
|
||||||
this.isPreview = context.isPreview === true
|
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
|
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
|
||||||
// by creating a deployment with that artifact
|
// by creating a deployment with that artifact
|
||||||
async create(idToken) {
|
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 {
|
try {
|
||||||
core.debug(`Actor: ${this.buildActor}`)
|
core.debug(`Actor: ${this.buildActor}`)
|
||||||
core.debug(`Action ID: ${this.actionsId}`)
|
core.debug(`Action ID: ${this.actionsId}`)
|
||||||
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
|
||||||
|
|
||||||
const artifactUrl = await getSignedArtifactUrl({
|
const artifactData = await getSignedArtifactMetadata({
|
||||||
runtimeToken: this.runTimeToken,
|
runtimeToken: this.runTimeToken,
|
||||||
workflowRunId: this.workflowRun,
|
workflowRunId: this.workflowRun,
|
||||||
artifactName: this.artifactName
|
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({
|
const deployment = await createPagesDeployment({
|
||||||
githubToken: this.githubToken,
|
githubToken: this.githubToken,
|
||||||
artifactUrl,
|
artifactUrl: artifactData.url,
|
||||||
buildVersion: this.buildVersion,
|
buildVersion: this.buildVersion,
|
||||||
idToken,
|
idToken,
|
||||||
isPreview: this.isPreview
|
isPreview: this.isPreview
|
||||||
@@ -69,6 +91,7 @@ class Deployment {
|
|||||||
id: deployment.id || deployment.status_url?.split('/')?.pop() || this.buildVersion,
|
id: deployment.id || deployment.status_url?.split('/')?.pop() || this.buildVersion,
|
||||||
pending: true
|
pending: true
|
||||||
}
|
}
|
||||||
|
this.startTime = Date.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Created deployment for ${this.buildVersion}, ID: ${this.deploymentInfo?.id}`)
|
core.info(`Created deployment for ${this.buildVersion}, ID: ${this.deploymentInfo?.id}`)
|
||||||
@@ -79,9 +102,6 @@ class Deployment {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error(error.stack)
|
core.error(error.stack)
|
||||||
|
|
||||||
// output raw error in debug mode.
|
|
||||||
core.debug(JSON.stringify(error))
|
|
||||||
|
|
||||||
// build customized error message based on server response
|
// build customized error message based on server response
|
||||||
if (error.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}. `
|
||||||
@@ -92,12 +112,18 @@ class Deployment {
|
|||||||
} else if (error.status === 404) {
|
} else if (error.status === 404) {
|
||||||
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
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 +=
|
||||||
|
'\nNote: This action version may not yet support GitHub Enterprise Server, please check the compatibility table.'
|
||||||
|
}
|
||||||
} else if (error.status >= 500) {
|
} else if (error.status >= 500) {
|
||||||
errorMessage +=
|
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)
|
throw new Error(errorMessage)
|
||||||
} else {
|
} else {
|
||||||
|
// istanbul ignore next
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,11 +142,9 @@ class Deployment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
||||||
const timeout = Number(core.getInput('timeout'))
|
|
||||||
const reportingInterval = Number(core.getInput('reporting_interval'))
|
const reportingInterval = Number(core.getInput('reporting_interval'))
|
||||||
const maxErrorCount = Number(core.getInput('error_count'))
|
const maxErrorCount = Number(core.getInput('error_count'))
|
||||||
|
|
||||||
let startTime = Date.now()
|
|
||||||
let errorCount = 0
|
let errorCount = 0
|
||||||
|
|
||||||
// Time in milliseconds between two deployment status report when status errored, default 0.
|
// Time in milliseconds between two deployment status report when status errored, default 0.
|
||||||
@@ -162,9 +186,6 @@ class Deployment {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error(error.stack)
|
core.error(error.stack)
|
||||||
|
|
||||||
// output raw error in debug mode.
|
|
||||||
core.debug(JSON.stringify(error))
|
|
||||||
|
|
||||||
// build customized error message based on server response
|
// build customized error message based on server response
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
errorStatus = error.status || error.response.status
|
errorStatus = error.status || error.response.status
|
||||||
@@ -188,7 +209,7 @@ class Deployment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle timeout
|
// Handle timeout
|
||||||
if (Date.now() - startTime >= timeout) {
|
if (Date.now() - this.startTime >= this.timeout) {
|
||||||
core.error('Timeout reached, aborting!')
|
core.error('Timeout reached, aborting!')
|
||||||
core.setFailed('Timeout reached, aborting!')
|
core.setFailed('Timeout reached, aborting!')
|
||||||
|
|
||||||
@@ -202,6 +223,7 @@ class Deployment {
|
|||||||
async cancel() {
|
async cancel() {
|
||||||
// Don't attempt to cancel if no deployment was created
|
// Don't attempt to cancel if no deployment was created
|
||||||
if (!this.deploymentInfo || this.deploymentInfo.pending !== true) {
|
if (!this.deploymentInfo || this.deploymentInfo.pending !== true) {
|
||||||
|
core.debug('No deployment to cancel')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,4 +246,4 @@ class Deployment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { Deployment }
|
module.exports = { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION }
|
||||||
Reference in New Issue
Block a user