You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows环境下GitHub Actions多步骤后台进程存活问题求助

问题

在GitHub Actions任务中,尝试在一个步骤启动本地服务器,后续步骤连接该服务器。此流程在Linux上可正常运行,但在Windows环境中,所有后台进程会在进入下一步前被终止,导致名为“Connect to server again (Windows)”的步骤执行失败。原工作流配置如下:

name: Test

on:
  push:
    branches:
      - '*'
  workflow_dispatch:

jobs:
  micro-server:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]

    steps:
      - name: Create index.html
        run: echo "<h1>Hello world!</h1>" > index.html

      - name: Install dotnet-serve
        run: dotnet tool install --global dotnet-serve

      - name: Start server and connect to server (Windows)
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
            $env:RUNNER_TRACKING_ID=""
            $web = Start-Job { dotnet serve --port 5100 } -WorkingDirectory $pwd
            
            $timeout = 30
            $startTime = Get-Date

            do {
                $elapsedTime = (Get-Date) - $startTime
                if ($elapsedTime.TotalSeconds -ge $timeout) {
                    Write-Host "Timeout reached. Server not available after $($elapsedTime.TotalSeconds) seconds."
                    exit 1
                }

                try {
                    $response = Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2
                    $serverAvailable = $response.StatusCode -eq 200
                } catch {
                    $serverAvailable = $false
                }

                if (-not $serverAvailable) {
                    Write-Host "Waiting for server to become available... Elapsed time: $($elapsedTime.ToString('hh\:mm\:ss'))"
                    Start-Sleep -Seconds 1  # Wait for 1 second before checking again
                }
            } while (-not $serverAvailable)

            $totalWaitTime = (Get-Date) - $startTime
            Write-Host "Server is now available after $($totalWaitTime.ToString('hh\:mm\:ss'))."

            Get-Process | Where-Object { $_.Modules.FileName -like "*dotnet*" }

      - name: List dotnet processes (Windows)
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
            Get-Process | Where-Object { $_.Modules.FileName -like "*dotnet*" }

      - name: Connect to server again (Windows)
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
            Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2

 
      - name: Start server and connect to server (Linux)
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
            dotnet serve --port 5100 &
            sleep 5
            curl http://localhost:5100

      - name: Connect to server again (Linux)
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
            curl http://localhost:5100
解决方案

方法1:用Start-Process启动独立进程

Windows上Start-Job创建的后台进程会随当前PowerShell会话结束而终止,改用Start-Process并配置参数让进程脱离当前会话,同时清除RUNNER_TRACKING_ID环境变量避免GitHub Actions自动清理进程:

修改Windows启动服务器的步骤:

- name: Start server and connect to server (Windows)
  if: ${{ matrix.os == 'windows-latest' }}
  run: |
      $env:RUNNER_TRACKING_ID=""
      # 启动dotnet-serve,使其脱离当前会话
      $process = Start-Process dotnet -ArgumentList "serve --port 5100" -WorkingDirectory $pwd -NoNewWindow -PassThru

      $timeout = 30
      $startTime = Get-Date

      do {
          $elapsedTime = (Get-Date) - $startTime
          if ($elapsedTime.TotalSeconds -ge $timeout) {
              Write-Host "Timeout reached. Server not available after $($elapsedTime.TotalSeconds) seconds."
              exit 1
          }

          try {
              $response = Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2
              $serverAvailable = $response.StatusCode -eq 200
          } catch {
              $serverAvailable = $false
          }

          if (-not $serverAvailable) {
              Write-Host "Waiting for server to become available... Elapsed time: $($elapsedTime.ToString('hh\:mm\:ss'))"
              Start-Sleep -Seconds 1
          }
      } while (-not $serverAvailable)

      $totalWaitTime = (Get-Date) - $startTime
      Write-Host "Server is now available after $($totalWaitTime.ToString('hh\:mm\:ss'))."

方法2:将服务器注册为临时Windows服务

使用nssm(轻量服务管理工具)把dotnet-serve注册为Windows服务,服务进程不会随步骤会话终止而关闭:

  1. 安装nssm:
- name: Install nssm (Windows)
  if: ${{ matrix.os == 'windows-latest' }}
  run: |
      Invoke-WebRequest -Uri "https://nssm.cc/release/nssm-2.24.zip" -OutFile "nssm.zip"
      Expand-Archive -Path "nssm.zip" -DestinationPath "nssm"
      $nssmPath = Join-Path (Get-ChildItem -Path "nssm" -Directory | Select-Object -First 1).FullName "win64\nssm.exe"
      Copy-Item $nssmPath -Destination "C:\Windows\System32\nssm.exe"
  1. 注册并启动服务:
- name: Start dotnet-serve as service (Windows)
  if: ${{ matrix.os == 'windows-latest' }}
  run: |
      $dotnetPath = (Get-Command dotnet).Source
      nssm install DotnetServe $dotnetPath
      nssm set DotnetServe AppParameters "serve --port 5100 --working-dir $pwd"
      nssm start DotnetServe

      # 等待服务启动完成
      $timeout = 30
      $startTime = Get-Date
      do {
          $elapsedTime = (Get-Date) - $startTime
          if ($elapsedTime.TotalSeconds -ge $timeout) {
              Write-Host "Timeout waiting for service to start."
              exit 1
          }
          try {
              $response = Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2
              if ($response.StatusCode -eq 200) { break }
          } catch {
              Start-Sleep -Seconds 1
          }
      } while ($true)
  1. 测试完成后清理服务:
- name: Cleanup dotnet-serve service (Windows)
  if: ${{ matrix.os == 'windows-latest' }}
  run: |
      nssm stop DotnetServe
      nssm remove DotnetServe confirm

方法3:合并所有操作到单个步骤(快速替代)

如果不需要拆分多个步骤,可将服务器启动、所有连接检查合并到同一个步骤,避免会话终止导致进程被清理:

- name: Start server and run all checks (Windows)
  if: ${{ matrix.os == 'windows-latest' }}
  run: |
      $env:RUNNER_TRACKING_ID=""
      $web = Start-Job { dotnet serve --port 5100 } -WorkingDirectory $pwd
      
      # 等待服务器启动
      $timeout = 30
      $startTime = Get-Date
      do {
          $elapsedTime = (Get-Date) - $startTime
          if ($elapsedTime.TotalSeconds -ge $timeout) {
              Write-Host "Timeout reached."
              exit 1
          }
          try {
              $response = Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2
              if ($response.StatusCode -eq 200) { break }
          } catch {
              Start-Sleep -Seconds 1
          }
      } while ($true)

      # 执行第一次连接检查
      Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2
      # 执行第二次连接检查(模拟原后续步骤)
      Invoke-WebRequest -Uri "http://localhost:5100" -UseBasicParsing -Method Head -TimeoutSec 2

内容的提问来源于stack exchange,提问作者PEK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:03:11