@@ -773,10 +773,6 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont
773773 }
774774 else
775775 {
776- // make sure we get a clean folder ready to use.
777- IOUtil . DeleteDirectory ( destDirectory , executionContext . CancellationToken ) ;
778- Directory . CreateDirectory ( destDirectory ) ;
779-
780776 if ( downloadInfo . PackageDetails != null )
781777 {
782778 executionContext . Output ( $ "##[group]Download immutable action package '{ downloadInfo . NameWithOwner } @{ downloadInfo . Ref } '") ;
@@ -811,6 +807,50 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont
811807 if ( ! string . IsNullOrEmpty ( actionArchiveCacheDir ) &&
812808 Directory . Exists ( actionArchiveCacheDir ) )
813809 {
810+ var symlinkCachedActions = StringUtil . ConvertToBoolean ( Environment . GetEnvironmentVariable ( Constants . Variables . Agent . SymlinkCachedActions ) ) ;
811+ if ( symlinkCachedActions )
812+ {
813+ Trace . Info ( $ "Checking if can symlink '{ downloadInfo . ResolvedNameWithOwner } @{ downloadInfo . ResolvedSha } '") ;
814+
815+ var cacheDirectory = Path . Combine ( actionArchiveCacheDir , downloadInfo . ResolvedNameWithOwner . Replace ( Path . DirectorySeparatorChar , '_' ) . Replace ( Path . AltDirectorySeparatorChar , '_' ) , downloadInfo . ResolvedSha ) ;
816+ if ( Directory . Exists ( cacheDirectory ) )
817+ {
818+ try
819+ {
820+ Trace . Info ( $ "Found unpacked action directory '{ cacheDirectory } ' in cache directory '{ actionArchiveCacheDir } '") ;
821+
822+ // repository archive from github always contains a nested folder
823+ var nestedDirectories = new DirectoryInfo ( cacheDirectory ) . GetDirectories ( ) ;
824+ if ( nestedDirectories . Length != 1 )
825+ {
826+ throw new InvalidOperationException ( $ "'{ cacheDirectory } ' contains '{ nestedDirectories . Length } ' directories") ;
827+ }
828+ else
829+ {
830+ executionContext . Debug ( $ "Symlink '{ nestedDirectories [ 0 ] . Name } ' to '{ destDirectory } '") ;
831+ // make sure we get a clean folder ready to use.
832+ IOUtil . DeleteDirectory ( destDirectory , executionContext . CancellationToken ) ;
833+ IOUtil . CreateSymbolicLink ( destDirectory , nestedDirectories [ 0 ] . FullName ) ;
834+ }
835+
836+ executionContext . Debug ( $ "Created symlink from cached directory '{ cacheDirectory } ' to '{ destDirectory } '") ;
837+ executionContext . Global . JobTelemetry . Add ( new JobTelemetry ( )
838+ {
839+ Type = JobTelemetryType . General ,
840+ Message = $ "Action archive cache usage: { downloadInfo . ResolvedNameWithOwner } @{ downloadInfo . ResolvedSha } use cache { useActionArchiveCache } has cache { hasActionArchiveCache } via symlink"
841+ } ) ;
842+
843+ Trace . Info ( "Finished getting action repository." ) ;
844+ return ;
845+ }
846+ catch ( Exception ex )
847+ {
848+ Trace . Error ( $ "Failed to create symlink from cached directory '{ cacheDirectory } ' to '{ destDirectory } '. Error: { ex } ") ;
849+ // Fall through to normal download logic
850+ }
851+ }
852+ }
853+
814854 hasActionArchiveCache = true ;
815855 Trace . Info ( $ "Check if action archive '{ downloadInfo . ResolvedNameWithOwner } @{ downloadInfo . ResolvedSha } ' already exists in cache directory '{ actionArchiveCacheDir } '") ;
816856#if OS_WINDOWS
@@ -892,6 +932,10 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont
892932 }
893933#endif
894934
935+ // make sure we get a clean folder ready to use.
936+ IOUtil . DeleteDirectory ( destDirectory , executionContext . CancellationToken ) ;
937+ Directory . CreateDirectory ( destDirectory ) ;
938+
895939 // repository archive from github always contains a nested folder
896940 var subDirectories = new DirectoryInfo ( stagingDirectory ) . GetDirectories ( ) ;
897941 if ( subDirectories . Length != 1 )
0 commit comments