Skip to content

Commit

Permalink
feat: add support for matching files in .gitignore
Browse files Browse the repository at this point in the history
  • Loading branch information
jackton1 committed Dec 15, 2022
1 parent b46bc77 commit 58485b4
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 21 deletions.
63 changes: 62 additions & 1 deletion src/__tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,18 @@ test('returns the paths of the filtered files in the paths-output-file', async (
// @ts-ignore
core.setOutput = jest.fn()

const pathsOutputFile = await tempfile('.txt')
const {tempfile: defaultTempfile} = await import('../utils')

const pathsOutputFile = await defaultTempfile('.txt')

// @ts-ignore
tempfile = jest.fn().mockResolvedValue(pathsOutputFile)

await run()

// @ts-ignore
tempfile = defaultTempfile

expect(core.setOutput).toHaveBeenNthCalledWith(
1,
'paths-output-file',
Expand All @@ -200,8 +205,64 @@ test('exits when the paths-output-file cannot be created', async () => {

const expectedError = new Error('Cannot create file')

const {tempfile: defaultTempfile} = await import('../utils')

// @ts-ignore
tempfile = jest.fn().mockRejectedValue(expectedError)

await expect(run()).rejects.toThrow(expectedError)

// @ts-ignore
tempfile = defaultTempfile
})

test('excludes default excluded files', async () => {
mockedEnv({
...defaultEnv,
INPUT_FILES: '**/node_modules/**\n.git/**',
'INPUT_FILES-FROM-SOURCE-FILE': 'src/__tests__/source-files.txt'
})

const EXPECTED_FILENAMES = [
'.github/workflows/greetings.yml',
'CODE_OF_CONDUCT.md',
'CONTRIBUTING.md',
'HISTORY.md',
'README.md'
]
.map(fName => normalizeSeparators(fName))
.join(process.env.INPUT_SEPARATOR)

// @ts-ignore
core.setOutput = jest.fn()

await run()

expect(core.setOutput).toHaveBeenNthCalledWith(2, 'paths', EXPECTED_FILENAMES)
})

test('includes patterns provided in the files input that are excluded in the .gitignore file', async () => {
mockedEnv({
...defaultEnv,
INPUT_FILES: 'coverage/clover.xml',
'INPUT_FILES-FROM-SOURCE-FILE': 'src/__tests__/source-files.txt'
})

const EXPECTED_FILENAMES = [
'.github/workflows/greetings.yml',
'CODE_OF_CONDUCT.md',
'CONTRIBUTING.md',
'HISTORY.md',
'README.md',
'coverage/clover.xml'
]
.map(fName => normalizeSeparators(fName))
.join(process.env.INPUT_SEPARATOR)

// @ts-ignore
core.setOutput = jest.fn()

await run()

expect(core.setOutput).toHaveBeenNthCalledWith(2, 'paths', EXPECTED_FILENAMES)
})
68 changes: 48 additions & 20 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {GlobOptions} from '@actions/glob'
import * as path from 'path'
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import {GlobOptions} from '@actions/glob'
import {existsSync, promises as fs} from 'fs'
import * as path from 'path'

import {
escapeString,
getDeletedFiles,
getFilesFromSourceFile,
normalizeSeparators,
escapeString,
tempfile
} from './utils'

Expand Down Expand Up @@ -74,18 +74,12 @@ export async function run(): Promise<void> {
core.getInput('working-directory', {required: true})
)

const gitignorePath = path.join(workingDirectory, '.gitignore')

let gitignoreExcludedFiles: string[] = []

if (existsSync(gitignorePath)) {
gitignoreExcludedFiles = await getFilesFromSourceFile({
filePaths: [gitignorePath],
excludedFiles: true
})
const globOptions: GlobOptions = {
followSymbolicLinks,
matchDirectories
}

core.debug(`.gitignore excluded files: ${gitignoreExcludedFiles.join(', ')}`)
const gitignorePath = path.join(workingDirectory, '.gitignore')

let filePatterns = files
.split(filesSeparator)
Expand Down Expand Up @@ -154,9 +148,7 @@ export async function run(): Promise<void> {
}
}

filePatterns += `\n${[...DEFAULT_EXCLUDED_FILES, ...gitignoreExcludedFiles]
.filter(p => !!p)
.join('\n')}`
filePatterns += `\n${DEFAULT_EXCLUDED_FILES.filter(p => !!p).join('\n')}`

filePatterns = [...new Set(filePatterns.split('\n').filter(p => p !== ''))]
.map(pt => {
Expand All @@ -179,19 +171,55 @@ export async function run(): Promise<void> {
})
.join('\n')

let allInclusive = false

if (filePatterns.split('\n').filter(p => !p.startsWith('!')).length === 0) {
allInclusive = true
filePatterns = `**\n${filePatterns}`
}

core.debug(`file patterns: ${filePatterns}`)

const globOptions: GlobOptions = {
followSymbolicLinks,
matchDirectories
}
const globber = await glob.create(filePatterns, globOptions)
let paths = await globber.glob()

if (existsSync(gitignorePath)) {
const gitignoreFilePatterns = (
await getFilesFromSourceFile({
filePaths: [gitignorePath]
})
)
.filter(p => !!p)
.map(pt => {
const parts = pt.split(path.sep)

const absolutePath = path.resolve(path.join(workingDirectory, parts[0]))

return path.join(absolutePath, ...parts.slice(1))
})
.join('\n')

const gitIgnoreGlobber = await glob.create(
gitignoreFilePatterns,
globOptions
)

const gitignoreMatchingFiles = await gitIgnoreGlobber.glob()

if (allInclusive) {
paths = paths.filter(p => !gitignoreMatchingFiles.includes(p))
} else {
paths = paths.filter(
p =>
![
...gitignoreMatchingFiles.filter(
pt => !paths.filter(pf => !pf.startsWith('!')).includes(pt)
)
].includes(p)
)
}
}

if (includeDeletedFiles) {
paths = paths.concat(
await getDeletedFiles({
Expand Down

0 comments on commit 58485b4

Please sign in to comment.