@@ -37,6 +37,7 @@ import actions.exec.ExecOptions
37
37
import actions.exec.exec
38
38
import actions.io.mkdirP
39
39
import actions.io.mv
40
+ import actions.io.rmRF
40
41
import actions.io.which
41
42
import actions.tool.cache.cacheDir
42
43
import actions.tool.cache.downloadTool
@@ -57,16 +58,25 @@ import node.buffer.BufferEncoding
57
58
import node.fs.exists
58
59
import node.fs.mkdtemp
59
60
import node.fs.readdir
61
+ import node.fs.stat
60
62
import node.fs.writeFile
61
63
import node.os.tmpdir
62
64
import node.path.path
63
65
import node.process.Platform
64
66
import node.process.process
65
67
import nullwritable.NullWritable
66
68
import org.w3c.dom.url.URL
69
+ import kotlin.time.Duration
70
+ import kotlin.time.Duration.Companion.minutes
67
71
import kotlin.time.Duration.Companion.seconds
68
72
import actions.tool.cache.extractZip as toolCacheExtractZip
69
73
74
+ private const val BAD_WSL_EXE_PATH =
75
+ " C:\\ Users\\ runneradmin\\ AppData\\ Local\\ Microsoft\\ WindowsApps\\ wsl.exe"
76
+
77
+ private const val BAD_WSLCONFIG_EXE_PATH =
78
+ " C:\\ Users\\ runneradmin\\ AppData\\ Local\\ Microsoft\\ WindowsApps\\ wslconfig.exe"
79
+
70
80
suspend fun wslOutput (vararg args : String ): String {
71
81
val stdoutBuilder = StringBuilder ()
72
82
val stdoutBuilderUtf16Le = StringBuilder ()
@@ -101,10 +111,6 @@ val wslHelp = GlobalScope.async(start = LAZY) {
101
111
wslOutput(" --help" )
102
112
}
103
113
104
- val wslStatus = GlobalScope .async(start = LAZY ) {
105
- wslOutput(" --status" )
106
- }
107
-
108
114
val distribution by lazy {
109
115
val distributionId = getInput(" distribution" , InputOptions (required = true ))
110
116
@@ -125,6 +131,10 @@ val wslId = GlobalScope.async(start = LAZY) {
125
131
distribution.wslId
126
132
}
127
133
134
+ val wslInstallationNeeded = GlobalScope .async(start = LAZY ) {
135
+ wslOutput(" --status" ).contains(" is not installed" )
136
+ }
137
+
128
138
val installationNeeded = GlobalScope .async(start = LAZY ) {
129
139
exec(
130
140
commandLine = " wsl" ,
@@ -292,6 +302,12 @@ suspend fun main() {
292
302
if (getInput(" only safe actions" ).isEmpty()
293
303
|| ! getBooleanInput(" only safe actions" )
294
304
) {
305
+ // on windows-2025 WSL is not installed at all currently, so install it without distribution
306
+ // work-around for https://github.com/actions/runner-images/issues/11265
307
+ if (wslInstallationNeeded()) {
308
+ group(" Install WSL" , ::installWsl)
309
+ }
310
+
295
311
if (installationNeeded()) {
296
312
group(" Install Distribution" , ::installDistribution)
297
313
}
@@ -370,31 +386,90 @@ suspend fun verifyWindowsEnvironment() {
370
386
}
371
387
}
372
388
389
+ suspend fun installWsl () {
390
+ // part of work-around for https://github.com/actions/toolkit/issues/1925
391
+ val deleteWslExe =
392
+ runCatching { stat(BAD_WSL_EXE_PATH ).isFile() }
393
+ .getOrDefault(false )
394
+ .not ()
395
+ val deleteWslConfigExe =
396
+ runCatching { stat(BAD_WSLCONFIG_EXE_PATH ).isFile() }
397
+ .getOrDefault(false )
398
+ .not ()
399
+
400
+ exec(
401
+ commandLine = " pwsh" ,
402
+ args = arrayOf(" -Command" , """ Start-Process wsl "--install --no-distribution"""" ),
403
+ options = ExecOptions (ignoreReturnCode = true )
404
+ )
405
+
406
+ waitForWslStatusNotContaining(" is not installed" , 5 .minutes) {
407
+ // part of work-around for https://github.com/actions/toolkit/issues/1925
408
+ if (deleteWslExe) {
409
+ rmRF(BAD_WSL_EXE_PATH )
410
+ }
411
+ if (deleteWslConfigExe) {
412
+ rmRF(BAD_WSLCONFIG_EXE_PATH )
413
+ }
414
+ }
415
+ }
416
+
373
417
suspend fun installDistribution () {
374
418
executeWslCommand(
375
419
wslArguments = arrayOf(" --set-default-version" , " ${wslVersion()} " )
376
420
)
421
+
377
422
if (wslVersion() != 1u ) {
378
- retry(5 ) {
423
+ // part of work-around for https://github.com/actions/toolkit/issues/1925
424
+ val deleteWslExe =
425
+ runCatching { stat(BAD_WSL_EXE_PATH ).isFile() }
426
+ .getOrDefault(false )
427
+ .not ()
428
+ val deleteWslConfigExe =
429
+ runCatching { stat(BAD_WSLCONFIG_EXE_PATH ).isFile() }
430
+ .getOrDefault(false )
431
+ .not ()
432
+
433
+ retry(10 ) {
379
434
executeWslCommand(
380
435
wslArguments = arrayOf(" --update" )
381
436
)
382
437
}
383
438
384
- (2 .. 30 )
385
- .asFlow()
386
- .onEach { delay(1 .seconds) }
387
- .onStart { emit(1 ) }
388
- .map { wslStatus() }
389
- .firstOrNull { ! it.contains(" WSL is finishing an upgrade..." ) }
439
+ // part of work-around for https://github.com/actions/toolkit/issues/1925
440
+ waitForWslStatusNotContaining(" WSL is finishing an upgrade..." ) {
441
+ if (deleteWslExe) {
442
+ rmRF(BAD_WSL_EXE_PATH )
443
+ }
444
+ if (deleteWslConfigExe) {
445
+ rmRF(BAD_WSLCONFIG_EXE_PATH )
446
+ }
447
+ }
390
448
}
449
+
391
450
exec(
392
451
commandLine = """ "${path.join(distributionDirectory(), distribution.installerFile)} """" ,
393
452
args = arrayOf(" install" , " --root" ),
394
453
options = ExecOptions (input = Buffer .from(" " ))
395
454
)
396
455
}
397
456
457
+ suspend fun waitForWslStatusNotContaining (
458
+ text : String ,
459
+ duration : Duration = 30.seconds,
460
+ preAction : suspend () -> Unit = {}
461
+ ) {
462
+ (2 .. duration.inWholeSeconds)
463
+ .asFlow()
464
+ .onEach { delay(1 .seconds) }
465
+ .onStart { emit(1 ) }
466
+ .map {
467
+ preAction()
468
+ wslOutput(" --status" )
469
+ }
470
+ .firstOrNull { ! it.contains(text) }
471
+ }
472
+
398
473
suspend fun adjustWslConf () {
399
474
exec(
400
475
commandLine = " wsl" ,
0 commit comments