mirror of
https://github.com/actions/upload-artifact.git
synced 2025-06-13 23:37:12 +02:00
Compare commits
12 Commits
v2-preview
...
v2.1.0
Author | SHA1 | Date | |
---|---|---|---|
ebad382c09 | |||
90f03bd03e | |||
f265ac5693 | |||
4347a0d55a | |||
97b7dace6c | |||
9bcc1e21d0 | |||
f729db2261 | |||
d6636db5bd | |||
1c797a4e6c | |||
4a30538ab4 | |||
e59920f5cf | |||
e7eefc4917 |
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
lib/
|
||||
dist/
|
19
.eslintrc.json
Normal file
19
.eslintrc.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"env": { "node": true, "jest": true },
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": { "ecmaVersion": 9, "sourceType": "module" },
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:import/typescript",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-empty-function": "off"
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "jest"]
|
||||
}
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Version**
|
||||
- [ ] V1
|
||||
- [ ] V2
|
||||
|
||||
**Environment**
|
||||
- [ ] self-hosted
|
||||
- [ ] Linux
|
||||
- [ ] Windows
|
||||
- [ ] Mac
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Run/Repo Url**
|
||||
If applicable, and if your repo/run is public, please include a URL so it is easier for us to investigate.
|
||||
|
||||
**How to reproduce**
|
||||
If applicable, add information on how to reproduce the problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
44
.github/workflows/codeql-analysis.yml
vendored
Normal file
44
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
schedule:
|
||||
- cron: '0 6 * * 3'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# 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@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
170
.github/workflows/test.yml
vendored
Normal file
170
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, macos-latest, windows-latest]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: Compile
|
||||
run: npm run build
|
||||
|
||||
- name: npm test
|
||||
run: npm test
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Format
|
||||
run: npm run format-check
|
||||
|
||||
# Test end-to-end by uploading two 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
|
||||
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 "This is a going to be a test for a large enough file that should get compressed with GZip. The @actions/artifact package uses GZip to upload files. This text should have a compression ratio greater than 100% so it should get uploaded using GZip" > path/to/dir-3/gzip.txt
|
||||
|
||||
# Upload a single file artifact
|
||||
- name: 'Upload artifact #1'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'Artifact-A'
|
||||
path: path/to/dir-1/file1.txt
|
||||
|
||||
# Upload using a wildcard pattern, name should default to 'artifact' if not provided
|
||||
- name: 'Upload artifact #2'
|
||||
uses: ./
|
||||
with:
|
||||
path: path/**/dir*/
|
||||
|
||||
# Upload a directory that contains a file that will be uploaded with GZip
|
||||
- name: 'Upload artifact #3'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'GZip-Artifact'
|
||||
path: path/to/dir-3/
|
||||
|
||||
# Upload a directory that contains a file that will be uploaded with GZip
|
||||
- name: 'Upload artifact #4'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'Multi-Path-Artifact'
|
||||
path: |
|
||||
path/to/dir-1/*
|
||||
path/to/dir-[23]/*
|
||||
!path/to/dir-3/*.txt
|
||||
|
||||
# Verify artifacts. Switch to download-artifact@v2 once it's out of preview
|
||||
|
||||
# Download Artifact #1 and verify the correctness of the content
|
||||
- name: 'Download artifact #1'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: 'Artifact-A'
|
||||
path: some/new/path
|
||||
|
||||
- name: 'Verify Artifact #1'
|
||||
run: |
|
||||
$file = "some/new/path/file1.txt"
|
||||
if(!(Test-Path -path $file))
|
||||
{
|
||||
Write-Error "Expected file does not exist"
|
||||
}
|
||||
if(!((Get-Content $file) -ceq "Lorem ipsum dolor sit amet"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifact are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
# Download Artifact #2 and verify the correctness of the content
|
||||
- name: 'Download artifact #2'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: 'artifact'
|
||||
path: some/other/path
|
||||
|
||||
- name: 'Verify Artifact #2'
|
||||
run: |
|
||||
$file1 = "some/other/path/to/dir-1/file1.txt"
|
||||
$file2 = "some/other/path/to/dir-2/file2.txt"
|
||||
if(!(Test-Path -path $file1) -or !(Test-Path -path $file2))
|
||||
{
|
||||
Write-Error "Expected files do not exist"
|
||||
}
|
||||
if(!((Get-Content $file1) -ceq "Lorem ipsum dolor sit amet") -or !((Get-Content $file2) -ceq "Hello world from file #2"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
# Download Artifact #3 and verify the correctness of the content
|
||||
- name: 'Download artifact #3'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: 'GZip-Artifact'
|
||||
path: gzip/artifact/path
|
||||
|
||||
# Because a directory was used as input during the upload the parent directories, path/to/dir-3/, should not be included in the uploaded artifact
|
||||
- name: 'Verify Artifact #3'
|
||||
run: |
|
||||
$gzipFile = "gzip/artifact/path/gzip.txt"
|
||||
if(!(Test-Path -path $gzipFile))
|
||||
{
|
||||
Write-Error "Expected file do not exist"
|
||||
}
|
||||
if(!((Get-Content $gzipFile) -ceq "This is a going to be a test for a large enough file that should get compressed with GZip. The @actions/artifact package uses GZip to upload files. This text should have a compression ratio greater than 100% so it should get uploaded using GZip"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifact is incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: 'Download artifact #4'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: 'Multi-Path-Artifact'
|
||||
path: multi/artifact
|
||||
|
||||
- name: 'Verify Artifact #4'
|
||||
run: |
|
||||
$file1 = "multi/artifact/dir-1/file1.txt"
|
||||
$file2 = "multi/artifact/dir-2/file2.txt"
|
||||
if(!(Test-Path -path $file1) -or !(Test-Path -path $file2))
|
||||
{
|
||||
Write-Error "Expected files do not exist"
|
||||
}
|
||||
if(!((Get-Content $file1) -ceq "Lorem ipsum dolor sit amet") -or !((Get-Content $file2) -ceq "Hello world from file #2"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
lib/
|
||||
__tests__/_temp/
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
11
.prettierrc.json
Normal file
11
.prettierrc.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid",
|
||||
"parser": "typescript"
|
||||
}
|
174
README.md
174
README.md
@ -1,38 +1,191 @@
|
||||
# upload-artifact
|
||||
# Upload-Artifact v2
|
||||
|
||||
This uploads artifacts from your workflow.
|
||||
This uploads artifacts from your workflow allowing you to share data between jobs and store data once a workflow is complete.
|
||||
|
||||
See also [download-artifact](https://github.com/actions/download-artifact).
|
||||
|
||||
# What's new
|
||||
|
||||
- Easier upload
|
||||
- Specify a wildcard pattern
|
||||
- Specify an individual file
|
||||
- Specify a directory (previously you were limited to only this option)
|
||||
- Multi path upload
|
||||
- Use a combination of individual files, wildcards or directories
|
||||
- Support for excluding certain files
|
||||
- Upload an artifact without providing a name
|
||||
- Fix for artifact uploads sometimes not working with containers
|
||||
- Proxy support out of the box
|
||||
- Port entire action to typescript from a runner plugin so it is easier to collaborate and accept contributions
|
||||
|
||||
Refer [here](https://github.com/actions/upload-artifact/tree/releases/v1) for the previous version
|
||||
|
||||
# Usage
|
||||
|
||||
See [action.yml](action.yml)
|
||||
|
||||
Basic:
|
||||
### Upload an Individual File
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- run: mkdir -p path/to/artifact
|
||||
|
||||
- run: echo hello > path/to/artifact/world.txt
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: my-artifact
|
||||
path: path/to/artifact
|
||||
path: path/to/artifact/world.txt
|
||||
```
|
||||
|
||||
### Upload an Entire Directory
|
||||
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: my-artifact
|
||||
path: path/to/artifact/ # or path/to/artifact
|
||||
```
|
||||
|
||||
### Upload using a Wildcard Pattern
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: my-artifact
|
||||
path: path/**/[abc]rtifac?/*
|
||||
```
|
||||
|
||||
### Upload using Multiple Paths and Exclusions
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: my-artifact
|
||||
path: |
|
||||
path/output/bin/
|
||||
path/output/test-results
|
||||
!path/**/*.tmp
|
||||
```
|
||||
|
||||
For supported wildcards along with behavior and documentation, see [@actions/glob](https://github.com/actions/toolkit/tree/master/packages/glob) which is used internally to search for files.
|
||||
|
||||
If a wildcard pattern is used, the path hierarchy will be preserved after the first wildcard pattern.
|
||||
|
||||
```
|
||||
path/to/*/directory/foo?.txt =>
|
||||
∟ path/to/some/directory/foo1.txt
|
||||
∟ path/to/some/directory/foo2.txt
|
||||
∟ path/to/other/directory/foo1.txt
|
||||
|
||||
would be flattened and uploaded as =>
|
||||
∟ some/directory/foo1.txt
|
||||
∟ some/directory/foo2.txt
|
||||
∟ other/directory/foo1.txt
|
||||
```
|
||||
If multiple paths are provided as input, the least common ancestor of all the search paths will be used as the root directory of the artifact. Exclude paths do not effect the directory structure.
|
||||
|
||||
Relative and absolute file paths are both allowed. Relative paths are rooted against the current working directory. Paths that begin with a wildcard character should be quoted to avoid being interpreted as YAML aliases.
|
||||
|
||||
The [@actions/artifact](https://github.com/actions/toolkit/tree/master/packages/artifact) package is used internally to handle most of the logic around uploading an artifact. There is extra documentation around upload limitations and behavior in the toolkit repo that is worth checking out.
|
||||
|
||||
### Conditional Artifact Upload
|
||||
|
||||
To upload artifacts only when the previous step of a job failed, use [`if: failure()`](https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions#job-status-check-functions):
|
||||
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v1
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: my-artifact
|
||||
path: path/to/artifact
|
||||
path: path/to/artifact/
|
||||
```
|
||||
|
||||
### Uploading without an artifact name
|
||||
|
||||
You can upload an artifact without specifying a name
|
||||
```yaml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: path/to/artifact/world.txt
|
||||
```
|
||||
|
||||
If not provided, `artifact` will be used as the default name which will manifest itself in the UI after upload.
|
||||
|
||||
### Uploading to the same artifact
|
||||
|
||||
Each artifact behaves as a file share. Uploading to the same artifact multiple times in the same workflow can overwrite and append already uploaded files
|
||||
|
||||
```yaml
|
||||
- run: echo hi > world.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: world.txt
|
||||
|
||||
- run: echo howdy > extra-file.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: extra-file.txt
|
||||
|
||||
- run: echo hello > world.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: world.txt
|
||||
```
|
||||
With the following example, the available artifact (named `artifact` which is the default if no name is provided) would contain both `world.txt` (`hello`) and `extra-file.txt` (`howdy`).
|
||||
|
||||
> **_Warning:_** Be careful when uploading to the same artifact via multiple jobs as artifacts may become corrupted
|
||||
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [8.x, 10.x, 12.x, 13.x]
|
||||
steps:
|
||||
- name: 'Create a file'
|
||||
run: echo ${{ matrix.node-version }} > my_file.txt
|
||||
- name: 'Accidently upload to the same artifact via multiple jobs'
|
||||
uses: 'actions/upload-artifact@v2'
|
||||
with:
|
||||
name: my-artifact
|
||||
path: ${{ github.workspace }}
|
||||
```
|
||||
|
||||
In the above example, four jobs will upload four different files to the same artifact but there will only be one file available when `my-artifact` is downloaded. Each job overwrites what was previously uploaded. To ensure that jobs don't overwrite existing artifacts, use a different name per job.
|
||||
|
||||
```yaml
|
||||
uses: 'actions/upload-artifact@v2'
|
||||
with:
|
||||
name: my-artifact ${{ matrix.node-version }}
|
||||
path: ${{ github.workspace }}
|
||||
```
|
||||
|
||||
### Environment Variables and Tilde Expansion
|
||||
|
||||
You can use `~` in the path input as a substitute for `$HOME`. Basic tilde expansion is supported.
|
||||
|
||||
```yaml
|
||||
- run: |
|
||||
mkdir -p ~/new/artifact
|
||||
echo hello > ~/new/artifact/world.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: 'Artifacts-V2'
|
||||
path: '~/new/**/*'
|
||||
```
|
||||
|
||||
Environment variables along with context expressions can also be used for input. For documentation see [context and expression syntax](https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions).
|
||||
|
||||
```yaml
|
||||
env:
|
||||
name: my-artifact
|
||||
steps:
|
||||
- run: |
|
||||
mkdir -p ${{ github.workspace }}/artifact
|
||||
echo hello > ${{ github.workspace }}/artifact/world.txt
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.name }}-name
|
||||
path: ${{ github.workspace }}/artifact/**/*
|
||||
```
|
||||
|
||||
## Where does the upload go?
|
||||
In the top right corner of a workflow run, once the run is over, if you used this action, there will be a `Artifacts` dropdown which you can download items from. Here's a screenshot of what it looks like<br/>
|
||||
@ -40,6 +193,11 @@ In the top right corner of a workflow run, once the run is over, if you used thi
|
||||
|
||||
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.
|
||||
|
||||
## Additional Documentation
|
||||
|
||||
See [persisting workflow data using artifacts](https://help.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts) for additional examples and tips.
|
||||
|
||||
See extra documentation for the [@actions/artifact](https://github.com/actions/toolkit/blob/master/packages/artifact/docs/additional-information.md) package that is used internally regarding certain behaviors and limitations.
|
||||
|
||||
# License
|
||||
|
||||
|
355
__tests__/search.test.ts
Normal file
355
__tests__/search.test.ts
Normal file
@ -0,0 +1,355 @@
|
||||
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'
|
||||
|
||||
const root = path.join(__dirname, '_temp', 'search')
|
||||
const searchItem1Path = path.join(
|
||||
root,
|
||||
'folder-a',
|
||||
'folder-b',
|
||||
'folder-c',
|
||||
'search-item1.txt'
|
||||
)
|
||||
const searchItem2Path = path.join(root, 'folder-d', 'search-item2.txt')
|
||||
const searchItem3Path = path.join(root, 'folder-d', 'search-item3.txt')
|
||||
const searchItem4Path = path.join(root, 'folder-d', 'search-item4.txt')
|
||||
const searchItem5Path = path.join(root, 'search-item5.txt')
|
||||
const extraSearchItem1Path = path.join(
|
||||
root,
|
||||
'folder-a',
|
||||
'folder-b',
|
||||
'folder-c',
|
||||
'extraSearch-item1.txt'
|
||||
)
|
||||
const extraSearchItem2Path = path.join(
|
||||
root,
|
||||
'folder-d',
|
||||
'extraSearch-item2.txt'
|
||||
)
|
||||
const extraSearchItem3Path = path.join(
|
||||
root,
|
||||
'folder-f',
|
||||
'extraSearch-item3.txt'
|
||||
)
|
||||
const extraSearchItem4Path = path.join(
|
||||
root,
|
||||
'folder-h',
|
||||
'folder-i',
|
||||
'extraSearch-item4.txt'
|
||||
)
|
||||
const extraSearchItem5Path = path.join(
|
||||
root,
|
||||
'folder-h',
|
||||
'folder-i',
|
||||
'extraSearch-item5.txt'
|
||||
)
|
||||
const extraFileInFolderCPath = path.join(
|
||||
root,
|
||||
'folder-a',
|
||||
'folder-b',
|
||||
'folder-c',
|
||||
'extra-file-in-folder-c.txt'
|
||||
)
|
||||
const amazingFileInFolderHPath = path.join(root, 'folder-h', 'amazing-item.txt')
|
||||
const lonelyFilePath = path.join(
|
||||
root,
|
||||
'folder-h',
|
||||
'folder-j',
|
||||
'folder-k',
|
||||
'lonely-file.txt'
|
||||
)
|
||||
|
||||
describe('Search', () => {
|
||||
beforeAll(async () => {
|
||||
// mock all output so that there is less noise when running tests
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {})
|
||||
jest.spyOn(core, 'debug').mockImplementation(() => {})
|
||||
jest.spyOn(core, 'info').mockImplementation(() => {})
|
||||
jest.spyOn(core, 'warning').mockImplementation(() => {})
|
||||
|
||||
// clear temp directory
|
||||
await io.rmRF(root)
|
||||
await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-e'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-d'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-f'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-g'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-h', 'folder-i'), {
|
||||
recursive: true
|
||||
})
|
||||
await fs.mkdir(path.join(root, 'folder-h', 'folder-j', 'folder-k'), {
|
||||
recursive: true
|
||||
})
|
||||
|
||||
await fs.writeFile(searchItem1Path, 'search item1 file')
|
||||
await fs.writeFile(searchItem2Path, 'search item2 file')
|
||||
await fs.writeFile(searchItem3Path, 'search item3 file')
|
||||
await fs.writeFile(searchItem4Path, 'search item4 file')
|
||||
await fs.writeFile(searchItem5Path, 'search item5 file')
|
||||
|
||||
await fs.writeFile(extraSearchItem1Path, 'extraSearch item1 file')
|
||||
await fs.writeFile(extraSearchItem2Path, 'extraSearch item2 file')
|
||||
await fs.writeFile(extraSearchItem3Path, 'extraSearch item3 file')
|
||||
await fs.writeFile(extraSearchItem4Path, 'extraSearch item4 file')
|
||||
await fs.writeFile(extraSearchItem5Path, 'extraSearch item5 file')
|
||||
|
||||
await fs.writeFile(extraFileInFolderCPath, 'extra file')
|
||||
|
||||
await fs.writeFile(amazingFileInFolderHPath, 'amazing file')
|
||||
|
||||
await fs.writeFile(lonelyFilePath, 'all by itself')
|
||||
/*
|
||||
Directory structure of files that get created:
|
||||
root/
|
||||
folder-a/
|
||||
folder-b/
|
||||
folder-c/
|
||||
search-item1.txt
|
||||
extraSearch-item1.txt
|
||||
extra-file-in-folder-c.txt
|
||||
folder-e/
|
||||
folder-d/
|
||||
search-item2.txt
|
||||
search-item3.txt
|
||||
search-item4.txt
|
||||
extraSearch-item2.txt
|
||||
folder-f/
|
||||
extraSearch-item3.txt
|
||||
folder-g/
|
||||
folder-h/
|
||||
amazing-item.txt
|
||||
folder-i/
|
||||
extraSearch-item4.txt
|
||||
extraSearch-item5.txt
|
||||
folder-j/
|
||||
folder-k/
|
||||
lonely-file.txt
|
||||
search-item5.txt
|
||||
*/
|
||||
})
|
||||
|
||||
it('Single file search - Absolute Path', async () => {
|
||||
const searchResult = await findFilesToUpload(extraFileInFolderCPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(1)
|
||||
expect(searchResult.filesToUpload[0]).toEqual(extraFileInFolderCPath)
|
||||
expect(searchResult.rootDirectory).toEqual(
|
||||
path.join(root, 'folder-a', 'folder-b', 'folder-c')
|
||||
)
|
||||
})
|
||||
|
||||
it('Single file search - Relative Path', async () => {
|
||||
const relativePath = path.join(
|
||||
'__tests__',
|
||||
'_temp',
|
||||
'search',
|
||||
'folder-a',
|
||||
'folder-b',
|
||||
'folder-c',
|
||||
'search-item1.txt'
|
||||
)
|
||||
|
||||
const searchResult = await findFilesToUpload(relativePath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(1)
|
||||
expect(searchResult.filesToUpload[0]).toEqual(searchItem1Path)
|
||||
expect(searchResult.rootDirectory).toEqual(
|
||||
path.join(root, 'folder-a', 'folder-b', 'folder-c')
|
||||
)
|
||||
})
|
||||
|
||||
it('Single file using wildcard', async () => {
|
||||
const expectedRoot = path.join(root, 'folder-h')
|
||||
const searchPath = path.join(root, 'folder-h', '**/*lonely*')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(1)
|
||||
expect(searchResult.filesToUpload[0]).toEqual(lonelyFilePath)
|
||||
expect(searchResult.rootDirectory).toEqual(expectedRoot)
|
||||
})
|
||||
|
||||
it('Single file using directory', async () => {
|
||||
const searchPath = path.join(root, 'folder-h', 'folder-j')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(1)
|
||||
expect(searchResult.filesToUpload[0]).toEqual(lonelyFilePath)
|
||||
expect(searchResult.rootDirectory).toEqual(searchPath)
|
||||
})
|
||||
|
||||
it('Directory search - Absolute Path', async () => {
|
||||
const searchPath = path.join(root, 'folder-h')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(4)
|
||||
|
||||
expect(
|
||||
searchResult.filesToUpload.includes(amazingFileInFolderHPath)
|
||||
).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(searchPath)
|
||||
})
|
||||
|
||||
it('Directory search - Relative Path', async () => {
|
||||
const searchPath = path.join('__tests__', '_temp', 'search', 'folder-h')
|
||||
const expectedRootDirectory = path.join(root, 'folder-h')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(4)
|
||||
|
||||
expect(
|
||||
searchResult.filesToUpload.includes(amazingFileInFolderHPath)
|
||||
).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(expectedRootDirectory)
|
||||
})
|
||||
|
||||
it('Wildcard search - Absolute Path', async () => {
|
||||
const searchPath = path.join(root, '**/*[Ss]earch*')
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(10)
|
||||
|
||||
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem3Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem5Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem1Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem3Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
|
||||
true
|
||||
)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
})
|
||||
|
||||
it('Wildcard search - Relative Path', async () => {
|
||||
const searchPath = path.join(
|
||||
'__tests__',
|
||||
'_temp',
|
||||
'search',
|
||||
'**/*[Ss]earch*'
|
||||
)
|
||||
const searchResult = await findFilesToUpload(searchPath)
|
||||
expect(searchResult.filesToUpload.length).toEqual(10)
|
||||
|
||||
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem3Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem5Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem1Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem3Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
|
||||
true
|
||||
)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
})
|
||||
|
||||
it('Multi path search - root directory', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-a')
|
||||
const searchPath2 = path.join(root, 'folder-d')
|
||||
|
||||
const searchPaths = searchPath1 + '\n' + searchPath2
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
expect(searchResult.filesToUpload.length).toEqual(7)
|
||||
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem3Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem1Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraFileInFolderCPath)).toEqual(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('Multi path search - with exclude character', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-a')
|
||||
const searchPath2 = path.join(root, 'folder-d')
|
||||
const searchPath3 = path.join(root, 'folder-a', 'folder-b', '**/extra*.txt')
|
||||
|
||||
// negating the third search path
|
||||
const searchPaths = searchPath1 + '\n' + searchPath2 + '\n!' + searchPath3
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
expect(searchResult.filesToUpload.length).toEqual(5)
|
||||
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem3Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('Multi path search - non root directory', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-h', 'folder-i')
|
||||
const searchPath2 = path.join(root, 'folder-h', 'folder-j', 'folder-k')
|
||||
const searchPath3 = amazingFileInFolderHPath
|
||||
|
||||
const searchPaths = [searchPath1, searchPath2, searchPath3].join('\n')
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(path.join(root, 'folder-h'))
|
||||
expect(searchResult.filesToUpload.length).toEqual(4)
|
||||
expect(
|
||||
searchResult.filesToUpload.includes(amazingFileInFolderHPath)
|
||||
).toEqual(true)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true)
|
||||
})
|
||||
})
|
@ -4,10 +4,10 @@ author: 'GitHub'
|
||||
inputs:
|
||||
name:
|
||||
description: 'Artifact name'
|
||||
required: true
|
||||
required: false
|
||||
path:
|
||||
description: 'Directory containing files to upload'
|
||||
description: 'A file, directory or wildcard pattern that describes what to upload'
|
||||
required: true
|
||||
runs:
|
||||
# Plugins live on the runner and are only available to a certain set of first party actions.
|
||||
plugin: 'publish'
|
||||
using: 'node12'
|
||||
main: 'dist/index.js'
|
8624
dist/index.js
vendored
Normal file
8624
dist/index.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
jest.config.js
Normal file
12
jest.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
roots: ['<rootDir>'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
testRunner: 'jest-circus/runner',
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
}
|
10392
package-lock.json
generated
Normal file
10392
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
51
package.json
Normal file
51
package.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "upload-artifact",
|
||||
"version": "2.0.1",
|
||||
"description": "Upload a build artifact that can be used by subsequent workflow steps",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"release": "ncc build src/upload-artifact.ts && git add -f dist/",
|
||||
"check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build\"",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts",
|
||||
"lint": "eslint **/*.ts",
|
||||
"test": "jest --testTimeout 10000"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/actions/upload-artifact.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Actions",
|
||||
"GitHub",
|
||||
"Artifacts",
|
||||
"Upload"
|
||||
],
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/actions/upload-artifact/issues"
|
||||
},
|
||||
"homepage": "https://github.com/actions/upload-artifact#readme",
|
||||
"devDependencies": {
|
||||
"@actions/artifact": "^0.3.2",
|
||||
"@actions/core": "^1.2.3",
|
||||
"@actions/glob": "^0.1.0",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/node": "^13.11.1",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"@zeit/ncc": "^0.22.1",
|
||||
"concurrently": "^5.1.0",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-plugin-github": "^3.4.1",
|
||||
"eslint-plugin-jest": "^23.8.2",
|
||||
"glob": "^7.1.6",
|
||||
"jest": "^26.1.0",
|
||||
"jest-circus": "^26.1.0",
|
||||
"prettier": "^2.0.4",
|
||||
"ts-jest": "^25.3.1",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
8
src/constants.ts
Normal file
8
src/constants.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export enum Inputs {
|
||||
Name = 'name',
|
||||
Path = 'path'
|
||||
}
|
||||
|
||||
export function getDefaultArtifactName(): string {
|
||||
return 'artifact'
|
||||
}
|
138
src/search.ts
Normal file
138
src/search.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import * as glob from '@actions/glob'
|
||||
import * as path from 'path'
|
||||
import {debug, info} from '@actions/core'
|
||||
import {lstatSync} from 'fs'
|
||||
import {dirname} from 'path'
|
||||
|
||||
export interface SearchResult {
|
||||
filesToUpload: string[]
|
||||
rootDirectory: string
|
||||
}
|
||||
|
||||
function getDefaultGlobOptions(): glob.GlobOptions {
|
||||
return {
|
||||
followSymbolicLinks: true,
|
||||
implicitDescendants: true,
|
||||
omitBrokenSymbolicLinks: true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple paths are specific, the least common ancestor (LCA) of the search paths is used as
|
||||
* the delimiter to control the directory structure for the artifact. This function returns the LCA
|
||||
* when given an array of search paths
|
||||
*
|
||||
* Example 1: The patterns `/foo/` and `/bar/` returns `/`
|
||||
*
|
||||
* Example 2: The patterns `~/foo/bar/*` and `~/foo/voo/two/*` and `~/foo/mo/` returns `~/foo`
|
||||
*/
|
||||
function getMultiPathLCA(searchPaths: string[]): string {
|
||||
if (searchPaths.length < 2) {
|
||||
throw new Error('At least two search paths must be provided')
|
||||
}
|
||||
|
||||
const commonPaths = new Array<string>()
|
||||
const splitPaths = new Array<string[]>()
|
||||
let smallestPathLength = Number.MAX_SAFE_INTEGER
|
||||
|
||||
// split each of the search paths using the platform specific separator
|
||||
for (const searchPath of searchPaths) {
|
||||
debug(`Using search path ${searchPath}`)
|
||||
|
||||
const splitSearchPath = path.normalize(searchPath).split(path.sep)
|
||||
|
||||
// keep track of the smallest path length so that we don't accidentally later go out of bounds
|
||||
smallestPathLength = Math.min(smallestPathLength, splitSearchPath.length)
|
||||
splitPaths.push(splitSearchPath)
|
||||
}
|
||||
|
||||
// on Unix-like file systems, the file separator exists at the beginning of the file path, make sure to preserve it
|
||||
if (searchPaths[0].startsWith(path.sep)) {
|
||||
commonPaths.push(path.sep)
|
||||
}
|
||||
|
||||
let splitIndex = 0
|
||||
// function to check if the paths are the same at a specific index
|
||||
function isPathTheSame(): boolean {
|
||||
const compare = splitPaths[0][splitIndex]
|
||||
for (let i = 1; i < splitPaths.length; i++) {
|
||||
if (compare !== splitPaths[i][splitIndex]) {
|
||||
// a non-common index has been reached
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Loop over all the search paths until there is a non-common ancestor or we go out of bounds
|
||||
while (splitIndex < smallestPathLength) {
|
||||
if (!isPathTheSame()) {
|
||||
break
|
||||
}
|
||||
// if all are the same, add to the end result & increment the index
|
||||
commonPaths.push(splitPaths[0][splitIndex])
|
||||
splitIndex++
|
||||
}
|
||||
return path.join(...commonPaths)
|
||||
}
|
||||
|
||||
export async function findFilesToUpload(
|
||||
searchPath: string,
|
||||
globOptions?: glob.GlobOptions
|
||||
): Promise<SearchResult> {
|
||||
const searchResults: string[] = []
|
||||
const globber = await glob.create(
|
||||
searchPath,
|
||||
globOptions || getDefaultGlobOptions()
|
||||
)
|
||||
const rawSearchResults: string[] = await globber.glob()
|
||||
|
||||
/*
|
||||
Directories will be rejected if attempted to be uploaded. This includes just empty
|
||||
directories so filter any directories out from the raw search results
|
||||
*/
|
||||
for (const searchResult of rawSearchResults) {
|
||||
if (!lstatSync(searchResult).isDirectory()) {
|
||||
debug(`File:${searchResult} was found using the provided searchPath`)
|
||||
searchResults.push(searchResult)
|
||||
} else {
|
||||
debug(
|
||||
`Removing ${searchResult} from rawSearchResults because it is a directory`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the root directory for the artifact using the search paths that were utilized
|
||||
const searchPaths: string[] = globber.getSearchPaths()
|
||||
|
||||
if (searchPaths.length > 1) {
|
||||
info(
|
||||
`Multiple search paths detected. Calculating the least common ancestor of all paths`
|
||||
)
|
||||
const lcaSearchPath = getMultiPathLCA(searchPaths)
|
||||
info(
|
||||
`The least common ancestor is ${lcaSearchPath}. This will be the root directory of the artifact`
|
||||
)
|
||||
|
||||
return {
|
||||
filesToUpload: searchResults,
|
||||
rootDirectory: lcaSearchPath
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Special case for a single file artifact that is uploaded without a directory or wildcard pattern. The directory structure is
|
||||
not preserved and the root directory will be the single files parent directory
|
||||
*/
|
||||
if (searchResults.length === 1 && searchPaths[0] === searchResults[0]) {
|
||||
return {
|
||||
filesToUpload: searchResults,
|
||||
rootDirectory: dirname(searchResults[0])
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
filesToUpload: searchResults,
|
||||
rootDirectory: searchPaths[0]
|
||||
}
|
||||
}
|
48
src/upload-artifact.ts
Normal file
48
src/upload-artifact.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import * as core from '@actions/core'
|
||||
import {create, UploadOptions} from '@actions/artifact'
|
||||
import {Inputs, getDefaultArtifactName} from './constants'
|
||||
import {findFilesToUpload} from './search'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const name = core.getInput(Inputs.Name, {required: false})
|
||||
const path = core.getInput(Inputs.Path, {required: true})
|
||||
|
||||
const searchResult = await findFilesToUpload(path)
|
||||
if (searchResult.filesToUpload.length === 0) {
|
||||
core.warning(
|
||||
`No files were found for the provided path: ${path}. No artifacts will be uploaded.`
|
||||
)
|
||||
} else {
|
||||
core.info(
|
||||
`With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded`
|
||||
)
|
||||
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`)
|
||||
|
||||
const artifactClient = create()
|
||||
const options: UploadOptions = {
|
||||
continueOnError: false
|
||||
}
|
||||
const uploadResponse = await artifactClient.uploadArtifact(
|
||||
name || getDefaultArtifactName(),
|
||||
searchResult.filesToUpload,
|
||||
searchResult.rootDirectory,
|
||||
options
|
||||
)
|
||||
|
||||
if (uploadResponse.failedItems.length > 0) {
|
||||
core.setFailed(
|
||||
`An error was encountered when uploading ${uploadResponse.artifactName}. There were ${uploadResponse.failedItems.length} items that failed to upload.`
|
||||
)
|
||||
} else {
|
||||
core.info(
|
||||
`Artifact ${uploadResponse.artifactName} has been successfully uploaded!`
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
core.setFailed(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": false,
|
||||
"sourceMap": true,
|
||||
"lib": ["es6"]
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
Reference in New Issue
Block a user