mirror of https://github.com/actions/toolkit
pull/271/head
parent
e29784d870
commit
e8758cfefd
|
@ -370,6 +370,7 @@ describe('glob (search)', () => {
|
||||||
await fs.mkdir(root, {recursive: true})
|
await fs.mkdir(root, {recursive: true})
|
||||||
await fs.writeFile(path.join(root, 'file'), 'test file content')
|
await fs.writeFile(path.join(root, 'file'), 'test file content')
|
||||||
await createSymlinkDir(root, path.join(root, 'symDir'))
|
await createSymlinkDir(root, path.join(root, 'symDir'))
|
||||||
|
await fs.stat(path.join(root, 'symDir'))
|
||||||
|
|
||||||
const itemPaths = await glob.glob(path.join(root, 'symDir'))
|
const itemPaths = await glob.glob(path.join(root, 'symDir'))
|
||||||
expect(itemPaths).toHaveLength(2)
|
expect(itemPaths).toHaveLength(2)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as patternHelper from './internal-pattern-helper'
|
import * as patternHelper from './internal-pattern-helper'
|
||||||
import {IGlobOptions} from './internal-glob-options'
|
import {IGlobOptions} from './internal-glob-options'
|
||||||
import {MatchResult} from './internal-match-result'
|
import {MatchKind} from './internal-match-kind'
|
||||||
import {Pattern} from './internal-pattern'
|
import {Pattern} from './internal-pattern'
|
||||||
import {SearchState} from './internal-search-state'
|
import {SearchState} from './internal-search-state'
|
||||||
|
|
||||||
|
@ -24,12 +24,9 @@ export async function glob(
|
||||||
// Parse patterns
|
// Parse patterns
|
||||||
const patterns: Pattern[] = patternHelper.parse([pattern], options)
|
const patterns: Pattern[] = patternHelper.parse([pattern], options)
|
||||||
|
|
||||||
// Get search paths
|
// Push the search paths
|
||||||
const searchPaths: string[] = patternHelper.getSearchPaths(patterns)
|
const stack: SearchState[] = []
|
||||||
|
for (const searchPath of patternHelper.getSearchPaths(patterns)) {
|
||||||
// Search
|
|
||||||
const result: string[] = []
|
|
||||||
for (const searchPath of searchPaths) {
|
|
||||||
// Exists? Note, intentionally using lstat. Detection for broken symlink
|
// Exists? Note, intentionally using lstat. Detection for broken symlink
|
||||||
// will be performed later (if following symlinks).
|
// will be performed later (if following symlinks).
|
||||||
try {
|
try {
|
||||||
|
@ -41,49 +38,56 @@ export async function glob(
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push the first item
|
stack.unshift(new SearchState(searchPath, 1))
|
||||||
const stack: SearchState[] = [new SearchState(searchPath, 1)]
|
}
|
||||||
const traversalChain: string[] = [] // used to detect cycles
|
|
||||||
|
|
||||||
while (stack.length) {
|
const result: string[] = []
|
||||||
// Pop
|
|
||||||
const item = stack.pop() as SearchState
|
|
||||||
const stats: fs.Stats | undefined = await stat(
|
|
||||||
item,
|
|
||||||
options,
|
|
||||||
traversalChain
|
|
||||||
)
|
|
||||||
|
|
||||||
// Broken symlink or symlink cycle detected
|
// Search
|
||||||
if (!stats) {
|
const traversalChain: string[] = [] // used to detect cycles
|
||||||
|
while (stack.length) {
|
||||||
|
// Pop
|
||||||
|
const item = stack.pop() as SearchState
|
||||||
|
|
||||||
|
// Match
|
||||||
|
const match = patternHelper.match(patterns, item.path)
|
||||||
|
if (!match) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat
|
||||||
|
const stats: fs.Stats | undefined = await stat(
|
||||||
|
item,
|
||||||
|
options,
|
||||||
|
traversalChain
|
||||||
|
)
|
||||||
|
|
||||||
|
// Broken symlink, or symlink cycle detected, or no longer exists
|
||||||
|
if (!stats) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directory
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
// Matched
|
||||||
|
if (match & MatchKind.Directory) {
|
||||||
|
result.push(item.path)
|
||||||
|
}
|
||||||
|
// Descend?
|
||||||
|
else if (!patternHelper.partialMatch(patterns, item.path)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match
|
// Push the child items in reverse
|
||||||
const matchResult = patternHelper.match(patterns, item.path)
|
const childLevel = item.level + 1
|
||||||
|
const childItems = (await fs.promises.readdir(item.path)).map(
|
||||||
// Directory
|
x => new SearchState(path.join(item.path, x), childLevel)
|
||||||
if (stats.isDirectory()) {
|
)
|
||||||
// Matched
|
stack.push(...childItems.reverse())
|
||||||
if (matchResult & MatchResult.Directory) {
|
}
|
||||||
result.push(item.path)
|
// File
|
||||||
}
|
else if (match & MatchKind.File) {
|
||||||
// Descend?
|
result.push(item.path)
|
||||||
else if (!patternHelper.partialMatch(patterns, item.path)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push the child items in reverse
|
|
||||||
const childLevel = item.level + 1
|
|
||||||
const childItems = (await fs.promises.readdir(item.path)).map(
|
|
||||||
x => new SearchState(path.join(item.path, x), childLevel)
|
|
||||||
)
|
|
||||||
stack.push(...childItems.reverse())
|
|
||||||
}
|
|
||||||
// File
|
|
||||||
else if (matchResult & MatchResult.File) {
|
|
||||||
result.push(item.path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Indicates whether a pattern matches a path
|
* Indicates whether a pattern matches a path
|
||||||
*/
|
*/
|
||||||
export enum MatchResult {
|
export enum MatchKind {
|
||||||
/** Not matched */
|
/** Not matched */
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
|
@ -76,6 +76,7 @@ export class Path {
|
||||||
!segment.includes(path.sep),
|
!segment.includes(path.sep),
|
||||||
`Parameter 'itemPath' contains unexpected path separators`
|
`Parameter 'itemPath' contains unexpected path separators`
|
||||||
)
|
)
|
||||||
|
this.segments.push(segment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as pathHelper from './internal-path-helper'
|
import * as pathHelper from './internal-path-helper'
|
||||||
import {IGlobOptions} from './internal-glob-options'
|
import {IGlobOptions} from './internal-glob-options'
|
||||||
import {MatchResult} from './internal-match-result'
|
import {MatchKind} from './internal-match-kind'
|
||||||
import {Pattern} from './internal-pattern'
|
import {Pattern} from './internal-pattern'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
@ -60,8 +60,8 @@ export function getSearchPaths(patterns: Pattern[]): string[] {
|
||||||
/**
|
/**
|
||||||
* Matches the patterns against the path
|
* Matches the patterns against the path
|
||||||
*/
|
*/
|
||||||
export function match(patterns: Pattern[], itemPath: string): MatchResult {
|
export function match(patterns: Pattern[], itemPath: string): MatchKind {
|
||||||
let result: MatchResult = MatchResult.None
|
let result: MatchKind = MatchKind.None
|
||||||
|
|
||||||
for (const pattern of patterns) {
|
for (const pattern of patterns) {
|
||||||
if (pattern.comment) {
|
if (pattern.comment) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as assert from 'assert'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as pathHelper from './internal-path-helper'
|
import * as pathHelper from './internal-path-helper'
|
||||||
import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch'
|
import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch'
|
||||||
import {MatchResult} from './internal-match-result'
|
import {MatchKind} from './internal-match-kind'
|
||||||
import {Path} from './internal-path'
|
import {Path} from './internal-path'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
@ -64,16 +64,16 @@ export class Pattern {
|
||||||
/**
|
/**
|
||||||
* Matches the pattern against the specified path
|
* Matches the pattern against the specified path
|
||||||
*/
|
*/
|
||||||
match(itemPath: string): MatchResult {
|
match(itemPath: string): MatchKind {
|
||||||
if (this.comment) {
|
if (this.comment) {
|
||||||
return MatchResult.None
|
return MatchKind.None
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.minimatch.match(itemPath)) {
|
if (this.minimatch.match(itemPath)) {
|
||||||
return this.trailingSlash ? MatchResult.Directory : MatchResult.All
|
return this.trailingSlash ? MatchKind.Directory : MatchKind.All
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchResult.None
|
return MatchKind.None
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,11 +146,11 @@ export class Pattern {
|
||||||
private initializePaths(pattern: string): void {
|
private initializePaths(pattern: string): void {
|
||||||
// Build the search path
|
// Build the search path
|
||||||
const searchSegments: string[] = []
|
const searchSegments: string[] = []
|
||||||
for (const literalSegment of this.getLiterals(pattern)) {
|
for (const literal of this.getLiterals(pattern)) {
|
||||||
if (!literalSegment) {
|
if (!literal) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
searchSegments.push(literalSegment)
|
searchSegments.push(literal)
|
||||||
}
|
}
|
||||||
this.searchPath = new Path(searchSegments).toString()
|
this.searchPath = new Path(searchSegments).toString()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue