mirror of
https://github.com/actions/upload-artifact.git
synced 2025-04-16 00:23:32 +00:00
Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6027e3dd17 | ||
![]() |
d7079ed267 | ||
![]() |
ea165f8d65 | ||
![]() |
08396203c1 | ||
![]() |
99ec7df36b | ||
![]() |
4cec3d8aa0 | ||
![]() |
e9fad966cc | ||
![]() |
b26fd06e9d | ||
![]() |
65c4c4a1dd | ||
![]() |
0207619228 | ||
![]() |
1ecca81102 | ||
![]() |
97422693d3 | ||
![]() |
6f51ac03b9 | ||
![]() |
c40c16d999 | ||
![]() |
735efb4a0a | ||
![]() |
184d73b71b | ||
![]() |
b4a0a984a0 | ||
![]() |
b4b15b8c7c | ||
![]() |
92b01ebffa | ||
![]() |
84480863f2 | ||
![]() |
b1d4642b69 | ||
![]() |
d50e66084c | ||
![]() |
aabe6f8050 | ||
![]() |
604373da63 | ||
![]() |
0150148bdf | ||
![]() |
a009b25faa | ||
![]() |
9f6f6f402e | ||
![]() |
3eadd8b791 | ||
![]() |
aeba9f7961 | ||
![]() |
b18b1d32f3 | ||
![]() |
d7c12077c4 | ||
![]() |
50769540e7 | ||
![]() |
d52396ac5d | ||
![]() |
710f362075 | ||
![]() |
3b315f26f6 | ||
![]() |
3be2180eb7 | ||
![]() |
453e8d0a40 | ||
![]() |
0a398c1480 | ||
![]() |
a0c40cf602 | ||
![]() |
acb59e4776 | ||
![]() |
cb6558bb10 | ||
![]() |
834a144ee9 | ||
![]() |
134dcf33c0 | ||
![]() |
73a0b9c954 | ||
![]() |
89ef406dd8 | ||
![]() |
23d796df36 | ||
![]() |
e445c64bc2 | ||
![]() |
0b2256b8c0 | ||
![]() |
488dcefb9b | ||
![]() |
04c51f5766 | ||
![]() |
32a9e276a8 | ||
![]() |
a954e16c38 | ||
![]() |
a1d85e775a | ||
![]() |
552bf3722c | ||
![]() |
79616d2ded | ||
![]() |
65462800fd | ||
![]() |
c004fb4bf6 | ||
![]() |
90aba496fc | ||
![]() |
b06cde36fc | ||
![]() |
1746f4ab65 | ||
![]() |
31685d04a0 | ||
![]() |
18bf333cd2 | ||
![]() |
dac413befa | ||
![]() |
bb3b4a3cdb | ||
![]() |
3e3da837d2 | ||
![]() |
e35774f165 | ||
![]() |
e63ea677fb | ||
![]() |
ef09cdac3e | ||
![]() |
00e36f94d8 | ||
![]() |
4c0ff1c489 | ||
![]() |
5d5d22a312 | ||
![]() |
f1e993d966 | ||
![]() |
4881bfd3f2 | ||
![]() |
a30777e265 | ||
![]() |
3a8048248f | ||
![]() |
9d63e3f2f8 | ||
![]() |
dfa1ab292d | ||
![]() |
d00351bf69 | ||
![]() |
707f5a7b71 | ||
![]() |
26f96dfa69 | ||
![]() |
530ed2c9b8 | ||
![]() |
53ef6987b3 | ||
![]() |
90b0f8eed8 | ||
![]() |
199a58f54f | ||
![]() |
3f353f9d9e | ||
![]() |
997fffa355 | ||
![]() |
52899c8c02 | ||
![]() |
da58a3f7b2 | ||
![]() |
1f64adb853 | ||
![]() |
8d531b15a6 | ||
![]() |
694cdabd8b | ||
![]() |
05d4fe6702 | ||
![]() |
40b3052821 | ||
![]() |
49552fcb82 | ||
![]() |
79615904cc | ||
![]() |
11ff42c7b1 | ||
![]() |
1eb3cb2b3e | ||
![]() |
8688a86492 | ||
![]() |
73d8b66ede | ||
![]() |
c320f57948 | ||
![]() |
cf8714cfea | ||
![]() |
7f16e37e88 | ||
![]() |
353073034f | ||
![]() |
6c139afa6f |
4
.github/workflows/check-dist.yml
vendored
4
.github/workflows/check-dist.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: npm run build
|
||||
run: npm run release
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
run: |
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
id: diff
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v4-beta
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
|
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@ -17,11 +17,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
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
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@ -29,7 +29,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
|
||||
@ -43,4 +43,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
20
.github/workflows/publish-immutable-actions.yml
vendored
Normal file
20
.github/workflows/publish-immutable-actions.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: 'Publish Immutable Action Version'
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checking out
|
||||
uses: actions/checkout@v4
|
||||
- name: Publish
|
||||
id: publish
|
||||
uses: actions/publish-immutable-action@0.0.3
|
@ -22,7 +22,7 @@ jobs:
|
||||
steps:
|
||||
- name: Update the ${{ env.TAG_NAME }} tag
|
||||
id: update-major-tag
|
||||
uses: actions/publish-action@v0.2.1
|
||||
uses: actions/publish-action@v0.3.0
|
||||
with:
|
||||
source-tag: ${{ env.TAG_NAME }}
|
||||
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
||||
|
158
.github/workflows/test.yml
vendored
158
.github/workflows/test.yml
vendored
@ -46,14 +46,19 @@ jobs:
|
||||
- name: Test
|
||||
run: npm run test
|
||||
|
||||
# Test end-to-end by uploading two artifacts and then downloading them
|
||||
# Test end-to-end by uploading a few artifacts and then downloading them
|
||||
- name: Create artifact files
|
||||
run: |
|
||||
mkdir -p path/to/dir-1
|
||||
mkdir -p path/to/dir-2
|
||||
mkdir -p path/to/dir-3
|
||||
mkdir -p symlink/
|
||||
echo "Lorem ipsum dolor sit amet" > path/to/dir-1/file1.txt
|
||||
echo "Hello world from file #2" > path/to/dir-2/file2.txt
|
||||
echo "Hello from a symlinked file" > symlink/original.txt
|
||||
ln -s $(pwd)/symlink/original.txt symlink/abs.txt
|
||||
ln -s original.txt symlink/rel.txt
|
||||
shell: bash
|
||||
|
||||
# Upload a single file artifact
|
||||
- name: 'Upload artifact #1'
|
||||
@ -79,9 +84,17 @@ jobs:
|
||||
path/to/dir-[23]/*
|
||||
!path/to/dir-3/*.txt
|
||||
|
||||
- name: 'Upload symlinked artifact'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'Symlinked-Artifact-${{ matrix.runs-on }}'
|
||||
path: |
|
||||
symlink/abs.txt
|
||||
symlink/rel.txt
|
||||
|
||||
# Download Artifact #1 and verify the correctness of the content
|
||||
- name: 'Download artifact #1'
|
||||
uses: actions/download-artifact@v4-beta
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: 'Artifact-A-${{ matrix.runs-on }}'
|
||||
path: some/new/path
|
||||
@ -101,7 +114,7 @@ jobs:
|
||||
|
||||
# Download Artifact #2 and verify the correctness of the content
|
||||
- name: 'Download artifact #2'
|
||||
uses: actions/download-artifact@v4-beta
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: 'Artifact-Wildcard-${{ matrix.runs-on }}'
|
||||
path: some/other/path
|
||||
@ -122,7 +135,7 @@ jobs:
|
||||
|
||||
# Download Artifact #4 and verify the correctness of the content
|
||||
- name: 'Download artifact #4'
|
||||
uses: actions/download-artifact@v4-beta
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: 'Multi-Path-Artifact-${{ matrix.runs-on }}'
|
||||
path: multi/artifact
|
||||
@ -140,3 +153,140 @@ jobs:
|
||||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: 'Download symlinked artifact'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: 'Symlinked-Artifact-${{ matrix.runs-on }}'
|
||||
path: from/symlink
|
||||
|
||||
- name: 'Verify symlinked artifact'
|
||||
run: |
|
||||
$abs = "from/symlink/abs.txt"
|
||||
if(!(Test-Path -path $abs))
|
||||
{
|
||||
Write-Error "Expected file does not exist"
|
||||
}
|
||||
if(!((Get-Content $abs) -ceq "Hello from a symlinked file"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifact are incorrect"
|
||||
}
|
||||
$rel = "from/symlink/rel.txt"
|
||||
if(!(Test-Path -path $rel))
|
||||
{
|
||||
Write-Error "Expected file does not exist"
|
||||
}
|
||||
if(!((Get-Content $rel) -ceq "Hello from a symlinked file"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifact are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: 'Alter file 1 content'
|
||||
run: |
|
||||
echo "This file has changed" > path/to/dir-1/file1.txt
|
||||
|
||||
# Replace the contents of Artifact #1
|
||||
- name: 'Overwrite artifact #1'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'Artifact-A-${{ matrix.runs-on }}'
|
||||
path: path/to/dir-1/file1.txt
|
||||
overwrite: true
|
||||
|
||||
# Download replaced Artifact #1 and verify the correctness of the content
|
||||
- name: 'Download artifact #1 again'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: 'Artifact-A-${{ matrix.runs-on }}'
|
||||
path: overwrite/some/new/path
|
||||
|
||||
- name: 'Verify Artifact #1 again'
|
||||
run: |
|
||||
$file = "overwrite/some/new/path/file1.txt"
|
||||
if(!(Test-Path -path $file))
|
||||
{
|
||||
Write-Error "Expected file does not exist"
|
||||
}
|
||||
if(!((Get-Content $file) -ceq "This file has changed"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifact are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
merge:
|
||||
name: Merge
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Merge all artifacts from previous jobs
|
||||
- name: Merge all artifacts in run
|
||||
uses: ./merge/
|
||||
with:
|
||||
# our matrix produces artifacts with the same file, this prevents "stomping" on each other, also makes it
|
||||
# easier to identify each of the merged artifacts
|
||||
separate-directories: true
|
||||
- name: 'Download merged artifacts'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: merged-artifacts
|
||||
path: all-merged-artifacts
|
||||
- name: 'Check merged artifact has directories for each artifact'
|
||||
run: |
|
||||
$artifacts = @(
|
||||
"Artifact-A-ubuntu-latest",
|
||||
"Artifact-A-macos-latest",
|
||||
"Artifact-A-windows-latest",
|
||||
"Artifact-Wildcard-ubuntu-latest",
|
||||
"Artifact-Wildcard-macos-latest",
|
||||
"Artifact-Wildcard-windows-latest",
|
||||
"Multi-Path-Artifact-ubuntu-latest",
|
||||
"Multi-Path-Artifact-macos-latest",
|
||||
"Multi-Path-Artifact-windows-latest"
|
||||
)
|
||||
|
||||
foreach ($artifact in $artifacts) {
|
||||
$path = "all-merged-artifacts/$artifact"
|
||||
if (!(Test-Path $path)) {
|
||||
Write-Error "$path does not exist."
|
||||
}
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
# Merge Artifact-A-* from previous jobs
|
||||
- name: Merge all Artifact-A
|
||||
uses: ./merge/
|
||||
with:
|
||||
name: Merged-Artifact-As
|
||||
pattern: 'Artifact-A-*'
|
||||
separate-directories: true
|
||||
|
||||
# Download merged artifacts and verify the correctness of the content
|
||||
- name: 'Download merged artifacts'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Merged-Artifact-As
|
||||
path: merged-artifact-a
|
||||
|
||||
- name: 'Verify merged artifacts'
|
||||
run: |
|
||||
$files = @(
|
||||
"merged-artifact-a/Artifact-A-ubuntu-latest/file1.txt",
|
||||
"merged-artifact-a/Artifact-A-macos-latest/file1.txt",
|
||||
"merged-artifact-a/Artifact-A-windows-latest/file1.txt"
|
||||
)
|
||||
|
||||
foreach ($file in $files) {
|
||||
if (!(Test-Path $file)) {
|
||||
Write-Error "$file does not exist."
|
||||
}
|
||||
|
||||
if (!((Get-Content $file) -ceq "This file has changed")) {
|
||||
Write-Error "$file has incorrect content."
|
||||
}
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
|
BIN
.licenses/npm/@actions/artifact.dep.yml
generated
BIN
.licenses/npm/@actions/artifact.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/core.dep.yml
generated
BIN
.licenses/npm/@actions/core.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@actions/github-5.1.1.dep.yml
generated
Normal file
BIN
.licenses/npm/@actions/github-5.1.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@actions/github.dep.yml
generated
BIN
.licenses/npm/@actions/github.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/glob.dep.yml
generated
BIN
.licenses/npm/@actions/glob.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/http-client.dep.yml
generated
BIN
.licenses/npm/@actions/http-client.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/io.dep.yml
generated
BIN
.licenses/npm/@actions/io.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@isaacs/cliui.dep.yml
generated
Normal file
BIN
.licenses/npm/@isaacs/cliui.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/auth-token-4.0.0.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/auth-token-4.0.0.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/core-5.0.2.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/core-5.0.2.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/endpoint-9.0.4.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/endpoint-9.0.4.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/graphql-7.0.2.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/graphql-7.0.2.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/plugin-paginate-rest-9.1.5.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/plugin-paginate-rest-9.1.5.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/plugin-rest-endpoint-methods-10.2.0.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/plugin-rest-endpoint-methods-10.2.0.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@octokit/request-8.1.6.dep.yml
generated
Normal file
BIN
.licenses/npm/@octokit/request-8.1.6.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@pkgjs/parseargs.dep.yml
generated
Normal file
BIN
.licenses/npm/@pkgjs/parseargs.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/@types/color-name.dep.yml
generated
Normal file
BIN
.licenses/npm/@types/color-name.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/abort-controller.dep.yml
generated
Normal file
BIN
.licenses/npm/abort-controller.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/ansi-regex-5.0.1.dep.yml
generated
Normal file
BIN
.licenses/npm/ansi-regex-5.0.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/ansi-regex-6.0.1.dep.yml
generated
Normal file
BIN
.licenses/npm/ansi-regex-6.0.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/ansi-styles-4.2.1.dep.yml
generated
Normal file
BIN
.licenses/npm/ansi-styles-4.2.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/ansi-styles-6.2.1.dep.yml
generated
Normal file
BIN
.licenses/npm/ansi-styles-6.2.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/archiver-utils-3.0.4.dep.yml
generated
BIN
.licenses/npm/archiver-utils-3.0.4.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/archiver.dep.yml
generated
BIN
.licenses/npm/archiver.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/b4a.dep.yml
generated
Normal file
BIN
.licenses/npm/b4a.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/bare-events.dep.yml
generated
Normal file
BIN
.licenses/npm/bare-events.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/buffer-crc32.dep.yml
generated
BIN
.licenses/npm/buffer-crc32.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/buffer.dep.yml
generated
BIN
.licenses/npm/buffer.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/color-name.dep.yml
generated
Normal file
BIN
.licenses/npm/color-name.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/compress-commons.dep.yml
generated
BIN
.licenses/npm/compress-commons.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/concat-map.dep.yml
generated
BIN
.licenses/npm/concat-map.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/crc32-stream.dep.yml
generated
BIN
.licenses/npm/crc32-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/cross-spawn.dep.yml
generated
Normal file
BIN
.licenses/npm/cross-spawn.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/crypto.dep.yml
generated
BIN
.licenses/npm/crypto.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/eastasianwidth.dep.yml
generated
Normal file
BIN
.licenses/npm/eastasianwidth.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/emoji-regex-8.0.0.dep.yml
generated
Normal file
BIN
.licenses/npm/emoji-regex-8.0.0.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/emoji-regex-9.2.2.dep.yml
generated
Normal file
BIN
.licenses/npm/emoji-regex-9.2.2.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/event-target-shim.dep.yml
generated
Normal file
BIN
.licenses/npm/event-target-shim.dep.yml
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/foreground-child.dep.yml
generated
Normal file
BIN
.licenses/npm/foreground-child.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/fs.realpath.dep.yml
generated
BIN
.licenses/npm/fs.realpath.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/glob-10.3.12.dep.yml
generated
Normal file
BIN
.licenses/npm/glob-10.3.12.dep.yml
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/inherits.dep.yml
generated
BIN
.licenses/npm/inherits.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/is-fullwidth-code-point.dep.yml
generated
Normal file
BIN
.licenses/npm/is-fullwidth-code-point.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/is-stream.dep.yml
generated
Normal file
BIN
.licenses/npm/is-stream.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/isexe.dep.yml
generated
Normal file
BIN
.licenses/npm/isexe.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/jackspeak.dep.yml
generated
Normal file
BIN
.licenses/npm/jackspeak.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/lodash.defaults.dep.yml
generated
BIN
.licenses/npm/lodash.defaults.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.difference.dep.yml
generated
BIN
.licenses/npm/lodash.difference.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.flatten.dep.yml
generated
BIN
.licenses/npm/lodash.flatten.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.isplainobject.dep.yml
generated
BIN
.licenses/npm/lodash.isplainobject.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lru-cache.dep.yml
generated
Normal file
BIN
.licenses/npm/lru-cache.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/minimatch.dep.yml
generated
Normal file
BIN
.licenses/npm/minimatch.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/minipass.dep.yml
generated
Normal file
BIN
.licenses/npm/minipass.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/once.dep.yml
generated
BIN
.licenses/npm/once.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/path-is-absolute.dep.yml
generated
BIN
.licenses/npm/path-is-absolute.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/path-key.dep.yml
generated
Normal file
BIN
.licenses/npm/path-key.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/path-scurry.dep.yml
generated
Normal file
BIN
.licenses/npm/path-scurry.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/path-to-regexp.dep.yml
generated
BIN
.licenses/npm/path-to-regexp.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/queue-tick.dep.yml
generated
Normal file
BIN
.licenses/npm/queue-tick.dep.yml
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/shebang-command.dep.yml
generated
Normal file
BIN
.licenses/npm/shebang-command.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/shebang-regex.dep.yml
generated
Normal file
BIN
.licenses/npm/shebang-regex.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/signal-exit.dep.yml
generated
Normal file
BIN
.licenses/npm/signal-exit.dep.yml
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/string-width-4.2.3.dep.yml
generated
Normal file
BIN
.licenses/npm/string-width-4.2.3.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/string-width-5.1.2.dep.yml
generated
Normal file
BIN
.licenses/npm/string-width-5.1.2.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/string-width-cjs.dep.yml
generated
Normal file
BIN
.licenses/npm/string-width-cjs.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/strip-ansi-6.0.1.dep.yml
generated
Normal file
BIN
.licenses/npm/strip-ansi-6.0.1.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/strip-ansi-7.1.0.dep.yml
generated
Normal file
BIN
.licenses/npm/strip-ansi-7.1.0.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/strip-ansi-cjs.dep.yml
generated
Normal file
BIN
.licenses/npm/strip-ansi-cjs.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/tar-stream.dep.yml
generated
BIN
.licenses/npm/tar-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/undici.dep.yml
generated
BIN
.licenses/npm/undici.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/unzip-stream.dep.yml
generated
BIN
.licenses/npm/unzip-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/uuid.dep.yml
generated
BIN
.licenses/npm/uuid.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/which.dep.yml
generated
Normal file
BIN
.licenses/npm/which.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/wrap-ansi-cjs.dep.yml
generated
Normal file
BIN
.licenses/npm/wrap-ansi-cjs.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/wrap-ansi.dep.yml
generated
Normal file
BIN
.licenses/npm/wrap-ansi.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/zip-stream.dep.yml
generated
BIN
.licenses/npm/zip-stream.dep.yml
generated
Binary file not shown.
36
.vscode/launch.json
vendored
Normal file
36
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Jest Tests",
|
||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
|
||||
"args": [
|
||||
"--runInBand",
|
||||
"--testTimeout",
|
||||
"10000"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Current Test File",
|
||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
|
||||
"args": [
|
||||
"--runInBand",
|
||||
"--testTimeout",
|
||||
"10000",
|
||||
"${relativeFile}"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true
|
||||
}
|
||||
]
|
||||
}
|
93
README.md
93
README.md
@ -1,5 +1,11 @@
|
||||
# `@actions/upload-artifact`
|
||||
|
||||
> [!WARNING]
|
||||
> actions/upload-artifact@v3 is scheduled for deprecation on **November 30, 2024**. [Learn more.](https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/)
|
||||
> Similarly, v1/v2 are scheduled for deprecation on **June 30, 2024**.
|
||||
> Please update your workflow to use v4 of the artifact actions.
|
||||
> This deprecation will not impact any existing versions of GitHub Enterprise Server being used by customers.
|
||||
|
||||
Upload [Actions Artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) from your Workflow Runs. Internally powered by [@actions/artifact](https://github.com/actions/toolkit/tree/main/packages/artifact) package.
|
||||
|
||||
See also [download-artifact](https://github.com/actions/download-artifact).
|
||||
@ -24,6 +30,7 @@ See also [download-artifact](https://github.com/actions/download-artifact).
|
||||
- [Using Outputs](#using-outputs)
|
||||
- [Example output between steps](#example-output-between-steps)
|
||||
- [Example output between jobs](#example-output-between-jobs)
|
||||
- [Overwriting an Artifact](#overwriting-an-artifact)
|
||||
- [Limitations](#limitations)
|
||||
- [Number of Artifacts](#number-of-artifacts)
|
||||
- [Zip archives](#zip-archives)
|
||||
@ -40,11 +47,13 @@ The release of upload-artifact@v4 and download-artifact@v4 are major changes to
|
||||
|
||||
For more information, see the [`@actions/artifact`](https://github.com/actions/toolkit/tree/main/packages/artifact) documentation.
|
||||
|
||||
There is also a new sub-action, `actions/upload-artifact/merge`. For more info, check out that action's [README](./merge/README.md).
|
||||
|
||||
### Improvements
|
||||
|
||||
1. Uploads are significantly faster, upwards of 90% improvement in worst case scenarios.
|
||||
2. Once uploaded, an Artifact ID is returned and Artifacts are immediately available in the UI and [REST API](https://docs.github.com/en/rest/actions/artifacts). Previously, you would have to wait for the run to be completed before an ID was available or any APIs could be utilized.
|
||||
3. The contents of an Artifact are uploaded together into an _immutable_ archive. They cannot be altered by subsequent jobs. Both of these factors help reduce the possibility of accidentally corrupting Artifact files.
|
||||
3. The contents of an Artifact are uploaded together into an _immutable_ archive. They cannot be altered by subsequent jobs unless the Artifacts are deleted and recreated (where they will have a new ID). Both of these factors help reduce the possibility of accidentally corrupting Artifact files.
|
||||
4. The compression level of an Artifact can be manually tweaked for speed or size reduction.
|
||||
|
||||
### Breaking Changes
|
||||
@ -54,7 +63,10 @@ For more information, see the [`@actions/artifact`](https://github.com/actions/t
|
||||
|
||||
Due to how Artifacts are created in this new version, it is no longer possible to upload to the same named Artifact multiple times. You must either split the uploads into multiple Artifacts with different names, or only upload once. Otherwise you _will_ encounter an error.
|
||||
|
||||
3. Limit of Artifacts for an individual job. Each job in a workflow run now has a limit of 10 artifacts.
|
||||
3. Limit of Artifacts for an individual job. Each job in a workflow run now has a limit of 500 artifacts.
|
||||
4. With `v4.4` and later, hidden files are excluded by default.
|
||||
|
||||
For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md).
|
||||
|
||||
## Usage
|
||||
|
||||
@ -90,6 +102,18 @@ For more information, see the [`@actions/artifact`](https://github.com/actions/t
|
||||
# For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads.
|
||||
# Optional. Default is '6'
|
||||
compression-level:
|
||||
|
||||
# If true, an artifact with a matching name will be deleted before a new one is uploaded.
|
||||
# If false, the action will fail if an artifact for the given name already exists.
|
||||
# Does not fail if the artifact does not exist.
|
||||
# Optional. Default is 'false'
|
||||
overwrite:
|
||||
|
||||
# Whether to include hidden files in the provided path in the artifact
|
||||
# The file contents of any hidden files in the path should be validated before
|
||||
# enabled this to avoid uploading sensitive information.
|
||||
# Optional. Default is 'false'
|
||||
include-hidden-files:
|
||||
```
|
||||
|
||||
### Outputs
|
||||
@ -97,6 +121,8 @@ For more information, see the [`@actions/artifact`](https://github.com/actions/t
|
||||
| Name | Description | Example |
|
||||
| - | - | - |
|
||||
| `artifact-id` | GitHub ID of an Artifact, can be used by the REST API | `1234` |
|
||||
| `artifact-url` | URL to download an Artifact. Can be used in many scenarios such as linking to artifacts in issues or pull requests. Users must be logged-in in order for this URL to work. This URL is valid as long as the artifact has not expired or the artifact, run or repository have not been deleted | `https://github.com/example-org/example-repo/actions/runs/1/artifacts/1234` |
|
||||
| `artifact-digest` | SHA-256 digest of an Artifact | 0fde654d4c6e659b45783a725dc92f1bfb0baa6c2de64b34e814dc206ff4aaaf |
|
||||
|
||||
## Examples
|
||||
|
||||
@ -271,7 +297,7 @@ You can use `~` in the path input as a substitute for `$HOME`. Basic tilde expan
|
||||
echo hello > ~/new/artifact/world.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Artifacts-V4-beta
|
||||
name: my-artifacts
|
||||
path: ~/new/**/*
|
||||
```
|
||||
|
||||
@ -346,7 +372,7 @@ jobs:
|
||||
job1:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
output1: ${{ steps.my-artifact.outputs.artifact-id }}
|
||||
output1: ${{ steps.artifact-upload-step.outputs.artifact-id }}
|
||||
steps:
|
||||
- uses: actions/upload-artifact@v4
|
||||
id: artifact-upload-step
|
||||
@ -362,11 +388,63 @@ jobs:
|
||||
run: echo "Artifact ID from previous job is $OUTPUT1"
|
||||
```
|
||||
|
||||
### Overwriting an Artifact
|
||||
|
||||
Although it's not possible to mutate an Artifact, can completely overwrite one. But do note that this will give the Artifact a new ID, the previous one will no longer exist:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create a file
|
||||
run: echo "hello world" > my-file.txt
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: my-artifact # NOTE: same artifact name
|
||||
path: my-file.txt
|
||||
upload-again:
|
||||
needs: upload
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create a different file
|
||||
run: echo "goodbye world" > my-file.txt
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: my-artifact # NOTE: same artifact name
|
||||
path: my-file.txt
|
||||
overwrite: true
|
||||
```
|
||||
|
||||
### Uploading Hidden Files
|
||||
|
||||
By default, hidden files are ignored by this action to avoid unintentionally uploading sensitive information.
|
||||
|
||||
If you need to upload hidden files, you can use the `include-hidden-files` input.
|
||||
Any files that contain sensitive information that should not be in the uploaded artifact can be excluded
|
||||
using the `path`:
|
||||
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: my-artifact
|
||||
include-hidden-files: true
|
||||
path: |
|
||||
path/output/
|
||||
!path/output/.production.env
|
||||
```
|
||||
|
||||
Hidden files are defined as any file beginning with `.` or files within folders beginning with `.`.
|
||||
On Windows, files and directories with the hidden attribute are not considered hidden files unless
|
||||
they have the `.` prefix.
|
||||
|
||||
## Limitations
|
||||
|
||||
### Number of Artifacts
|
||||
|
||||
Within an individual job, there is a limit of 10 artifacts that can be created for that job.
|
||||
Within an individual job, there is a limit of 500 artifacts that can be created for that job.
|
||||
|
||||
You may also be limited by Artifacts if you have exceeded your shared storage quota. Storage is calculated every 6-12 hours. See [the documentation](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#calculating-minute-and-storage-spending) for more info.
|
||||
|
||||
@ -395,8 +473,9 @@ If you must preserve permissions, you can `tar` all of your files together befor
|
||||
|
||||
At the bottom of the workflow summary page, there is a dedicated section for artifacts. Here's a screenshot of something you might see:
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/16109154/103645952-223c6880-4f59-11eb-8268-8dca6937b5f9.png" width="700" height="300">
|
||||
<img src="https://github.com/user-attachments/assets/bcb7120f-f445-4a3e-9596-77f85f7e0af0" width="700" height="300">
|
||||
|
||||
|
||||
There is a trashcan icon that can be used to delete the artifact. This icon will only appear for users who have write permissions to the repository.
|
||||
|
||||
The size of the artifact is denoted in bytes. The displayed artifact size denotes the size of the zip that `upload-artifact` creates during upload.
|
||||
The size of the artifact is denoted in bytes. The displayed artifact size denotes the size of the zip that `upload-artifact` creates during upload. The Digest column will display the SHA256 digest of the artifact being uploaded.
|
||||
|
175
__tests__/merge.test.ts
Normal file
175
__tests__/merge.test.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import * as core from '@actions/core'
|
||||
import artifact from '@actions/artifact'
|
||||
import {run} from '../src/merge/merge-artifacts'
|
||||
import {Inputs} from '../src/merge/constants'
|
||||
import * as search from '../src/shared/search'
|
||||
|
||||
const fixtures = {
|
||||
artifactName: 'my-merged-artifact',
|
||||
tmpDirectory: '/tmp/merge-artifact',
|
||||
filesToUpload: [
|
||||
'/some/artifact/path/file-a.txt',
|
||||
'/some/artifact/path/file-b.txt',
|
||||
'/some/artifact/path/file-c.txt'
|
||||
],
|
||||
artifacts: [
|
||||
{
|
||||
name: 'my-artifact-a',
|
||||
id: 1,
|
||||
size: 100,
|
||||
createdAt: new Date('2024-01-01T00:00:00Z')
|
||||
},
|
||||
{
|
||||
name: 'my-artifact-b',
|
||||
id: 2,
|
||||
size: 100,
|
||||
createdAt: new Date('2024-01-01T00:00:00Z')
|
||||
},
|
||||
{
|
||||
name: 'my-artifact-c',
|
||||
id: 3,
|
||||
size: 100,
|
||||
createdAt: new Date('2024-01-01T00:00:00Z')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
jest.mock('@actions/github', () => ({
|
||||
context: {
|
||||
repo: {
|
||||
owner: 'actions',
|
||||
repo: 'toolkit'
|
||||
},
|
||||
runId: 123,
|
||||
serverUrl: 'https://github.com'
|
||||
}
|
||||
}))
|
||||
|
||||
jest.mock('@actions/core')
|
||||
|
||||
jest.mock('fs/promises', () => ({
|
||||
mkdtemp: jest.fn().mockResolvedValue('/tmp/merge-artifact'),
|
||||
rm: jest.fn().mockResolvedValue(undefined)
|
||||
}))
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => {
|
||||
const inputs = {
|
||||
[Inputs.Name]: 'my-merged-artifact',
|
||||
[Inputs.Pattern]: '*',
|
||||
[Inputs.SeparateDirectories]: false,
|
||||
[Inputs.RetentionDays]: 0,
|
||||
[Inputs.CompressionLevel]: 6,
|
||||
[Inputs.DeleteMerged]: false,
|
||||
...overrides
|
||||
}
|
||||
|
||||
;(core.getInput as jest.Mock).mockImplementation((name: string) => {
|
||||
return inputs[name]
|
||||
})
|
||||
;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => {
|
||||
return inputs[name]
|
||||
})
|
||||
|
||||
return inputs
|
||||
}
|
||||
|
||||
describe('merge', () => {
|
||||
beforeEach(async () => {
|
||||
mockInputs()
|
||||
|
||||
jest
|
||||
.spyOn(artifact, 'listArtifacts')
|
||||
.mockResolvedValue({artifacts: fixtures.artifacts})
|
||||
|
||||
jest.spyOn(artifact, 'downloadArtifact').mockResolvedValue({
|
||||
downloadPath: fixtures.tmpDirectory
|
||||
})
|
||||
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: fixtures.filesToUpload,
|
||||
rootDirectory: fixtures.tmpDirectory
|
||||
})
|
||||
|
||||
jest.spyOn(artifact, 'uploadArtifact').mockResolvedValue({
|
||||
size: 123,
|
||||
id: 1337
|
||||
})
|
||||
|
||||
jest
|
||||
.spyOn(artifact, 'deleteArtifact')
|
||||
.mockImplementation(async artifactName => {
|
||||
const artifact = fixtures.artifacts.find(a => a.name === artifactName)
|
||||
if (!artifact) throw new Error(`Artifact ${artifactName} not found`)
|
||||
return {id: artifact.id}
|
||||
})
|
||||
})
|
||||
|
||||
it('merges artifacts', async () => {
|
||||
await run()
|
||||
|
||||
for (const a of fixtures.artifacts) {
|
||||
expect(artifact.downloadArtifact).toHaveBeenCalledWith(a.id, {
|
||||
path: fixtures.tmpDirectory
|
||||
})
|
||||
}
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.tmpDirectory,
|
||||
{compressionLevel: 6}
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if no artifacts found', async () => {
|
||||
mockInputs({[Inputs.Pattern]: 'this-does-not-match'})
|
||||
|
||||
expect(run()).rejects.toThrow()
|
||||
|
||||
expect(artifact.uploadArtifact).not.toBeCalled()
|
||||
expect(artifact.downloadArtifact).not.toBeCalled()
|
||||
})
|
||||
|
||||
it('supports custom compression level', async () => {
|
||||
mockInputs({
|
||||
[Inputs.CompressionLevel]: 2
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.tmpDirectory,
|
||||
{compressionLevel: 2}
|
||||
)
|
||||
})
|
||||
|
||||
it('supports custom retention days', async () => {
|
||||
mockInputs({
|
||||
[Inputs.RetentionDays]: 7
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.tmpDirectory,
|
||||
{retentionDays: 7, compressionLevel: 6}
|
||||
)
|
||||
})
|
||||
|
||||
it('supports deleting artifacts after merge', async () => {
|
||||
mockInputs({
|
||||
[Inputs.DeleteMerged]: true
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
for (const a of fixtures.artifacts) {
|
||||
expect(artifact.deleteArtifact).toHaveBeenCalledWith(a.name)
|
||||
}
|
||||
})
|
||||
})
|
@ -2,7 +2,7 @@ import * as core from '@actions/core'
|
||||
import * as path from 'path'
|
||||
import * as io from '@actions/io'
|
||||
import {promises as fs} from 'fs'
|
||||
import {findFilesToUpload} from '../src/search'
|
||||
import {findFilesToUpload} from '../src/shared/search'
|
||||
|
||||
const root = path.join(__dirname, '_temp', 'search')
|
||||
const searchItem1Path = path.join(
|
||||
@ -61,6 +61,20 @@ const lonelyFilePath = path.join(
|
||||
'lonely-file.txt'
|
||||
)
|
||||
|
||||
const hiddenFile = path.join(root, '.hidden-file.txt')
|
||||
const fileInHiddenFolderPath = path.join(
|
||||
root,
|
||||
'.hidden-folder',
|
||||
'folder-in-hidden-folder',
|
||||
'file.txt'
|
||||
)
|
||||
const fileInHiddenFolderInFolderA = path.join(
|
||||
root,
|
||||
'folder-a',
|
||||
'.hidden-folder-in-folder-a',
|
||||
'file.txt'
|
||||
)
|
||||
|
||||
describe('Search', () => {
|
||||
beforeAll(async () => {
|
||||
// mock all output so that there is less noise when running tests
|
||||
@ -93,6 +107,14 @@ describe('Search', () => {
|
||||
recursive: true
|
||||
})
|
||||
|
||||
await fs.mkdir(
|
||||
path.join(root, '.hidden-folder', 'folder-in-hidden-folder'),
|
||||
{recursive: true}
|
||||
)
|
||||
await fs.mkdir(path.join(root, 'folder-a', '.hidden-folder-in-folder-a'), {
|
||||
recursive: true
|
||||
})
|
||||
|
||||
await fs.writeFile(searchItem1Path, 'search item1 file')
|
||||
await fs.writeFile(searchItem2Path, 'search item2 file')
|
||||
await fs.writeFile(searchItem3Path, 'search item3 file')
|
||||
@ -110,10 +132,19 @@ describe('Search', () => {
|
||||
await fs.writeFile(amazingFileInFolderHPath, 'amazing file')
|
||||
|
||||
await fs.writeFile(lonelyFilePath, 'all by itself')
|
||||
|
||||
await fs.writeFile(hiddenFile, 'hidden file')
|
||||
await fs.writeFile(fileInHiddenFolderPath, 'file in hidden directory')
|
||||
await fs.writeFile(fileInHiddenFolderInFolderA, 'file in hidden directory')
|
||||
/*
|
||||
Directory structure of files that get created:
|
||||
root/
|
||||
.hidden-folder/
|
||||
folder-in-hidden-folder/
|
||||
file.txt
|
||||
folder-a/
|
||||
.hidden-folder-in-folder-a/
|
||||
file.txt
|
||||
folder-b/
|
||||
folder-c/
|
||||
search-item1.txt
|
||||
@ -136,6 +167,7 @@ describe('Search', () => {
|
||||
folder-j/
|
||||
folder-k/
|
||||
lonely-file.txt
|
||||
.hidden-file.txt
|
||||
search-item5.txt
|
||||
*/
|
||||
})
|
||||
@ -352,4 +384,24 @@ describe('Search', () => {
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true)
|
||||
})
|
||||
|
||||
it('Hidden files ignored by default', async () => {
|
||||
const searchPath = path.join(root, '**/*')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
|
||||
expect(searchResult.filesToUpload).not.toContain(hiddenFile)
|
||||
expect(searchResult.filesToUpload).not.toContain(fileInHiddenFolderPath)
|
||||
expect(searchResult.filesToUpload).not.toContain(
|
||||
fileInHiddenFolderInFolderA
|
||||
)
|
||||
})
|
||||
|
||||
it('Hidden files included', async () => {
|
||||
const searchPath = path.join(root, '**/*')
|
||||
const searchResult = await findFilesToUpload(searchPath, true)
|
||||
|
||||
expect(searchResult.filesToUpload).toContain(hiddenFile)
|
||||
expect(searchResult.filesToUpload).toContain(fileInHiddenFolderPath)
|
||||
expect(searchResult.filesToUpload).toContain(fileInHiddenFolderInFolderA)
|
||||
})
|
||||
})
|
||||
|
233
__tests__/upload.test.ts
Normal file
233
__tests__/upload.test.ts
Normal file
@ -0,0 +1,233 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import artifact, {ArtifactNotFoundError} from '@actions/artifact'
|
||||
import {run} from '../src/upload/upload-artifact'
|
||||
import {Inputs} from '../src/upload/constants'
|
||||
import * as search from '../src/shared/search'
|
||||
|
||||
const fixtures = {
|
||||
artifactName: 'artifact-name',
|
||||
rootDirectory: '/some/artifact/path',
|
||||
filesToUpload: [
|
||||
'/some/artifact/path/file1.txt',
|
||||
'/some/artifact/path/file2.txt'
|
||||
]
|
||||
}
|
||||
|
||||
jest.mock('@actions/github', () => ({
|
||||
context: {
|
||||
repo: {
|
||||
owner: 'actions',
|
||||
repo: 'toolkit'
|
||||
},
|
||||
runId: 123,
|
||||
serverUrl: 'https://github.com'
|
||||
}
|
||||
}))
|
||||
|
||||
jest.mock('@actions/core')
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => {
|
||||
const inputs = {
|
||||
[Inputs.Name]: 'artifact-name',
|
||||
[Inputs.Path]: '/some/artifact/path',
|
||||
[Inputs.IfNoFilesFound]: 'warn',
|
||||
[Inputs.RetentionDays]: 0,
|
||||
[Inputs.CompressionLevel]: 6,
|
||||
[Inputs.Overwrite]: false,
|
||||
...overrides
|
||||
}
|
||||
|
||||
;(core.getInput as jest.Mock).mockImplementation((name: string) => {
|
||||
return inputs[name]
|
||||
})
|
||||
;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => {
|
||||
return inputs[name]
|
||||
})
|
||||
|
||||
return inputs
|
||||
}
|
||||
|
||||
describe('upload', () => {
|
||||
beforeEach(async () => {
|
||||
mockInputs()
|
||||
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: fixtures.filesToUpload,
|
||||
rootDirectory: fixtures.rootDirectory
|
||||
})
|
||||
|
||||
jest.spyOn(artifact, 'uploadArtifact').mockResolvedValue({
|
||||
size: 123,
|
||||
id: 1337,
|
||||
digest: 'facefeed'
|
||||
})
|
||||
})
|
||||
|
||||
it('uploads a single file', async () => {
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: [fixtures.filesToUpload[0]],
|
||||
rootDirectory: fixtures.rootDirectory
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
[fixtures.filesToUpload[0]],
|
||||
fixtures.rootDirectory,
|
||||
{compressionLevel: 6}
|
||||
)
|
||||
})
|
||||
|
||||
it('uploads multiple files', async () => {
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.rootDirectory,
|
||||
{compressionLevel: 6}
|
||||
)
|
||||
})
|
||||
|
||||
it('sets outputs', async () => {
|
||||
await run()
|
||||
|
||||
expect(core.setOutput).toHaveBeenCalledWith('artifact-id', 1337)
|
||||
expect(core.setOutput).toHaveBeenCalledWith('artifact-digest', 'facefeed')
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'artifact-url',
|
||||
`${github.context.serverUrl}/${github.context.repo.owner}/${
|
||||
github.context.repo.repo
|
||||
}/actions/runs/${github.context.runId}/artifacts/${1337}`
|
||||
)
|
||||
})
|
||||
|
||||
it('supports custom compression level', async () => {
|
||||
mockInputs({
|
||||
[Inputs.CompressionLevel]: 2
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.rootDirectory,
|
||||
{compressionLevel: 2}
|
||||
)
|
||||
})
|
||||
|
||||
it('supports custom retention days', async () => {
|
||||
mockInputs({
|
||||
[Inputs.RetentionDays]: 7
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.rootDirectory,
|
||||
{retentionDays: 7, compressionLevel: 6}
|
||||
)
|
||||
})
|
||||
|
||||
it('supports warn if-no-files-found', async () => {
|
||||
mockInputs({
|
||||
[Inputs.IfNoFilesFound]: 'warn'
|
||||
})
|
||||
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: [],
|
||||
rootDirectory: fixtures.rootDirectory
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
|
||||
)
|
||||
})
|
||||
|
||||
it('supports error if-no-files-found', async () => {
|
||||
mockInputs({
|
||||
[Inputs.IfNoFilesFound]: 'error'
|
||||
})
|
||||
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: [],
|
||||
rootDirectory: fixtures.rootDirectory
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
|
||||
)
|
||||
})
|
||||
|
||||
it('supports ignore if-no-files-found', async () => {
|
||||
mockInputs({
|
||||
[Inputs.IfNoFilesFound]: 'ignore'
|
||||
})
|
||||
|
||||
jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({
|
||||
filesToUpload: [],
|
||||
rootDirectory: fixtures.rootDirectory
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
`No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.`
|
||||
)
|
||||
})
|
||||
|
||||
it('supports overwrite', async () => {
|
||||
mockInputs({
|
||||
[Inputs.Overwrite]: true
|
||||
})
|
||||
|
||||
jest.spyOn(artifact, 'deleteArtifact').mockResolvedValue({
|
||||
id: 1337
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.rootDirectory,
|
||||
{compressionLevel: 6}
|
||||
)
|
||||
|
||||
expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName)
|
||||
})
|
||||
|
||||
it('supports overwrite and continues if not found', async () => {
|
||||
mockInputs({
|
||||
[Inputs.Overwrite]: true
|
||||
})
|
||||
|
||||
jest
|
||||
.spyOn(artifact, 'deleteArtifact')
|
||||
.mockRejectedValue(new ArtifactNotFoundError('not found'))
|
||||
|
||||
await run()
|
||||
|
||||
expect(artifact.uploadArtifact).toHaveBeenCalledWith(
|
||||
fixtures.artifactName,
|
||||
fixtures.filesToUpload,
|
||||
fixtures.rootDirectory,
|
||||
{compressionLevel: 6}
|
||||
)
|
||||
|
||||
expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName)
|
||||
expect(core.debug).toHaveBeenCalledWith(
|
||||
`Skipping deletion of '${fixtures.artifactName}', it does not exist`
|
||||
)
|
||||
})
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user