mirror of
https://github.com/actions/deploy-pages.git
synced 2026-03-28 08:54:53 +00:00
Compare commits
342 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
decdde0ac0 | ||
|
|
0b3be6ba52 | ||
|
|
c2c861cca0 | ||
|
|
294fbcd300 | ||
|
|
2a4b535672 | ||
|
|
4825f57d2c | ||
|
|
fa29843a5a | ||
|
|
d005625ad3 | ||
|
|
636701b46d | ||
|
|
25b80099b0 | ||
|
|
ace85779a4 | ||
|
|
22931f5a71 | ||
|
|
87c3283f01 | ||
|
|
87625d9f1e | ||
|
|
8de45ac1dc | ||
|
|
7a9bd943aa | ||
|
|
eee8a27158 | ||
|
|
b6e5c85160 | ||
|
|
b8d2528df3 | ||
|
|
53d1eac7fd | ||
|
|
3f0ef9d75d | ||
|
|
82751044df | ||
|
|
9be9d731c9 | ||
|
|
d8afefafec | ||
|
|
304d0b77f8 | ||
|
|
3a33eeefa1 | ||
|
|
d8af841ac3 | ||
|
|
35a0f06cfc | ||
|
|
5cba2b1245 | ||
|
|
e03d00b325 | ||
|
|
d6fbcf80b1 | ||
|
|
013b725db3 | ||
|
|
ed0e794532 | ||
|
|
1c0b543596 | ||
|
|
fe3d75dd3a | ||
|
|
b3879bac7d | ||
|
|
72ab98158a | ||
|
|
c704b8a6e2 | ||
|
|
02cb90ee32 | ||
|
|
88807a7a35 | ||
|
|
e386446c2a | ||
|
|
340b369533 | ||
|
|
4be34033fc | ||
|
|
0486580c63 | ||
|
|
af9eaca61c | ||
|
|
81251b551f | ||
|
|
47cf65bf47 | ||
|
|
1a1c979b91 | ||
|
|
925d92be20 | ||
|
|
b80d7fe30e | ||
|
|
d724c5069f | ||
|
|
f33f41b675 | ||
|
|
0d45f33cde | ||
|
|
d1e23d0efd | ||
|
|
13b55b33dd | ||
|
|
c284911fe1 | ||
|
|
ae44070880 | ||
|
|
34a8f6066a | ||
|
|
b65643c0ab | ||
|
|
faceed1585 | ||
|
|
230466c706 | ||
|
|
38d34f8e6f | ||
|
|
cd33125d26 | ||
|
|
dd970aea72 | ||
|
|
7433d78cba | ||
|
|
56d63c68a6 | ||
|
|
1050c5c646 | ||
|
|
f8e6f4a56d | ||
|
|
b687ea25c8 | ||
|
|
ccfe503c3f | ||
|
|
ed119588ad | ||
|
|
a53c6bc7f1 | ||
|
|
cc18e529bf | ||
|
|
b751a79e06 | ||
|
|
dda23d18e4 | ||
|
|
3430969625 | ||
|
|
6784dc0464 | ||
|
|
89c47f5a13 | ||
|
|
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 | ||
|
|
125d07c91d | ||
|
|
7496661f94 | ||
|
|
4279385f7d | ||
|
|
a075a5970d | ||
|
|
b15b65d3e5 | ||
|
|
fa898e325d | ||
|
|
d45e4be1a6 | ||
|
|
b12897a760 | ||
|
|
499890a085 | ||
|
|
324d9f15de | ||
|
|
2196d013ad | ||
|
|
ce81cfb969 | ||
|
|
da4705cd08 | ||
|
|
961a4a1557 | ||
|
|
38d3e68bdc | ||
|
|
08232476b8 | ||
|
|
fa86ad3bc1 | ||
|
|
976abf18ca | ||
|
|
377be98d59 | ||
|
|
f69bebbdd5 | ||
|
|
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 | ||
|
|
dcda0bb2bc | ||
|
|
7ed610198d | ||
|
|
30c6e00b35 | ||
|
|
7fec4b245d | ||
|
|
73594fd4fc | ||
|
|
e690f03220 | ||
|
|
c3f52457f2 | ||
|
|
72f6a10043 | ||
|
|
c9063b9615 | ||
|
|
6e7778e824 | ||
|
|
497da40f52 | ||
|
|
ce35ecd745 | ||
|
|
a8d1a4cdd2 | ||
|
|
44b836afed | ||
|
|
7e85efa568 | ||
|
|
df61277554 | ||
|
|
f824564d16 | ||
|
|
02bdb05bdf | ||
|
|
360f95f104 | ||
|
|
7ee7a04145 | ||
|
|
54e5e8c886 | ||
|
|
8ca11f25a3 | ||
|
|
4c1957985f | ||
|
|
3adc03ee17 | ||
|
|
64868855a8 | ||
|
|
405a1ea745 | ||
|
|
4e8b484add | ||
|
|
ce8433f767 | ||
|
|
44cd7be663 | ||
|
|
a7b43fb46d | ||
|
|
f2ea9e954b | ||
|
|
973ee9bf72 | ||
|
|
b4ef3c2b4b | ||
|
|
b88aa87bba | ||
|
|
93872af9c4 | ||
|
|
6f75b9951c | ||
|
|
452af0a0c2 | ||
|
|
c981f59b6f | ||
|
|
e013d9f49d | ||
|
|
c8c985cff1 | ||
|
|
1947635a3f | ||
|
|
f3ccae1b04 | ||
|
|
a2b8e8c9db | ||
|
|
800d4ba350 | ||
|
|
5f0a0ff0b4 | ||
|
|
70116d1421 | ||
|
|
90e90be05e | ||
|
|
479102aa60 | ||
|
|
e47a74ee39 | ||
|
|
36aae3e29a | ||
|
|
07cfc07e56 | ||
|
|
e39f45b976 | ||
|
|
09d8b68bd9 | ||
|
|
58d3ea9333 | ||
|
|
c33bfc33f9 | ||
|
|
cb8a04dd0e | ||
|
|
4d06ee6166 | ||
|
|
2864cb64c0 | ||
|
|
d6aec1031d | ||
|
|
155a7a7d81 | ||
|
|
b93f1293af | ||
|
|
cb00670952 | ||
|
|
65f1527542 | ||
|
|
594fd8fbbf | ||
|
|
29db692e2d | ||
|
|
f577a41201 | ||
|
|
a0faa043a9 | ||
|
|
12b8ec3b9b | ||
|
|
e7873ec09d | ||
|
|
c2918206ce | ||
|
|
20fe504b3b | ||
|
|
93c1fe6967 | ||
|
|
f215e05bdc | ||
|
|
2fe84ca11a | ||
|
|
a7cb42fc67 | ||
|
|
ff669327f7 | ||
|
|
b7a3d169e8 | ||
|
|
0c677bf5dd | ||
|
|
4014f178cc | ||
|
|
1bdd0138c1 | ||
|
|
024ce73336 | ||
|
|
899d42d10e | ||
|
|
0a94f060a9 | ||
|
|
da1c9ce783 | ||
|
|
dfb0b0286e | ||
|
|
802083045a | ||
|
|
f0b70b47de | ||
|
|
ec96bc5017 | ||
|
|
953889442f | ||
|
|
423b8fadd3 | ||
|
|
51ec3f54b3 | ||
|
|
5acfbbfe53 | ||
|
|
8199da9054 | ||
|
|
51b5f463c8 | ||
|
|
a47774e76f |
@@ -13,5 +13,5 @@
|
||||
"rules": {
|
||||
"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
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -4,8 +4,14 @@ updates:
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
groups:
|
||||
non-breaking-changes:
|
||||
update-types: [minor, patch]
|
||||
|
||||
- package-ecosystem: 'npm'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
groups:
|
||||
non-breaking-changes:
|
||||
update-types: [minor, patch]
|
||||
|
||||
4
.github/release-drafter.yml
vendored
4
.github/release-drafter.yml
vendored
@@ -6,7 +6,11 @@ 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/#compatibility).
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
|
||||
23
.github/workflows/check-dist.yml
vendored
23
.github/workflows/check-dist.yml
vendored
@@ -3,7 +3,7 @@
|
||||
# `index.js` is the code that will run.
|
||||
# For our project, we generate this file using `ncc`
|
||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||
name: Check dist/
|
||||
name: Check distributables
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -21,12 +21,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -36,6 +37,7 @@ 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:"
|
||||
@@ -43,8 +45,9 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(git diff --ignore-space-at-eol pre/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after build in pre 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@v4
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
12
.github/workflows/check-formatting.yml
vendored
12
.github/workflows/check-formatting.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Checking formatting
|
||||
name: Check formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -15,17 +15,17 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
12
.github/workflows/check-linter.yml
vendored
12
.github/workflows/check-linter.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Check linter
|
||||
name: Check linting
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -15,17 +15,17 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
12
.github/workflows/codeql-analysis.yml
vendored
12
.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,12 +37,12 @@ jobs:
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
16
.github/workflows/draft-release.yml
vendored
Normal file
16
.github/workflows/draft-release.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Draft release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@09c613e259eb8d4e7c81c2cb00618eb5fc4575a7 # v5.25.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
51
.github/workflows/rebuild-dependabot-prs.yml
vendored
Normal file
51
.github/workflows/rebuild-dependabot-prs.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Rebuild distributables for Dependabot PRs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dependabot/npm**'
|
||||
|
||||
# No permissions needed for `GITHUB_TOKEN` since we're using a PAT instead
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
rebuild-dist:
|
||||
if: ${{ github.event.sender.login == 'dependabot[bot]' }}
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs.
|
||||
# It is evaluated AFTER the job's `if` condition, so a push triggered by this
|
||||
# workflow's PAT will NOT interrupt a run triggered by a push from Dependabot.
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} / ${{ github.job }} @ ${{ github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.PAGES_AUTOMATION_PAT }}
|
||||
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: npm run prepare
|
||||
|
||||
- name: Commit any differences present in the dist/ directory
|
||||
run: |
|
||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after rebuild in dist folder. Committing..."
|
||||
git add dist/
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git commit -m "Update distributables after Dependabot 🤖"
|
||||
echo "Pushing branch ${{ github.ref_name }}"
|
||||
git push origin ${{ github.ref_name }}
|
||||
fi
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
steps:
|
||||
- name: Update the ${{ env.TAG_NAME }} tag
|
||||
id: update-major-tag
|
||||
uses: actions/publish-action@v0.2.2
|
||||
uses: actions/publish-action@v0.3.0
|
||||
with:
|
||||
source-tag: ${{ env.TAG_NAME }}
|
||||
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
||||
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
@@ -1,28 +1,29 @@
|
||||
name: Run Tests
|
||||
name: Run tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
- name: Setup Node.JS
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version-file: '.node-version'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
# Drafts your next Release notes as Pull Requests are merged into "main"
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
if: github.ref_name == 'main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
20.10.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`.
|
||||
64
README.md
64
README.md
@@ -1,18 +1,16 @@
|
||||
# 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
|
||||
|
||||
⚠️ Official support for building Pages with Actions is in public beta at the moment.
|
||||
This action is used to deploy [Actions artifacts][artifacts] to [GitHub Pages](https://pages.github.com/).
|
||||
|
||||
## Usage
|
||||
|
||||
See [action.yml](action.yml) for the various `inputs` this action supports.
|
||||
See [action.yml](action.yml) for the various `inputs` this action supports (or [below](#inputs-📥)).
|
||||
|
||||
For examples that make use of this action, check out our [starter-workflows][starter-workflows] in a variety of frameworks.
|
||||
|
||||
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 deploys a Pages site previously uploaded as an artifact (e.g. using [`actions/upload-pages-artifact`][upload-pages-artifact]).
|
||||
|
||||
We recommend this action to be used in a dedicated job:
|
||||
|
||||
@@ -43,14 +41,37 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
uses: actions/deploy-pages@v4 # or specific "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 reports (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:
|
||||
|
||||
1. The artifact being deployed must have been uploaded in a previous step, either in the same job or a separate job that doesn't execute until the upload is complete.
|
||||
1. The artifact being deployed must have been uploaded in a previous step, either in the same job or a separate job that doesn't execute until the upload is complete. See [`actions/upload-pages-artifact`][upload-pages-artifact] for more information about the format of the artifact we expect.
|
||||
|
||||
2. The job that executes the deployment must at minimum have the following permissions:
|
||||
- `pages: write`
|
||||
@@ -60,9 +81,26 @@ 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 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 designed 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 |
|
||||
|:---|:---|
|
||||
| [`v4`](https://github.com/actions/deploy-pages/releases/tag/v4) | :warning: Incompatible at this time |
|
||||
| [`v3`](https://github.com/actions/deploy-pages/releases/tag/v3) | `>= 3.9` |
|
||||
| `v3.x.x` | `>= 3.9` |
|
||||
| [`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:
|
||||
|
||||
@@ -70,7 +108,7 @@ In order to release a new version of this Action:
|
||||
|
||||
2. Publish the draft release from the `main` branch with semantic version as the tag name, _with_ the checkbox to publish to the GitHub Marketplace checked. :ballot_box_with_check:
|
||||
|
||||
3. After publishing the release, the [`release` workflow][release] will automatically run to create/update the corresponding the major version tag such as `v1`.
|
||||
3. After publishing the release, the [`release` workflow][release] will automatically run to create/update the corresponding major version tag such as `v1`.
|
||||
|
||||
⚠️ Environment approval is required. Check the [Release workflow run list][release-workflow-runs].
|
||||
|
||||
|
||||
10
action.yml
10
action.yml
@@ -2,17 +2,9 @@ name: 'Deploy GitHub Pages site'
|
||||
description: 'A GitHub Action to deploy an artifact as a GitHub Pages site'
|
||||
author: 'GitHub'
|
||||
runs:
|
||||
using: 'node16'
|
||||
using: 'node20'
|
||||
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:
|
||||
description: 'The status of the previous build.'
|
||||
required: false
|
||||
token:
|
||||
description: '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: 81.13%"><title>Coverage: 81.13%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">81.13%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">81.13%</text></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
151384
dist/index.js
generated
vendored
151384
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
3109
dist/licenses.txt
generated
vendored
3109
dist/licenses.txt
generated
vendored
File diff suppressed because it is too large
Load Diff
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
8866
package-lock.json
generated
8866
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
48
package.json
48
package.json
@@ -1,24 +1,29 @@
|
||||
{
|
||||
"name": "deploy-pages",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"description": "Deploy an actions artifact to GitHub Pages",
|
||||
"main": "./dist/index.js",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"axios": "^1.3.0",
|
||||
"axios-retry": "^3.2.4"
|
||||
"@actions/artifact": "^2.1.1",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@octokit/request-error": "^5.0.1",
|
||||
"http-status-messages": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "^0.31.1",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-github": "^4.3.4",
|
||||
"jest": "^29.4.1",
|
||||
"nock": "^13.2.0",
|
||||
"prettier": "^2.8.3"
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-github": "^4.10.1",
|
||||
"jest": "^29.7.0",
|
||||
"make-coverage-badge": "^1.2.0",
|
||||
"nock": "^13.4.0",
|
||||
"prettier": "^3.1.0",
|
||||
"undici": "^6.2.1"
|
||||
},
|
||||
"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:check": "prettier --check .",
|
||||
"lint": "DEBUG=eslint:cli-engine eslint --fix .",
|
||||
@@ -39,5 +44,22 @@
|
||||
"bugs": {
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/__tests__/index.test.js
Normal file
36
src/__tests__/index.test.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const process = require('process')
|
||||
const cp = require('child_process')
|
||||
const path = require('path')
|
||||
|
||||
describe('with all environment variables set', () => {
|
||||
beforeEach(() => {
|
||||
process.env.GITHUB_RUN_ID = '123'
|
||||
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.GITHUB_RUN_ID
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
1077
src/__tests__/internal/deployment.test.js
Normal file
1077
src/__tests__/internal/deployment.test.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,233 +0,0 @@
|
||||
const core = require('@actions/core')
|
||||
const axios = require('axios')
|
||||
|
||||
// All variables we need from the runtime are loaded here
|
||||
const getContext = require('./context')
|
||||
|
||||
const errorStatus = {
|
||||
unknown_status: 'Unable to get deployment status.',
|
||||
not_found: 'Deployment not found.',
|
||||
deployment_attempt_error: 'Deployment temporarily failed, a retry will be automatically scheduled...'
|
||||
}
|
||||
|
||||
class Deployment {
|
||||
constructor() {
|
||||
const context = getContext()
|
||||
this.runTimeUrl = context.runTimeUrl
|
||||
this.repositoryNwo = context.repositoryNwo
|
||||
this.runTimeToken = context.runTimeToken
|
||||
this.buildVersion = context.buildVersion
|
||||
this.buildActor = context.buildActor
|
||||
this.actionsId = context.workflowRun
|
||||
this.githubToken = context.githubToken
|
||||
this.workflowRun = context.workflowRun
|
||||
this.requestedDeployment = false
|
||||
this.deploymentInfo = null
|
||||
this.githubApiUrl = context.githubApiUrl
|
||||
this.githubServerUrl = context.githubServerUrl
|
||||
this.artifactName = context.artifactName
|
||||
this.isPreview = context.isPreview === true
|
||||
}
|
||||
|
||||
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
|
||||
// by creating a deployment with that artifact
|
||||
async create(idToken) {
|
||||
try {
|
||||
core.info(`Actor: ${this.buildActor}`)
|
||||
core.info(`Action ID: ${this.actionsId}`)
|
||||
const pagesDeployEndpoint = `${this.githubApiUrl}/repos/${this.repositoryNwo}/pages/deployment`
|
||||
const artifactExgUrl = `${this.runTimeUrl}_apis/pipelines/workflows/${this.workflowRun}/artifacts?api-version=6.0-preview`
|
||||
core.info(`Artifact URL: ${artifactExgUrl}`)
|
||||
const { data } = await axios.get(artifactExgUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.runTimeToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
core.info(JSON.stringify(data))
|
||||
const artifactRawUrl = data?.value?.find(artifact => artifact.name === this.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.'
|
||||
)
|
||||
}
|
||||
|
||||
const artifactUrl = `${artifactRawUrl}&%24expand=SignedContent`
|
||||
const payload = {
|
||||
artifact_url: artifactUrl,
|
||||
pages_build_version: this.buildVersion,
|
||||
oidc_token: idToken
|
||||
}
|
||||
if (this.isPreview === true) {
|
||||
payload.preview = true
|
||||
}
|
||||
core.info(`Creating deployment with payload:\n${JSON.stringify(payload, null, '\t')}`)
|
||||
const response = await axios.post(pagesDeployEndpoint, payload, {
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${this.githubToken}`,
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
this.requestedDeployment = true
|
||||
core.info(`Created deployment for ${this.buildVersion}`)
|
||||
if (response && response.data) {
|
||||
core.info(JSON.stringify(response.data))
|
||||
this.deploymentInfo = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
core.info(error.stack)
|
||||
|
||||
// output raw error in debug mode.
|
||||
core.debug(JSON.stringify(error))
|
||||
|
||||
// build customized error message based on server response
|
||||
if (error.response) {
|
||||
let errorMessage = `Failed to create deployment (status: ${error.response.status}) with build version ${this.buildVersion}. `
|
||||
if (error.response.status == 400) {
|
||||
let message = ''
|
||||
if (error.response.data && error.response.data.message) {
|
||||
message = error.response.data.message
|
||||
} else {
|
||||
message = error.response.data
|
||||
}
|
||||
errorMessage += `Responded with: ${message}`
|
||||
} else if (error.response.status == 403) {
|
||||
errorMessage += `Ensure GITHUB_TOKEN has permission "pages: write".`
|
||||
} else if (error.response.status == 404) {
|
||||
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
||||
errorMessage += `Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
|
||||
} else if (error.response.status >= 500) {
|
||||
errorMessage += `Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.`
|
||||
}
|
||||
throw errorMessage
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poll the deployment endpoint for status
|
||||
async check() {
|
||||
try {
|
||||
const statusUrl =
|
||||
this.deploymentInfo != null
|
||||
? this.deploymentInfo['status_url']
|
||||
: `${this.githubApiUrl}/repos/${this.repositoryNwo}/pages/deployment/status/${this.buildVersion}`
|
||||
let pageUrl = this.deploymentInfo != null ? this.deploymentInfo['page_url'] : ''
|
||||
const previewUrl = this.deploymentInfo != null ? this.deploymentInfo['preview_url'] : ''
|
||||
if (this.isPreview && previewUrl) {
|
||||
pageUrl = previewUrl
|
||||
}
|
||||
core.setOutput('page_url', pageUrl)
|
||||
const timeout = Number(core.getInput('timeout'))
|
||||
const reportingInterval = Number(core.getInput('reporting_interval'))
|
||||
const maxErrorCount = Number(core.getInput('error_count'))
|
||||
var startTime = Date.now()
|
||||
var errorCount = 0
|
||||
|
||||
// Time in milliseconds between two deployment status report when status errored, default 0.
|
||||
var errorReportingInterval = 0
|
||||
|
||||
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
|
||||
while (true) {
|
||||
// Handle reporting interval
|
||||
await new Promise(r => setTimeout(r, reportingInterval + errorReportingInterval))
|
||||
|
||||
// Check status
|
||||
var res = await axios.get(statusUrl, {
|
||||
headers: {
|
||||
Authorization: `token ${this.githubToken}`
|
||||
}
|
||||
})
|
||||
|
||||
if (res.data.status == 'succeed') {
|
||||
core.info('Reported success!')
|
||||
core.setOutput('status', 'succeed')
|
||||
break
|
||||
} else if (res.data.status == 'deployment_failed') {
|
||||
// Fall into permanent error, it may be caused by ongoing incident or malicious deployment content or exhausted automatic retry times.
|
||||
core.setFailed('Deployment failed, try again later.')
|
||||
break
|
||||
} else if (res.data.status == 'deployment_content_failed') {
|
||||
// The uploaded artifact is invalid.
|
||||
core.setFailed(
|
||||
'Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.'
|
||||
)
|
||||
break
|
||||
} else if (errorStatus[res.data.status]) {
|
||||
// A temporary error happened, will query the status again
|
||||
core.info(errorStatus[res.data.status])
|
||||
} else {
|
||||
core.info('Current status: ' + res.data.status)
|
||||
}
|
||||
|
||||
if (res.status != 200 || !!errorStatus[res.data.status]) {
|
||||
errorCount++
|
||||
|
||||
// set the Maximum error reporting interval greater than 15 sec but below 30 sec.
|
||||
if (errorReportingInterval < 1000 * 15) {
|
||||
errorReportingInterval = (errorReportingInterval << 1) | 1
|
||||
}
|
||||
} else {
|
||||
// reset the error reporting interval once get the proper status back.
|
||||
errorReportingInterval = 0
|
||||
}
|
||||
|
||||
if (errorCount >= maxErrorCount) {
|
||||
core.info('Too many errors, aborting!')
|
||||
core.setFailed('Failed with status code: ' + res.status)
|
||||
|
||||
// Explicitly cancel the deployment
|
||||
await this.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
if (Date.now() - startTime >= timeout) {
|
||||
core.info('Timeout reached, aborting!')
|
||||
core.setFailed('Timeout reached, aborting!')
|
||||
|
||||
// Explicitly cancel the deployment
|
||||
await this.cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error)
|
||||
if (error.response && error.response.data) {
|
||||
core.info(JSON.stringify(error.response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
// Don't attemp to cancel if no deployment was created
|
||||
if (!this.requestedDeployment) {
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the deployment
|
||||
try {
|
||||
const pagesCancelDeployEndpoint = `${this.githubApiUrl}/repos/${this.repositoryNwo}/pages/deployment/cancel/${this.buildVersion}`
|
||||
await axios.put(
|
||||
pagesCancelDeployEndpoint,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${this.githubToken}`,
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
core.info(`Deployment cancelled with ${pagesCancelDeployEndpoint}`)
|
||||
} catch (error) {
|
||||
core.setFailed(error)
|
||||
if (error.response && error.response.data) {
|
||||
core.info(JSON.stringify(error.response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = { Deployment }
|
||||
26
src/index.js
26
src/index.js
@@ -3,9 +3,10 @@
|
||||
// without the user having to do the tar process themselves.
|
||||
|
||||
const core = require('@actions/core')
|
||||
// const github = require('@actions/github'); // TODO: Not used until we publish API endpoint to the @action/github package
|
||||
|
||||
const { Deployment } = require('./deployment')
|
||||
const { Deployment } = require('./internal/deployment')
|
||||
const getContext = require('./internal/context')
|
||||
|
||||
const deployment = new Deployment()
|
||||
|
||||
async function cancelHandler(evtOrExitCodeOrError) {
|
||||
@@ -14,6 +15,8 @@ async function cancelHandler(evtOrExitCodeOrError) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { isPreview } = getContext()
|
||||
|
||||
let idToken = ''
|
||||
try {
|
||||
idToken = await core.getIDToken()
|
||||
@@ -22,8 +25,18 @@ async function main() {
|
||||
core.setFailed(`Ensure GITHUB_TOKEN has permission "id-token: write".`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await deployment.create(idToken)
|
||||
const deploymentInfo = await deployment.create(idToken)
|
||||
|
||||
// Output the deployed Pages URL
|
||||
let pageUrl = deploymentInfo?.['page_url'] || ''
|
||||
const previewUrl = deploymentInfo?.['preview_url'] || ''
|
||||
if (isPreview && previewUrl) {
|
||||
pageUrl = previewUrl
|
||||
}
|
||||
core.setOutput('page_url', pageUrl)
|
||||
|
||||
await deployment.check()
|
||||
} catch (error) {
|
||||
core.setFailed(error)
|
||||
@@ -35,9 +48,4 @@ process.on('SIGINT', cancelHandler)
|
||||
process.on('SIGTERM', cancelHandler)
|
||||
|
||||
// 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()
|
||||
}
|
||||
main()
|
||||
|
||||
@@ -1,281 +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 axios = require('axios')
|
||||
|
||||
const { Deployment } = require('./deployment')
|
||||
|
||||
describe('with all environment variables set', () => {
|
||||
beforeEach(() => {
|
||||
process.env.ACTIONS_RUNTIME_URL = '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'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// Remove mock for `core.getInput('preview')`
|
||||
delete process.env.INPUT_PREVIEW
|
||||
})
|
||||
|
||||
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('create', () => {
|
||||
beforeAll(() => {
|
||||
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())
|
||||
})
|
||||
|
||||
it('can successfully create a deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
const fakeJwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
|
||||
const scope = 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' }
|
||||
]
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
axios.post = jest.fn().mockResolvedValue('test')
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(axios.post).toBeCalledWith(
|
||||
'https://api.github.com/repos/actions/is-awesome/pages/deployment',
|
||||
{
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: 'valid-build-version',
|
||||
oidc_token: fakeJwt
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer gha-token`,
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenCalledWith('Created deployment for valid-build-version')
|
||||
|
||||
scope.done()
|
||||
})
|
||||
|
||||
it('can successfully create a preview deployment', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
const fakeJwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiNjllMWIxOC1jOGFiLTRhZGQtOGYxOC03MzVlMzVjZGJhZjAiLCJzdWIiOiJyZXBvOnBhcGVyLXNwYS9taW55aTplbnZpcm9ubWVudDpQcm9kdWN0aW9uIiwiYXVkIjoiaHR0cHM6Ly9naXRodWIuY29tL3BhcGVyLXNwYSIsInJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInNoYSI6ImEyODU1MWJmODdiZDk3NTFiMzdiMmM0YjM3M2MxZjU3NjFmYWM2MjYiLCJyZXBvc2l0b3J5IjoicGFwZXItc3BhL21pbnlpIiwicmVwb3NpdG9yeV9vd25lciI6InBhcGVyLXNwYSIsInJ1bl9pZCI6IjE1NDY0NTkzNjQiLCJydW5fbnVtYmVyIjoiMzQiLCJydW5fYXR0ZW1wdCI6IjIiLCJhY3RvciI6IllpTXlzdHkiLCJ3b3JrZmxvdyI6IkNJIiwiaGVhZF9yZWYiOiIiLCJiYXNlX3JlZiI6IiIsImV2ZW50X25hbWUiOiJwdXNoIiwicmVmX3R5cGUiOiJicmFuY2giLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJqb2Jfd29ya2Zsb3dfcmVmIjoicGFwZXItc3BhL21pbnlpLy5naXRodWIvd29ya2Zsb3dzL2JsYW5rLnltbEByZWZzL2hlYWRzL21haW4iLCJpc3MiOiJodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tIiwibmJmIjoxNjM4ODI4MDI4LCJleHAiOjE2Mzg4Mjg5MjgsImlhdCI6MTYzODgyODYyOH0.1wyupfxu1HGoTyIqatYg0hIxy2-0bMO-yVlmLSMuu2w'
|
||||
const scope = 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' }
|
||||
]
|
||||
})
|
||||
|
||||
core.getIDToken = jest.fn().mockResolvedValue(fakeJwt)
|
||||
axios.post = jest.fn().mockResolvedValue('test')
|
||||
|
||||
// Return `"true"` for `core.getInput("preview")`
|
||||
process.env.INPUT_PREVIEW = 'true'
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
await deployment.create(fakeJwt)
|
||||
|
||||
expect(axios.post).toBeCalledWith(
|
||||
'https://api.github.com/repos/actions/is-awesome/pages/deployment',
|
||||
{
|
||||
artifact_url: 'https://fake-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: 'valid-build-version',
|
||||
oidc_token: fakeJwt,
|
||||
preview: true
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer gha-token`,
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
expect(core.info).toHaveBeenCalledWith('Created deployment for valid-build-version')
|
||||
|
||||
scope.done()
|
||||
})
|
||||
|
||||
it('Reports errors with failed deployments', async () => {
|
||||
process.env.GITHUB_SHA = 'invalid-build-version'
|
||||
const scope = 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' }] })
|
||||
|
||||
axios.post = jest.fn().mockRejectedValue({
|
||||
status: 400
|
||||
})
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
try {
|
||||
deployment.create()
|
||||
} catch (err) {
|
||||
expect(axios.post).toBeCalledWith(
|
||||
'https://api.github.com/repos/actions/is-awesome/pages/deployment',
|
||||
{
|
||||
artifact_url: 'https://invalid-artifact.com&%24expand=SignedContent',
|
||||
pages_build_version: 'invalid-build-version'
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: 'Bearer ',
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(core.info).toHaveBeenLastCalledWith('Failed to create deployment for invalid-build-version.')
|
||||
expect(core.setFailed).toHaveBeenCalledWith({ status: 400 })
|
||||
|
||||
scope.done()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('check', () => {
|
||||
beforeAll(() => {
|
||||
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'
|
||||
process.env.ARTIFACT_NAME = 'github-pages'
|
||||
|
||||
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())
|
||||
})
|
||||
|
||||
it('sets output to success when deployment is successful', async () => {
|
||||
process.env.GITHUB_SHA = 'valid-build-version'
|
||||
let repositoryNwo = process.env.GITHUB_REPOSITORY
|
||||
let buildVersion = process.env.GITHUB_SHA
|
||||
|
||||
// mock a successful call to create a deployment
|
||||
axios.post = jest.fn().mockResolvedValue({ status: 200 })
|
||||
|
||||
// mock a completed deployment with status = 'succeed'
|
||||
axios.get = jest.fn().mockResolvedValue({
|
||||
status: 200,
|
||||
data: {
|
||||
status: 'succeed'
|
||||
}
|
||||
})
|
||||
|
||||
// Create the deployment
|
||||
const deployment = new Deployment()
|
||||
core.GetInput = jest.fn(input => {
|
||||
switch (input) {
|
||||
case 'timeout':
|
||||
return 10 * 1000
|
||||
case 'reporting_interval':
|
||||
return 0
|
||||
}
|
||||
})
|
||||
jest.spyOn(core, 'getInput')
|
||||
await deployment.check()
|
||||
|
||||
expect(axios.get).toBeCalledWith(
|
||||
`https://api.github.com/repos/${repositoryNwo}/pages/deployment/status/${buildVersion}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: 'token gha-token'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(core.setOutput).toBeCalledWith('status', 'succeed')
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith('Reported success!')
|
||||
})
|
||||
})
|
||||
179
src/internal/api-client.js
Normal file
179
src/internal/api-client.js
Normal file
@@ -0,0 +1,179 @@
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const { DefaultArtifactClient } = require('@actions/artifact')
|
||||
const { RequestError } = require('@octokit/request-error')
|
||||
const HttpStatusMessages = require('http-status-messages')
|
||||
|
||||
function wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions) {
|
||||
// Specific response shape aligned with Octokit
|
||||
const response = {
|
||||
url: requestOptions.url,
|
||||
status: 200,
|
||||
headers: {
|
||||
...requestOptions.headers
|
||||
},
|
||||
data: twirpResponse
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// Mimic the errors thrown by Octokit for consistency.
|
||||
function wrapTwirpErrorLikeOctokit(twirpError, requestOptions) {
|
||||
const rawErrorMsg = twirpError?.message || twirpError?.toString() || ''
|
||||
const statusCodeMatch = rawErrorMsg.match(/Failed request: \((?<statusCode>\d+)\)/)
|
||||
const statusCode = statusCodeMatch?.groups?.statusCode ?? 500
|
||||
|
||||
// Try to provide the best error message
|
||||
const errorMsg =
|
||||
rawErrorMsg ||
|
||||
// Fallback to the HTTP status message based on the status code
|
||||
HttpStatusMessages[statusCode] ||
|
||||
// Or if the status code is unexpected...
|
||||
`Unknown error (${statusCode})`
|
||||
|
||||
// RequestError is an Octokit-specific class
|
||||
return new RequestError(errorMsg, statusCode, {
|
||||
response: {
|
||||
url: requestOptions.url,
|
||||
status: statusCode,
|
||||
headers: {
|
||||
...requestOptions.headers
|
||||
},
|
||||
data: rawErrorMsg ? { message: rawErrorMsg } : ''
|
||||
},
|
||||
request: requestOptions
|
||||
})
|
||||
}
|
||||
|
||||
function getArtifactsServiceOrigin() {
|
||||
const resultsUrl = process.env.ACTIONS_RESULTS_URL
|
||||
return resultsUrl ? new URL(resultsUrl).origin : ''
|
||||
}
|
||||
|
||||
async function getArtifactMetadata({ artifactName }) {
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
|
||||
// Primarily for debugging purposes, accuracy is not critical
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
url: `${getArtifactsServiceOrigin()}/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts`,
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: {}
|
||||
}
|
||||
|
||||
try {
|
||||
core.info(`Fetching artifact metadata for "${artifactName}" in this workflow run`)
|
||||
|
||||
let response
|
||||
try {
|
||||
const twirpResponse = await artifactClient.listArtifacts()
|
||||
response = wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions)
|
||||
} catch (twirpError) {
|
||||
core.error('Listing artifact metadata failed', twirpError)
|
||||
const octokitError = wrapTwirpErrorLikeOctokit(twirpError, requestOptions)
|
||||
throw octokitError
|
||||
}
|
||||
|
||||
const filteredArtifacts = response.data.artifacts.filter(artifact => artifact.name === artifactName)
|
||||
|
||||
const artifactCount = filteredArtifacts.length
|
||||
core.debug(`List artifact count: ${artifactCount}`)
|
||||
|
||||
if (artifactCount === 0) {
|
||||
throw new Error(
|
||||
`No artifacts named "${artifactName}" were found for this workflow run. Ensure artifacts are uploaded with actions/upload-artifact@v4 or later.`
|
||||
)
|
||||
} else if (artifactCount > 1) {
|
||||
throw new Error(
|
||||
`Multiple artifacts named "${artifactName}" were unexpectedly found for this workflow run. Artifact count is ${artifactCount}.`
|
||||
)
|
||||
}
|
||||
|
||||
const artifact = filteredArtifacts[0]
|
||||
core.debug(`Artifact: ${JSON.stringify(artifact)}`)
|
||||
|
||||
if (!artifact.size) {
|
||||
core.warning('Artifact size was not found. Unable to verify if artifact size exceeds the allowed size.')
|
||||
}
|
||||
|
||||
return artifact
|
||||
} catch (error) {
|
||||
core.error(
|
||||
'Fetching artifact metadata failed. Is githubstatus.com reporting issues with API requests, Pages, or Actions? Please re-run the deployment at a later time.',
|
||||
error
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function createPagesDeployment({ githubToken, artifactId, buildVersion, idToken, isPreview = false }) {
|
||||
const octokit = github.getOctokit(githubToken)
|
||||
|
||||
const payload = {
|
||||
artifact_id: artifactId,
|
||||
pages_build_version: buildVersion,
|
||||
oidc_token: idToken
|
||||
}
|
||||
if (isPreview === true) {
|
||||
payload.preview = true
|
||||
}
|
||||
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', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
...payload
|
||||
})
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
core.error('Creating Pages deployment failed', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function getPagesDeploymentStatus({ githubToken, deploymentId }) {
|
||||
const octokit = github.getOctokit(githubToken)
|
||||
|
||||
core.info('Getting Pages deployment status...')
|
||||
try {
|
||||
const response = await octokit.request('GET /repos/{owner}/{repo}/pages/deployments/{deploymentId}', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
deploymentId
|
||||
})
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
core.error('Getting Pages deployment status failed', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelPagesDeployment({ githubToken, deploymentId }) {
|
||||
const octokit = github.getOctokit(githubToken)
|
||||
|
||||
core.info('Canceling Pages deployment...')
|
||||
try {
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/pages/deployments/{deploymentId}/cancel', {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
deploymentId
|
||||
})
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
core.error('Canceling Pages deployment failed', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getArtifactMetadata,
|
||||
createPagesDeployment,
|
||||
getPagesDeploymentStatus,
|
||||
cancelPagesDeployment
|
||||
}
|
||||
@@ -3,9 +3,7 @@ const core = require('@actions/core')
|
||||
// Load variables from Actions runtime
|
||||
function getRequiredVars() {
|
||||
return {
|
||||
runTimeUrl: process.env.ACTIONS_RUNTIME_URL,
|
||||
workflowRun: process.env.GITHUB_RUN_ID,
|
||||
runTimeToken: process.env.ACTIONS_RUNTIME_TOKEN,
|
||||
repositoryNwo: process.env.GITHUB_REPOSITORY,
|
||||
buildVersion: process.env.GITHUB_SHA,
|
||||
buildActor: process.env.GITHUB_ACTOR,
|
||||
243
src/internal/deployment.js
Normal file
243
src/internal/deployment.js
Normal file
@@ -0,0 +1,243 @@
|
||||
const core = require('@actions/core')
|
||||
|
||||
// All variables we need from the runtime are loaded here
|
||||
const getContext = require('./context')
|
||||
const {
|
||||
getArtifactMetadata,
|
||||
createPagesDeployment,
|
||||
getPagesDeploymentStatus,
|
||||
cancelPagesDeployment
|
||||
} = require('./api-client')
|
||||
|
||||
const temporaryErrorStatus = {
|
||||
unknown_status: 'Unable to get deployment status.',
|
||||
not_found: 'Deployment not found.',
|
||||
deployment_attempt_error: 'Deployment temporarily failed, a retry will be automatically scheduled...'
|
||||
}
|
||||
|
||||
const finalErrorStatus = {
|
||||
deployment_failed: 'Deployment failed, try again later.',
|
||||
deployment_perms_error: 'Deployment failed. Please ensure that the file permissions are correct.',
|
||||
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.',
|
||||
deployment_cancelled: 'Deployment cancelled.',
|
||||
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()
|
||||
this.repositoryNwo = context.repositoryNwo
|
||||
this.buildVersion = context.buildVersion
|
||||
this.buildActor = context.buildActor
|
||||
this.actionsId = context.actionsId
|
||||
this.githubToken = context.githubToken
|
||||
this.workflowRun = context.workflowRun
|
||||
this.deploymentInfo = null
|
||||
this.githubApiUrl = context.githubApiUrl
|
||||
this.githubServerUrl = context.githubServerUrl
|
||||
this.artifactName = context.artifactName
|
||||
this.isPreview = context.isPreview === true
|
||||
this.timeout = MAX_TIMEOUT
|
||||
this.startTime = null
|
||||
}
|
||||
|
||||
// Call GitHub api to fetch artifacts matching the provided name and deploy to GitHub Pages
|
||||
// by creating a deployment with that artifact id
|
||||
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 getArtifactMetadata({ 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,
|
||||
artifactId: artifactData.id,
|
||||
buildVersion: this.buildVersion,
|
||||
idToken,
|
||||
isPreview: this.isPreview
|
||||
})
|
||||
|
||||
if (deployment) {
|
||||
this.deploymentInfo = {
|
||||
...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}`)
|
||||
|
||||
core.debug(JSON.stringify(deployment))
|
||||
|
||||
return deployment
|
||||
} catch (error) {
|
||||
core.error(error.stack)
|
||||
|
||||
// build customized error message based on server response
|
||||
if (error.response) {
|
||||
let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}.`
|
||||
if (error.status === 400) {
|
||||
errorMessage += ` Responded with: ${error.message}`
|
||||
} else if (error.status === 403) {
|
||||
errorMessage += ' Ensure GITHUB_TOKEN has permission "pages: write".'
|
||||
} else if (error.status === 404) {
|
||||
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
||||
errorMessage += ` Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poll the deployment endpoint for status
|
||||
async check() {
|
||||
// Don't attempt to check status if no deployment was created
|
||||
if (!this.deploymentInfo) {
|
||||
core.setFailed(temporaryErrorStatus.not_found)
|
||||
return
|
||||
}
|
||||
if (this.deploymentInfo.pending !== true) {
|
||||
core.setFailed(temporaryErrorStatus.unknown_status)
|
||||
return
|
||||
}
|
||||
|
||||
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
||||
const reportingInterval = Number(core.getInput('reporting_interval'))
|
||||
const maxErrorCount = Number(core.getInput('error_count'))
|
||||
|
||||
let errorCount = 0
|
||||
|
||||
// Time in milliseconds between two deployment status report when status errored, default 0.
|
||||
let errorReportingInterval = 0
|
||||
let deployment = null
|
||||
let errorStatus = 0
|
||||
|
||||
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
|
||||
while (true) {
|
||||
// Handle reporting interval
|
||||
await new Promise(resolve => setTimeout(resolve, reportingInterval + errorReportingInterval))
|
||||
|
||||
// Check status
|
||||
try {
|
||||
deployment = await getPagesDeploymentStatus({
|
||||
githubToken: this.githubToken,
|
||||
deploymentId
|
||||
})
|
||||
|
||||
if (deployment.status === 'succeed') {
|
||||
core.info('Reported success!')
|
||||
core.setOutput('status', 'succeed')
|
||||
this.deploymentInfo.pending = false
|
||||
break
|
||||
} else if (finalErrorStatus[deployment.status]) {
|
||||
// Fall into permanent error, it may be caused by ongoing incident, malicious deployment content, exhausted automatic retry times, invalid artifact, etc.
|
||||
core.setFailed(finalErrorStatus[deployment.status])
|
||||
this.deploymentInfo.pending = false
|
||||
break
|
||||
} else if (temporaryErrorStatus[deployment.status]) {
|
||||
// A temporary error happened, will query the status again
|
||||
core.warning(temporaryErrorStatus[deployment.status])
|
||||
} else {
|
||||
core.info('Current status: ' + deployment.status)
|
||||
}
|
||||
|
||||
// reset the error reporting interval once get the proper status back.
|
||||
errorReportingInterval = 0
|
||||
} catch (error) {
|
||||
core.error(error.stack)
|
||||
|
||||
// build customized error message based on server response
|
||||
if (error.response) {
|
||||
errorStatus = error.status || error.response.status
|
||||
|
||||
errorCount++
|
||||
|
||||
// set the maximum error reporting interval greater than 15 sec but below 30 sec.
|
||||
if (errorReportingInterval < 1000 * 15) {
|
||||
errorReportingInterval = (errorReportingInterval << 1) | 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount >= maxErrorCount) {
|
||||
core.error('Too many errors, aborting!')
|
||||
core.setFailed('Failed with status code: ' + errorStatus)
|
||||
|
||||
// Explicitly cancel the deployment
|
||||
await this.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
if (Date.now() - this.startTime >= this.timeout) {
|
||||
core.error('Timeout reached, aborting!')
|
||||
core.setFailed('Timeout reached, aborting!')
|
||||
|
||||
// Explicitly cancel the deployment
|
||||
await this.cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Cancel the deployment
|
||||
try {
|
||||
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
||||
await cancelPagesDeployment({
|
||||
githubToken: this.githubToken,
|
||||
deploymentId
|
||||
})
|
||||
core.info(`Canceled deployment with ID ${deploymentId}`)
|
||||
|
||||
this.deploymentInfo.pending = false
|
||||
} catch (error) {
|
||||
core.setFailed(error)
|
||||
if (error.response?.data) {
|
||||
core.error(JSON.stringify(error.response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Deployment, MAX_TIMEOUT, ONE_GIGABYTE, SIZE_LIMIT_DESCRIPTION }
|
||||
Reference in New Issue
Block a user