diff --git a/README.md b/README.md index a4f4c7f..e74500d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ See also [download-artifact](https://github.com/actions/download-artifact). - [Breaking Changes](#breaking-changes) - [Usage](#usage) - [Inputs](#inputs) + - [Uploading the `.git` directory](#uploading-the-git-directory) - [Outputs](#outputs) - [Examples](#examples) - [Upload an Individual File](#upload-an-individual-file) @@ -64,6 +65,7 @@ There is also a new sub-action, `actions/upload-artifact/merge`. For more info, Due to how Artifacts are created in this new version, it is no longer possible to upload to the same named Artifact multiple times. You must either split the uploads into multiple Artifacts with different names, or only upload once. Otherwise you _will_ encounter an error. 3. Limit of Artifacts for an individual job. Each job in a workflow run now has a limit of 500 artifacts. +4. With `v4.4` and later, the `.git` directory is excluded by default. For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md). @@ -109,6 +111,30 @@ For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md). overwrite: ``` +#### Uploading the `.git` directory + +By default, files in a `.git` directory are ignored in the uploaded artifact. +This is intended to prevent accidentally uploading Git credentials into an artifact that could then +be extracted. +If files in the `.git` directory are needed, ensure that `actions/checkout` is being used with +`persist-credentials: false`. + +```yaml +jobs: + upload: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false # Ensure credentials are not saved in `.git/config` + + - uses: actions/upload-artifact@v4 + with: + path: . + include-git-directory: true +``` + ### Outputs | Name | Description | Example | diff --git a/__tests__/search.test.ts b/__tests__/search.test.ts index e0ab26d..dd87efe 100644 --- a/__tests__/search.test.ts +++ b/__tests__/search.test.ts @@ -61,6 +61,12 @@ const lonelyFilePath = path.join( 'lonely-file.txt' ) +const gitConfigPath = path.join(root, '.git', 'config') +const gitHeadPath = path.join(root, '.git', 'HEAD') + +const nestedGitConfigPath = path.join(root, 'repository-name', '.git', 'config') +const nestedGitHeadPath = path.join(root, 'repository-name', '.git', 'HEAD') + describe('Search', () => { beforeAll(async () => { // mock all output so that there is less noise when running tests @@ -93,6 +99,11 @@ describe('Search', () => { recursive: true }) + await fs.mkdir(path.join(root, '.git')) + await fs.mkdir(path.join(root, 'repository-name', '.git'), { + recursive: true + }) + await fs.writeFile(searchItem1Path, 'search item1 file') await fs.writeFile(searchItem2Path, 'search item2 file') await fs.writeFile(searchItem3Path, 'search item3 file') @@ -110,9 +121,17 @@ describe('Search', () => { await fs.writeFile(amazingFileInFolderHPath, 'amazing file') await fs.writeFile(lonelyFilePath, 'all by itself') + + await fs.writeFile(gitConfigPath, 'git config file') + await fs.writeFile(gitHeadPath, 'git head file') + await fs.writeFile(nestedGitConfigPath, 'nested git config file') + await fs.writeFile(nestedGitHeadPath, 'nested git head file') /* Directory structure of files that get created: root/ + .git/ + config + HEAD folder-a/ folder-b/ folder-c/ @@ -136,6 +155,10 @@ describe('Search', () => { folder-j/ folder-k/ lonely-file.txt + repository-name/ + .git/ + config + HEAD search-item5.txt */ }) @@ -352,4 +375,18 @@ describe('Search', () => { ) expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true) }) + + it('Excludes .git directory by default', async () => { + const searchResult = await findFilesToUpload(root) + expect(searchResult.filesToUpload.length).toEqual(13) + expect(searchResult.filesToUpload).not.toContain(gitConfigPath) + }) + + it('Includes .git directory when includeGitDirectory is true', async () => { + const searchResult = await findFilesToUpload(root, { + includeGitDirectory: true + }) + expect(searchResult.filesToUpload.length).toEqual(17) + expect(searchResult.filesToUpload).toContain(gitConfigPath) + }) }) diff --git a/action.yml b/action.yml index 38d4fdc..5108415 100644 --- a/action.yml +++ b/action.yml @@ -40,6 +40,9 @@ inputs: If false, the action will fail if an artifact for the given name already exists. Does not fail if the artifact does not exist. default: 'false' + include-git-directory: + description: 'Include files in the .git directory in the artifact.' + default: 'false' outputs: artifact-id: diff --git a/dist/merge/index.js b/dist/merge/index.js index c09bb45..0946687 100644 --- a/dist/merge/index.js +++ b/dist/merge/index.js @@ -8881,16 +8881,18 @@ exports.create = create; * Computes the sha256 hash of a glob * * @param patterns Patterns separated by newlines + * @param currentWorkspace Workspace used when matching files * @param options Glob options + * @param verbose Enables verbose logging */ -function hashFiles(patterns, options, verbose = false) { +function hashFiles(patterns, currentWorkspace = '', options, verbose = false) { return __awaiter(this, void 0, void 0, function* () { let followSymbolicLinks = true; if (options && typeof options.followSymbolicLinks === 'boolean') { followSymbolicLinks = options.followSymbolicLinks; } const globber = yield create(patterns, { followSymbolicLinks }); - return internal_hash_files_1.hashFiles(globber, verbose); + return (0, internal_hash_files_1.hashFiles)(globber, currentWorkspace, verbose); }); } exports.hashFiles = hashFiles; @@ -8905,7 +8907,11 @@ exports.hashFiles = hashFiles; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -8918,7 +8924,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -8933,7 +8939,8 @@ function getOptions(copy) { followSymbolicLinks: true, implicitDescendants: true, matchDirectories: true, - omitBrokenSymbolicLinks: true + omitBrokenSymbolicLinks: true, + excludeHiddenFiles: false }; if (copy) { if (typeof copy.followSymbolicLinks === 'boolean') { @@ -8952,6 +8959,10 @@ function getOptions(copy) { result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks; core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); } + if (typeof copy.excludeHiddenFiles === 'boolean') { + result.excludeHiddenFiles = copy.excludeHiddenFiles; + core.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); + } } return result; } @@ -8967,7 +8978,11 @@ exports.getOptions = getOptions; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -8980,7 +8995,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9034,19 +9049,21 @@ class DefaultGlobber { return this.searchPaths.slice(); } glob() { - var e_1, _a; + var _a, e_1, _b, _c; return __awaiter(this, void 0, void 0, function* () { const result = []; try { - for (var _b = __asyncValues(this.globGenerator()), _c; _c = yield _b.next(), !_c.done;) { - const itemPath = _c.value; + for (var _d = true, _e = __asyncValues(this.globGenerator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) { + _c = _f.value; + _d = false; + const itemPath = _c; result.push(itemPath); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { - if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); + if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); } finally { if (e_1) throw e_1.error; } } @@ -9104,6 +9121,10 @@ class DefaultGlobber { if (!stats) { continue; } + // Hidden file or directory? + if (options.excludeHiddenFiles && path.basename(item.path).match(/^\./)) { + continue; + } // Directory if (stats.isDirectory()) { // Matched @@ -9209,7 +9230,11 @@ exports.DefaultGlobber = DefaultGlobber; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9222,7 +9247,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9250,18 +9275,22 @@ const fs = __importStar(__nccwpck_require__(57147)); const stream = __importStar(__nccwpck_require__(12781)); const util = __importStar(__nccwpck_require__(73837)); const path = __importStar(__nccwpck_require__(71017)); -function hashFiles(globber, verbose = false) { - var e_1, _a; - var _b; +function hashFiles(globber, currentWorkspace, verbose = false) { + var _a, e_1, _b, _c; + var _d; return __awaiter(this, void 0, void 0, function* () { const writeDelegate = verbose ? core.info : core.debug; let hasMatch = false; - const githubWorkspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); + const githubWorkspace = currentWorkspace + ? currentWorkspace + : (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); const result = crypto.createHash('sha256'); let count = 0; try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) { + _c = _g.value; + _e = false; + const file = _c; writeDelegate(file); if (!file.startsWith(`${githubWorkspace}${path.sep}`)) { writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`); @@ -9284,7 +9313,7 @@ function hashFiles(globber, verbose = false) { catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); } finally { if (e_1) throw e_1.error; } } @@ -9324,7 +9353,7 @@ var MatchKind; MatchKind[MatchKind["File"] = 2] = "File"; /** Matched */ MatchKind[MatchKind["All"] = 3] = "All"; -})(MatchKind = exports.MatchKind || (exports.MatchKind = {})); +})(MatchKind || (exports.MatchKind = MatchKind = {})); //# sourceMappingURL=internal-match-kind.js.map /***/ }), @@ -9336,7 +9365,11 @@ var MatchKind; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9349,7 +9382,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9399,8 +9432,8 @@ exports.dirname = dirname; * or `C:` are expanded based on the current working directory. */ function ensureAbsoluteRoot(root, itemPath) { - assert_1.default(root, `ensureAbsoluteRoot parameter 'root' must not be empty`); - assert_1.default(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`); + (0, assert_1.default)(root, `ensureAbsoluteRoot parameter 'root' must not be empty`); + (0, assert_1.default)(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`); // Already rooted if (hasAbsoluteRoot(itemPath)) { return itemPath; @@ -9410,7 +9443,7 @@ function ensureAbsoluteRoot(root, itemPath) { // Check for itemPath like C: or C:foo if (itemPath.match(/^[A-Z]:[^\\/]|^[A-Z]:$/i)) { let cwd = process.cwd(); - assert_1.default(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); + (0, assert_1.default)(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); // Drive letter matches cwd? Expand to cwd if (itemPath[0].toUpperCase() === cwd[0].toUpperCase()) { // Drive only, e.g. C: @@ -9435,11 +9468,11 @@ function ensureAbsoluteRoot(root, itemPath) { // Check for itemPath like \ or \foo else if (normalizeSeparators(itemPath).match(/^\\$|^\\[^\\]/)) { const cwd = process.cwd(); - assert_1.default(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); + (0, assert_1.default)(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); return `${cwd[0]}:\\${itemPath.substr(1)}`; } } - assert_1.default(hasAbsoluteRoot(root), `ensureAbsoluteRoot parameter 'root' must have an absolute root`); + (0, assert_1.default)(hasAbsoluteRoot(root), `ensureAbsoluteRoot parameter 'root' must have an absolute root`); // Otherwise ensure root ends with a separator if (root.endsWith('/') || (IS_WINDOWS && root.endsWith('\\'))) { // Intentionally empty @@ -9456,7 +9489,7 @@ exports.ensureAbsoluteRoot = ensureAbsoluteRoot; * `\\hello\share` and `C:\hello` (and using alternate separator). */ function hasAbsoluteRoot(itemPath) { - assert_1.default(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`); // Normalize separators itemPath = normalizeSeparators(itemPath); // Windows @@ -9473,7 +9506,7 @@ exports.hasAbsoluteRoot = hasAbsoluteRoot; * `\`, `\hello`, `\\hello\share`, `C:`, and `C:\hello` (and using alternate separator). */ function hasRoot(itemPath) { - assert_1.default(itemPath, `isRooted parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `isRooted parameter 'itemPath' must not be empty`); // Normalize separators itemPath = normalizeSeparators(itemPath); // Windows @@ -9541,7 +9574,11 @@ exports.safeTrimTrailingSeparator = safeTrimTrailingSeparator; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9554,7 +9591,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9579,7 +9616,7 @@ class Path { this.segments = []; // String if (typeof itemPath === 'string') { - assert_1.default(itemPath, `Parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `Parameter 'itemPath' must not be empty`); // Normalize slashes and trim unnecessary trailing slash itemPath = pathHelper.safeTrimTrailingSeparator(itemPath); // Not rooted @@ -9606,24 +9643,24 @@ class Path { // Array else { // Must not be empty - assert_1.default(itemPath.length > 0, `Parameter 'itemPath' must not be an empty array`); + (0, assert_1.default)(itemPath.length > 0, `Parameter 'itemPath' must not be an empty array`); // Each segment for (let i = 0; i < itemPath.length; i++) { let segment = itemPath[i]; // Must not be empty - assert_1.default(segment, `Parameter 'itemPath' must not contain any empty segments`); + (0, assert_1.default)(segment, `Parameter 'itemPath' must not contain any empty segments`); // Normalize slashes segment = pathHelper.normalizeSeparators(itemPath[i]); // Root segment if (i === 0 && pathHelper.hasRoot(segment)) { segment = pathHelper.safeTrimTrailingSeparator(segment); - assert_1.default(segment === pathHelper.dirname(segment), `Parameter 'itemPath' root segment contains information for multiple segments`); + (0, assert_1.default)(segment === pathHelper.dirname(segment), `Parameter 'itemPath' root segment contains information for multiple segments`); this.segments.push(segment); } // All other segments else { // Must not contain slash - assert_1.default(!segment.includes(path.sep), `Parameter 'itemPath' contains unexpected path separators`); + (0, assert_1.default)(!segment.includes(path.sep), `Parameter 'itemPath' contains unexpected path separators`); this.segments.push(segment); } } @@ -9661,7 +9698,11 @@ exports.Path = Path; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9674,7 +9715,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9762,7 +9803,11 @@ exports.partialMatch = partialMatch; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9775,7 +9820,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9807,9 +9852,9 @@ class Pattern { else { // Convert to pattern segments = segments || []; - assert_1.default(segments.length, `Parameter 'segments' must not empty`); + (0, assert_1.default)(segments.length, `Parameter 'segments' must not empty`); const root = Pattern.getLiteral(segments[0]); - assert_1.default(root && pathHelper.hasAbsoluteRoot(root), `Parameter 'segments' first element must be a root path`); + (0, assert_1.default)(root && pathHelper.hasAbsoluteRoot(root), `Parameter 'segments' first element must be a root path`); pattern = new internal_path_1.Path(segments).toString().trim(); if (patternOrNegate) { pattern = `!${pattern}`; @@ -9903,13 +9948,13 @@ class Pattern { */ static fixupPattern(pattern, homedir) { // Empty - assert_1.default(pattern, 'pattern cannot be empty'); + (0, assert_1.default)(pattern, 'pattern cannot be empty'); // Must not contain `.` segment, unless first segment // Must not contain `..` segment const literalSegments = new internal_path_1.Path(pattern).segments.map(x => Pattern.getLiteral(x)); - assert_1.default(literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.`); + (0, assert_1.default)(literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.`); // Must not contain globs in root, e.g. Windows UNC path \\foo\b*r - assert_1.default(!pathHelper.hasRoot(pattern) || literalSegments[0], `Invalid pattern '${pattern}'. Root segment must not contain globs.`); + (0, assert_1.default)(!pathHelper.hasRoot(pattern) || literalSegments[0], `Invalid pattern '${pattern}'. Root segment must not contain globs.`); // Normalize slashes pattern = pathHelper.normalizeSeparators(pattern); // Replace leading `.` segment @@ -9919,8 +9964,8 @@ class Pattern { // Replace leading `~` segment else if (pattern === '~' || pattern.startsWith(`~${path.sep}`)) { homedir = homedir || os.homedir(); - assert_1.default(homedir, 'Unable to determine HOME directory'); - assert_1.default(pathHelper.hasAbsoluteRoot(homedir), `Expected HOME directory to be a rooted path. Actual '${homedir}'`); + (0, assert_1.default)(homedir, 'Unable to determine HOME directory'); + (0, assert_1.default)(pathHelper.hasAbsoluteRoot(homedir), `Expected HOME directory to be a rooted path. Actual '${homedir}'`); pattern = Pattern.globEscape(homedir) + pattern.substr(1); } // Replace relative drive root, e.g. pattern is C: or C:foo @@ -125727,6 +125772,7 @@ var Inputs; Inputs["RetentionDays"] = "retention-days"; Inputs["CompressionLevel"] = "compression-level"; Inputs["DeleteMerged"] = "delete-merged"; + Inputs["IncludeGitDirectory"] = "include-git-directory"; })(Inputs = exports.Inputs || (exports.Inputs = {})); @@ -125810,13 +125856,15 @@ function getInputs() { const pattern = core.getInput(constants_1.Inputs.Pattern, { required: true }); const separateDirectories = core.getBooleanInput(constants_1.Inputs.SeparateDirectories); const deleteMerged = core.getBooleanInput(constants_1.Inputs.DeleteMerged); + const includeGitDirectory = core.getBooleanInput(constants_1.Inputs.IncludeGitDirectory); const inputs = { name, pattern, separateDirectories, deleteMerged, retentionDays: 0, - compressionLevel: 6 + compressionLevel: 6, + includeGitDirectory }; const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays); if (retentionDaysStr) { @@ -125932,7 +125980,9 @@ function run() { if (typeof inputs.compressionLevel !== 'undefined') { options.compressionLevel = inputs.compressionLevel; } - const searchResult = yield (0, search_1.findFilesToUpload)(tmpDir); + const searchResult = yield (0, search_1.findFilesToUpload)(tmpDir, { + includeGitDirectory: inputs.includeGitDirectory + }); yield (0, upload_artifact_1.uploadArtifact)(inputs.name, searchResult.filesToUpload, searchResult.rootDirectory, options); core.info(`The ${artifacts.length} artifact(s) have been successfully merged!`); if (inputs.deleteMerged) { @@ -126057,10 +126107,10 @@ function getMultiPathLCA(searchPaths) { } return path.join(...commonPaths); } -function findFilesToUpload(searchPath, globOptions) { +function findFilesToUpload(searchPath, searchOptions) { return __awaiter(this, void 0, void 0, function* () { const searchResults = []; - const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions()); + const globber = yield glob.create(searchPath, getDefaultGlobOptions()); const rawSearchResults = yield globber.glob(); /* Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten @@ -126076,6 +126126,10 @@ function findFilesToUpload(searchPath, globOptions) { // isDirectory() returns false for symlinks if using fs.lstat(), make sure to use fs.stat() instead if (!fileStats.isDirectory()) { (0, core_1.debug)(`File:${searchResult} was found using the provided searchPath`); + if (!(searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.includeGitDirectory) && inGitDirectory(searchResult)) { + (0, core_1.debug)(`Ignoring ${searchResult} because it is in the .git directory`); + continue; + } searchResults.push(searchResult); // detect any files that would be overwritten because of case insensitivity if (set.has(searchResult.toLowerCase())) { @@ -126117,6 +126171,16 @@ function findFilesToUpload(searchPath, globOptions) { }); } exports.findFilesToUpload = findFilesToUpload; +function inGitDirectory(filePath) { + // The .git directory is a directory, so we need to check if the file path is a directory + // and if it is a .git directory + for (const part of filePath.split(path.sep)) { + if (part === '.git') { + return true; + } + } + return false; +} /***/ }), diff --git a/dist/upload/index.js b/dist/upload/index.js index b28794f..2913422 100644 --- a/dist/upload/index.js +++ b/dist/upload/index.js @@ -8881,16 +8881,18 @@ exports.create = create; * Computes the sha256 hash of a glob * * @param patterns Patterns separated by newlines + * @param currentWorkspace Workspace used when matching files * @param options Glob options + * @param verbose Enables verbose logging */ -function hashFiles(patterns, options, verbose = false) { +function hashFiles(patterns, currentWorkspace = '', options, verbose = false) { return __awaiter(this, void 0, void 0, function* () { let followSymbolicLinks = true; if (options && typeof options.followSymbolicLinks === 'boolean') { followSymbolicLinks = options.followSymbolicLinks; } const globber = yield create(patterns, { followSymbolicLinks }); - return internal_hash_files_1.hashFiles(globber, verbose); + return (0, internal_hash_files_1.hashFiles)(globber, currentWorkspace, verbose); }); } exports.hashFiles = hashFiles; @@ -8905,7 +8907,11 @@ exports.hashFiles = hashFiles; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -8918,7 +8924,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -8933,7 +8939,8 @@ function getOptions(copy) { followSymbolicLinks: true, implicitDescendants: true, matchDirectories: true, - omitBrokenSymbolicLinks: true + omitBrokenSymbolicLinks: true, + excludeHiddenFiles: false }; if (copy) { if (typeof copy.followSymbolicLinks === 'boolean') { @@ -8952,6 +8959,10 @@ function getOptions(copy) { result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks; core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); } + if (typeof copy.excludeHiddenFiles === 'boolean') { + result.excludeHiddenFiles = copy.excludeHiddenFiles; + core.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); + } } return result; } @@ -8967,7 +8978,11 @@ exports.getOptions = getOptions; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -8980,7 +8995,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9034,19 +9049,21 @@ class DefaultGlobber { return this.searchPaths.slice(); } glob() { - var e_1, _a; + var _a, e_1, _b, _c; return __awaiter(this, void 0, void 0, function* () { const result = []; try { - for (var _b = __asyncValues(this.globGenerator()), _c; _c = yield _b.next(), !_c.done;) { - const itemPath = _c.value; + for (var _d = true, _e = __asyncValues(this.globGenerator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) { + _c = _f.value; + _d = false; + const itemPath = _c; result.push(itemPath); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { - if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); + if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); } finally { if (e_1) throw e_1.error; } } @@ -9104,6 +9121,10 @@ class DefaultGlobber { if (!stats) { continue; } + // Hidden file or directory? + if (options.excludeHiddenFiles && path.basename(item.path).match(/^\./)) { + continue; + } // Directory if (stats.isDirectory()) { // Matched @@ -9209,7 +9230,11 @@ exports.DefaultGlobber = DefaultGlobber; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9222,7 +9247,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9250,18 +9275,22 @@ const fs = __importStar(__nccwpck_require__(57147)); const stream = __importStar(__nccwpck_require__(12781)); const util = __importStar(__nccwpck_require__(73837)); const path = __importStar(__nccwpck_require__(71017)); -function hashFiles(globber, verbose = false) { - var e_1, _a; - var _b; +function hashFiles(globber, currentWorkspace, verbose = false) { + var _a, e_1, _b, _c; + var _d; return __awaiter(this, void 0, void 0, function* () { const writeDelegate = verbose ? core.info : core.debug; let hasMatch = false; - const githubWorkspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); + const githubWorkspace = currentWorkspace + ? currentWorkspace + : (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); const result = crypto.createHash('sha256'); let count = 0; try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) { + _c = _g.value; + _e = false; + const file = _c; writeDelegate(file); if (!file.startsWith(`${githubWorkspace}${path.sep}`)) { writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`); @@ -9284,7 +9313,7 @@ function hashFiles(globber, verbose = false) { catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); } finally { if (e_1) throw e_1.error; } } @@ -9324,7 +9353,7 @@ var MatchKind; MatchKind[MatchKind["File"] = 2] = "File"; /** Matched */ MatchKind[MatchKind["All"] = 3] = "All"; -})(MatchKind = exports.MatchKind || (exports.MatchKind = {})); +})(MatchKind || (exports.MatchKind = MatchKind = {})); //# sourceMappingURL=internal-match-kind.js.map /***/ }), @@ -9336,7 +9365,11 @@ var MatchKind; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9349,7 +9382,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9399,8 +9432,8 @@ exports.dirname = dirname; * or `C:` are expanded based on the current working directory. */ function ensureAbsoluteRoot(root, itemPath) { - assert_1.default(root, `ensureAbsoluteRoot parameter 'root' must not be empty`); - assert_1.default(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`); + (0, assert_1.default)(root, `ensureAbsoluteRoot parameter 'root' must not be empty`); + (0, assert_1.default)(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`); // Already rooted if (hasAbsoluteRoot(itemPath)) { return itemPath; @@ -9410,7 +9443,7 @@ function ensureAbsoluteRoot(root, itemPath) { // Check for itemPath like C: or C:foo if (itemPath.match(/^[A-Z]:[^\\/]|^[A-Z]:$/i)) { let cwd = process.cwd(); - assert_1.default(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); + (0, assert_1.default)(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); // Drive letter matches cwd? Expand to cwd if (itemPath[0].toUpperCase() === cwd[0].toUpperCase()) { // Drive only, e.g. C: @@ -9435,11 +9468,11 @@ function ensureAbsoluteRoot(root, itemPath) { // Check for itemPath like \ or \foo else if (normalizeSeparators(itemPath).match(/^\\$|^\\[^\\]/)) { const cwd = process.cwd(); - assert_1.default(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); + (0, assert_1.default)(cwd.match(/^[A-Z]:\\/i), `Expected current directory to start with an absolute drive root. Actual '${cwd}'`); return `${cwd[0]}:\\${itemPath.substr(1)}`; } } - assert_1.default(hasAbsoluteRoot(root), `ensureAbsoluteRoot parameter 'root' must have an absolute root`); + (0, assert_1.default)(hasAbsoluteRoot(root), `ensureAbsoluteRoot parameter 'root' must have an absolute root`); // Otherwise ensure root ends with a separator if (root.endsWith('/') || (IS_WINDOWS && root.endsWith('\\'))) { // Intentionally empty @@ -9456,7 +9489,7 @@ exports.ensureAbsoluteRoot = ensureAbsoluteRoot; * `\\hello\share` and `C:\hello` (and using alternate separator). */ function hasAbsoluteRoot(itemPath) { - assert_1.default(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`); // Normalize separators itemPath = normalizeSeparators(itemPath); // Windows @@ -9473,7 +9506,7 @@ exports.hasAbsoluteRoot = hasAbsoluteRoot; * `\`, `\hello`, `\\hello\share`, `C:`, and `C:\hello` (and using alternate separator). */ function hasRoot(itemPath) { - assert_1.default(itemPath, `isRooted parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `isRooted parameter 'itemPath' must not be empty`); // Normalize separators itemPath = normalizeSeparators(itemPath); // Windows @@ -9541,7 +9574,11 @@ exports.safeTrimTrailingSeparator = safeTrimTrailingSeparator; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9554,7 +9591,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9579,7 +9616,7 @@ class Path { this.segments = []; // String if (typeof itemPath === 'string') { - assert_1.default(itemPath, `Parameter 'itemPath' must not be empty`); + (0, assert_1.default)(itemPath, `Parameter 'itemPath' must not be empty`); // Normalize slashes and trim unnecessary trailing slash itemPath = pathHelper.safeTrimTrailingSeparator(itemPath); // Not rooted @@ -9606,24 +9643,24 @@ class Path { // Array else { // Must not be empty - assert_1.default(itemPath.length > 0, `Parameter 'itemPath' must not be an empty array`); + (0, assert_1.default)(itemPath.length > 0, `Parameter 'itemPath' must not be an empty array`); // Each segment for (let i = 0; i < itemPath.length; i++) { let segment = itemPath[i]; // Must not be empty - assert_1.default(segment, `Parameter 'itemPath' must not contain any empty segments`); + (0, assert_1.default)(segment, `Parameter 'itemPath' must not contain any empty segments`); // Normalize slashes segment = pathHelper.normalizeSeparators(itemPath[i]); // Root segment if (i === 0 && pathHelper.hasRoot(segment)) { segment = pathHelper.safeTrimTrailingSeparator(segment); - assert_1.default(segment === pathHelper.dirname(segment), `Parameter 'itemPath' root segment contains information for multiple segments`); + (0, assert_1.default)(segment === pathHelper.dirname(segment), `Parameter 'itemPath' root segment contains information for multiple segments`); this.segments.push(segment); } // All other segments else { // Must not contain slash - assert_1.default(!segment.includes(path.sep), `Parameter 'itemPath' contains unexpected path separators`); + (0, assert_1.default)(!segment.includes(path.sep), `Parameter 'itemPath' contains unexpected path separators`); this.segments.push(segment); } } @@ -9661,7 +9698,11 @@ exports.Path = Path; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9674,7 +9715,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9762,7 +9803,11 @@ exports.partialMatch = partialMatch; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[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]; @@ -9775,7 +9820,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + 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; }; @@ -9807,9 +9852,9 @@ class Pattern { else { // Convert to pattern segments = segments || []; - assert_1.default(segments.length, `Parameter 'segments' must not empty`); + (0, assert_1.default)(segments.length, `Parameter 'segments' must not empty`); const root = Pattern.getLiteral(segments[0]); - assert_1.default(root && pathHelper.hasAbsoluteRoot(root), `Parameter 'segments' first element must be a root path`); + (0, assert_1.default)(root && pathHelper.hasAbsoluteRoot(root), `Parameter 'segments' first element must be a root path`); pattern = new internal_path_1.Path(segments).toString().trim(); if (patternOrNegate) { pattern = `!${pattern}`; @@ -9903,13 +9948,13 @@ class Pattern { */ static fixupPattern(pattern, homedir) { // Empty - assert_1.default(pattern, 'pattern cannot be empty'); + (0, assert_1.default)(pattern, 'pattern cannot be empty'); // Must not contain `.` segment, unless first segment // Must not contain `..` segment const literalSegments = new internal_path_1.Path(pattern).segments.map(x => Pattern.getLiteral(x)); - assert_1.default(literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.`); + (0, assert_1.default)(literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.`); // Must not contain globs in root, e.g. Windows UNC path \\foo\b*r - assert_1.default(!pathHelper.hasRoot(pattern) || literalSegments[0], `Invalid pattern '${pattern}'. Root segment must not contain globs.`); + (0, assert_1.default)(!pathHelper.hasRoot(pattern) || literalSegments[0], `Invalid pattern '${pattern}'. Root segment must not contain globs.`); // Normalize slashes pattern = pathHelper.normalizeSeparators(pattern); // Replace leading `.` segment @@ -9919,8 +9964,8 @@ class Pattern { // Replace leading `~` segment else if (pattern === '~' || pattern.startsWith(`~${path.sep}`)) { homedir = homedir || os.homedir(); - assert_1.default(homedir, 'Unable to determine HOME directory'); - assert_1.default(pathHelper.hasAbsoluteRoot(homedir), `Expected HOME directory to be a rooted path. Actual '${homedir}'`); + (0, assert_1.default)(homedir, 'Unable to determine HOME directory'); + (0, assert_1.default)(pathHelper.hasAbsoluteRoot(homedir), `Expected HOME directory to be a rooted path. Actual '${homedir}'`); pattern = Pattern.globEscape(homedir) + pattern.substr(1); } // Replace relative drive root, e.g. pattern is C: or C:foo @@ -125815,10 +125860,10 @@ function getMultiPathLCA(searchPaths) { } return path.join(...commonPaths); } -function findFilesToUpload(searchPath, globOptions) { +function findFilesToUpload(searchPath, searchOptions) { return __awaiter(this, void 0, void 0, function* () { const searchResults = []; - const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions()); + const globber = yield glob.create(searchPath, getDefaultGlobOptions()); const rawSearchResults = yield globber.glob(); /* Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten @@ -125834,6 +125879,10 @@ function findFilesToUpload(searchPath, globOptions) { // isDirectory() returns false for symlinks if using fs.lstat(), make sure to use fs.stat() instead if (!fileStats.isDirectory()) { (0, core_1.debug)(`File:${searchResult} was found using the provided searchPath`); + if (!(searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.includeGitDirectory) && inGitDirectory(searchResult)) { + (0, core_1.debug)(`Ignoring ${searchResult} because it is in the .git directory`); + continue; + } searchResults.push(searchResult); // detect any files that would be overwritten because of case insensitivity if (set.has(searchResult.toLowerCase())) { @@ -125875,6 +125924,16 @@ function findFilesToUpload(searchPath, globOptions) { }); } exports.findFilesToUpload = findFilesToUpload; +function inGitDirectory(filePath) { + // The .git directory is a directory, so we need to check if the file path is a directory + // and if it is a .git directory + for (const part of filePath.split(path.sep)) { + if (part === '.git') { + return true; + } + } + return false; +} /***/ }), @@ -125956,6 +126015,7 @@ var Inputs; Inputs["RetentionDays"] = "retention-days"; Inputs["CompressionLevel"] = "compression-level"; Inputs["Overwrite"] = "overwrite"; + Inputs["IncludeGitDirectory"] = "include-git-directory"; })(Inputs = exports.Inputs || (exports.Inputs = {})); var NoFileOptions; (function (NoFileOptions) { @@ -126053,6 +126113,7 @@ 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 includeGitDirectory = core.getBooleanInput(constants_1.Inputs.IncludeGitDirectory); const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound); const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound]; if (!noFileBehavior) { @@ -126062,7 +126123,8 @@ function getInputs() { artifactName: name, searchPath: path, ifNoFilesFound: noFileBehavior, - overwrite: overwrite + overwrite: overwrite, + includeGitDirectory: includeGitDirectory }; const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays); if (retentionDaysStr) { @@ -126151,7 +126213,9 @@ function deleteArtifactIfExists(artifactName) { function run() { return __awaiter(this, void 0, void 0, function* () { const inputs = (0, input_helper_1.getInputs)(); - const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath); + const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath, { + includeGitDirectory: inputs.includeGitDirectory + }); if (searchResult.filesToUpload.length === 0) { // No files were found, different use cases warrant different types of behavior if nothing is found switch (inputs.ifNoFilesFound) { diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 1c656fc..8a68a50 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -207,3 +207,41 @@ jobs: ``` Note that this will download all artifacts to a temporary directory and reupload them as a single artifact. For more information on inputs and other use cases for `actions/upload-artifact/merge@v4`, see [the action documentation](../merge/README.md). + +## `.git` Directory + +By default, files in the `.git` directory are ignored to avoid unintentionally uploading +credentials. + +In versions of this action before `v4.4.0`, files in the `.git` directory were included by default. +If this directory is required, ensure credentials are not saved in `.git/config` and then +enable the `include-git-directory` input. + +```yaml +jobs: + upload: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Upload Artifact + uses: actions/upload-artifact@v3 + with: + path: . +``` + + +```diff +jobs: + upload: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 ++ with: ++ persist-credentials: false + - name: Upload Artifact +- uses: actions/upload-artifact@v3 ++ uses: actions/upload-artifact@v4 + with: + path: . ++ include-git-directory: true +``` \ No newline at end of file diff --git a/merge/README.md b/merge/README.md index b2f390e..a60ca2c 100644 --- a/merge/README.md +++ b/merge/README.md @@ -5,6 +5,7 @@ Merge multiple [Actions Artifacts](https://docs.github.com/en/actions/using-work - [`@actions/upload-artifact/merge`](#actionsupload-artifactmerge) - [Usage](#usage) - [Inputs](#inputs) + - [Uploading the `.git` directory](#uploading-the-git-directory) - [Outputs](#outputs) - [Examples](#examples) - [Combining all artifacts in a workflow run](#combining-all-artifacts-in-a-workflow-run) @@ -59,6 +60,44 @@ For most cases, this may not be the most efficient solution. See [the migration compression-level: ``` +#### Uploading the `.git` directory + +By default, files in a `.git` directory are ignored in the merged artifact. +This is intended to prevent accidentally uploading Git credentials into an artifact that could then +be extracted. +If files in the `.git` directory are needed, ensure that `actions/checkout` is being used with +`persist-credentials: false`. + +```yaml +jobs: + upload: + runs-on: ubuntu-latest + + strategy: + matrix: + foo: [a, b, c] + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false # Ensure credentials are not saved in `.git/config` + + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: my-artifact-${{ matrix.foo }} + path: . + include-git-directory: true + + merge: + runs-on: ubuntu-latest + + steps: + - uses: actions/upload-artifact/merge@v4 + with: + include-git-directory: true +``` + ### Outputs | Name | Description | Example | diff --git a/merge/action.yml b/merge/action.yml index 8d85864..eb25765 100644 --- a/merge/action.yml +++ b/merge/action.yml @@ -36,6 +36,9 @@ inputs: If true, the artifacts that were merged will be deleted. If false, the artifacts will still exist. default: 'false' + include-git-directory: + description: 'Include files in the .git directory in the merged artifact.' + default: 'false' outputs: artifact-id: diff --git a/package.json b/package.json index 8f51092..7219abe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "upload-artifact", - "version": "4.3.6", + "version": "4.4.0", "description": "Upload an Actions Artifact in a workflow run", "main": "dist/upload/index.js", "scripts": { diff --git a/src/merge/constants.ts b/src/merge/constants.ts index 8bc9539..1586f1c 100644 --- a/src/merge/constants.ts +++ b/src/merge/constants.ts @@ -5,5 +5,6 @@ export enum Inputs { SeparateDirectories = 'separate-directories', RetentionDays = 'retention-days', CompressionLevel = 'compression-level', - DeleteMerged = 'delete-merged' + DeleteMerged = 'delete-merged', + IncludeGitDirectory = 'include-git-directory' } diff --git a/src/merge/input-helper.ts b/src/merge/input-helper.ts index de53a2f..521dd22 100644 --- a/src/merge/input-helper.ts +++ b/src/merge/input-helper.ts @@ -10,6 +10,7 @@ export function getInputs(): MergeInputs { const pattern = core.getInput(Inputs.Pattern, {required: true}) const separateDirectories = core.getBooleanInput(Inputs.SeparateDirectories) const deleteMerged = core.getBooleanInput(Inputs.DeleteMerged) + const includeGitDirectory = core.getBooleanInput(Inputs.IncludeGitDirectory) const inputs = { name, @@ -17,7 +18,8 @@ export function getInputs(): MergeInputs { separateDirectories, deleteMerged, retentionDays: 0, - compressionLevel: 6 + compressionLevel: 6, + includeGitDirectory } as MergeInputs const retentionDaysStr = core.getInput(Inputs.RetentionDays) diff --git a/src/merge/merge-artifacts.ts b/src/merge/merge-artifacts.ts index b45ef9c..768ebb3 100644 --- a/src/merge/merge-artifacts.ts +++ b/src/merge/merge-artifacts.ts @@ -62,7 +62,9 @@ export async function run(): Promise { options.compressionLevel = inputs.compressionLevel } - const searchResult = await findFilesToUpload(tmpDir) + const searchResult = await findFilesToUpload(tmpDir, { + includeGitDirectory: inputs.includeGitDirectory + }) await uploadArtifact( inputs.name, diff --git a/src/merge/merge-inputs.ts b/src/merge/merge-inputs.ts index def507a..5c0653c 100644 --- a/src/merge/merge-inputs.ts +++ b/src/merge/merge-inputs.ts @@ -30,4 +30,9 @@ export interface MergeInputs { * If false, the artifacts will be merged into the root of the destination. */ separateDirectories: boolean + + /** + * Include files in the `.git` directory in the artifact + */ + includeGitDirectory: boolean } diff --git a/src/shared/search.ts b/src/shared/search.ts index bd80164..abf7ebe 100644 --- a/src/shared/search.ts +++ b/src/shared/search.ts @@ -78,14 +78,23 @@ function getMultiPathLCA(searchPaths: string[]): string { return path.join(...commonPaths) } +export interface SearchOptions { + /** + * Indicates whether files in the .git directory should be included in the artifact + * + * @default false + */ + includeGitDirectory: boolean +} + export async function findFilesToUpload( searchPath: string, - globOptions?: glob.GlobOptions + searchOptions?: SearchOptions, ): Promise { const searchResults: string[] = [] const globber = await glob.create( searchPath, - globOptions || getDefaultGlobOptions() + getDefaultGlobOptions() ) const rawSearchResults: string[] = await globber.glob() @@ -104,6 +113,12 @@ export async function findFilesToUpload( // 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`) + + if (!searchOptions?.includeGitDirectory && inGitDirectory(searchResult)) { + debug(`Ignoring ${searchResult} because it is in the .git directory`) + continue + } + searchResults.push(searchResult) // detect any files that would be overwritten because of case insensitivity @@ -155,3 +170,15 @@ export async function findFilesToUpload( rootDirectory: searchPaths[0] } } + +function inGitDirectory(filePath: string): boolean { + // The .git directory is a directory, so we need to check if the file path is a directory + // and if it is a .git directory + for (const part of filePath.split(path.sep)) { + if (part === '.git') { + return true + } + } + + return false +} diff --git a/src/upload/constants.ts b/src/upload/constants.ts index 272f842..71ec176 100644 --- a/src/upload/constants.ts +++ b/src/upload/constants.ts @@ -5,7 +5,8 @@ export enum Inputs { IfNoFilesFound = 'if-no-files-found', RetentionDays = 'retention-days', CompressionLevel = 'compression-level', - Overwrite = 'overwrite' + Overwrite = 'overwrite', + IncludeGitDirectory = 'include-git-directory' } export enum NoFileOptions { diff --git a/src/upload/input-helper.ts b/src/upload/input-helper.ts index 3e24f25..d43c97f 100644 --- a/src/upload/input-helper.ts +++ b/src/upload/input-helper.ts @@ -9,6 +9,7 @@ export function getInputs(): UploadInputs { const name = core.getInput(Inputs.Name) const path = core.getInput(Inputs.Path, {required: true}) const overwrite = core.getBooleanInput(Inputs.Overwrite) + const includeGitDirectory = core.getBooleanInput(Inputs.IncludeGitDirectory) const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound) const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound] @@ -27,7 +28,8 @@ export function getInputs(): UploadInputs { artifactName: name, searchPath: path, ifNoFilesFound: noFileBehavior, - overwrite: overwrite + overwrite: overwrite, + includeGitDirectory: includeGitDirectory } as UploadInputs const retentionDaysStr = core.getInput(Inputs.RetentionDays) diff --git a/src/upload/upload-artifact.ts b/src/upload/upload-artifact.ts index 8c77543..045dd45 100644 --- a/src/upload/upload-artifact.ts +++ b/src/upload/upload-artifact.ts @@ -24,7 +24,9 @@ async function deleteArtifactIfExists(artifactName: string): Promise { export async function run(): Promise { const inputs = getInputs() - const searchResult = await findFilesToUpload(inputs.searchPath) + const searchResult = await findFilesToUpload(inputs.searchPath, { + includeGitDirectory: inputs.includeGitDirectory + }) if (searchResult.filesToUpload.length === 0) { // No files were found, different use cases warrant different types of behavior if nothing is found switch (inputs.ifNoFilesFound) { diff --git a/src/upload/upload-inputs.ts b/src/upload/upload-inputs.ts index 1e7a46f..9a60bb5 100644 --- a/src/upload/upload-inputs.ts +++ b/src/upload/upload-inputs.ts @@ -30,4 +30,9 @@ export interface UploadInputs { * Whether or not to replace an existing artifact with the same name */ overwrite: boolean + + /** + * Include files in the `.git` directory in the artifact + */ + includeGitDirectory: boolean }