3
0
Fork 0
mirror of https://github.com/tj-actions/changed-files synced 2025-02-06 09:41:22 +00:00

feat: add support for recovering deleted files (#1269)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tonye Jack 2023-06-16 18:57:12 -06:00 committed by GitHub
parent 0621d936c0
commit 77f9e6c7c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 222 additions and 5 deletions

View file

@ -601,6 +601,88 @@ jobs:
shell:
bash
test_recover_deleted_file:
name: Test changed-files recover deleted file
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
max-parallel: 4
matrix:
fetch-depth: [0, 1, 2]
steps:
- name: Checkout branch
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
fetch-depth: ${{ matrix.fetch-depth }}
- name: Download build assets
uses: actions/download-artifact@v3
with:
name: build-assets
- name: Run changed-files with recover_deleted_files
id: changed-files-recover-deleted-files
uses: ./
with:
base_sha: "fcdeb5b3d797752d95f6dbe98552a95c29dad338"
sha: "432e0c810c60ef1332850a971c5ec39022034b4c"
recover_deleted_files: true
- name: Show output
run: |
echo "${{ toJSON(steps.changed-files-recover-deleted-files.outputs) }}"
shell:
bash
- name: Verify deleted files
if: steps.changed-files-recover-deleted-files.outputs.deleted_files != 'test/test deleted.txt'
run: |
echo "Expected: (test/test deleted.txt) got ${{ steps.changed-files-recover-deleted-files.outputs.deleted_files }}"
exit 1
- name: Verify that test/test deleted.txt is restored
run: |
if [ ! -f "test/test deleted.txt" ]; then
echo "Expected: (test/test deleted.txt) to exist"
exit 1
else
cat "test/test deleted.txt"
fi
- name: Run changed-files with recover_deleted_files and recover_deleted_files_to_destination
id: changed-files-recover-deleted-files-to-destination
uses: ./
with:
base_sha: "fcdeb5b3d797752d95f6dbe98552a95c29dad338"
sha: "432e0c810c60ef1332850a971c5ec39022034b4c"
recover_deleted_files: true
recover_deleted_files_to_destination: "deleted_files"
- name: Show output
run: |
echo "${{ toJSON(steps.changed-files-recover-deleted-files-to-destination.outputs) }}"
shell:
bash
- name: Verify deleted files
if: steps.changed-files-recover-deleted-files-to-destination.outputs.deleted_files != 'test/test deleted.txt'
run: |
echo "Expected: (test/test deleted.txt) got ${{ steps.changed-files-recover-deleted-files-to-destination.outputs.deleted_files }}"
exit 1
- name: Verify that test/test deleted.txt is restored
run: |
if [ ! -f "deleted_files/test/test deleted.txt" ]; then
echo "Expected: (deleted_files/test/test deleted.txt) to exist"
exit 1
else
cat "deleted_files/test/test deleted.txt"
fi
test:
name: Test changed-files
runs-on: ${{ matrix.platform }}

View file

@ -145,6 +145,14 @@ inputs:
description: "Output renamed files as deleted and added files."
required: false
default: "false"
recover_deleted_files:
description: "Recover deleted files."
required: false
default: "false"
recover_deleted_files_to_destination:
description: "Recover deleted files to a new destination directory, defaults to the original location."
required: false
default: ""
outputs:
added_files:

47
dist/index.js generated vendored
View file

@ -1073,6 +1073,10 @@ const getInputs = () => {
});
const outputDir = core.getInput('output_dir', { required: false });
const outputRenamedFilesAsDeletedAndAdded = core.getBooleanInput('output_renamed_files_as_deleted_and_added', { required: false });
const recoverDeletedFiles = core.getBooleanInput('recover_deleted_files', {
required: false
});
const recoverDeletedFilesToDestination = core.getInput('recover_deleted_files_to_destination', { required: false });
const inputs = {
files,
filesSeparator,
@ -1107,7 +1111,9 @@ const getInputs = () => {
sinceLastRemoteCommit,
writeOutputFiles,
outputDir,
outputRenamedFilesAsDeletedAndAdded
outputRenamedFilesAsDeletedAndAdded,
recoverDeletedFiles,
recoverDeletedFilesToDestination
};
if (fetchDepth) {
inputs.fetchDepth = Math.max(parseInt(fetchDepth, 10), 2);
@ -1233,6 +1239,12 @@ function run() {
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`);
core.info('All Done!');
core.endGroup();
yield (0, utils_1.recoverDeletedFiles)({
inputs,
workingDirectory,
deletedFiles: allDiffFiles[changedFiles_1.ChangeTypeEnum.Deleted],
sha: diffResult.previousSha
});
const filePatterns = yield (0, utils_1.getFilePatterns)({
inputs,
workingDirectory
@ -1367,7 +1379,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.setOutput = exports.getYamlFilePatterns = exports.getFilePatterns = exports.jsonOutput = exports.getDirnameMaxDepth = exports.canDiffCommits = exports.getPreviousGitTag = exports.verifyCommitSha = exports.getParentSha = exports.getRemoteBranchHeadSha = 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.verifyMinimumGitVersion = void 0;
exports.recoverDeletedFiles = exports.setOutput = exports.getYamlFilePatterns = exports.getFilePatterns = exports.jsonOutput = exports.getDirnameMaxDepth = exports.canDiffCommits = exports.getPreviousGitTag = exports.verifyCommitSha = exports.getParentSha = exports.getRemoteBranchHeadSha = 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.verifyMinimumGitVersion = void 0;
/*global AsyncIterableIterator*/
const core = __importStar(__nccwpck_require__(2186));
const exec = __importStar(__nccwpck_require__(1514));
@ -2039,6 +2051,37 @@ const setOutput = ({ key, value, inputs }) => __awaiter(void 0, void 0, void 0,
}
});
exports.setOutput = setOutput;
const getDeletedFileContents = ({ cwd, filePath, sha }) => __awaiter(void 0, void 0, void 0, function* () {
const { stdout, exitCode, stderr } = yield exec.getExecOutput('git', ['show', `${sha}:${filePath}`], {
cwd,
silent: process.env.RUNNER_DEBUG !== '1',
ignoreReturnCode: true
});
if (exitCode !== 0) {
throw new Error(`Error getting file content from git history "${filePath}": ${stderr}`);
}
return stdout;
});
const recoverDeletedFiles = ({ inputs, workingDirectory, deletedFiles, sha }) => __awaiter(void 0, void 0, void 0, function* () {
if (inputs.recoverDeletedFiles) {
for (const deletedFile of deletedFiles) {
let target = path.join(workingDirectory, deletedFile);
if (inputs.recoverDeletedFilesToDestination) {
target = path.join(workingDirectory, inputs.recoverDeletedFilesToDestination, deletedFile);
}
const deletedFileContents = yield getDeletedFileContents({
cwd: workingDirectory,
filePath: deletedFile,
sha
});
if (!(yield exists(path.dirname(target)))) {
yield fs_1.promises.mkdir(path.dirname(target), { recursive: true });
}
yield fs_1.promises.writeFile(target, deletedFileContents);
}
}
});
exports.recoverDeletedFiles = recoverDeletedFiles;
/***/ }),

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View file

@ -37,6 +37,8 @@ export type Inputs = {
writeOutputFiles: boolean
outputDir: string
outputRenamedFilesAsDeletedAndAdded: boolean
recoverDeletedFiles: boolean
recoverDeletedFilesToDestination: string
}
export const getInputs = (): Inputs => {
@ -145,6 +147,13 @@ export const getInputs = (): Inputs => {
'output_renamed_files_as_deleted_and_added',
{required: false}
)
const recoverDeletedFiles = core.getBooleanInput('recover_deleted_files', {
required: false
})
const recoverDeletedFilesToDestination = core.getInput(
'recover_deleted_files_to_destination',
{required: false}
)
const inputs: Inputs = {
files,
@ -180,7 +189,9 @@ export const getInputs = (): Inputs => {
sinceLastRemoteCommit,
writeOutputFiles,
outputDir,
outputRenamedFilesAsDeletedAndAdded
outputRenamedFilesAsDeletedAndAdded,
recoverDeletedFiles,
recoverDeletedFilesToDestination
}
if (fetchDepth) {

View file

@ -1,6 +1,6 @@
import * as core from '@actions/core'
import path from 'path'
import {getAllDiffFiles, getRenamedFiles} from './changedFiles'
import {ChangeTypeEnum, getAllDiffFiles, getRenamedFiles} from './changedFiles'
import {setChangedFilesOutput} from './changedFilesOutput'
import {
DiffResult,
@ -14,6 +14,7 @@ import {
getSubmodulePath,
getYamlFilePatterns,
isRepoShallow,
recoverDeletedFiles,
setOutput,
submoduleExists,
updateGitGlobalConfig,
@ -118,6 +119,13 @@ export async function run(): Promise<void> {
core.info('All Done!')
core.endGroup()
await recoverDeletedFiles({
inputs,
workingDirectory,
deletedFiles: allDiffFiles[ChangeTypeEnum.Deleted],
sha: diffResult.previousSha
})
const filePatterns = await getFilePatterns({
inputs,
workingDirectory

View file

@ -1032,3 +1032,68 @@ export const setOutput = async ({
await fs.writeFile(outputFilePath, cleanedValue.replace(/\\"/g, '"'))
}
}
const getDeletedFileContents = async ({
cwd,
filePath,
sha
}: {
cwd: string
filePath: string
sha: string
}): Promise<string> => {
const {stdout, exitCode, stderr} = await exec.getExecOutput(
'git',
['show', `${sha}:${filePath}`],
{
cwd,
silent: process.env.RUNNER_DEBUG !== '1',
ignoreReturnCode: true
}
)
if (exitCode !== 0) {
throw new Error(
`Error getting file content from git history "${filePath}": ${stderr}`
)
}
return stdout
}
export const recoverDeletedFiles = async ({
inputs,
workingDirectory,
deletedFiles,
sha
}: {
inputs: Inputs
workingDirectory: string
deletedFiles: string[]
sha: string
}): Promise<void> => {
if (inputs.recoverDeletedFiles) {
for (const deletedFile of deletedFiles) {
let target = path.join(workingDirectory, deletedFile)
if (inputs.recoverDeletedFilesToDestination) {
target = path.join(
workingDirectory,
inputs.recoverDeletedFilesToDestination,
deletedFile
)
}
const deletedFileContents = await getDeletedFileContents({
cwd: workingDirectory,
filePath: deletedFile,
sha
})
if (!(await exists(path.dirname(target)))) {
await fs.mkdir(path.dirname(target), {recursive: true})
}
await fs.writeFile(target, deletedFileContents)
}
}
}