feat: add support for retrieving changed files via github rest api (#1289)
Co-authored-by: GitHub Action <action@github.com> Co-authored-by: tj-actions[bot] <109116665+tj-actions-bot@users.noreply.github.com> Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import type {RestEndpointMethodTypes} from '@octokit/rest'
|
||||
import * as path from 'path'
|
||||
|
||||
import {DiffResult} from './commitSha'
|
||||
import {Env} from './env'
|
||||
import {Inputs} from './inputs'
|
||||
import {
|
||||
getDirnameMaxDepth,
|
||||
@@ -248,3 +252,65 @@ export const getAllChangeTypeFiles = async ({
|
||||
count: files.length.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export const getChangedFilesFromGithubAPI = async ({
|
||||
inputs,
|
||||
env
|
||||
}: {
|
||||
inputs: Inputs
|
||||
env: Env
|
||||
}): Promise<ChangedFiles> => {
|
||||
const octokit = github.getOctokit(inputs.token)
|
||||
const changedFiles: ChangedFiles = {
|
||||
[ChangeTypeEnum.Added]: [],
|
||||
[ChangeTypeEnum.Copied]: [],
|
||||
[ChangeTypeEnum.Deleted]: [],
|
||||
[ChangeTypeEnum.Modified]: [],
|
||||
[ChangeTypeEnum.Renamed]: [],
|
||||
[ChangeTypeEnum.TypeChanged]: [],
|
||||
[ChangeTypeEnum.Unmerged]: [],
|
||||
[ChangeTypeEnum.Unknown]: []
|
||||
}
|
||||
|
||||
core.info('Getting changed files from GitHub API...')
|
||||
|
||||
const options = octokit.rest.pulls.listFiles.endpoint.merge({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
pull_number: env.GITHUB_EVENT_PULL_REQUEST_NUMBER,
|
||||
per_page: 100
|
||||
})
|
||||
|
||||
const paginatedResponse = await octokit.paginate<
|
||||
RestEndpointMethodTypes['pulls']['listFiles']['response']['data'][0]
|
||||
>(options)
|
||||
|
||||
core.info(`Got ${paginatedResponse.length} changed files from GitHub API`)
|
||||
const statusMap: Record<string, ChangeTypeEnum> = {
|
||||
added: ChangeTypeEnum.Added,
|
||||
removed: ChangeTypeEnum.Deleted,
|
||||
modified: ChangeTypeEnum.Modified,
|
||||
renamed: ChangeTypeEnum.Renamed,
|
||||
copied: ChangeTypeEnum.Copied,
|
||||
changed: ChangeTypeEnum.TypeChanged,
|
||||
unchanged: ChangeTypeEnum.Unmerged
|
||||
}
|
||||
|
||||
for await (const item of paginatedResponse) {
|
||||
const changeType: ChangeTypeEnum =
|
||||
statusMap[item.status] || ChangeTypeEnum.Unknown
|
||||
|
||||
if (changeType === ChangeTypeEnum.Renamed) {
|
||||
if (inputs.outputRenamedFilesAsDeletedAndAdded) {
|
||||
changedFiles[ChangeTypeEnum.Deleted].push(item.filename)
|
||||
changedFiles[ChangeTypeEnum.Added].push(item.filename)
|
||||
} else {
|
||||
changedFiles[ChangeTypeEnum.Renamed].push(item.filename)
|
||||
}
|
||||
} else {
|
||||
changedFiles[changeType].push(item.filename)
|
||||
}
|
||||
}
|
||||
|
||||
return changedFiles
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ export const setChangedFilesOutput = async ({
|
||||
outputPrefix = ''
|
||||
}: {
|
||||
allDiffFiles: ChangedFiles
|
||||
filePatterns?: string[]
|
||||
inputs: Inputs
|
||||
workingDirectory: string
|
||||
diffResult: DiffResult
|
||||
diffResult?: DiffResult
|
||||
filePatterns?: string[]
|
||||
outputPrefix?: string
|
||||
}): Promise<void> => {
|
||||
const allFilteredDiffFiles = await getFilteredChangedFiles({
|
||||
@@ -34,12 +34,14 @@ export const setChangedFilesOutput = async ({
|
||||
})
|
||||
core.debug(`All filtered diff files: ${JSON.stringify(allFilteredDiffFiles)}`)
|
||||
|
||||
await recoverDeletedFiles({
|
||||
inputs,
|
||||
workingDirectory,
|
||||
deletedFiles: allFilteredDiffFiles[ChangeTypeEnum.Deleted],
|
||||
sha: diffResult.previousSha
|
||||
})
|
||||
if (diffResult) {
|
||||
await recoverDeletedFiles({
|
||||
inputs,
|
||||
workingDirectory,
|
||||
deletedFiles: allFilteredDiffFiles[ChangeTypeEnum.Deleted],
|
||||
sha: diffResult.previousSha
|
||||
})
|
||||
}
|
||||
|
||||
const addedFiles = await getChangeTypeFiles({
|
||||
inputs,
|
||||
|
||||
@@ -17,6 +17,8 @@ export type Env = {
|
||||
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: string
|
||||
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: string
|
||||
GITHUB_EVENT_PULL_REQUEST_BASE_REF: string
|
||||
GITHUB_REPOSITORY_OWNER: string
|
||||
GITHUB_REPOSITORY: string
|
||||
}
|
||||
|
||||
type GithubEvent = {
|
||||
@@ -71,6 +73,8 @@ export const getEnv = async (): Promise<Env> => {
|
||||
GITHUB_REF_NAME: process.env.GITHUB_REF_NAME || '',
|
||||
GITHUB_REF: process.env.GITHUB_REF || '',
|
||||
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE || '',
|
||||
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || ''
|
||||
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || '',
|
||||
GITHUB_REPOSITORY_OWNER: process.env.GITHUB_REPOSITORY_OWNER || '',
|
||||
GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY || ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ export type Inputs = {
|
||||
outputRenamedFilesAsDeletedAndAdded: boolean
|
||||
recoverDeletedFiles: boolean
|
||||
recoverDeletedFilesToDestination: string
|
||||
token: string
|
||||
api_url: string
|
||||
}
|
||||
|
||||
export const getInputs = (): Inputs => {
|
||||
@@ -154,6 +156,8 @@ export const getInputs = (): Inputs => {
|
||||
'recover_deleted_files_to_destination',
|
||||
{required: false}
|
||||
)
|
||||
const token = core.getInput('token', {required: false})
|
||||
const api_url = core.getInput('api_url', {required: false})
|
||||
|
||||
const inputs: Inputs = {
|
||||
files,
|
||||
@@ -171,9 +175,7 @@ export const getInputs = (): Inputs => {
|
||||
filesIgnoreYamlFromSourceFile,
|
||||
filesIgnoreYamlFromSourceFileSeparator,
|
||||
separator,
|
||||
includeAllOldNewRenamedFiles,
|
||||
oldNewSeparator,
|
||||
oldNewFilesSeparator,
|
||||
// Not Supported via REST API
|
||||
sha,
|
||||
baseSha,
|
||||
since,
|
||||
@@ -181,17 +183,23 @@ export const getInputs = (): Inputs => {
|
||||
path,
|
||||
quotePath,
|
||||
diffRelative,
|
||||
sinceLastRemoteCommit,
|
||||
recoverDeletedFiles,
|
||||
recoverDeletedFilesToDestination,
|
||||
includeAllOldNewRenamedFiles,
|
||||
oldNewSeparator,
|
||||
oldNewFilesSeparator,
|
||||
// End Not Supported via REST API
|
||||
dirNames,
|
||||
dirNamesExcludeRoot,
|
||||
dirNamesExcludeCurrentDir,
|
||||
json,
|
||||
escapeJson,
|
||||
sinceLastRemoteCommit,
|
||||
writeOutputFiles,
|
||||
outputDir,
|
||||
outputRenamedFilesAsDeletedAndAdded,
|
||||
recoverDeletedFiles,
|
||||
recoverDeletedFilesToDestination
|
||||
token,
|
||||
api_url
|
||||
}
|
||||
|
||||
if (fetchDepth) {
|
||||
|
||||
150
src/main.ts
150
src/main.ts
@@ -1,18 +1,23 @@
|
||||
import * as core from '@actions/core'
|
||||
import path from 'path'
|
||||
import {getAllDiffFiles, getRenamedFiles} from './changedFiles'
|
||||
import {
|
||||
getAllDiffFiles,
|
||||
getChangedFilesFromGithubAPI,
|
||||
getRenamedFiles
|
||||
} from './changedFiles'
|
||||
import {setChangedFilesOutput} from './changedFilesOutput'
|
||||
import {
|
||||
DiffResult,
|
||||
getSHAForPullRequestEvent,
|
||||
getSHAForPushEvent
|
||||
} from './commitSha'
|
||||
import {getEnv} from './env'
|
||||
import {getInputs} from './inputs'
|
||||
import {Env, getEnv} from './env'
|
||||
import {getInputs, Inputs} from './inputs'
|
||||
import {
|
||||
getFilePatterns,
|
||||
getSubmodulePath,
|
||||
getYamlFilePatterns,
|
||||
hasLocalGitDirectory,
|
||||
isRepoShallow,
|
||||
setOutput,
|
||||
submoduleExists,
|
||||
@@ -20,14 +25,15 @@ import {
|
||||
verifyMinimumGitVersion
|
||||
} from './utils'
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
core.startGroup('changed-files')
|
||||
|
||||
const env = await getEnv()
|
||||
core.debug(`Env: ${JSON.stringify(env, null, 2)}`)
|
||||
const inputs = getInputs()
|
||||
core.debug(`Inputs: ${JSON.stringify(inputs, null, 2)}`)
|
||||
|
||||
const getChangedFilesFromLocalGit = async ({
|
||||
inputs,
|
||||
env,
|
||||
workingDirectory
|
||||
}: {
|
||||
inputs: Inputs
|
||||
env: Env
|
||||
workingDirectory: string
|
||||
}): Promise<void> => {
|
||||
await verifyMinimumGitVersion()
|
||||
|
||||
let quotePathValue = 'on'
|
||||
@@ -48,10 +54,6 @@ export async function run(): Promise<void> {
|
||||
})
|
||||
}
|
||||
|
||||
const workingDirectory = path.resolve(
|
||||
env.GITHUB_WORKSPACE || process.cwd(),
|
||||
inputs.path
|
||||
)
|
||||
const isShallow = await isRepoShallow({cwd: workingDirectory})
|
||||
const hasSubmodule = await submoduleExists({cwd: workingDirectory})
|
||||
let gitFetchExtraArgs = ['--no-tags', '--prune', '--recurse-submodules']
|
||||
@@ -196,6 +198,124 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const getChangedFilesFromRESTAPI = async ({
|
||||
inputs,
|
||||
env,
|
||||
workingDirectory
|
||||
}: {
|
||||
inputs: Inputs
|
||||
env: Env
|
||||
workingDirectory: string
|
||||
}): Promise<void> => {
|
||||
const allDiffFiles = await getChangedFilesFromGithubAPI({
|
||||
inputs,
|
||||
env
|
||||
})
|
||||
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
|
||||
core.info('All Done!')
|
||||
|
||||
const filePatterns = await getFilePatterns({
|
||||
inputs,
|
||||
workingDirectory
|
||||
})
|
||||
core.debug(`File patterns: ${filePatterns}`)
|
||||
|
||||
if (filePatterns.length > 0) {
|
||||
core.startGroup('changed-files-patterns')
|
||||
await setChangedFilesOutput({
|
||||
allDiffFiles,
|
||||
filePatterns,
|
||||
inputs,
|
||||
workingDirectory
|
||||
})
|
||||
core.info('All Done!')
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
const yamlFilePatterns = await getYamlFilePatterns({
|
||||
inputs,
|
||||
workingDirectory
|
||||
})
|
||||
core.debug(`Yaml file patterns: ${JSON.stringify(yamlFilePatterns)}`)
|
||||
|
||||
if (Object.keys(yamlFilePatterns).length > 0) {
|
||||
for (const key of Object.keys(yamlFilePatterns)) {
|
||||
core.startGroup(`changed-files-yaml-${key}`)
|
||||
await setChangedFilesOutput({
|
||||
allDiffFiles,
|
||||
filePatterns: yamlFilePatterns[key],
|
||||
outputPrefix: key,
|
||||
inputs,
|
||||
workingDirectory
|
||||
})
|
||||
core.info('All Done!')
|
||||
core.endGroup()
|
||||
}
|
||||
}
|
||||
|
||||
if (filePatterns.length === 0 && Object.keys(yamlFilePatterns).length === 0) {
|
||||
core.startGroup('changed-files-all')
|
||||
await setChangedFilesOutput({
|
||||
allDiffFiles,
|
||||
inputs,
|
||||
workingDirectory
|
||||
})
|
||||
core.info('All Done!')
|
||||
core.endGroup()
|
||||
}
|
||||
}
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
core.startGroup('changed-files')
|
||||
|
||||
const env = await getEnv()
|
||||
core.debug(`Env: ${JSON.stringify(env, null, 2)}`)
|
||||
const inputs = getInputs()
|
||||
core.debug(`Inputs: ${JSON.stringify(inputs, null, 2)}`)
|
||||
const workingDirectory = path.resolve(
|
||||
env.GITHUB_WORKSPACE || process.cwd(),
|
||||
inputs.path
|
||||
)
|
||||
const hasGitDirectory = await hasLocalGitDirectory({workingDirectory})
|
||||
|
||||
if (
|
||||
inputs.token &&
|
||||
env.GITHUB_EVENT_PULL_REQUEST_NUMBER &&
|
||||
!hasGitDirectory
|
||||
) {
|
||||
core.info("Using GitHub's REST API to get changed files")
|
||||
const unsupportedInputs: (keyof Inputs)[] = [
|
||||
'sha',
|
||||
'baseSha',
|
||||
'since',
|
||||
'until',
|
||||
'sinceLastRemoteCommit',
|
||||
'recoverDeletedFiles',
|
||||
'recoverDeletedFilesToDestination',
|
||||
'includeAllOldNewRenamedFiles'
|
||||
]
|
||||
|
||||
for (const input of unsupportedInputs) {
|
||||
if (inputs[input]) {
|
||||
core.warning(
|
||||
`Input "${input}" is not supported when using GitHub's REST API to get changed files`
|
||||
)
|
||||
}
|
||||
}
|
||||
await getChangedFilesFromRESTAPI({inputs, env, workingDirectory})
|
||||
} else {
|
||||
if (!hasGitDirectory) {
|
||||
core.setFailed(
|
||||
"Can't find local .git directory. Please run actions/checkout before this action"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
core.info('Using local .git directory')
|
||||
await getChangedFilesFromLocalGit({inputs, env, workingDirectory})
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (!process.env.TESTING) {
|
||||
// eslint-disable-next-line github/no-then
|
||||
|
||||
47
src/utils.ts
47
src/utils.ts
@@ -97,7 +97,7 @@ export const verifyMinimumGitVersion = async (): Promise<void> => {
|
||||
const {exitCode, stdout, stderr} = await exec.getExecOutput(
|
||||
'git',
|
||||
['--version'],
|
||||
{silent: process.env.RUNNER_DEBUG !== '1'}
|
||||
{silent: !core.isDebug()}
|
||||
)
|
||||
|
||||
if (exitCode !== 0) {
|
||||
@@ -181,7 +181,7 @@ export const updateGitGlobalConfig = async ({
|
||||
['config', '--global', name, value],
|
||||
{
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -197,7 +197,7 @@ export const isRepoShallow = async ({cwd}: {cwd: string}): Promise<boolean> => {
|
||||
['rev-parse', '--is-shallow-repository'],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -215,7 +215,7 @@ export const submoduleExists = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -236,7 +236,7 @@ export const gitFetch = async ({
|
||||
const {exitCode} = await exec.getExecOutput('git', ['fetch', '-q', ...args], {
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
})
|
||||
|
||||
return exitCode
|
||||
@@ -255,7 +255,7 @@ export const gitFetchSubmodules = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -280,7 +280,7 @@ export const getSubmodulePath = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -313,7 +313,7 @@ export const gitSubmoduleDiffSHA = async ({
|
||||
['diff', parentSha1, parentSha2, '--', submodulePath],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -366,7 +366,7 @@ export const gitRenamedFiles = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -436,7 +436,7 @@ export const getAllChangedFiles = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
const changedFiles: ChangedFiles = {
|
||||
@@ -537,7 +537,7 @@ export const gitLog = async ({
|
||||
}): Promise<string> => {
|
||||
const {stdout} = await exec.getExecOutput('git', ['log', ...args], {
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
})
|
||||
|
||||
return stdout.trim()
|
||||
@@ -546,7 +546,7 @@ export const gitLog = async ({
|
||||
export const getHeadSha = async ({cwd}: {cwd: string}): Promise<string> => {
|
||||
const {stdout} = await exec.getExecOutput('git', ['rev-parse', 'HEAD'], {
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
})
|
||||
|
||||
return stdout.trim()
|
||||
@@ -564,7 +564,7 @@ export const getRemoteBranchHeadSha = async ({
|
||||
['rev-parse', `origin/${branch}`],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -578,7 +578,7 @@ export const getParentSha = async ({cwd}: {cwd: string}): Promise<string> => {
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -604,7 +604,7 @@ export const verifyCommitSha = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -634,7 +634,7 @@ export const getPreviousGitTag = async ({
|
||||
['tag', '--sort=-version:refname'],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -652,7 +652,7 @@ export const getPreviousGitTag = async ({
|
||||
['rev-parse', previousTag],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -678,7 +678,7 @@ export const canDiffCommits = async ({
|
||||
{
|
||||
cwd,
|
||||
ignoreReturnCode: true,
|
||||
silent: process.env.RUNNER_DEBUG !== '1'
|
||||
silent: !core.isDebug()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1047,7 +1047,7 @@ const getDeletedFileContents = async ({
|
||||
['show', `${sha}:${filePath}`],
|
||||
{
|
||||
cwd,
|
||||
silent: process.env.RUNNER_DEBUG !== '1',
|
||||
silent: !core.isDebug(),
|
||||
ignoreReturnCode: true
|
||||
}
|
||||
)
|
||||
@@ -1097,3 +1097,12 @@ export const recoverDeletedFiles = async ({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const hasLocalGitDirectory = async ({
|
||||
workingDirectory
|
||||
}: {
|
||||
workingDirectory: string
|
||||
}): Promise<boolean> => {
|
||||
const gitDirectory = path.join(workingDirectory, '.git')
|
||||
return await exists(gitDirectory)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user