feat: add support for excluding symlinks and fix bug with commit not found (#2770)
This commit is contained in:
107
.github/workflows/test.yml
vendored
107
.github/workflows/test.yml
vendored
@@ -275,6 +275,113 @@ jobs:
|
||||
shell:
|
||||
bash
|
||||
|
||||
test-skip-same-base-and-commit-sha:
|
||||
name: Test changed-files skip same base and commit sha
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: needs.build.outputs.files_changed != 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download build assets
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: build-assets
|
||||
|
||||
- name: Get head SHA
|
||||
id: head-sha
|
||||
run: |
|
||||
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
shell:
|
||||
bash
|
||||
|
||||
- name: Run changed-files with same base and commit sha
|
||||
id: changed-files
|
||||
uses: ./
|
||||
with:
|
||||
base_sha: ${{ steps.head-sha.outputs.sha }}
|
||||
sha: ${{ steps.head-sha.outputs.sha }}
|
||||
skip_same_sha: true
|
||||
|
||||
- name: Verify empty outputs
|
||||
if: steps.changed-files.outputs.all_changed_files_count != '0' || steps.changed-files.outputs.any_changed != 'false'
|
||||
run: |
|
||||
echo "Expected empty outputs; got count=${{ steps.changed-files.outputs.all_changed_files_count }} any_changed=${{ steps.changed-files.outputs.any_changed }}"
|
||||
exit 1
|
||||
shell:
|
||||
bash
|
||||
|
||||
- name: Show output
|
||||
run: |
|
||||
echo '${{ toJSON(steps.changed-files.outputs) }}'
|
||||
shell:
|
||||
bash
|
||||
|
||||
test-exclude-symlinks:
|
||||
name: Test changed-files exclude symlinks
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: needs.build.outputs.files_changed != 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download build assets
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: build-assets
|
||||
|
||||
- name: Run changed-files without symlink exclusion
|
||||
id: changed-files-symlink-included
|
||||
uses: ./
|
||||
with:
|
||||
base_sha: 955f38aa4f30f1ec92d08049b26e6d736ffea013
|
||||
sha: ddde96a55848a649c09322c7094b22ef4b4abe23
|
||||
|
||||
- name: Verify symlink is present
|
||||
if: "!contains(steps.changed-files-symlink-included.outputs.added_files, 'test/symlink-to-target')"
|
||||
run: |
|
||||
echo "Expected symlink to be present in added_files; got ${{ steps.changed-files-symlink-included.outputs.added_files }}"
|
||||
exit 1
|
||||
shell:
|
||||
bash
|
||||
|
||||
- name: Run changed-files excluding symlinks
|
||||
id: changed-files-symlink-excluded
|
||||
uses: ./
|
||||
with:
|
||||
base_sha: 955f38aa4f30f1ec92d08049b26e6d736ffea013
|
||||
sha: ddde96a55848a649c09322c7094b22ef4b4abe23
|
||||
exclude_symlinks: true
|
||||
|
||||
- name: Verify symlink is excluded
|
||||
if: "contains(steps.changed-files-symlink-excluded.outputs.added_files, 'test/symlink-to-target') || contains(steps.changed-files-symlink-excluded.outputs.all_changed_files, 'test/symlink-to-target')"
|
||||
run: |
|
||||
echo "Expected symlink to be excluded; got added=${{ steps.changed-files-symlink-excluded.outputs.added_files }} all=${{ steps.changed-files-symlink-excluded.outputs.all_changed_files }}"
|
||||
exit 1
|
||||
shell:
|
||||
bash
|
||||
|
||||
- name: Show output
|
||||
run: |
|
||||
echo '${{ toJSON(steps.changed-files-symlink-included.outputs) }}'
|
||||
echo '${{ toJSON(steps.changed-files-symlink-excluded.outputs) }}'
|
||||
shell:
|
||||
bash
|
||||
|
||||
test-using-branch-names-for-base-sha-and-sha-inputs:
|
||||
name: Test using branch names for base_sha and sha inputs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
10
README.md
10
README.md
@@ -412,6 +412,16 @@ Support this project with a :star:
|
||||
# Default: "false"
|
||||
exclude_submodules: ''
|
||||
|
||||
# Exclude symlinks from changed files.
|
||||
# Type: boolean
|
||||
# Default: "false"
|
||||
exclude_symlinks: ''
|
||||
|
||||
# Do not fail when base and head SHAs are identical.
|
||||
# Type: boolean
|
||||
# Default: "false"
|
||||
skip_same_sha: ''
|
||||
|
||||
# Fail when the initial diff
|
||||
# fails.
|
||||
# Type: boolean
|
||||
|
||||
@@ -227,6 +227,14 @@ inputs:
|
||||
description: "Exclude changes to submodules."
|
||||
required: false
|
||||
default: "false"
|
||||
exclude_symlinks:
|
||||
description: "Exclude symlinks from changed files."
|
||||
required: false
|
||||
default: "false"
|
||||
skip_same_sha:
|
||||
description: "Do not fail when base and head SHAs are identical."
|
||||
required: false
|
||||
default: "false"
|
||||
fetch_missing_history_max_retries:
|
||||
description: "Maximum number of retries to fetch missing history."
|
||||
required: false
|
||||
|
||||
296
dist/index.js
generated
vendored
296
dist/index.js
generated
vendored
@@ -43,7 +43,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getChangedFilesFromGithubAPI = exports.getAllChangeTypeFiles = exports.getChangeTypeFiles = exports.getAllDiffFiles = exports.ChangeTypeEnum = exports.getRenamedFiles = exports.processChangedFiles = void 0;
|
||||
exports.getChangedFilesFromGithubAPI = exports.getAllChangeTypeFiles = exports.getChangeTypeFiles = exports.filterSymlinksFromChangedFiles = exports.getAllDiffFiles = exports.ChangeTypeEnum = exports.getRenamedFiles = exports.processChangedFiles = void 0;
|
||||
const core = __importStar(__nccwpck_require__(7484));
|
||||
const github = __importStar(__nccwpck_require__(3228));
|
||||
const flatten_1 = __importDefault(__nccwpck_require__(7047));
|
||||
@@ -190,7 +190,7 @@ var ChangeTypeEnum;
|
||||
ChangeTypeEnum["Unmerged"] = "U";
|
||||
ChangeTypeEnum["Unknown"] = "X";
|
||||
})(ChangeTypeEnum || (exports.ChangeTypeEnum = ChangeTypeEnum = {}));
|
||||
const getAllDiffFiles = async ({ workingDirectory, diffSubmodule, diffResult, submodulePaths, outputRenamedFilesAsDeletedAndAdded, fetchAdditionalSubmoduleHistory, failOnInitialDiffError, failOnSubmoduleDiffError }) => {
|
||||
const getAllDiffFiles = async ({ workingDirectory, diffSubmodule, diffResult, submodulePaths, outputRenamedFilesAsDeletedAndAdded, fetchAdditionalSubmoduleHistory, failOnInitialDiffError, failOnSubmoduleDiffError, submoduleShas }) => {
|
||||
const files = await (0, utils_1.getAllChangedFiles)({
|
||||
cwd: workingDirectory,
|
||||
sha1: diffResult.previousSha,
|
||||
@@ -210,6 +210,9 @@ const getAllDiffFiles = async ({ workingDirectory, diffSubmodule, diffResult, su
|
||||
});
|
||||
const submoduleWorkingDirectory = path.join(workingDirectory, submodulePath);
|
||||
if (submoduleShaResult.currentSha && submoduleShaResult.previousSha) {
|
||||
if (submoduleShas) {
|
||||
submoduleShas[submodulePath] = submoduleShaResult;
|
||||
}
|
||||
let diff = '...';
|
||||
if (!(await (0, utils_1.canDiffCommits)({
|
||||
cwd: submoduleWorkingDirectory,
|
||||
@@ -246,6 +249,96 @@ const getAllDiffFiles = async ({ workingDirectory, diffSubmodule, diffResult, su
|
||||
return files;
|
||||
};
|
||||
exports.getAllDiffFiles = getAllDiffFiles;
|
||||
const filterSymlinksFromChangedFiles = async ({ changedFiles, workingDirectory, diffResult, submodulePaths, submoduleShas }) => {
|
||||
const filtered = {
|
||||
[ChangeTypeEnum.Added]: [],
|
||||
[ChangeTypeEnum.Copied]: [],
|
||||
[ChangeTypeEnum.Deleted]: [],
|
||||
[ChangeTypeEnum.Modified]: [],
|
||||
[ChangeTypeEnum.Renamed]: [],
|
||||
[ChangeTypeEnum.TypeChanged]: [],
|
||||
[ChangeTypeEnum.Unmerged]: [],
|
||||
[ChangeTypeEnum.Unknown]: []
|
||||
};
|
||||
const cache = new Map();
|
||||
const diskCache = new Map();
|
||||
const getSubmoduleContext = (filePath) => {
|
||||
const submodulePath = submodulePaths.find(p => filePath.startsWith(`${p}${path.sep}`));
|
||||
if (!submodulePath) {
|
||||
return {
|
||||
cwd: workingDirectory,
|
||||
relativePath: filePath,
|
||||
currentSha: diffResult.currentSha,
|
||||
previousSha: diffResult.previousSha,
|
||||
isSubmoduleRoot: false
|
||||
};
|
||||
}
|
||||
if (filePath === submodulePath) {
|
||||
return {
|
||||
cwd: workingDirectory,
|
||||
relativePath: filePath,
|
||||
currentSha: diffResult.currentSha,
|
||||
previousSha: diffResult.previousSha,
|
||||
isSubmoduleRoot: true
|
||||
};
|
||||
}
|
||||
const submoduleWorkingDirectory = path.join(workingDirectory, submodulePath);
|
||||
const relativePath = filePath.substring(submodulePath.length + 1);
|
||||
const submoduleSha = submoduleShas === null || submoduleShas === void 0 ? void 0 : submoduleShas[submodulePath];
|
||||
return {
|
||||
cwd: submoduleWorkingDirectory,
|
||||
relativePath,
|
||||
currentSha: (submoduleSha === null || submoduleSha === void 0 ? void 0 : submoduleSha.currentSha) || diffResult.currentSha,
|
||||
previousSha: (submoduleSha === null || submoduleSha === void 0 ? void 0 : submoduleSha.previousSha) || diffResult.previousSha,
|
||||
isSubmoduleRoot: false
|
||||
};
|
||||
};
|
||||
const isSymlinkCached = async ({ cwd, filePath, sha, preferDisk }) => {
|
||||
if (preferDisk) {
|
||||
const diskKey = `${cwd}|disk|${filePath}`;
|
||||
const cachedDisk = diskCache.get(diskKey);
|
||||
if (cachedDisk !== undefined) {
|
||||
return cachedDisk;
|
||||
}
|
||||
const diskResult = await (0, utils_1.isSymlinkOnDisk)({ cwd, filePath });
|
||||
diskCache.set(diskKey, diskResult);
|
||||
if (diskResult) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const treeKey = `${cwd}|${sha}|${filePath}`;
|
||||
const cachedTree = cache.get(treeKey);
|
||||
if (cachedTree !== undefined) {
|
||||
return cachedTree;
|
||||
}
|
||||
const treeResult = await (0, utils_1.isSymlinkInGitTree)({ cwd, sha, filePath });
|
||||
cache.set(treeKey, treeResult);
|
||||
return treeResult;
|
||||
};
|
||||
for (const changeType of Object.keys(changedFiles)) {
|
||||
const files = changedFiles[changeType] || [];
|
||||
for (const filePath of files) {
|
||||
const context = getSubmoduleContext(filePath);
|
||||
if (context.isSubmoduleRoot) {
|
||||
filtered[changeType].push(filePath);
|
||||
continue;
|
||||
}
|
||||
const isDeleted = changeType === ChangeTypeEnum.Deleted;
|
||||
const sha = isDeleted ? context.previousSha : context.currentSha;
|
||||
const isSymlink = await isSymlinkCached({
|
||||
cwd: context.cwd,
|
||||
filePath: context.relativePath,
|
||||
sha,
|
||||
preferDisk: !isDeleted
|
||||
});
|
||||
if (!isSymlink) {
|
||||
filtered[changeType].push(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
};
|
||||
exports.filterSymlinksFromChangedFiles = filterSymlinksFromChangedFiles;
|
||||
function* getFilePaths({ inputs, filePaths, dirNamesIncludeFilePatterns }) {
|
||||
for (const filePath of filePaths) {
|
||||
if (inputs.dirNames) {
|
||||
@@ -1007,6 +1100,17 @@ const getSHAForNonPullRequestEvent = async ({ inputs, env, workingDirectory, isS
|
||||
}
|
||||
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`);
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
};
|
||||
}
|
||||
core.error(`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`);
|
||||
core.error(`Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${inputs.fetchDepth}.`);
|
||||
throw new Error('Similar commit hashes detected.');
|
||||
@@ -1095,6 +1199,17 @@ const getSHAForNonPullRequestEvent = async ({ inputs, env, workingDirectory, isS
|
||||
core.debug(`Target branch: ${targetBranch}`);
|
||||
core.debug(`Current branch: ${currentBranch}`);
|
||||
if (!initialCommit && previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`);
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
};
|
||||
}
|
||||
core.error(`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`);
|
||||
core.error(`Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${inputs.fetchDepth}.`);
|
||||
throw new Error('Similar commit hashes detected.');
|
||||
@@ -1193,6 +1308,17 @@ const getSHAForPullRequestEvent = async ({ inputs, workingDirectory, isShallow,
|
||||
let diff = '...';
|
||||
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`);
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
};
|
||||
}
|
||||
core.error(`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`);
|
||||
core.error(`Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${inputs.fetchDepth}.`);
|
||||
throw new Error('Similar commit hashes detected.');
|
||||
@@ -1314,6 +1440,17 @@ const getSHAForPullRequestEvent = async ({ inputs, workingDirectory, isShallow,
|
||||
throw new Error(`Unable to determine a difference between ${previousSha}${diff}${currentSha}`);
|
||||
}
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`);
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
};
|
||||
}
|
||||
core.error(`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`);
|
||||
// This occurs if a PR is created from a forked repository and the event is pull_request_target.
|
||||
// - name: Checkout to branch
|
||||
@@ -1376,6 +1513,8 @@ exports.DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS = {
|
||||
fetchAdditionalSubmoduleHistory: false,
|
||||
dirNamesDeletedFilesIncludeOnlyDeletedDirs: false,
|
||||
excludeSubmodules: false,
|
||||
excludeSymlinks: false,
|
||||
skipSameSha: false,
|
||||
fetchMissingHistoryMaxRetries: 20,
|
||||
usePosixPathSeparator: false,
|
||||
tagsPattern: '*',
|
||||
@@ -1573,6 +1712,12 @@ const getInputs = () => {
|
||||
const excludeSubmodules = core.getBooleanInput('exclude_submodules', {
|
||||
required: false
|
||||
});
|
||||
const excludeSymlinks = core.getBooleanInput('exclude_symlinks', {
|
||||
required: false
|
||||
});
|
||||
const skipSameSha = core.getBooleanInput('skip_same_sha', {
|
||||
required: false
|
||||
});
|
||||
const fetchMissingHistoryMaxRetries = core.getInput('fetch_missing_history_max_retries', { required: false });
|
||||
const usePosixPathSeparator = core.getBooleanInput('use_posix_path_separator', {
|
||||
required: false
|
||||
@@ -1625,6 +1770,8 @@ const getInputs = () => {
|
||||
fetchAdditionalSubmoduleHistory,
|
||||
dirNamesDeletedFilesIncludeOnlyDeletedDirs,
|
||||
excludeSubmodules,
|
||||
excludeSymlinks,
|
||||
skipSameSha,
|
||||
usePosixPathSeparator,
|
||||
tagsPattern,
|
||||
tagsIgnorePattern,
|
||||
@@ -1782,8 +1929,49 @@ const getChangedFilesFromLocalGitHistory = async ({ inputs, env, workingDirector
|
||||
core.endGroup();
|
||||
return;
|
||||
}
|
||||
if (diffResult.sameSha) {
|
||||
core.info('Base and head SHAs are identical; no changed files to report.');
|
||||
const emptyChangedFiles = {
|
||||
[changedFiles_1.ChangeTypeEnum.Added]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Copied]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Deleted]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Modified]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Renamed]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.TypeChanged]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Unmerged]: [],
|
||||
[changedFiles_1.ChangeTypeEnum.Unknown]: []
|
||||
};
|
||||
await (0, changedFiles_1.processChangedFiles)({
|
||||
filePatterns,
|
||||
allDiffFiles: emptyChangedFiles,
|
||||
inputs,
|
||||
yamlFilePatterns,
|
||||
workingDirectory
|
||||
});
|
||||
if (inputs.includeAllOldNewRenamedFiles) {
|
||||
await (0, utils_1.setOutput)({
|
||||
key: 'all_old_new_renamed_files',
|
||||
value: inputs.json ? [] : '',
|
||||
writeOutputFiles: inputs.writeOutputFiles,
|
||||
outputDir: inputs.outputDir,
|
||||
json: inputs.json,
|
||||
safeOutput: inputs.safeOutput
|
||||
});
|
||||
await (0, utils_1.setOutput)({
|
||||
key: 'all_old_new_renamed_files_count',
|
||||
value: '0',
|
||||
writeOutputFiles: inputs.writeOutputFiles,
|
||||
outputDir: inputs.outputDir,
|
||||
json: inputs.json
|
||||
});
|
||||
}
|
||||
core.info('All Done!');
|
||||
core.endGroup();
|
||||
return;
|
||||
}
|
||||
core.info(`Retrieving changes between ${diffResult.previousSha} (${diffResult.targetBranch}) → ${diffResult.currentSha} (${diffResult.currentBranch})`);
|
||||
const allDiffFiles = await (0, changedFiles_1.getAllDiffFiles)({
|
||||
const submoduleShas = {};
|
||||
let allDiffFiles = await (0, changedFiles_1.getAllDiffFiles)({
|
||||
workingDirectory,
|
||||
diffSubmodule,
|
||||
diffResult,
|
||||
@@ -1791,8 +1979,19 @@ const getChangedFilesFromLocalGitHistory = async ({ inputs, env, workingDirector
|
||||
outputRenamedFilesAsDeletedAndAdded,
|
||||
fetchAdditionalSubmoduleHistory: inputs.fetchAdditionalSubmoduleHistory,
|
||||
failOnInitialDiffError: inputs.failOnInitialDiffError,
|
||||
failOnSubmoduleDiffError: inputs.failOnSubmoduleDiffError
|
||||
failOnSubmoduleDiffError: inputs.failOnSubmoduleDiffError,
|
||||
submoduleShas
|
||||
});
|
||||
if (inputs.excludeSymlinks) {
|
||||
core.info('Excluding symlinks from the diff');
|
||||
allDiffFiles = await (0, changedFiles_1.filterSymlinksFromChangedFiles)({
|
||||
changedFiles: allDiffFiles,
|
||||
workingDirectory,
|
||||
diffResult,
|
||||
submodulePaths,
|
||||
submoduleShas
|
||||
});
|
||||
}
|
||||
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`);
|
||||
core.info('All Done!');
|
||||
core.endGroup();
|
||||
@@ -1961,7 +2160,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.warnUnsupportedRESTAPIInputs = exports.hasLocalGitDirectory = exports.recoverDeletedFiles = exports.setOutput = exports.setArrayOutput = exports.getOutputKey = exports.getRecoverFilePatterns = exports.getYamlFilePatterns = exports.getFilePatterns = exports.getDirNamesIncludeFilesPattern = exports.jsonOutput = exports.getDirnameMaxDepth = exports.canDiffCommits = exports.getPreviousGitTag = exports.cleanShaInput = exports.verifyCommitSha = exports.getParentSha = exports.getCurrentBranchName = exports.getRemoteBranchHeadSha = exports.isInsideWorkTree = exports.getHeadSha = exports.gitLog = exports.getFilteredChangedFiles = exports.getAllChangedFiles = exports.gitRenamedFiles = exports.gitSubmoduleDiffSHA = exports.getSubmodulePath = exports.gitFetchSubmodules = exports.gitFetch = exports.submoduleExists = exports.isRepoShallow = exports.updateGitGlobalConfig = exports.exists = exports.verifyMinimumGitVersion = exports.getDirname = exports.normalizeSeparators = exports.isWindows = void 0;
|
||||
exports.warnUnsupportedRESTAPIInputs = exports.hasLocalGitDirectory = exports.recoverDeletedFiles = exports.setOutput = exports.setArrayOutput = exports.getOutputKey = exports.getRecoverFilePatterns = exports.getYamlFilePatterns = exports.getFilePatterns = exports.getDirNamesIncludeFilesPattern = exports.jsonOutput = exports.getDirnameMaxDepth = exports.canDiffCommits = exports.getPreviousGitTag = exports.cleanShaInput = exports.verifyCommitSha = exports.getParentSha = exports.getCurrentBranchName = exports.getRemoteBranchHeadSha = exports.isInsideWorkTree = exports.getHeadSha = exports.gitLog = exports.getFilteredChangedFiles = exports.getAllChangedFiles = exports.gitRenamedFiles = exports.gitSubmoduleDiffSHA = exports.getSubmodulePath = exports.gitFetchSubmodules = exports.gitFetch = exports.submoduleExists = exports.isRepoShallow = exports.updateGitGlobalConfig = exports.isSymlinkInGitTree = exports.isSymlinkOnDisk = exports.exists = exports.verifyMinimumGitVersion = exports.getDirname = exports.normalizeSeparators = exports.isWindows = void 0;
|
||||
/*global AsyncIterableIterator*/
|
||||
const core = __importStar(__nccwpck_require__(7484));
|
||||
const exec = __importStar(__nccwpck_require__(5236));
|
||||
@@ -2092,6 +2291,49 @@ const exists = async (filePath) => {
|
||||
}
|
||||
};
|
||||
exports.exists = exists;
|
||||
/**
|
||||
* Checks if a file is a symlink on disk
|
||||
* @param cwd - working directory
|
||||
* @param filePath - path to check
|
||||
* @returns file is a symlink
|
||||
*/
|
||||
const isSymlinkOnDisk = async ({ cwd, filePath }) => {
|
||||
try {
|
||||
const stat = await fs_1.promises.lstat(path.join(cwd, filePath));
|
||||
return stat.isSymbolicLink();
|
||||
}
|
||||
catch (_a) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
exports.isSymlinkOnDisk = isSymlinkOnDisk;
|
||||
/**
|
||||
* Checks if a file is a symlink in a git tree
|
||||
* @param cwd - working directory
|
||||
* @param sha - commit sha
|
||||
* @param filePath - path to check
|
||||
* @returns file is a symlink
|
||||
*/
|
||||
const isSymlinkInGitTree = async ({ cwd, sha, filePath }) => {
|
||||
if (!sha) {
|
||||
return false;
|
||||
}
|
||||
const { stdout, exitCode } = await exec.getExecOutput('git', ['ls-tree', '-r', sha, '--', filePath], {
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: !core.isDebug()
|
||||
});
|
||||
if (exitCode !== 0) {
|
||||
return false;
|
||||
}
|
||||
const line = stdout.split('\n').find(Boolean);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
const [mode] = line.split(/\s+/);
|
||||
return mode === '120000';
|
||||
};
|
||||
exports.isSymlinkInGitTree = isSymlinkInGitTree;
|
||||
/**
|
||||
* Generates lines of a file as an async iterable iterator
|
||||
* @param filePath - path of file to read
|
||||
@@ -2517,13 +2759,45 @@ const cleanShaInput = async ({ sha, cwd, token }) => {
|
||||
});
|
||||
if (exitCode !== 0) {
|
||||
const octokit = github.getOctokit(token);
|
||||
// If it's not a valid commit sha, assume it's a branch name and get the HEAD sha
|
||||
const { data: refData } = await octokit.rest.git.getRef({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
ref: `heads/${sha}`
|
||||
const owner = github.context.repo.owner;
|
||||
const repo = github.context.repo.repo;
|
||||
const isNotFoundError = (error) => typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'status' in error &&
|
||||
error.status === 404;
|
||||
// If it's not a valid commit sha, assume it's a ref name first.
|
||||
try {
|
||||
const { data: refData } = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${sha}`
|
||||
});
|
||||
return refData.object.sha;
|
||||
}
|
||||
catch (error) {
|
||||
if (!isNotFoundError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const { data: refData } = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${sha}`
|
||||
});
|
||||
return refData.object.sha;
|
||||
}
|
||||
catch (error) {
|
||||
if (!isNotFoundError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const { data: commitData } = await octokit.rest.git.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
commit_sha: sha
|
||||
});
|
||||
return refData.object.sha;
|
||||
return commitData.sha;
|
||||
}
|
||||
return stdout.trim();
|
||||
};
|
||||
|
||||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -12,6 +12,7 @@ exports[`getInputs should correctly parse boolean inputs 1`] = `
|
||||
"dirNamesIncludeFilesSeparator": "",
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": "false",
|
||||
"excludeSymlinks": "false",
|
||||
"failOnInitialDiffError": "false",
|
||||
"failOnSubmoduleDiffError": "false",
|
||||
"fetchAdditionalSubmoduleHistory": "false",
|
||||
@@ -53,6 +54,7 @@ exports[`getInputs should correctly parse boolean inputs 1`] = `
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": "false",
|
||||
"skipInitialFetch": "true",
|
||||
"skipSameSha": "false",
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "*",
|
||||
"token": "",
|
||||
@@ -76,6 +78,7 @@ exports[`getInputs should correctly parse numeric inputs 1`] = `
|
||||
"dirNamesMaxDepth": 2,
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": false,
|
||||
"excludeSymlinks": false,
|
||||
"failOnInitialDiffError": false,
|
||||
"failOnSubmoduleDiffError": false,
|
||||
"fetchAdditionalSubmoduleHistory": false,
|
||||
@@ -115,6 +118,7 @@ exports[`getInputs should correctly parse numeric inputs 1`] = `
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": false,
|
||||
"skipInitialFetch": false,
|
||||
"skipSameSha": false,
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "",
|
||||
"token": "",
|
||||
@@ -137,6 +141,7 @@ exports[`getInputs should correctly parse string inputs 1`] = `
|
||||
"dirNamesIncludeFilesSeparator": "",
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": false,
|
||||
"excludeSymlinks": false,
|
||||
"failOnInitialDiffError": false,
|
||||
"failOnSubmoduleDiffError": false,
|
||||
"fetchAdditionalSubmoduleHistory": false,
|
||||
@@ -175,6 +180,7 @@ exports[`getInputs should correctly parse string inputs 1`] = `
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": false,
|
||||
"skipInitialFetch": false,
|
||||
"skipSameSha": false,
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "",
|
||||
"token": "token",
|
||||
@@ -198,6 +204,7 @@ exports[`getInputs should handle invalid numeric inputs correctly 1`] = `
|
||||
"dirNamesMaxDepth": 2,
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": false,
|
||||
"excludeSymlinks": false,
|
||||
"failOnInitialDiffError": false,
|
||||
"failOnSubmoduleDiffError": false,
|
||||
"fetchAdditionalSubmoduleHistory": false,
|
||||
@@ -237,6 +244,7 @@ exports[`getInputs should handle invalid numeric inputs correctly 1`] = `
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": false,
|
||||
"skipInitialFetch": false,
|
||||
"skipSameSha": false,
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "",
|
||||
"token": "",
|
||||
@@ -260,6 +268,7 @@ exports[`getInputs should handle negative numeric inputs correctly 1`] = `
|
||||
"dirNamesMaxDepth": -2,
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": false,
|
||||
"excludeSymlinks": false,
|
||||
"failOnInitialDiffError": false,
|
||||
"failOnSubmoduleDiffError": false,
|
||||
"fetchAdditionalSubmoduleHistory": false,
|
||||
@@ -299,6 +308,7 @@ exports[`getInputs should handle negative numeric inputs correctly 1`] = `
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": false,
|
||||
"skipInitialFetch": false,
|
||||
"skipSameSha": false,
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "",
|
||||
"token": "",
|
||||
@@ -321,6 +331,7 @@ exports[`getInputs should return default values when no inputs are provided 1`]
|
||||
"dirNamesIncludeFilesSeparator": "",
|
||||
"escapeJson": false,
|
||||
"excludeSubmodules": false,
|
||||
"excludeSymlinks": false,
|
||||
"failOnInitialDiffError": false,
|
||||
"failOnSubmoduleDiffError": false,
|
||||
"fetchAdditionalSubmoduleHistory": false,
|
||||
@@ -362,6 +373,7 @@ exports[`getInputs should return default values when no inputs are provided 1`]
|
||||
"since": "",
|
||||
"sinceLastRemoteCommit": false,
|
||||
"skipInitialFetch": false,
|
||||
"skipSameSha": false,
|
||||
"tagsIgnorePattern": "",
|
||||
"tagsPattern": "*",
|
||||
"token": "",
|
||||
|
||||
@@ -639,6 +639,8 @@ describe('utils test', () => {
|
||||
negationPatternsFirst: false,
|
||||
useRestApi: false,
|
||||
excludeSubmodules: false,
|
||||
excludeSymlinks: false,
|
||||
skipSameSha: false,
|
||||
fetchMissingHistoryMaxRetries: 20,
|
||||
usePosixPathSeparator: false,
|
||||
tagsPattern: '*',
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
getFilteredChangedFiles,
|
||||
gitRenamedFiles,
|
||||
gitSubmoduleDiffSHA,
|
||||
isSymlinkInGitTree,
|
||||
isSymlinkOnDisk,
|
||||
isWindows,
|
||||
jsonOutput,
|
||||
setArrayOutput
|
||||
@@ -220,7 +222,8 @@ export const getAllDiffFiles = async ({
|
||||
outputRenamedFilesAsDeletedAndAdded,
|
||||
fetchAdditionalSubmoduleHistory,
|
||||
failOnInitialDiffError,
|
||||
failOnSubmoduleDiffError
|
||||
failOnSubmoduleDiffError,
|
||||
submoduleShas
|
||||
}: {
|
||||
workingDirectory: string
|
||||
diffSubmodule: boolean
|
||||
@@ -230,6 +233,7 @@ export const getAllDiffFiles = async ({
|
||||
fetchAdditionalSubmoduleHistory: boolean
|
||||
failOnInitialDiffError: boolean
|
||||
failOnSubmoduleDiffError: boolean
|
||||
submoduleShas?: Record<string, {previousSha?: string; currentSha?: string}>
|
||||
}): Promise<ChangedFiles> => {
|
||||
const files = await getAllChangedFiles({
|
||||
cwd: workingDirectory,
|
||||
@@ -256,6 +260,9 @@ export const getAllDiffFiles = async ({
|
||||
)
|
||||
|
||||
if (submoduleShaResult.currentSha && submoduleShaResult.previousSha) {
|
||||
if (submoduleShas) {
|
||||
submoduleShas[submodulePath] = submoduleShaResult
|
||||
}
|
||||
let diff = '...'
|
||||
|
||||
if (
|
||||
@@ -300,6 +307,139 @@ export const getAllDiffFiles = async ({
|
||||
return files
|
||||
}
|
||||
|
||||
export const filterSymlinksFromChangedFiles = async ({
|
||||
changedFiles,
|
||||
workingDirectory,
|
||||
diffResult,
|
||||
submodulePaths,
|
||||
submoduleShas
|
||||
}: {
|
||||
changedFiles: ChangedFiles
|
||||
workingDirectory: string
|
||||
diffResult: DiffResult
|
||||
submodulePaths: string[]
|
||||
submoduleShas?: Record<string, {previousSha?: string; currentSha?: string}>
|
||||
}): Promise<ChangedFiles> => {
|
||||
const filtered: ChangedFiles = {
|
||||
[ChangeTypeEnum.Added]: [],
|
||||
[ChangeTypeEnum.Copied]: [],
|
||||
[ChangeTypeEnum.Deleted]: [],
|
||||
[ChangeTypeEnum.Modified]: [],
|
||||
[ChangeTypeEnum.Renamed]: [],
|
||||
[ChangeTypeEnum.TypeChanged]: [],
|
||||
[ChangeTypeEnum.Unmerged]: [],
|
||||
[ChangeTypeEnum.Unknown]: []
|
||||
}
|
||||
|
||||
const cache = new Map<string, boolean>()
|
||||
const diskCache = new Map<string, boolean>()
|
||||
|
||||
const getSubmoduleContext = (
|
||||
filePath: string
|
||||
): {
|
||||
cwd: string
|
||||
relativePath: string
|
||||
currentSha: string
|
||||
previousSha: string
|
||||
isSubmoduleRoot: boolean
|
||||
} => {
|
||||
const submodulePath = submodulePaths.find(p =>
|
||||
filePath.startsWith(`${p}${path.sep}`)
|
||||
)
|
||||
if (!submodulePath) {
|
||||
return {
|
||||
cwd: workingDirectory,
|
||||
relativePath: filePath,
|
||||
currentSha: diffResult.currentSha,
|
||||
previousSha: diffResult.previousSha,
|
||||
isSubmoduleRoot: false
|
||||
}
|
||||
}
|
||||
|
||||
if (filePath === submodulePath) {
|
||||
return {
|
||||
cwd: workingDirectory,
|
||||
relativePath: filePath,
|
||||
currentSha: diffResult.currentSha,
|
||||
previousSha: diffResult.previousSha,
|
||||
isSubmoduleRoot: true
|
||||
}
|
||||
}
|
||||
|
||||
const submoduleWorkingDirectory = path.join(workingDirectory, submodulePath)
|
||||
const relativePath = filePath.substring(submodulePath.length + 1)
|
||||
const submoduleSha = submoduleShas?.[submodulePath]
|
||||
|
||||
return {
|
||||
cwd: submoduleWorkingDirectory,
|
||||
relativePath,
|
||||
currentSha: submoduleSha?.currentSha || diffResult.currentSha,
|
||||
previousSha: submoduleSha?.previousSha || diffResult.previousSha,
|
||||
isSubmoduleRoot: false
|
||||
}
|
||||
}
|
||||
|
||||
const isSymlinkCached = async ({
|
||||
cwd,
|
||||
filePath,
|
||||
sha,
|
||||
preferDisk
|
||||
}: {
|
||||
cwd: string
|
||||
filePath: string
|
||||
sha: string
|
||||
preferDisk: boolean
|
||||
}): Promise<boolean> => {
|
||||
if (preferDisk) {
|
||||
const diskKey = `${cwd}|disk|${filePath}`
|
||||
const cachedDisk = diskCache.get(diskKey)
|
||||
if (cachedDisk !== undefined) {
|
||||
return cachedDisk
|
||||
}
|
||||
const diskResult = await isSymlinkOnDisk({cwd, filePath})
|
||||
diskCache.set(diskKey, diskResult)
|
||||
if (diskResult) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const treeKey = `${cwd}|${sha}|${filePath}`
|
||||
const cachedTree = cache.get(treeKey)
|
||||
if (cachedTree !== undefined) {
|
||||
return cachedTree
|
||||
}
|
||||
const treeResult = await isSymlinkInGitTree({cwd, sha, filePath})
|
||||
cache.set(treeKey, treeResult)
|
||||
return treeResult
|
||||
}
|
||||
|
||||
for (const changeType of Object.keys(changedFiles) as ChangeTypeEnum[]) {
|
||||
const files = changedFiles[changeType] || []
|
||||
for (const filePath of files) {
|
||||
const context = getSubmoduleContext(filePath)
|
||||
if (context.isSubmoduleRoot) {
|
||||
filtered[changeType].push(filePath)
|
||||
continue
|
||||
}
|
||||
|
||||
const isDeleted = changeType === ChangeTypeEnum.Deleted
|
||||
const sha = isDeleted ? context.previousSha : context.currentSha
|
||||
const isSymlink = await isSymlinkCached({
|
||||
cwd: context.cwd,
|
||||
filePath: context.relativePath,
|
||||
sha,
|
||||
preferDisk: !isDeleted
|
||||
})
|
||||
|
||||
if (!isSymlink) {
|
||||
filtered[changeType].push(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
function* getFilePaths({
|
||||
inputs,
|
||||
filePaths,
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface DiffResult {
|
||||
targetBranch: string
|
||||
diff: string
|
||||
initialCommit?: boolean
|
||||
sameSha?: boolean
|
||||
}
|
||||
|
||||
interface SHAForNonPullRequestEvent {
|
||||
@@ -198,6 +199,19 @@ export const getSHAForNonPullRequestEvent = async ({
|
||||
|
||||
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(
|
||||
`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`
|
||||
)
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
}
|
||||
}
|
||||
core.error(
|
||||
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
|
||||
)
|
||||
@@ -305,6 +319,19 @@ export const getSHAForNonPullRequestEvent = async ({
|
||||
core.debug(`Current branch: ${currentBranch}`)
|
||||
|
||||
if (!initialCommit && previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(
|
||||
`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`
|
||||
)
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
}
|
||||
}
|
||||
core.error(
|
||||
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
|
||||
)
|
||||
@@ -430,6 +457,19 @@ export const getSHAForPullRequestEvent = async ({
|
||||
|
||||
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(
|
||||
`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`
|
||||
)
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
}
|
||||
}
|
||||
core.error(
|
||||
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
|
||||
)
|
||||
@@ -608,6 +648,19 @@ export const getSHAForPullRequestEvent = async ({
|
||||
}
|
||||
|
||||
if (previousSha === currentSha) {
|
||||
if (inputs.skipSameSha) {
|
||||
core.info(
|
||||
`Skipping diff because previous sha ${previousSha} is equivalent to the current sha ${currentSha}.`
|
||||
)
|
||||
return {
|
||||
previousSha,
|
||||
currentSha,
|
||||
currentBranch,
|
||||
targetBranch,
|
||||
diff,
|
||||
sameSha: true
|
||||
}
|
||||
}
|
||||
core.error(
|
||||
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
|
||||
)
|
||||
|
||||
@@ -22,6 +22,8 @@ export const DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS: Partial<Inputs> = {
|
||||
fetchAdditionalSubmoduleHistory: false,
|
||||
dirNamesDeletedFilesIncludeOnlyDeletedDirs: false,
|
||||
excludeSubmodules: false,
|
||||
excludeSymlinks: false,
|
||||
skipSameSha: false,
|
||||
fetchMissingHistoryMaxRetries: 20,
|
||||
usePosixPathSeparator: false,
|
||||
tagsPattern: '*',
|
||||
|
||||
@@ -55,6 +55,8 @@ export type Inputs = {
|
||||
negationPatternsFirst: boolean
|
||||
useRestApi: boolean
|
||||
excludeSubmodules: boolean
|
||||
excludeSymlinks: boolean
|
||||
skipSameSha: boolean
|
||||
fetchMissingHistoryMaxRetries?: number
|
||||
usePosixPathSeparator: boolean
|
||||
tagsPattern: string
|
||||
@@ -249,6 +251,14 @@ export const getInputs = (): Inputs => {
|
||||
required: false
|
||||
})
|
||||
|
||||
const excludeSymlinks = core.getBooleanInput('exclude_symlinks', {
|
||||
required: false
|
||||
})
|
||||
|
||||
const skipSameSha = core.getBooleanInput('skip_same_sha', {
|
||||
required: false
|
||||
})
|
||||
|
||||
const fetchMissingHistoryMaxRetries = core.getInput(
|
||||
'fetch_missing_history_max_retries',
|
||||
{required: false}
|
||||
@@ -310,6 +320,8 @@ export const getInputs = (): Inputs => {
|
||||
fetchAdditionalSubmoduleHistory,
|
||||
dirNamesDeletedFilesIncludeOnlyDeletedDirs,
|
||||
excludeSubmodules,
|
||||
excludeSymlinks,
|
||||
skipSameSha,
|
||||
usePosixPathSeparator,
|
||||
tagsPattern,
|
||||
tagsIgnorePattern,
|
||||
|
||||
66
src/main.ts
66
src/main.ts
@@ -5,8 +5,10 @@ import {
|
||||
processChangedFiles,
|
||||
ChangeTypeEnum,
|
||||
getAllDiffFiles,
|
||||
filterSymlinksFromChangedFiles,
|
||||
getChangedFilesFromGithubAPI,
|
||||
getRenamedFiles
|
||||
getRenamedFiles,
|
||||
ChangedFiles
|
||||
} from './changedFiles'
|
||||
import {
|
||||
DiffResult,
|
||||
@@ -127,11 +129,57 @@ const getChangedFilesFromLocalGitHistory = async ({
|
||||
return
|
||||
}
|
||||
|
||||
if (diffResult.sameSha) {
|
||||
core.info('Base and head SHAs are identical; no changed files to report.')
|
||||
const emptyChangedFiles: ChangedFiles = {
|
||||
[ChangeTypeEnum.Added]: [],
|
||||
[ChangeTypeEnum.Copied]: [],
|
||||
[ChangeTypeEnum.Deleted]: [],
|
||||
[ChangeTypeEnum.Modified]: [],
|
||||
[ChangeTypeEnum.Renamed]: [],
|
||||
[ChangeTypeEnum.TypeChanged]: [],
|
||||
[ChangeTypeEnum.Unmerged]: [],
|
||||
[ChangeTypeEnum.Unknown]: []
|
||||
}
|
||||
await processChangedFiles({
|
||||
filePatterns,
|
||||
allDiffFiles: emptyChangedFiles,
|
||||
inputs,
|
||||
yamlFilePatterns,
|
||||
workingDirectory
|
||||
})
|
||||
|
||||
if (inputs.includeAllOldNewRenamedFiles) {
|
||||
await setOutput({
|
||||
key: 'all_old_new_renamed_files',
|
||||
value: inputs.json ? [] : '',
|
||||
writeOutputFiles: inputs.writeOutputFiles,
|
||||
outputDir: inputs.outputDir,
|
||||
json: inputs.json,
|
||||
safeOutput: inputs.safeOutput
|
||||
})
|
||||
await setOutput({
|
||||
key: 'all_old_new_renamed_files_count',
|
||||
value: '0',
|
||||
writeOutputFiles: inputs.writeOutputFiles,
|
||||
outputDir: inputs.outputDir,
|
||||
json: inputs.json
|
||||
})
|
||||
}
|
||||
core.info('All Done!')
|
||||
core.endGroup()
|
||||
return
|
||||
}
|
||||
|
||||
core.info(
|
||||
`Retrieving changes between ${diffResult.previousSha} (${diffResult.targetBranch}) → ${diffResult.currentSha} (${diffResult.currentBranch})`
|
||||
)
|
||||
|
||||
const allDiffFiles = await getAllDiffFiles({
|
||||
const submoduleShas: Record<
|
||||
string,
|
||||
{previousSha?: string; currentSha?: string}
|
||||
> = {}
|
||||
let allDiffFiles = await getAllDiffFiles({
|
||||
workingDirectory,
|
||||
diffSubmodule,
|
||||
diffResult,
|
||||
@@ -139,8 +187,20 @@ const getChangedFilesFromLocalGitHistory = async ({
|
||||
outputRenamedFilesAsDeletedAndAdded,
|
||||
fetchAdditionalSubmoduleHistory: inputs.fetchAdditionalSubmoduleHistory,
|
||||
failOnInitialDiffError: inputs.failOnInitialDiffError,
|
||||
failOnSubmoduleDiffError: inputs.failOnSubmoduleDiffError
|
||||
failOnSubmoduleDiffError: inputs.failOnSubmoduleDiffError,
|
||||
submoduleShas
|
||||
})
|
||||
|
||||
if (inputs.excludeSymlinks) {
|
||||
core.info('Excluding symlinks from the diff')
|
||||
allDiffFiles = await filterSymlinksFromChangedFiles({
|
||||
changedFiles: allDiffFiles,
|
||||
workingDirectory,
|
||||
diffResult,
|
||||
submodulePaths,
|
||||
submoduleShas
|
||||
})
|
||||
}
|
||||
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
|
||||
core.info('All Done!')
|
||||
core.endGroup()
|
||||
|
||||
110
src/utils.ts
110
src/utils.ts
@@ -152,6 +152,69 @@ export const exists = async (filePath: string): Promise<boolean> => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is a symlink on disk
|
||||
* @param cwd - working directory
|
||||
* @param filePath - path to check
|
||||
* @returns file is a symlink
|
||||
*/
|
||||
export const isSymlinkOnDisk = async ({
|
||||
cwd,
|
||||
filePath
|
||||
}: {
|
||||
cwd: string
|
||||
filePath: string
|
||||
}): Promise<boolean> => {
|
||||
try {
|
||||
const stat = await fs.lstat(path.join(cwd, filePath))
|
||||
return stat.isSymbolicLink()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is a symlink in a git tree
|
||||
* @param cwd - working directory
|
||||
* @param sha - commit sha
|
||||
* @param filePath - path to check
|
||||
* @returns file is a symlink
|
||||
*/
|
||||
export const isSymlinkInGitTree = async ({
|
||||
cwd,
|
||||
sha,
|
||||
filePath
|
||||
}: {
|
||||
cwd: string
|
||||
sha: string
|
||||
filePath: string
|
||||
}): Promise<boolean> => {
|
||||
if (!sha) {
|
||||
return false
|
||||
}
|
||||
const {stdout, exitCode} = await exec.getExecOutput(
|
||||
'git',
|
||||
['ls-tree', '-r', sha, '--', filePath],
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
if (exitCode !== 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
const line = stdout.split('\n').find(Boolean)
|
||||
if (!line) {
|
||||
return false
|
||||
}
|
||||
|
||||
const [mode] = line.split(/\s+/)
|
||||
return mode === '120000'
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates lines of a file as an async iterable iterator
|
||||
* @param filePath - path of file to read
|
||||
@@ -819,14 +882,49 @@ export const cleanShaInput = async ({
|
||||
|
||||
if (exitCode !== 0) {
|
||||
const octokit = github.getOctokit(token)
|
||||
// If it's not a valid commit sha, assume it's a branch name and get the HEAD sha
|
||||
const {data: refData} = await octokit.rest.git.getRef({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
ref: `heads/${sha}`
|
||||
const owner = github.context.repo.owner
|
||||
const repo = github.context.repo.repo
|
||||
const isNotFoundError = (error: unknown): boolean =>
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'status' in error &&
|
||||
(error as {status?: number}).status === 404
|
||||
// If it's not a valid commit sha, assume it's a ref name first.
|
||||
try {
|
||||
const {data: refData} = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${sha}`
|
||||
})
|
||||
|
||||
return refData.object.sha
|
||||
} catch (error) {
|
||||
if (!isNotFoundError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const {data: refData} = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${sha}`
|
||||
})
|
||||
|
||||
return refData.object.sha
|
||||
} catch (error) {
|
||||
if (!isNotFoundError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const {data: commitData} = await octokit.rest.git.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
commit_sha: sha
|
||||
})
|
||||
|
||||
return refData.object.sha
|
||||
return commitData.sha
|
||||
}
|
||||
|
||||
return stdout.trim()
|
||||
|
||||
1
test/symlink-target.txt
Normal file
1
test/symlink-target.txt
Normal file
@@ -0,0 +1 @@
|
||||
Symlink target fixture.
|
||||
1
test/symlink-to-target
Symbolic link
1
test/symlink-to-target
Symbolic link
@@ -0,0 +1 @@
|
||||
symlink-target.txt
|
||||
Reference in New Issue
Block a user