diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b8171bd..e0fa2ad 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,6 +10,10 @@ on:
paths-ignore:
- '**.md'
+permissions:
+ contents: read
+ actions: write
+
jobs:
build:
name: Build
@@ -94,7 +98,7 @@ jobs:
# Download Artifact #1 and verify the correctness of the content
- name: 'Download artifact #1'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: 'Artifact-A-${{ matrix.runs-on }}'
path: some/new/path
@@ -114,7 +118,7 @@ jobs:
# Download Artifact #2 and verify the correctness of the content
- name: 'Download artifact #2'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: 'Artifact-Wildcard-${{ matrix.runs-on }}'
path: some/other/path
@@ -135,7 +139,7 @@ jobs:
# Download Artifact #4 and verify the correctness of the content
- name: 'Download artifact #4'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: 'Multi-Path-Artifact-${{ matrix.runs-on }}'
path: multi/artifact
@@ -155,7 +159,7 @@ jobs:
shell: pwsh
- name: 'Download symlinked artifact'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: 'Symlinked-Artifact-${{ matrix.runs-on }}'
path: from/symlink
@@ -196,7 +200,7 @@ jobs:
# Download replaced Artifact #1 and verify the correctness of the content
- name: 'Download artifact #1 again'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: 'Artifact-A-${{ matrix.runs-on }}'
path: overwrite/some/new/path
@@ -213,6 +217,101 @@ jobs:
Write-Error "File contents of downloaded artifact are incorrect"
}
shell: pwsh
+
+ # Upload a single file without archiving (direct file upload)
+ - name: 'Create direct upload file'
+ run: echo -n 'direct file upload content' > direct-upload-${{ matrix.runs-on }}.txt
+ shell: bash
+
+ - name: 'Upload direct file artifact'
+ uses: ./
+ with:
+ name: 'Direct-File-${{ matrix.runs-on }}'
+ path: direct-upload-${{ matrix.runs-on }}.txt
+ archive: false
+
+ - name: 'Download direct file artifact'
+ uses: actions/download-artifact@main
+ with:
+ name: direct-upload-${{ matrix.runs-on }}.txt
+ path: direct-download
+
+ - name: 'Verify direct file artifact'
+ run: |
+ $file = "direct-download/direct-upload-${{ matrix.runs-on }}.txt"
+ if(!(Test-Path -path $file))
+ {
+ Write-Error "Expected file does not exist"
+ }
+ if(!((Get-Content $file -Raw).TrimEnd() -ceq "direct file upload content"))
+ {
+ Write-Error "File contents of downloaded artifact are incorrect"
+ }
+ shell: pwsh
+
+ upload-html-report:
+ name: Upload HTML Report
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node 24
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24.x
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Compile
+ run: npm run build
+
+ - name: Create HTML report
+ run: |
+ cat > report.html << 'EOF'
+
+
+
+
+
+ Artifact Upload Test Report
+
+
+
+ Artifact Upload Test Report
+
+ This HTML file was uploaded as a single un-zipped artifact.
+ If you can see this in the browser, the feature is working correctly!
+
+
+ | Property | Value |
+ | Upload method | archive: false |
+ | Content-Type | text/html |
+ | File | report.html |
+
+ ✔ Single file upload is working!
+
+
+ EOF
+
+ - name: Upload HTML report (no archive)
+ uses: ./
+ with:
+ name: 'test-report'
+ path: report.html
+ archive: false
+
merge:
name: Merge
needs: build
@@ -230,7 +329,7 @@ jobs:
# easier to identify each of the merged artifacts
separate-directories: true
- name: 'Download merged artifacts'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: merged-artifacts
path: all-merged-artifacts
@@ -266,7 +365,7 @@ jobs:
# Download merged artifacts and verify the correctness of the content
- name: 'Download merged artifacts'
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@main
with:
name: Merged-Artifact-As
path: merged-artifact-a
@@ -290,3 +389,40 @@ jobs:
}
shell: pwsh
+ cleanup:
+ name: Cleanup Artifacts
+ needs: [build, merge]
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Delete test artifacts
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const keep = ['report.html'];
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+ const runId = context.runId;
+
+ const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({
+ owner,
+ repo,
+ run_id: runId
+ });
+
+ for (const a of artifacts) {
+ if (keep.includes(a.name)) {
+ console.log(`Keeping artifact '${a.name}'`);
+ continue;
+ }
+ try {
+ await github.rest.actions.deleteArtifact({
+ owner,
+ repo,
+ artifact_id: a.id
+ });
+ console.log(`Deleted artifact '${a.name}'`);
+ } catch (err) {
+ console.log(`Could not delete artifact '${a.name}': ${err.message}`);
+ }
+ }
diff --git a/.licenses/npm/balanced-match-4.0.4.dep.yml b/.licenses/npm/balanced-match-4.0.4.dep.yml
deleted file mode 100644
index 101c2c3..0000000
--- a/.licenses/npm/balanced-match-4.0.4.dep.yml
+++ /dev/null
@@ -1,34 +0,0 @@
----
-name: balanced-match
-version: 4.0.4
-type: npm
-summary: Match balanced character pairs, like "{" and "}"
-homepage:
-license: mit
-licenses:
-- sources: LICENSE.md
- text: |
- (MIT)
-
- Original code Copyright Julian Gruber
-
- Port to TypeScript Copyright Isaac Z. Schlueter
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-notices: []
diff --git a/.licenses/npm/brace-expansion-5.0.3.dep.yml b/.licenses/npm/brace-expansion-5.0.3.dep.yml
deleted file mode 100644
index 07359b8..0000000
--- a/.licenses/npm/brace-expansion-5.0.3.dep.yml
+++ /dev/null
@@ -1,34 +0,0 @@
----
-name: brace-expansion
-version: 5.0.3
-type: npm
-summary: Brace expansion as known from sh/bash
-homepage:
-license: mit
-licenses:
-- sources: LICENSE
- text: |
- MIT License
-
- Copyright Julian Gruber
-
- TypeScript port Copyright Isaac Z. Schlueter
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-notices: []
diff --git a/.licenses/npm/fast-content-type-parse.dep.yml b/.licenses/npm/fast-content-type-parse.dep.yml
deleted file mode 100644
index e411b38..0000000
--- a/.licenses/npm/fast-content-type-parse.dep.yml
+++ /dev/null
@@ -1,37 +0,0 @@
----
-name: fast-content-type-parse
-version: 3.0.0
-type: npm
-summary: Parse HTTP Content-Type header according to RFC 7231
-homepage: https://github.com/fastify/fast-content-type-parse#readme
-license: other
-licenses:
-- sources: LICENSE
- text: |-
- MIT License
-
- Copyright (c) 2023 The Fastify Team
-
- The Fastify team members are listed at https://github.com/fastify/fastify#team
- and in the README file.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-- sources: README.md
- text: Licensed under [MIT](./LICENSE).
-notices: []
diff --git a/.licenses/npm/json-with-bigint.dep.yml b/.licenses/npm/json-with-bigint.dep.yml
deleted file mode 100644
index d496868..0000000
--- a/.licenses/npm/json-with-bigint.dep.yml
+++ /dev/null
@@ -1,33 +0,0 @@
----
-name: json-with-bigint
-version: 3.5.3
-type: npm
-summary: JS library that allows you to easily serialize and deserialize data with
- BigInt values
-homepage:
-license: mit
-licenses:
-- sources: LICENSE
- text: |
- MIT License
-
- Copyright (c) 2023 Ivan Korolenko
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-notices: []
diff --git a/.licenses/npm/minimatch-9.0.8.dep.yml b/.licenses/npm/minimatch-9.0.8.dep.yml
deleted file mode 100644
index e8d3203..0000000
--- a/.licenses/npm/minimatch-9.0.8.dep.yml
+++ /dev/null
@@ -1,66 +0,0 @@
----
-name: minimatch
-version: 9.0.8
-type: npm
-summary:
-homepage:
-license: blueoak-1.0.0
-licenses:
-- sources: LICENSE.md
- text: |
- # Blue Oak Model License
-
- Version 1.0.0
-
- ## Purpose
-
- This license gives everyone as much permission to work with
- this software as possible, while protecting contributors
- from liability.
-
- ## Acceptance
-
- In order to receive this license, you must agree to its
- rules. The rules of this license are both obligations
- under that agreement and conditions to your license.
- You must not do anything with this software that triggers
- a rule that you cannot or will not follow.
-
- ## Copyright
-
- Each contributor licenses you to do everything with this
- software that would otherwise infringe that contributor's
- copyright in it.
-
- ## Notices
-
- You must ensure that everyone who gets a copy of
- any part of this software from you, with or without
- changes, also gets the text of this license or a link to
- .
-
- ## Excuse
-
- If anyone notifies you in writing that you have not
- complied with [Notices](#notices), you can keep your
- license by taking all practical steps to comply within 30
- days after the notice. If you do not do so, your license
- ends immediately.
-
- ## Patent
-
- Each contributor licenses you to do everything with this
- software that would otherwise infringe any patent claims
- they can license or become able to license.
-
- ## Reliability
-
- No contributor can revoke this license.
-
- ## No Liability
-
- **_As far as the law allows, this software comes as is,
- without any warranty or condition, and no contributor
- will be liable to anyone for any damages related to this
- software or this license, under any kind of legal claim._**
-notices: []
diff --git a/.licenses/npm/minimatch.dep.yml b/.licenses/npm/minimatch.dep.yml
index b3a5b56..8ed580a 100644
--- a/.licenses/npm/minimatch.dep.yml
+++ b/.licenses/npm/minimatch.dep.yml
@@ -1,9 +1,9 @@
---
name: minimatch
-version: 10.1.1
+version: 10.2.4
type: npm
-summary: a glob matcher in javascript
-homepage:
+summary:
+homepage:
license: blueoak-1.0.0
licenses:
- sources: LICENSE.md
@@ -63,4 +63,4 @@ licenses:
without any warranty or condition, and no contributor
will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim._**
-notices: []
\ No newline at end of file
+notices: []
diff --git a/__tests__/upload.test.ts b/__tests__/upload.test.ts
index 6e0c26a..de81a1c 100644
--- a/__tests__/upload.test.ts
+++ b/__tests__/upload.test.ts
@@ -72,6 +72,7 @@ const mockInputs = (
[Inputs.RetentionDays]: 0,
[Inputs.CompressionLevel]: 6,
[Inputs.Overwrite]: false,
+ [Inputs.Archive]: true,
...overrides
}
@@ -273,4 +274,57 @@ describe('upload', () => {
`Skipping deletion of '${fixtures.artifactName}', it does not exist`
)
})
+
+ test('passes skipArchive when archive is false', async () => {
+ mockInputs({
+ [Inputs.Archive]: false
+ })
+
+ mockFindFilesToUpload.mockResolvedValue({
+ filesToUpload: [fixtures.filesToUpload[0]],
+ rootDirectory: fixtures.rootDirectory
+ })
+
+ await run()
+
+ expect(artifact.default.uploadArtifact).toHaveBeenCalledWith(
+ fixtures.artifactName,
+ [fixtures.filesToUpload[0]],
+ fixtures.rootDirectory,
+ {compressionLevel: 6, skipArchive: true}
+ )
+ })
+
+ test('does not pass skipArchive when archive is true', async () => {
+ mockInputs({
+ [Inputs.Archive]: true
+ })
+
+ mockFindFilesToUpload.mockResolvedValue({
+ filesToUpload: [fixtures.filesToUpload[0]],
+ rootDirectory: fixtures.rootDirectory
+ })
+
+ await run()
+
+ expect(artifact.default.uploadArtifact).toHaveBeenCalledWith(
+ fixtures.artifactName,
+ [fixtures.filesToUpload[0]],
+ fixtures.rootDirectory,
+ {compressionLevel: 6}
+ )
+ })
+
+ test('fails when archive is false and multiple files are provided', async () => {
+ mockInputs({
+ [Inputs.Archive]: false
+ })
+
+ await run()
+
+ expect(core.setFailed).toHaveBeenCalledWith(
+ `When 'archive' is set to false, only a single file can be uploaded. Found ${fixtures.filesToUpload.length} files to upload.`
+ )
+ expect(artifact.default.uploadArtifact).not.toHaveBeenCalled()
+ })
})
diff --git a/action.yml b/action.yml
index 28f04cc..7cb4d1e 100644
--- a/action.yml
+++ b/action.yml
@@ -3,10 +3,10 @@ description: 'Upload a build artifact that can be used by subsequent workflow st
author: 'GitHub'
inputs:
name:
- description: 'Artifact name'
+ description: 'Artifact name. If the `archive` input is `false`, the name of the file uploaded will be the artifact name.'
default: 'artifact'
path:
- description: 'A file, directory or wildcard pattern that describes what to upload'
+ description: 'A file, directory or wildcard pattern that describes what to upload.'
required: true
if-no-files-found:
description: >
@@ -45,6 +45,12 @@ inputs:
If true, hidden files will be included in the artifact.
If false, hidden files will be excluded from the artifact.
default: 'false'
+ archive:
+ description: >
+ If true, the artifact will be archived (zipped) before uploading.
+ If false, the artifact will be uploaded as-is without archiving.
+ When `archive` is `false`, only a single file can be uploaded. The name of the file will be used as the artifact name (ignoring the `name` parameter).
+ default: 'true'
outputs:
artifact-id:
diff --git a/dist/upload/index.js b/dist/upload/index.js
index f08f27c..a4ca651 100644
--- a/dist/upload/index.js
+++ b/dist/upload/index.js
@@ -130457,6 +130457,7 @@ var Inputs;
Inputs["CompressionLevel"] = "compression-level";
Inputs["Overwrite"] = "overwrite";
Inputs["IncludeHiddenFiles"] = "include-hidden-files";
+ Inputs["Archive"] = "archive";
})(Inputs || (Inputs = {}));
var NoFileOptions;
(function (NoFileOptions) {
@@ -130485,6 +130486,7 @@ function getInputs() {
const path = getInput(Inputs.Path, { required: true });
const overwrite = getBooleanInput(Inputs.Overwrite);
const includeHiddenFiles = getBooleanInput(Inputs.IncludeHiddenFiles);
+ const archive = getBooleanInput(Inputs.Archive);
const ifNoFilesFound = getInput(Inputs.IfNoFilesFound);
const noFileBehavior = NoFileOptions[ifNoFilesFound];
if (!noFileBehavior) {
@@ -130495,7 +130497,8 @@ function getInputs() {
searchPath: path,
ifNoFilesFound: noFileBehavior,
overwrite: overwrite,
- includeHiddenFiles: includeHiddenFiles
+ includeHiddenFiles: includeHiddenFiles,
+ archive: archive
};
const retentionDaysStr = getInput(Inputs.RetentionDays);
if (retentionDaysStr) {
@@ -130576,6 +130579,11 @@ async function run() {
const s = searchResult.filesToUpload.length === 1 ? '' : 's';
info(`With the provided path, there will be ${searchResult.filesToUpload.length} file${s} uploaded`);
core_debug(`Root artifact directory is ${searchResult.rootDirectory}`);
+ // Validate that only a single file is uploaded when archive is false
+ if (!inputs.archive && searchResult.filesToUpload.length > 1) {
+ setFailed(`When 'archive' is set to false, only a single file can be uploaded. Found ${searchResult.filesToUpload.length} files to upload.`);
+ return;
+ }
if (inputs.overwrite) {
await deleteArtifactIfExists(inputs.artifactName);
}
@@ -130586,6 +130594,9 @@ async function run() {
if (typeof inputs.compressionLevel !== 'undefined') {
options.compressionLevel = inputs.compressionLevel;
}
+ if (!inputs.archive) {
+ options.skipArchive = true;
+ }
await upload_artifact_uploadArtifact(inputs.artifactName, searchResult.filesToUpload, searchResult.rootDirectory, options);
}
}
diff --git a/package-lock.json b/package-lock.json
index a0c89f3..5739b1d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "7.0.0",
"license": "MIT",
"dependencies": {
- "@actions/artifact": "^6.1.0",
+ "@actions/artifact": "^6.2.0",
"@actions/core": "^3.0.0",
"@actions/github": "^9.0.0",
"@actions/glob": "^0.6.1",
diff --git a/package.json b/package.json
index c668d43..b71533f 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"node": ">=24"
},
"dependencies": {
- "@actions/artifact": "^6.1.0",
+ "@actions/artifact": "^6.2.0",
"@actions/core": "^3.0.0",
"@actions/github": "^9.0.0",
"@actions/glob": "^0.6.1",
diff --git a/src/upload/constants.ts b/src/upload/constants.ts
index 8407bc1..61ec717 100644
--- a/src/upload/constants.ts
+++ b/src/upload/constants.ts
@@ -6,7 +6,8 @@ export enum Inputs {
RetentionDays = 'retention-days',
CompressionLevel = 'compression-level',
Overwrite = 'overwrite',
- IncludeHiddenFiles = 'include-hidden-files'
+ IncludeHiddenFiles = 'include-hidden-files',
+ Archive = 'archive'
}
export enum NoFileOptions {
diff --git a/src/upload/input-helper.ts b/src/upload/input-helper.ts
index 527ce6c..29605c6 100644
--- a/src/upload/input-helper.ts
+++ b/src/upload/input-helper.ts
@@ -10,6 +10,7 @@ export function getInputs(): UploadInputs {
const path = core.getInput(Inputs.Path, {required: true})
const overwrite = core.getBooleanInput(Inputs.Overwrite)
const includeHiddenFiles = core.getBooleanInput(Inputs.IncludeHiddenFiles)
+ const archive = core.getBooleanInput(Inputs.Archive)
const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound)
const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound]
@@ -29,7 +30,8 @@ export function getInputs(): UploadInputs {
searchPath: path,
ifNoFilesFound: noFileBehavior,
overwrite: overwrite,
- includeHiddenFiles: includeHiddenFiles
+ includeHiddenFiles: includeHiddenFiles,
+ archive: archive
} as UploadInputs
const retentionDaysStr = core.getInput(Inputs.RetentionDays)
diff --git a/src/upload/upload-artifact.ts b/src/upload/upload-artifact.ts
index 1cffa3d..432ec11 100644
--- a/src/upload/upload-artifact.ts
+++ b/src/upload/upload-artifact.ts
@@ -57,6 +57,14 @@ export async function run(): Promise {
)
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`)
+ // Validate that only a single file is uploaded when archive is false
+ if (!inputs.archive && searchResult.filesToUpload.length > 1) {
+ core.setFailed(
+ `When 'archive' is set to false, only a single file can be uploaded. Found ${searchResult.filesToUpload.length} files to upload.`
+ )
+ return
+ }
+
if (inputs.overwrite) {
await deleteArtifactIfExists(inputs.artifactName)
}
@@ -70,6 +78,10 @@ export async function run(): Promise {
options.compressionLevel = inputs.compressionLevel
}
+ if (!inputs.archive) {
+ options.skipArchive = true
+ }
+
await uploadArtifact(
inputs.artifactName,
searchResult.filesToUpload,
diff --git a/src/upload/upload-inputs.ts b/src/upload/upload-inputs.ts
index b3fa72e..61f221d 100644
--- a/src/upload/upload-inputs.ts
+++ b/src/upload/upload-inputs.ts
@@ -35,4 +35,10 @@ export interface UploadInputs {
* Whether or not to include hidden files in the artifact
*/
includeHiddenFiles: boolean
+
+ /**
+ * Whether or not to archive (zip) the artifact before uploading.
+ * When false, only a single file can be uploaded.
+ */
+ archive: boolean
}