diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index cd90053..dec03b6 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -34,7 +34,7 @@ jobs: run: npm ci - name: Rebuild the dist/ directory - run: npm run build + run: npm run release - name: Compare the expected and actual dist/ directories run: | diff --git a/__tests__/search.test.ts b/__tests__/search.test.ts index cbf54ac..e0ab26d 100644 --- a/__tests__/search.test.ts +++ b/__tests__/search.test.ts @@ -2,7 +2,7 @@ import * as core from '@actions/core' import * as path from 'path' import * as io from '@actions/io' import {promises as fs} from 'fs' -import {findFilesToUpload} from '../src/search' +import {findFilesToUpload} from '../src/shared/search' const root = path.join(__dirname, '_temp', 'search') const searchItem1Path = path.join( diff --git a/__tests__/upload.test.ts b/__tests__/upload.test.ts new file mode 100644 index 0000000..0b74e66 --- /dev/null +++ b/__tests__/upload.test.ts @@ -0,0 +1,231 @@ +import * as core from '@actions/core' +import * as github from '@actions/github' +import artifact, {ArtifactNotFoundError} from '@actions/artifact' +import {run} from '../src/upload/upload-artifact' +import {Inputs} from '../src/upload/constants' +import * as search from '../src/shared/search' + +const fixtures = { + artifactName: 'artifact-name', + rootDirectory: '/some/artifact/path', + filesToUpload: [ + '/some/artifact/path/file1.txt', + '/some/artifact/path/file2.txt' + ] +} + +jest.mock('@actions/github', () => ({ + context: { + repo: { + owner: 'actions', + repo: 'toolkit' + }, + runId: 123, + serverUrl: 'https://github.com' + } +})) + +jest.mock('@actions/core') + +/* eslint-disable no-unused-vars */ +const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => { + const inputs = { + [Inputs.Name]: 'artifact-name', + [Inputs.Path]: '/some/artifact/path', + [Inputs.IfNoFilesFound]: 'warn', + [Inputs.RetentionDays]: 0, + [Inputs.CompressionLevel]: 6, + [Inputs.Overwrite]: false, + ...overrides + } + + ;(core.getInput as jest.Mock).mockImplementation((name: string) => { + return inputs[name] + }) + ;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => { + return inputs[name] + }) + + return inputs +} + +describe('upload', () => { + beforeEach(async () => { + mockInputs() + + jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({ + filesToUpload: fixtures.filesToUpload, + rootDirectory: fixtures.rootDirectory + }) + + jest.spyOn(artifact, 'uploadArtifact').mockResolvedValue({ + size: 123, + id: 1337 + }) + }) + + it('uploads a single file', async () => { + jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({ + filesToUpload: [fixtures.filesToUpload[0]], + rootDirectory: fixtures.rootDirectory + }) + + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + [fixtures.filesToUpload[0]], + fixtures.rootDirectory, + {compressionLevel: 6} + ) + }) + + it('uploads multiple files', async () => { + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + fixtures.filesToUpload, + fixtures.rootDirectory, + {compressionLevel: 6} + ) + }) + + it('sets outputs', async () => { + await run() + + expect(core.setOutput).toHaveBeenCalledWith('artifact-id', 1337) + expect(core.setOutput).toHaveBeenCalledWith( + 'artifact-url', + `${github.context.serverUrl}/${github.context.repo.owner}/${ + github.context.repo.repo + }/actions/runs/${github.context.runId}/artifacts/${1337}` + ) + }) + + it('supports custom compression level', async () => { + mockInputs({ + [Inputs.CompressionLevel]: 2 + }) + + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + fixtures.filesToUpload, + fixtures.rootDirectory, + {compressionLevel: 2} + ) + }) + + it('supports custom retention days', async () => { + mockInputs({ + [Inputs.RetentionDays]: 7 + }) + + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + fixtures.filesToUpload, + fixtures.rootDirectory, + {retentionDays: 7, compressionLevel: 6} + ) + }) + + it('supports warn if-no-files-found', async () => { + mockInputs({ + [Inputs.IfNoFilesFound]: 'warn' + }) + + jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({ + filesToUpload: [], + rootDirectory: fixtures.rootDirectory + }) + + await run() + + expect(core.warning).toHaveBeenCalledWith( + `No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.` + ) + }) + + it('supports error if-no-files-found', async () => { + mockInputs({ + [Inputs.IfNoFilesFound]: 'error' + }) + + jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({ + filesToUpload: [], + rootDirectory: fixtures.rootDirectory + }) + + await run() + + expect(core.setFailed).toHaveBeenCalledWith( + `No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.` + ) + }) + + it('supports ignore if-no-files-found', async () => { + mockInputs({ + [Inputs.IfNoFilesFound]: 'ignore' + }) + + jest.spyOn(search, 'findFilesToUpload').mockResolvedValue({ + filesToUpload: [], + rootDirectory: fixtures.rootDirectory + }) + + await run() + + expect(core.info).toHaveBeenCalledWith( + `No files were found with the provided path: ${fixtures.rootDirectory}. No artifacts will be uploaded.` + ) + }) + + it('supports overwrite', async () => { + mockInputs({ + [Inputs.Overwrite]: true + }) + + jest.spyOn(artifact, 'deleteArtifact').mockResolvedValue({ + id: 1337 + }) + + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + fixtures.filesToUpload, + fixtures.rootDirectory, + {compressionLevel: 6} + ) + + expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName) + }) + + it('supports overwrite and continues if not found', async () => { + mockInputs({ + [Inputs.Overwrite]: true + }) + + jest + .spyOn(artifact, 'deleteArtifact') + .mockRejectedValue(new ArtifactNotFoundError('not found')) + + await run() + + expect(artifact.uploadArtifact).toHaveBeenCalledWith( + fixtures.artifactName, + fixtures.filesToUpload, + fixtures.rootDirectory, + {compressionLevel: 6} + ) + + expect(artifact.deleteArtifact).toHaveBeenCalledWith(fixtures.artifactName) + expect(core.debug).toHaveBeenCalledWith( + `Skipping deletion of '${fixtures.artifactName}', it does not exist` + ) + }) +}) diff --git a/action.yml b/action.yml index 7cb5ad9..38d4fdc 100644 --- a/action.yml +++ b/action.yml @@ -58,4 +58,4 @@ outputs: Common uses cases for such a download URL can be adding download links to artifacts in descriptions or comments on pull requests or issues. runs: using: 'node20' - main: 'dist/index.js' + main: 'dist/upload/index.js' diff --git a/dist/index.js b/dist/upload/index.js similarity index 99% rename from dist/index.js rename to dist/upload/index.js index 4a0e9c2..249d6cf 100644 --- a/dist/index.js +++ b/dist/upload/index.js @@ -127068,117 +127068,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/***/ 69042: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.NoFileOptions = exports.Inputs = void 0; -/* eslint-disable no-unused-vars */ -var Inputs; -(function (Inputs) { - Inputs["Name"] = "name"; - Inputs["Path"] = "path"; - Inputs["IfNoFilesFound"] = "if-no-files-found"; - Inputs["RetentionDays"] = "retention-days"; - Inputs["CompressionLevel"] = "compression-level"; - Inputs["Overwrite"] = "overwrite"; -})(Inputs = exports.Inputs || (exports.Inputs = {})); -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 = {})); - - -/***/ }), - -/***/ 46455: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getInputs = void 0; -const core = __importStar(__nccwpck_require__(42186)); -const constants_1 = __nccwpck_require__(69042); -/** - * 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 overwrite = core.getBooleanInput(constants_1.Inputs.Overwrite); - 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)}`); - } - const inputs = { - artifactName: name, - searchPath: path, - ifNoFilesFound: noFileBehavior, - overwrite: overwrite - }; - const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays); - if (retentionDaysStr) { - inputs.retentionDays = parseInt(retentionDaysStr); - if (isNaN(inputs.retentionDays)) { - core.setFailed('Invalid retention-days'); - } - } - const compressionLevelStr = core.getInput(constants_1.Inputs.CompressionLevel); - if (compressionLevelStr) { - inputs.compressionLevel = parseInt(compressionLevelStr); - if (isNaN(inputs.compressionLevel)) { - core.setFailed('Invalid compression-level'); - } - if (inputs.compressionLevel < 0 || inputs.compressionLevel > 9) { - core.setFailed('Invalid compression-level. Valid values are 0-9'); - } - } - return inputs; -} -exports.getInputs = getInputs; - - -/***/ }), - -/***/ 13930: +/***/ 8725: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -127346,7 +127236,216 @@ exports.findFilesToUpload = findFilesToUpload; /***/ }), -/***/ 10334: +/***/ 56680: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.uploadArtifact = void 0; +const core = __importStar(__nccwpck_require__(42186)); +const github = __importStar(__nccwpck_require__(95438)); +const artifact_1 = __importDefault(__nccwpck_require__(79450)); +function uploadArtifact(artifactName, filesToUpload, rootDirectory, options) { + return __awaiter(this, void 0, void 0, function* () { + const uploadResponse = yield artifact_1.default.uploadArtifact(artifactName, filesToUpload, rootDirectory, options); + core.info(`Artifact ${artifactName} has been successfully uploaded! Final size is ${uploadResponse.size} bytes. Artifact ID is ${uploadResponse.id}`); + core.setOutput('artifact-id', uploadResponse.id); + const repository = github.context.repo; + const artifactURL = `${github.context.serverUrl}/${repository.owner}/${repository.repo}/actions/runs/${github.context.runId}/artifacts/${uploadResponse.id}`; + core.info(`Artifact download URL: ${artifactURL}`); + core.setOutput('artifact-url', artifactURL); + }); +} +exports.uploadArtifact = uploadArtifact; + + +/***/ }), + +/***/ 86154: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.NoFileOptions = exports.Inputs = void 0; +/* eslint-disable no-unused-vars */ +var Inputs; +(function (Inputs) { + Inputs["Name"] = "name"; + Inputs["Path"] = "path"; + Inputs["IfNoFilesFound"] = "if-no-files-found"; + Inputs["RetentionDays"] = "retention-days"; + Inputs["CompressionLevel"] = "compression-level"; + Inputs["Overwrite"] = "overwrite"; +})(Inputs = exports.Inputs || (exports.Inputs = {})); +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 = {})); + + +/***/ }), + +/***/ 36664: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const core = __importStar(__nccwpck_require__(42186)); +const upload_artifact_1 = __nccwpck_require__(18569); +(0, upload_artifact_1.run)().catch(error => { + core.setFailed(error.message); +}); + + +/***/ }), + +/***/ 67022: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getInputs = void 0; +const core = __importStar(__nccwpck_require__(42186)); +const constants_1 = __nccwpck_require__(86154); +/** + * 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 overwrite = core.getBooleanInput(constants_1.Inputs.Overwrite); + 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)}`); + } + const inputs = { + artifactName: name, + searchPath: path, + ifNoFilesFound: noFileBehavior, + overwrite: overwrite + }; + const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays); + if (retentionDaysStr) { + inputs.retentionDays = parseInt(retentionDaysStr); + if (isNaN(inputs.retentionDays)) { + core.setFailed('Invalid retention-days'); + } + } + const compressionLevelStr = core.getInput(constants_1.Inputs.CompressionLevel); + if (compressionLevelStr) { + inputs.compressionLevel = parseInt(compressionLevelStr); + if (isNaN(inputs.compressionLevel)) { + core.setFailed('Invalid compression-level'); + } + if (inputs.compressionLevel < 0 || inputs.compressionLevel > 9) { + core.setFailed('Invalid compression-level. Valid values are 0-9'); + } + } + return inputs; +} +exports.getInputs = getInputs; + + +/***/ }), + +/***/ 18569: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -127384,12 +127483,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.run = void 0; const core = __importStar(__nccwpck_require__(42186)); -const github = __importStar(__nccwpck_require__(95438)); const artifact_1 = __importStar(__nccwpck_require__(79450)); -const search_1 = __nccwpck_require__(13930); -const input_helper_1 = __nccwpck_require__(46455); -const constants_1 = __nccwpck_require__(69042); +const search_1 = __nccwpck_require__(8725); +const input_helper_1 = __nccwpck_require__(67022); +const constants_1 = __nccwpck_require__(86154); +const upload_artifact_1 = __nccwpck_require__(56680); function deleteArtifactIfExists(artifactName) { return __awaiter(this, void 0, void 0, function* () { try { @@ -127407,55 +127507,44 @@ function deleteArtifactIfExists(artifactName) { } function run() { return __awaiter(this, void 0, void 0, function* () { - try { - const inputs = (0, input_helper_1.getInputs)(); - const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath); - if (searchResult.filesToUpload.length === 0) { - // 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; - } + const inputs = (0, input_helper_1.getInputs)(); + const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath); + if (searchResult.filesToUpload.length === 0) { + // 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; } - } - else { - const s = searchResult.filesToUpload.length === 1 ? '' : 's'; - core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} file${s} uploaded`); - core.debug(`Root artifact directory is ${searchResult.rootDirectory}`); - if (inputs.overwrite) { - yield deleteArtifactIfExists(inputs.artifactName); + case constants_1.NoFileOptions.error: { + core.setFailed(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`); + break; } - const options = {}; - if (inputs.retentionDays) { - options.retentionDays = inputs.retentionDays; + case constants_1.NoFileOptions.ignore: { + core.info(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`); + break; } - if (typeof inputs.compressionLevel !== 'undefined') { - options.compressionLevel = inputs.compressionLevel; - } - const uploadResponse = yield artifact_1.default.uploadArtifact(inputs.artifactName, searchResult.filesToUpload, searchResult.rootDirectory, options); - core.info(`Artifact ${inputs.artifactName} has been successfully uploaded! Final size is ${uploadResponse.size} bytes. Artifact ID is ${uploadResponse.id}`); - core.setOutput('artifact-id', uploadResponse.id); - const repository = github.context.repo; - const artifactURL = `${github.context.serverUrl}/${repository.owner}/${repository.repo}/actions/runs/${github.context.runId}/artifacts/${uploadResponse.id}`; - core.info(`Artifact download URL: ${artifactURL}`); - core.setOutput('artifact-url', artifactURL); } } - catch (error) { - core.setFailed(error.message); + else { + const s = searchResult.filesToUpload.length === 1 ? '' : 's'; + core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} file${s} uploaded`); + core.debug(`Root artifact directory is ${searchResult.rootDirectory}`); + if (inputs.overwrite) { + yield deleteArtifactIfExists(inputs.artifactName); + } + const options = {}; + if (inputs.retentionDays) { + options.retentionDays = inputs.retentionDays; + } + if (typeof inputs.compressionLevel !== 'undefined') { + options.compressionLevel = inputs.compressionLevel; + } + yield (0, upload_artifact_1.uploadArtifact)(inputs.artifactName, searchResult.filesToUpload, searchResult.rootDirectory, options); } }); } -run(); +exports.run = run; /***/ }), @@ -129409,7 +129498,7 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(10334); +/******/ var __webpack_exports__ = __nccwpck_require__(36664); /******/ module.exports = __webpack_exports__; /******/ /******/ })() diff --git a/package.json b/package.json index a360d89..40c9c39 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "upload-artifact", "version": "4.2.0", "description": "Upload an Actions Artifact in a workflow run", - "main": "dist/index.js", + "main": "dist/upload/index.js", "scripts": { "build": "tsc", - "release": "ncc build src/upload-artifact.ts && git add -f dist/index.js", + "release": "ncc build src/upload/index.ts -o dist/upload", "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build\"", "format": "prettier --write **/*.ts", "format-check": "prettier --check **/*.ts", diff --git a/src/search.ts b/src/shared/search.ts similarity index 100% rename from src/search.ts rename to src/shared/search.ts diff --git a/src/shared/upload-artifact.ts b/src/shared/upload-artifact.ts new file mode 100644 index 0000000..e8ece24 --- /dev/null +++ b/src/shared/upload-artifact.ts @@ -0,0 +1,28 @@ +import * as core from '@actions/core' +import * as github from '@actions/github' +import artifact, {UploadArtifactOptions} from '@actions/artifact' + +export async function uploadArtifact( + artifactName: string, + filesToUpload: string[], + rootDirectory: string, + options: UploadArtifactOptions +) { + const uploadResponse = await artifact.uploadArtifact( + artifactName, + filesToUpload, + rootDirectory, + options + ) + + core.info( + `Artifact ${artifactName} has been successfully uploaded! Final size is ${uploadResponse.size} bytes. Artifact ID is ${uploadResponse.id}` + ) + core.setOutput('artifact-id', uploadResponse.id) + + const repository = github.context.repo + const artifactURL = `${github.context.serverUrl}/${repository.owner}/${repository.repo}/actions/runs/${github.context.runId}/artifacts/${uploadResponse.id}` + + core.info(`Artifact download URL: ${artifactURL}`) + core.setOutput('artifact-url', artifactURL) +} diff --git a/src/upload-artifact.ts b/src/upload-artifact.ts deleted file mode 100644 index 8d99a68..0000000 --- a/src/upload-artifact.ts +++ /dev/null @@ -1,94 +0,0 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' -import artifact, { - UploadArtifactOptions, - ArtifactNotFoundError -} from '@actions/artifact' -import {findFilesToUpload} from './search' -import {getInputs} from './input-helper' -import {NoFileOptions} from './constants' - -async function deleteArtifactIfExists(artifactName: string): Promise { - try { - await artifact.deleteArtifact(artifactName) - } catch (error) { - if (error instanceof ArtifactNotFoundError) { - core.debug(`Skipping deletion of '${artifactName}', it does not exist`) - return - } - - // Best effort, we don't want to fail the action if this fails - core.debug(`Unable to delete artifact: ${(error as Error).message}`) - } -} - -async function run(): Promise { - try { - const inputs = getInputs() - const searchResult = await findFilesToUpload(inputs.searchPath) - if (searchResult.filesToUpload.length === 0) { - // 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 { - const s = searchResult.filesToUpload.length === 1 ? '' : 's' - core.info( - `With the provided path, there will be ${searchResult.filesToUpload.length} file${s} uploaded` - ) - core.debug(`Root artifact directory is ${searchResult.rootDirectory}`) - - if (inputs.overwrite) { - await deleteArtifactIfExists(inputs.artifactName) - } - - const options: UploadArtifactOptions = {} - if (inputs.retentionDays) { - options.retentionDays = inputs.retentionDays - } - - if (typeof inputs.compressionLevel !== 'undefined') { - options.compressionLevel = inputs.compressionLevel - } - - const uploadResponse = await artifact.uploadArtifact( - inputs.artifactName, - searchResult.filesToUpload, - searchResult.rootDirectory, - options - ) - - core.info( - `Artifact ${inputs.artifactName} has been successfully uploaded! Final size is ${uploadResponse.size} bytes. Artifact ID is ${uploadResponse.id}` - ) - core.setOutput('artifact-id', uploadResponse.id) - - const repository = github.context.repo - const artifactURL = `${github.context.serverUrl}/${repository.owner}/${repository.repo}/actions/runs/${github.context.runId}/artifacts/${uploadResponse.id}` - - core.info(`Artifact download URL: ${artifactURL}`) - core.setOutput('artifact-url', artifactURL) - } - } catch (error) { - core.setFailed((error as Error).message) - } -} - -run() diff --git a/src/constants.ts b/src/upload/constants.ts similarity index 100% rename from src/constants.ts rename to src/upload/constants.ts diff --git a/src/upload/index.ts b/src/upload/index.ts new file mode 100644 index 0000000..16f4e8c --- /dev/null +++ b/src/upload/index.ts @@ -0,0 +1,6 @@ +import * as core from '@actions/core' +import {run} from './upload-artifact' + +run().catch(error => { + core.setFailed((error as Error).message) +}) diff --git a/src/input-helper.ts b/src/upload/input-helper.ts similarity index 100% rename from src/input-helper.ts rename to src/upload/input-helper.ts diff --git a/src/upload/upload-artifact.ts b/src/upload/upload-artifact.ts new file mode 100644 index 0000000..8c77543 --- /dev/null +++ b/src/upload/upload-artifact.ts @@ -0,0 +1,77 @@ +import * as core from '@actions/core' +import artifact, { + UploadArtifactOptions, + ArtifactNotFoundError +} from '@actions/artifact' +import {findFilesToUpload} from '../shared/search' +import {getInputs} from './input-helper' +import {NoFileOptions} from './constants' +import {uploadArtifact} from '../shared/upload-artifact' + +async function deleteArtifactIfExists(artifactName: string): Promise { + try { + await artifact.deleteArtifact(artifactName) + } catch (error) { + if (error instanceof ArtifactNotFoundError) { + core.debug(`Skipping deletion of '${artifactName}', it does not exist`) + return + } + + // Best effort, we don't want to fail the action if this fails + core.debug(`Unable to delete artifact: ${(error as Error).message}`) + } +} + +export async function run(): Promise { + const inputs = getInputs() + const searchResult = await findFilesToUpload(inputs.searchPath) + if (searchResult.filesToUpload.length === 0) { + // 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 { + const s = searchResult.filesToUpload.length === 1 ? '' : 's' + core.info( + `With the provided path, there will be ${searchResult.filesToUpload.length} file${s} uploaded` + ) + core.debug(`Root artifact directory is ${searchResult.rootDirectory}`) + + if (inputs.overwrite) { + await deleteArtifactIfExists(inputs.artifactName) + } + + const options: UploadArtifactOptions = {} + if (inputs.retentionDays) { + options.retentionDays = inputs.retentionDays + } + + if (typeof inputs.compressionLevel !== 'undefined') { + options.compressionLevel = inputs.compressionLevel + } + + await uploadArtifact( + inputs.artifactName, + searchResult.filesToUpload, + searchResult.rootDirectory, + options + ) + } +} diff --git a/src/upload-inputs.ts b/src/upload/upload-inputs.ts similarity index 100% rename from src/upload-inputs.ts rename to src/upload/upload-inputs.ts