@@ -13,8 +13,8 @@ type OptionCases<T extends string> = `-${Uppercase<T> | Lowercase<T>}`;
13
13
14
14
type OptionsPermutations < T extends string , U extends string = '' > =
15
15
T extends `${infer First } ${infer Rest } `
16
- ? OptionCases < `${U } ${First } `> | OptionCases < `${First } ${U } `> | OptionsPermutations < Rest , `${U } ${First } `> | OptionCases < First >
17
- : OptionCases < U > | '' ;
16
+ ? OptionCases < `${U } ${First } `> | OptionCases < `${First } ${U } `> | OptionsPermutations < Rest , `${U } ${First } `> | OptionCases < First >
17
+ : OptionCases < U > | '' ;
18
18
19
19
export enum TaskResult {
20
20
Succeeded = 0 ,
@@ -893,7 +893,7 @@ export function popd(index: string = ''): string[] {
893
893
const _path = path . resolve ( dirStack . shift ( ) ! ) ;
894
894
cd ( _path ) ;
895
895
}
896
-
896
+
897
897
return getActualStack ( ) ;
898
898
}
899
899
@@ -1065,7 +1065,7 @@ export function ls(optionsOrPaths?: unknown, ...paths: unknown[]): string[] {
1065
1065
paths . push ( ...pathsFromOptions ) ;
1066
1066
}
1067
1067
}
1068
-
1068
+
1069
1069
if ( paths . length === 0 ) {
1070
1070
paths . push ( path . resolve ( '.' ) ) ;
1071
1071
}
@@ -1101,7 +1101,7 @@ export function ls(optionsOrPaths?: unknown, ...paths: unknown[]): string[] {
1101
1101
continue ;
1102
1102
}
1103
1103
const baseDir = pathsCopy . find ( p => entry . startsWith ( path . resolve ( p as string ) ) ) as string || path . resolve ( '.' ) ;
1104
-
1104
+
1105
1105
if ( fs . lstatSync ( entry ) . isDirectory ( ) && isRecursive ) {
1106
1106
preparedPaths . push ( ...fs . readdirSync ( entry ) . map ( x => path . join ( entry , x ) ) ) ;
1107
1107
entries . push ( path . relative ( baseDir , entry ) ) ;
@@ -1179,13 +1179,17 @@ export function cp(sourceOrOptions: unknown, destinationOrSource: string, option
1179
1179
throw new Error ( loc ( 'LIB_PathNotFound' , 'cp' , destination ) ) ;
1180
1180
}
1181
1181
1182
- const lstatSource = fs . lstatSync ( source ) ;
1182
+ var lstatSource = fs . lstatSync ( source ) ;
1183
1183
1184
1184
if ( ! force && fs . existsSync ( destination ) ) {
1185
1185
return ;
1186
1186
}
1187
1187
1188
1188
try {
1189
+ if ( lstatSource . isSymbolicLink ( ) ) {
1190
+ source = fs . readlinkSync ( source ) ;
1191
+ lstatSource = fs . lstatSync ( source ) ;
1192
+ }
1189
1193
if ( lstatSource . isFile ( ) ) {
1190
1194
if ( fs . existsSync ( destination ) && fs . lstatSync ( destination ) . isDirectory ( ) ) {
1191
1195
destination = path . join ( destination , path . basename ( source ) ) ;
@@ -1197,14 +1201,49 @@ export function cp(sourceOrOptions: unknown, destinationOrSource: string, option
1197
1201
fs . copyFileSync ( source , destination , fs . constants . COPYFILE_EXCL ) ;
1198
1202
}
1199
1203
} else {
1200
- fs . cpSync ( source , path . join ( destination , path . basename ( source ) ) , { recursive , force } ) ;
1204
+ copyDirectoryWithResolvedSymlinks ( source , path . join ( destination , path . basename ( source ) ) , force ) ;
1201
1205
}
1202
1206
} catch ( error ) {
1203
1207
throw new Error ( loc ( 'LIB_OperationFailed' , 'cp' , error ) ) ;
1204
1208
}
1205
- } , [ ] , { retryCount, continueOnError} ) ;
1209
+ } , [ ] , { retryCount, continueOnError } ) ;
1206
1210
}
1207
1211
1212
+ const copyDirectoryWithResolvedSymlinks = ( src : string , dest : string , force : boolean ) => {
1213
+ var srcPath : string ;
1214
+ var destPath : string ;
1215
+ var entry : fs . Dirent ;
1216
+ const entries = fs . readdirSync ( src , { withFileTypes : true } ) ;
1217
+
1218
+ if ( ! fs . existsSync ( dest ) ) {
1219
+ fs . mkdirSync ( dest , { recursive : true } ) ;
1220
+ }
1221
+
1222
+ for ( entry of entries ) {
1223
+ srcPath = path . join ( src , entry . name ) ;
1224
+ destPath = path . join ( dest , entry . name ) ;
1225
+
1226
+ if ( entry . isSymbolicLink ( ) ) {
1227
+ // Resolve the symbolic link and copy the target
1228
+ const resolvedPath = fs . readlinkSync ( srcPath ) ;
1229
+ const stat = fs . lstatSync ( resolvedPath ) ;
1230
+
1231
+ if ( stat . isFile ( ) ) {
1232
+ // Use the actual target file's name instead of the symbolic link's name
1233
+ const targetFileName = path . basename ( resolvedPath ) ;
1234
+ const targetDestPath = path . join ( dest , targetFileName ) ;
1235
+ fs . copyFileSync ( resolvedPath , targetDestPath ) ;
1236
+ } else if ( stat . isDirectory ( ) ) {
1237
+ copyDirectoryWithResolvedSymlinks ( resolvedPath , destPath , force ) ;
1238
+ }
1239
+ } else if ( entry . isFile ( ) ) {
1240
+ fs . copyFileSync ( srcPath , destPath ) ;
1241
+ } else if ( entry . isDirectory ( ) ) {
1242
+ copyDirectoryWithResolvedSymlinks ( srcPath , destPath , force ) ;
1243
+ }
1244
+ }
1245
+ } ;
1246
+
1208
1247
type MoveOptionsVariants = OptionsPermutations < 'fn' > ;
1209
1248
1210
1249
/**
@@ -1705,95 +1744,81 @@ function _legacyFindFiles_getMatchingItems(
1705
1744
*/
1706
1745
export function rmRF ( inputPath : string ) : void {
1707
1746
debug ( 'rm -rf ' + inputPath ) ;
1708
-
1709
1747
if ( getPlatform ( ) == Platform . Windows ) {
1710
1748
// Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
1711
1749
// program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
1712
1750
try {
1713
- if ( fs . statSync ( inputPath ) . isDirectory ( ) ) {
1751
+ const lstats = fs . lstatSync ( inputPath ) ;
1752
+ if ( lstats . isDirectory ( ) && ! lstats . isSymbolicLink ( ) ) {
1714
1753
debug ( 'removing directory ' + inputPath ) ;
1715
1754
childProcess . execFileSync ( "cmd.exe" , [ "/c" , "rd" , "/s" , "/q" , inputPath ] ) ;
1716
- }
1717
- else {
1755
+
1756
+ } else if ( lstats . isSymbolicLink ( ) ) {
1757
+ debug ( 'removing symbolic link ' + inputPath ) ;
1758
+ const realPath = fs . readlinkSync ( inputPath ) ;
1759
+ if ( fs . existsSync ( realPath ) ) {
1760
+ const stats = fs . statSync ( realPath ) ;
1761
+ if ( stats . isDirectory ( ) ) {
1762
+ childProcess . execFileSync ( "cmd.exe" , [ "/c" , "rd" , "/s" , "/q" , realPath ] ) ;
1763
+ fs . unlinkSync ( inputPath ) ;
1764
+ } else {
1765
+ fs . unlinkSync ( inputPath ) ;
1766
+ }
1767
+ } else {
1768
+ debug ( `Symbolic link '${ inputPath } ' points to a non-existing target '${ realPath } '. Removing the symbolic link.` ) ;
1769
+ fs . unlinkSync ( inputPath ) ;
1770
+ }
1771
+ } else {
1718
1772
debug ( 'removing file ' + inputPath ) ;
1719
1773
childProcess . execFileSync ( "cmd.exe" , [ "/c" , "del" , "/f" , "/a" , inputPath ] ) ;
1720
1774
}
1721
1775
} catch ( err ) {
1722
- // if you try to delete a file that doesn't exist, desired result is achieved
1723
- // other errors are valid
1724
- if ( err . code != 'ENOENT' ) {
1725
- throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , err . message ) ) ;
1726
- }
1727
- }
1728
-
1729
- // Shelling out fails to remove a symlink folder with missing source, this unlink catches that
1730
- try {
1731
- fs . unlinkSync ( inputPath ) ;
1732
- } catch ( err ) {
1733
- // if you try to delete a file that doesn't exist, desired result is achieved
1734
- // other errors are valid
1776
+ debug ( 'Error: ' + err . message ) ;
1735
1777
if ( err . code != 'ENOENT' ) {
1736
1778
throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , err . message ) ) ;
1737
1779
}
1738
1780
}
1739
1781
} else {
1740
- // get the lstats in order to workaround a bug in shelljs@0.3.0 where symlinks
1741
- // with missing targets are not handled correctly by "rm('-rf', path)"
1742
1782
let lstats : fs . Stats ;
1743
1783
1744
1784
try {
1745
1785
if ( inputPath . includes ( '*' ) ) {
1746
1786
const entries = findMatch ( path . dirname ( inputPath ) , [ path . basename ( inputPath ) ] ) ;
1747
1787
1748
1788
for ( const entry of entries ) {
1749
- fs . rmSync ( entry , { recursive : true , force : true } ) ;
1789
+ rmRF ( entry ) ;
1750
1790
}
1751
-
1752
- return ;
1753
1791
} else {
1754
1792
lstats = fs . lstatSync ( inputPath ) ;
1793
+ if ( lstats . isDirectory ( ) && ! lstats . isSymbolicLink ( ) ) {
1794
+ debug ( 'removing directory ' + inputPath ) ;
1795
+ fs . rmSync ( inputPath , { recursive : true , force : true } ) ;
1796
+ } else if ( lstats . isSymbolicLink ( ) ) {
1797
+ debug ( 'removing symbolic link ' + inputPath ) ;
1798
+ const realPath = fs . readlinkSync ( inputPath ) ;
1799
+ if ( fs . existsSync ( realPath ) ) {
1800
+ const stats = fs . statSync ( realPath ) ;
1801
+ if ( stats . isDirectory ( ) ) {
1802
+ fs . rmSync ( realPath , { recursive : true , force : true } ) ;
1803
+ fs . unlinkSync ( inputPath ) ;
1804
+ } else {
1805
+ fs . unlinkSync ( inputPath ) ;
1806
+ }
1807
+ } else {
1808
+ debug ( `Symbolic link '${ inputPath } ' points to a non-existing target '${ realPath } '. Removing the symbolic link.` ) ;
1809
+ fs . unlinkSync ( inputPath ) ;
1810
+ }
1811
+ } else {
1812
+ debug ( 'removing file ' + inputPath ) ;
1813
+ fs . unlinkSync ( inputPath ) ;
1814
+ }
1755
1815
}
1756
1816
} catch ( err ) {
1757
- // if you try to delete a file that doesn't exist, desired result is achieved
1758
- // other errors are valid
1759
- if ( err . code == 'ENOENT' ) {
1760
- return ;
1761
- }
1762
-
1763
- throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , err . message ) ) ;
1764
- }
1765
-
1766
- if ( lstats . isDirectory ( ) ) {
1767
- debug ( 'removing directory' ) ;
1768
- try {
1769
- fs . rmSync ( inputPath , { recursive : true , force : true } ) ;
1770
- } catch ( errMsg ) {
1771
- throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , errMsg ) ) ;
1772
- }
1773
-
1774
- return ;
1775
- } else if ( lstats . isSymbolicLink ( ) ) {
1776
- debug ( 'removing symbolic link' ) ;
1777
- try {
1778
- fs . unlinkSync ( inputPath ) ;
1779
- } catch ( errMsg ) {
1780
- throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , errMsg ) ) ;
1781
- }
1782
-
1783
- return ;
1784
- }
1785
-
1786
- debug ( 'removing file' ) ;
1787
- try {
1788
- const entries = findMatch ( path . dirname ( inputPath ) , [ path . basename ( inputPath ) ] ) ;
1789
-
1790
- for ( const entry of entries ) {
1791
- fs . rmSync ( entry , { recursive : true , force : true } ) ;
1817
+ debug ( 'Error: ' + err . message ) ;
1818
+ if ( err . code != 'ENOENT' ) {
1819
+ throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , err . message ) ) ;
1792
1820
}
1793
1821
}
1794
- catch ( err ) {
1795
- throw new Error ( loc ( 'LIB_OperationFailed' , 'rmRF' , err . message ) ) ;
1796
- }
1797
1822
}
1798
1823
}
1799
1824
@@ -2726,4 +2751,4 @@ if (!global['_vsts_task_lib_loaded']) {
2726
2751
im . _loadData ( ) ;
2727
2752
im . _exposeProxySettings ( ) ;
2728
2753
im . _exposeCertSettings ( ) ;
2729
- }
2754
+ }
0 commit comments