Compare commits

...

8 Commits

Author SHA1 Message Date
c8879bf5ae Detect case insensitive uploads + Bump @actions/artifact to version 0.3.3 (#106)
* Detect case insensitive uploads

* PR feedback
2020-07-31 19:22:08 +02:00
5ba29a7d5b Add new option to specify behavior if no files found (#104)
* Add new option to specify behavior if no files found
2020-07-31 17:27:37 +02:00
5f948bc1f0 Correctly check symlinks (#103) 2020-07-27 15:41:16 +02:00
589ca5fbdd Bump lodash from 4.17.15 to 4.17.19 (#99)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-22 12:45:55 +02:00
8ec57c93cb Update README.md 2020-07-14 12:25:21 +02:00
ee5fe7718d Update test.yml 2020-07-14 12:20:40 +02:00
0c366cb4fc Create CONTRIBUTING.md 2020-07-10 14:17:19 +02:00
63d6076e6f Create CODE_OF_CONDUCT.md 2020-07-10 13:49:39 +02:00
13 changed files with 400 additions and 48 deletions

View File

@ -2,7 +2,7 @@ name: Test
on:
push:
branches:
- master
- main
paths-ignore:
- '**.md'
pull_request:

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at opensource@github.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

52
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,52 @@
## Contributing
[fork]: https://github.com/actions/upload-artifact/fork
[pr]: https://github.com/actions/upload-artifact/compare
[style]: https://github.com/styleguide/js
[code-of-conduct]: CODE_OF_CONDUCT.md
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
## Found a bug?
- **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/actions/upload-artifact/issues).
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/actions/upload-artifact/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or a **reproducable test case** demonstrating the expected behavior that is not occurring.
- If possible, use the relevant bug report templates to create the issue.
## What should I know before submitting a pull request or issue
The code related to `upload-artifact` is split between this repository and [actions/toolkit](https://github.com/actions/toolkit) where the `@actions/artifact` npm package is housed. The npm package contains the core functionality to interact with artifacts. Any extra functionality on top of interacting with the apis such as search is inside this repository.
Artifact related issues will be tracked in this repository so please do not open duplicate issues in `actions/toolkit`.
## Submitting a pull request
1. [Fork][fork] and clone the repository
2. Configure and install the dependencies: `npm install`
3. Make sure the tests pass on your machine: `npm run test`
4. Create a new branch: `git checkout -b my-branch-name`
5. Make your change, add tests, and make sure the tests still pass
6. Make sure your code is correctly formatted: `npm run format`
7. Make sure your code passes linting: `npm run lint`
8. Update `dist/index.js` using `npm run release`. This creates a single javascript file that is used as an entry-point for the action
7. Push to your fork and [submit a pull request][pr]
8. Pat your self on the back and wait for your pull request to be reviewed and merged.
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Write tests.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
## Resources
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)
Thanks! :heart: :heart: :heart:
GitHub Actions Team :octocat:

View File

@ -67,7 +67,7 @@ steps:
!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.
For supported wildcards along with behavior and documentation, see [@actions/glob](https://github.com/actions/toolkit/tree/main/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.
@ -86,7 +86,19 @@ If multiple paths are provided as input, the least common ancestor of all the se
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.
The [@actions/artifact](https://github.com/actions/toolkit/tree/main/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.
### Customization if no files are found
If a path (or paths), result in no files being found for the artifact, the action will succeed but print out a warning. In certain scenarios it may be desirable to fail the action or suppress the warning. The `if-no-files-found` option allows you to customize the behavior of the action if no files are found.
```yaml
- uses: actions/upload-artifact@v2
with:
name: my-artifact
path: path/to/artifact/
if-no-files-found: error # 'warn' or 'ignore' are also available, defaults to `warn`
```
### Conditional Artifact Upload
@ -191,7 +203,32 @@ Environment variables along with context expressions can also be used for input.
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/>
<img src="https://user-images.githubusercontent.com/16109154/72556687-20235a80-386d-11ea-9e2a-b534faa77083.png" width="375" height="140">
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.
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.
# Limitations
### Permission Loss
:exclamation: File permissions are not maintained during artifact upload :exclamation: For example, if you make a file executable using `chmod` and then upload that file, post-download the file is no longer guaranteed to be set as an executable.
### Case Insensitive Uploads
:exclamation: File uploads are case insensitive :exclamation: If you upload `A.txt` and `a.txt` with the same root path, only a single file will be saved and available during download.
### Maintaining file permissions and case sensitive files
If file permissions and case sensitivity are required, you can `tar` all of your files together before artifact upload. Post download, the `tar` file will maintain file permissions and case sensitivity.
```yaml
- name: 'Tar files'
run: tar -cvf my_files.tar /path/to/my/directory
- name: 'Upload Artifact'
uses: actions/upload-artifact@v2
with:
name: my-artifact
path: my_files.tar
```
## Additional Documentation

View File

@ -4,10 +4,19 @@ author: 'GitHub'
inputs:
name:
description: 'Artifact name'
required: false
default: 'artifact'
path:
description: 'A file, directory or wildcard pattern that describes what to upload'
required: true
if-no-files-found:
description: >
The desired behavior if no files are found using the provided path.
Available Options:
warn: Output a warning but do not fail the action
error: Fail the action with an error message
ignore: Do not output any warnings or errors, the action does not fail
default: 'warn'
runs:
using: 'node12'
main: 'dist/index.js'

120
dist/index.js vendored
View File

@ -3987,25 +3987,39 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const artifact_1 = __webpack_require__(214);
const constants_1 = __webpack_require__(694);
const search_1 = __webpack_require__(575);
const input_helper_1 = __webpack_require__(583);
const constants_1 = __webpack_require__(694);
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
const name = core.getInput(constants_1.Inputs.Name, { required: false });
const path = core.getInput(constants_1.Inputs.Path, { required: true });
const searchResult = yield search_1.findFilesToUpload(path);
const inputs = input_helper_1.getInputs();
const searchResult = yield search_1.findFilesToUpload(inputs.searchPath);
if (searchResult.filesToUpload.length === 0) {
core.warning(`No files were found for the provided path: ${path}. No artifacts will be uploaded.`);
// No files were found, different use cases warrant different types of behavior if nothing is found
switch (inputs.ifNoFilesFound) {
case constants_1.NoFileOptions.warn: {
core.warning(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
break;
}
case constants_1.NoFileOptions.error: {
core.setFailed(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
break;
}
case constants_1.NoFileOptions.ignore: {
core.info(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
break;
}
}
}
else {
core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded`);
core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} file(s) uploaded`);
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`);
const artifactClient = artifact_1.create();
const options = {
continueOnError: false
};
const uploadResponse = yield artifactClient.uploadArtifact(name || constants_1.getDefaultArtifactName(), searchResult.filesToUpload, searchResult.rootDirectory, options);
const uploadResponse = yield artifactClient.uploadArtifact(inputs.artifactName, 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.`);
}
@ -4978,11 +4992,12 @@ const utils_1 = __webpack_require__(870);
* Used for managing http clients during either upload or download
*/
class HttpManager {
constructor(clientCount) {
constructor(clientCount, userAgent) {
if (clientCount < 1) {
throw new Error('There must be at least one client');
}
this.clients = new Array(clientCount).fill(utils_1.createHttpClient());
this.userAgent = userAgent;
this.clients = new Array(clientCount).fill(utils_1.createHttpClient(userAgent));
}
getClient(index) {
return this.clients[index];
@ -4991,7 +5006,7 @@ class HttpManager {
// for more information see: https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292
disposeAndReplaceClient(index) {
this.clients[index].dispose();
this.clients[index] = utils_1.createHttpClient();
this.clients[index] = utils_1.createHttpClient(this.userAgent);
}
disposeAndReplaceAllClients() {
for (const [index] of this.clients.entries()) {
@ -6225,6 +6240,8 @@ const path = __importStar(__webpack_require__(622));
const core_1 = __webpack_require__(470);
const fs_1 = __webpack_require__(747);
const path_1 = __webpack_require__(622);
const util_1 = __webpack_require__(669);
const stats = util_1.promisify(fs_1.stat);
function getDefaultGlobOptions() {
return {
followSymbolicLinks: true,
@ -6272,7 +6289,7 @@ function getMultiPathLCA(searchPaths) {
}
return true;
}
// Loop over all the search paths until there is a non-common ancestor or we go out of bounds
// 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;
@ -6288,14 +6305,28 @@ function findFilesToUpload(searchPath, globOptions) {
const searchResults = [];
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
const rawSearchResults = yield globber.glob();
/*
Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten
Detect any files that could be overwritten for user awareness
*/
const set = new Set();
/*
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 (!fs_1.lstatSync(searchResult).isDirectory()) {
const fileStats = yield stats(searchResult);
// isDirectory() returns false for symlinks if using fs.lstat(), make sure to use fs.stat() instead
if (!fileStats.isDirectory()) {
core_1.debug(`File:${searchResult} was found using the provided searchPath`);
searchResults.push(searchResult);
// detect any files that would be overwritten because of case insensitivity
if (set.has(searchResult.toLowerCase())) {
core_1.info(`Uploads are case insensitive: ${searchResult} was detected that it will be overwritten by another file with the same path`);
}
else {
set.add(searchResult.toLowerCase());
}
}
else {
core_1.debug(`Removing ${searchResult} from rawSearchResults because it is a directory`);
@ -6331,6 +6362,43 @@ function findFilesToUpload(searchPath, globOptions) {
exports.findFilesToUpload = findFilesToUpload;
/***/ }),
/***/ 583:
/***/ (function(__unusedmodule, exports, __webpack_require__) {
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(694);
/**
* Helper to get all the inputs for the action
*/
function getInputs() {
const name = core.getInput(constants_1.Inputs.Name);
const path = core.getInput(constants_1.Inputs.Path, { required: true });
const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound);
const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound];
if (!noFileBehavior) {
core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_1.NoFileOptions)}`);
}
return {
artifactName: name,
searchPath: path,
ifNoFilesFound: noFileBehavior
};
}
exports.getInputs = getInputs;
/***/ }),
/***/ 590:
@ -6590,7 +6658,7 @@ const upload_gzip_1 = __webpack_require__(647);
const stat = util_1.promisify(fs.stat);
class UploadHttpClient {
constructor() {
this.uploadHttpManager = new http_manager_1.HttpManager(config_variables_1.getUploadFileConcurrency());
this.uploadHttpManager = new http_manager_1.HttpManager(config_variables_1.getUploadFileConcurrency(), 'actions/upload-artifact');
this.statusReporter = new status_reporter_1.StatusReporter(10000);
}
/**
@ -7245,11 +7313,23 @@ var Inputs;
(function (Inputs) {
Inputs["Name"] = "name";
Inputs["Path"] = "path";
Inputs["IfNoFilesFound"] = "if-no-files-found";
})(Inputs = exports.Inputs || (exports.Inputs = {}));
function getDefaultArtifactName() {
return 'artifact';
}
exports.getDefaultArtifactName = getDefaultArtifactName;
var NoFileOptions;
(function (NoFileOptions) {
/**
* Default. Output a warning but do not fail the action
*/
NoFileOptions["warn"] = "warn";
/**
* Fail the action with an error message
*/
NoFileOptions["error"] = "error";
/**
* Do not output any warnings or errors, the action does not fail
*/
NoFileOptions["ignore"] = "ignore";
})(NoFileOptions = exports.NoFileOptions || (exports.NoFileOptions = {}));
/***/ }),
@ -7332,7 +7412,7 @@ const http_manager_1 = __webpack_require__(452);
const config_variables_1 = __webpack_require__(401);
class DownloadHttpClient {
constructor() {
this.downloadHttpManager = new http_manager_1.HttpManager(config_variables_1.getDownloadFileConcurrency());
this.downloadHttpManager = new http_manager_1.HttpManager(config_variables_1.getDownloadFileConcurrency(), 'actions/download-artifact');
// downloads are usually significantly faster than uploads so display status information every second
this.statusReporter = new status_reporter_1.StatusReporter(1000);
}
@ -7967,8 +8047,8 @@ function getUploadHeaders(contentType, isKeepAlive, isGzip, uncompressedLength,
return requestOptions;
}
exports.getUploadHeaders = getUploadHeaders;
function createHttpClient() {
return new http_client_1.HttpClient('actions/artifact', [
function createHttpClient(userAgent) {
return new http_client_1.HttpClient(userAgent, [
new auth_1.BearerCredentialHandler(config_variables_1.getRuntimeToken())
]);
}

12
package-lock.json generated
View File

@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"@actions/artifact": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.2.tgz",
"integrity": "sha512-KzUe5DEeVXprAodxfGKtx9f7ukuVKE6V6pge6t5GDGk0cdkfiMEfahoq7HfBsOsmVy4J7rr1YZQPUTvXveYinw==",
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.3.tgz",
"integrity": "sha512-sKC1uA5p6064C6Qypmmt6O8iKlpDyMTfqqDlS4/zfJX1Hs8NbbzPLLN81RpewuJPWQNnroeF52w4VCWypbSNaA==",
"dev": true,
"requires": {
"@actions/core": "^1.2.1",
@ -7975,9 +7975,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true
},
"lodash.memoize": {

View File

@ -29,7 +29,7 @@
},
"homepage": "https://github.com/actions/upload-artifact#readme",
"devDependencies": {
"@actions/artifact": "^0.3.2",
"@actions/artifact": "^0.3.3",
"@actions/core": "^1.2.3",
"@actions/glob": "^0.1.0",
"@actions/io": "^1.0.2",

View File

@ -1,8 +1,22 @@
export enum Inputs {
Name = 'name',
Path = 'path'
Path = 'path',
IfNoFilesFound = 'if-no-files-found'
}
export function getDefaultArtifactName(): string {
return 'artifact'
export enum NoFileOptions {
/**
* Default. Output a warning but do not fail the action
*/
warn = 'warn',
/**
* Fail the action with an error message
*/
error = 'error',
/**
* Do not output any warnings or errors, the action does not fail
*/
ignore = 'ignore'
}

30
src/input-helper.ts Normal file
View File

@ -0,0 +1,30 @@
import * as core from '@actions/core'
import {Inputs, NoFileOptions} from './constants'
import {UploadInputs} from './upload-inputs'
/**
* Helper to get all the inputs for the action
*/
export function getInputs(): UploadInputs {
const name = core.getInput(Inputs.Name)
const path = core.getInput(Inputs.Path, {required: true})
const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound)
const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound]
if (!noFileBehavior) {
core.setFailed(
`Unrecognized ${
Inputs.IfNoFilesFound
} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(
NoFileOptions
)}`
)
}
return {
artifactName: name,
searchPath: path,
ifNoFilesFound: noFileBehavior
}
}

View File

@ -1,8 +1,10 @@
import * as glob from '@actions/glob'
import * as path from 'path'
import {debug, info} from '@actions/core'
import {lstatSync} from 'fs'
import {stat} from 'fs'
import {dirname} from 'path'
import {promisify} from 'util'
const stats = promisify(stat)
export interface SearchResult {
filesToUpload: string[]
@ -64,7 +66,7 @@ function getMultiPathLCA(searchPaths: string[]): string {
return true
}
// Loop over all the search paths until there is a non-common ancestor or we go out of bounds
// 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
@ -87,14 +89,31 @@ export async function findFilesToUpload(
)
const rawSearchResults: string[] = await globber.glob()
/*
Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten
Detect any files that could be overwritten for user awareness
*/
const set = new Set<string>()
/*
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()) {
const fileStats = await stats(searchResult)
// isDirectory() returns false for symlinks if using fs.lstat(), make sure to use fs.stat() instead
if (!fileStats.isDirectory()) {
debug(`File:${searchResult} was found using the provided searchPath`)
searchResults.push(searchResult)
// detect any files that would be overwritten because of case insensitivity
if (set.has(searchResult.toLowerCase())) {
info(
`Uploads are case insensitive: ${searchResult} was detected that it will be overwritten by another file with the same path`
)
} else {
set.add(searchResult.toLowerCase())
}
} else {
debug(
`Removing ${searchResult} from rawSearchResults because it is a directory`

View File

@ -1,21 +1,38 @@
import * as core from '@actions/core'
import {create, UploadOptions} from '@actions/artifact'
import {Inputs, getDefaultArtifactName} from './constants'
import {findFilesToUpload} from './search'
import {getInputs} from './input-helper'
import {NoFileOptions} from './constants'
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)
const inputs = getInputs()
const searchResult = await findFilesToUpload(inputs.searchPath)
if (searchResult.filesToUpload.length === 0) {
core.warning(
`No files were found for the provided path: ${path}. No artifacts will be uploaded.`
)
// No files were found, different use cases warrant different types of behavior if nothing is found
switch (inputs.ifNoFilesFound) {
case NoFileOptions.warn: {
core.warning(
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
)
break
}
case NoFileOptions.error: {
core.setFailed(
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
)
break
}
case NoFileOptions.ignore: {
core.info(
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
)
break
}
}
} else {
core.info(
`With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded`
`With the provided path, there will be ${searchResult.filesToUpload.length} file(s) uploaded`
)
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`)
@ -24,7 +41,7 @@ async function run(): Promise<void> {
continueOnError: false
}
const uploadResponse = await artifactClient.uploadArtifact(
name || getDefaultArtifactName(),
inputs.artifactName,
searchResult.filesToUpload,
searchResult.rootDirectory,
options

18
src/upload-inputs.ts Normal file
View File

@ -0,0 +1,18 @@
import {NoFileOptions} from './constants'
export interface UploadInputs {
/**
* The name of the artifact that will be uploaded
*/
artifactName: string
/**
* The search path used to describe what to upload as part of the artifact
*/
searchPath: string
/**
* The desired behavior if no files are found with the provided search path
*/
ifNoFilesFound: NoFileOptions
}