-
Bug
-
Resolution: Unresolved
-
Low
-
None
-
1
-
Severity 2 - Major
-
115
-
Issue Summary
When symlinks are created in the build directory of a Pipelines build running with a Windows runner, the runner sometimes cannot clean the build directory, and it goes into an unhealth state.
Steps to Reproduce
1. Use a yml file like the following with a Windows self-hosted runner:
pipelines: default: - step: name: 'Build and Test' runs-on: - self.hosted - windows script: - echo "Your build and test goes here..." - mkdir dir1 - New-Item -ItemType SymbolicLink -Path .\link -Target .\dir1 - ls
2. Run a Pipelines build with this yml.
Notes:
The issue is consistently reproduced when the name of the symlink comes after the name of the directory that the symlink is pointing to in alphabetical order.
E.g. it is reproducible when the symlink link is pointing to dir1.
The issue is not reproducible when the symlink alink points to dir1.
The issue has been reproduced with version 2.6.0 of the Windows Runner.
Expected Results
When the build finishes, the runner should be able to clean up the build directory.
Actual Results
The runner cannot clean the build directory after the step finishes, and it goes into an unhealthy state.
The below exception is thrown in runner's log:
An error occurred whilst tearing down directories. java.nio.file.NoSuchFileException: C:\Users\<someuser>\Documents\atlassian-bitbucket-pipelines-runner\bin\..\temp\<runner-uuid>\build\link at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85) at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103) at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108) at java.base/sun.nio.fs.WindowsLinkSupport.getFinalPath(WindowsLinkSupport.java:74) at java.base/sun.nio.fs.WindowsLinkSupport.getFinalPath(WindowsLinkSupport.java:112) at java.base/sun.nio.fs.WindowsFileAttributeViews$Dos.updateAttributes(WindowsFileAttributeViews.java:228) at java.base/sun.nio.fs.WindowsFileAttributeViews$Dos.setReadOnly(WindowsFileAttributeViews.java:248) at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl$1.visitFile(WindowsDirectoryImpl.java:84) at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl$1.visitFile(WindowsDirectoryImpl.java:73) at java.base/java.nio.file.Files.walkFileTree(Files.java:2812) at java.base/java.nio.file.Files.walkFileTree(Files.java:2883) at com.atlassian.pipelines.runner.core.directory.windows.WindowsDirectoryImpl.delete(WindowsDirectoryImpl.java:71) at com.atlassian.pipelines.runner.core.runtime.common.NativeTemporaryDirectoryStepRuntimeImpl.lambda$tearDownDirectory$5(NativeTemporaryDirectoryStepRuntimeImpl.java:77) at io.reactivex.internal.operators.completable.CompletableFromAction.subscribeActual(CompletableFromAction.java:35) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onNext(CompletableMerge.java:99) at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onNext(CompletableMerge.java:44) at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:68) at io.reactivex.internal.operators.flowable.FlowableFromIterable$IteratorSubscription.fastPath(FlowableFromIterable.java:178) at io.reactivex.internal.operators.flowable.FlowableFromIterable$BaseRangeSubscription.request(FlowableFromIterable.java:122) at io.reactivex.internal.subscribers.BasicFuseableSubscriber.request(BasicFuseableSubscriber.java:153) at io.reactivex.internal.operators.completable.CompletableMerge$CompletableMergeSubscriber.onSubscribe(CompletableMerge.java:86) at io.reactivex.internal.subscribers.BasicFuseableSubscriber.onSubscribe(BasicFuseableSubscriber.java:67) at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribe(FlowableFromIterable.java:69) at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribeActual(FlowableFromIterable.java:47) at io.reactivex.Flowable.subscribe(Flowable.java:14935) at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37) at io.reactivex.Flowable.subscribe(Flowable.java:14935) at io.reactivex.Flowable.subscribe(Flowable.java:14882) at io.reactivex.internal.operators.completable.CompletableMerge.subscribeActual(CompletableMerge.java:41) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletableResumeNext.subscribeActual(CompletableResumeNext.java:41) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.single.SingleDelayWithCompletable.subscribeActual(SingleDelayWithCompletable.java:36) at io.reactivex.Single.subscribe(Single.java:3666) at io.reactivex.internal.operators.single.SingleResumeNext.subscribeActual(SingleResumeNext.java:39) at io.reactivex.Single.subscribe(Single.java:3666) at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onSuccess(SingleFlatMap.java:84) at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver.onSuccess(SingleFlatMap.java:111) at io.reactivex.internal.operators.single.SingleResumeNext$ResumeMainSingleObserver.onSuccess(SingleResumeNext.java:65) at io.reactivex.internal.operators.single.SingleResumeNext$ResumeMainSingleObserver.onSuccess(SingleResumeNext.java:65) at io.reactivex.internal.observers.ResumeSingleObserver.onSuccess(ResumeSingleObserver.java:46) at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:56) at io.reactivex.Single.subscribe(Single.java:3666) at io.reactivex.internal.operators.single.SingleDelayWithCompletable$OtherObserver.onComplete(SingleDelayWithCompletable.java:69) at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99) at io.reactivex.internal.operators.completable.CompletableFromRunnable.subscribeActual(CompletableFromRunnable.java:47) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$SourceObserver.onComplete(CompletableAndThenCompletable.java:67) at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$NextObserver.onComplete(CompletableAndThenCompletable.java:99) at io.reactivex.internal.operators.completable.CompletableDoFinally$DoFinallyObserver.onComplete(CompletableDoFinally.java:78) at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115) at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115) at io.reactivex.internal.operators.completable.CompletableMergeIterable$MergeCompletableObserver.onComplete(CompletableMergeIterable.java:132) at io.reactivex.internal.operators.completable.CompletableMergeIterable.subscribeActual(CompletableMergeIterable.java:94) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletableDoFinally.subscribeActual(CompletableDoFinally.java:43) at io.reactivex.Completable.subscribe(Completable.java:2309) at io.reactivex.internal.operators.completable.CompletableAndThenCompletable$SourceObserver.onComplete(CompletableAndThenCompletable.java:67) at io.reactivex.internal.operators.completable.CompletableOnErrorComplete$OnError.onComplete(CompletableOnErrorComplete.java:48) at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115) at io.reactivex.internal.operators.completable.CompletablePeek$CompletableObserverImplementation.onComplete(CompletablePeek.java:115) at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.run(CompletableObserveOn.java:89) at brave.propagation.CurrentTraceContext$1CurrentTraceContextRunnable.run(CurrentTraceContext.java:264) at com.atlassian.pipelines.common.trace.rxjava.CopyMdcSchedulerHandler$CopyMdcRunnableAdapter.run(CopyMdcSchedulerHandler.java:74) at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:608) at brave.propagation.CurrentTraceContext$1CurrentTraceContextRunnable.run(CurrentTraceContext.java:264) at com.atlassian.pipelines.common.trace.rxjava.CopyMdcSchedulerHandler$CopyMdcRunnableAdapter.run(CopyMdcSchedulerHandler.java:74) at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66) at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833) [2024-08-02 12:15:46,315] Cancelling timeout [2024-08-02 12:15:46,320] Updating runner state to "UNHEALTHY". [2024-08-02 12:15:46,581] Completing step with result Result{status=FAILED, error=Some(Error{key='runner.working-directory-teardown-error', message='An error occurred while attempting to clean the build folder. Check the runner logs and manually clean up the build folder on the host machine to fix this issue.', arguments={}})}. [2024-08-02 12:15:46,590] Runner cannot accept new steps since it's unhealthy. [2024-08-02 12:15:46,895] Setting runner state to not executing step.
Workaround
Delete the symlinks in the after-script of the step. Then, the runner will be able to clean up the build directory during Build teardown.