Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

META - Attach Enhancements

未關閉
#2,246 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
5/5
預估耗時
一週以上
新手友好度
25/100
Issue 類型
功能
描述清晰度
需要釐清
活躍度
停滯
技術堆疊
powershell, python, vscode

研究方向

請先從已連結的 issue 2242-2245 和 2247 開始,因為這個 meta issue 協調了數個獨立的增強功能。接著檢視所參照的進入點 ansible-pwsh.ps1 和 ansible-debug.py,以了解提議的除錯工作流程。完成的要求是實作並驗證一項特定的已連結增強功能,而不是將這個協調 issue 作為整體處理。

由索引模型根據 Issue 內容生成。

描述

Issue-Enhancement Needs: Triage
Prerequisites
  • I have written a descriptive issue title.
  • I have searched all issues to ensure it has not already been requested.
Summary

This is a meta issue I'm opening to hopefully explain some of the new feature requests I've opened and why.

I'm currently working on trying to add support for debugging Ansible modules written in PowerShell through a client like VSCode. My current approach is the following:

  • I have this launch configuration
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Ansible PowerShell Debugger",
      "type": "PowerShell",
      "request": "launch",
      "script": "/home/jborean/dev/ansible/bin/ansible-pwsh.ps1",
      "args": [],
      "cwd": "/home/jborean/dev/ansible",
      "serverReadyAction": {
        "action": "startDebugging",
        "pattern": "Listening for debug request on port \\d+",
        "name": "Ansible Launch Debugger Environment"
      }
    },
    {
      "name": "Ansible Launch Debugger Environment",
      "type": "debugpy",
      "request": "launch",
      "program": "/home/jborean/dev/ansible/bin/ansible-debug.py",
      "args": [
        "win-hostname"
      ],
      "console": "integratedTerminal",
      "cwd": "/home/jborean/dev/ansible-tester",
    },
  ],
}

This launch configuration starts a PowerShell script ansible-pwsh.ps1 which sets up a local socket and exposes it through a named pipe locally. It will listen for requests on the socket and setup an attach configuration to start the debug session.

ansible-pwsh.ps1
#!/usr/bin/env pwsh

using namespace System.IO
using namespace System.IO.Pipes
using namespace System.Management.Automation.Language
using namespace System.Net.Sockets
using namespace System.Text
using namespace System.Threading
using namespace System.Threading.Tasks

#Requires -Version 7.4

[CmdletBinding()]
param ()

$ErrorActionPreference = 'Stop'

$configPath = '~/.ansible/test/debugging'
if (-not (Test-Path -LiteralPath $configPath)) {
    New-Item -ItemType Directory -Path $configPath | Out-Null
}
$configFile = Join-Path -Path $configPath -ChildPath 'pwsh_debugger.json'

$sock = $null
try {
    # Start listening to a socket locally that we will forward to the remote
    # host via SSH.
    $sock = [TcpListener]::new(
        [IPAddress]::IPv6Loopback,
        0)
    $sock.Server.DualMode = $true
    $sock.Start()

    $configInfo = @{
        pid = $PID
        port = $sock.LocalEndpoint.Port
    } | ConvertTo-Json -Compress
    Set-Content -LiteralPath $configFile -Value $configInfo

    Write-Host "Listening for debug request on port $($sock.LocalEndpoint.Port)"

    while ($true) {
        $task = $sock.AcceptTcpClientAsync()
        while (-not $task.AsyncWaitHandle.WaitOne(300)) { }
        $client = $task.GetAwaiter().GetResult()

        Write-Host "Client connected from $($client.Client.RemoteEndPoint)"
        $pipe = $stream = $null
        try {
            $stream = $client.GetStream()

            $reader = [StreamReader]::new($stream, [Encoding]::UTF8, $true, $client.ReceiveBufferSize, $true)
            $task = $reader.ReadLineAsync()
            while (-not $task.AsyncWaitHandle.WaitOne(300)) { }
            $debugInfoRaw = $task.GetAwaiter().GetResult()
            $reader.Dispose()

            Write-Host "Received debug request`n$debugInfoRaw"
            $debugInfo = ConvertFrom-Json -InputObject $debugInfoRaw

            $pipeName = "MyPipe-$(New-Guid)"
            $pipe = [NamedPipeServerStream]::new(
                $pipeName,
                [PipeDirection]::InOut,
                1,
                [PipeTransmissionMode]::Byte,
                [PipeOptions]::Asynchronous)

            $task = $pipe.WaitForConnectionAsync()

            Write-Host "Starting VSCode attach to Pipe $pipeName"
            $attachConfig = @{
                name = $debugInfo.ModuleName
                type = "PowerShell"
                request = "attach"
                customPipeName = $pipeName
                runspaceId = $debugInfo.RunspaceId
                pathMappings = @($debugInfo.PathMappings)
            }
            $attachTask = Start-NewDebugSession -Request Attach -Configuration $attachConfig

            Write-Host "Waiting for VSCode to attach to Pipe"
            while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
            $null = $task.GetAwaiter().GetResult()

            Write-Host "VSCode attached to pipe. Sending back confirmation byte to debug session."
            $task = $stream.WriteAsync([byte[]]@(0), 0, 1)
            while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
            $null = $task.GetAwaiter().GetResult()

            # FIXME: SSH socket forwarding won't break the copy so need to find out how to detect that.
            Write-Host "Starting socket <-> pipe streaming"
            $writeTask = $pipe.CopyToAsync($stream)
            $readTask = $stream.CopyToAsync($pipe)

            Write-Host "Waiting for startDebugging attach response to arrive"
            while (-not $attachTask.AsyncWaitHandle.WaitOne(300)) { }
            $null = $attachTask.GetAwaiter().GetResult()

            $task = [Task]::WhenAny($writeTask, $readTask)
            while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
            $finishedTask = $task.GetAwaiter().GetResult()

            if ($finishedTask -eq $writeTask) {
                Write-Host "VSCode disconnected from Pipe $pipeName and RunspaceId $runspaceId"
            }
            elseif ($finishedTask -eq $readTask) {
                Write-Host "Socket disconnected from Pipe $pipeName and RunspaceId $runspaceId"
            }
            else {
                Write-Host "Unknown task finished for Pipe $pipeName and RunspaceId $runspaceId"
            }
        }
        finally {
            ${pipe}?.Dispose()
            ${stream}?.Dispose()
            $client.Dispose()
        }
    }
}
finally {
    if (Test-Path -LiteralPath $configFile) {
        Remove-Item -LiteralPath $configFile -Force
    }
    ${sock}?.Dispose()
}

The ansible-debug.py script is a simple script that will forward that local socket to the Windows host through SSH and setup some env var that Ansible uses to read the PowerShell debug information.

ansible-debug.py
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK

from __future__ import annotations

import argparse
import contextlib
import json
import os
import pathlib
import re
import subprocess
import sys
import typing as t

HAS_ARGCOMPLETE = False
try:
    import argcomplete

    HAS_ARGCOMPLETE = True
except ImportError:
    pass

_PORT_ALLOC_PATTERN = re.compile(r'Allocated port (\d+) for remote forward to .*')

def _parse_args(args: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Ansible Module Debugging CLI")

    parser.add_argument(
        'hostname',
        nargs=1,
        type=str,
        help="The SSH hostname to forward the debug ports to. This should include username and/or port if necessary.",
    )

    if HAS_ARGCOMPLETE:
        argcomplete.autocomplete(parser)

    return parser.parse_args(args)


@contextlib.contextmanager
def forward_ports(
    hostname: str,
    local_port: int,
) -> t.Generator[int]:
    ssh_cmd = [
        'ssh',
        '-v',  # Verbose output to capture port allocation
        '-NT',  # Don't execute remote command, disable PTY allocation
        '-o', 'ExitOnForwardFailure=yes',
        '-R', f'localhost:0:localhost:{local_port}',
        hostname
    ]

    # Start SSH process with stderr captured for debug output
    process = subprocess.Popen(
        ssh_cmd,
        stderr=subprocess.STDOUT,
        stdout=subprocess.PIPE,
        text=True,
    )
    try:
        assert process.stdout is not None

        proc_out = []
        port = 0
        for line in process.stdout:
            if match := _PORT_ALLOC_PATTERN.match(line):
                port = int(match.group(1))
                break

            proc_out.append(line)

        if port == 0:
            raise Exception(f"SSH failed to forward ports. Output:\n{'\n'.join(proc_out)}")

        yield port

    finally:
        process.terminate()


def main() -> None:
    args = _parse_args(sys.argv[1:])

    config_path = pathlib.Path.home() / '.ansible' / 'test' / 'debugging' / 'pwsh_debugger.json'
    if not config_path.exists():
        raise Exception(f"PowerShell debugger configuration file not found. Please ensure it exists at {config_path.absolute()}")

    debug_info = json.loads(config_path.read_text())
    local_port = debug_info.get('port', 0)
    if not local_port:
        raise Exception("No local port specified in the PowerShell debugger configuration file.")

    with forward_ports(args.hostname[0], local_port=local_port) as remote_port:
        debug_options = {
            'wait': False,
            'host': 'localhost',
            'port': remote_port,
        }
        new_env = os.environ.copy() | {
            '_ANSIBLE_ANSIBALLZ_PWSH_DEBUGGER_CONFIG': json.dumps(debug_options),
        }
        res = subprocess.run('/bin/bash', env=new_env, check=False)
        sys.exit(res.returncode)


if __name__ == '__main__':
    main()

From there when Ansible starts the PowerShell process on the Windows host, it'll connect to the socket, send the runspace/debug information over that socket, redirect any socket data to the local named pipe for that process and wait for VSCode to "attach".

From a workflow perspective this works fine but it has required a few changes to PSES to get working locally. These changes are mentioned in the following issues:

Proposed Design

See the linked issues.

主要語言
C#
星號
776
分支
268
PR 合併指標
30 天內沒有已合併 PR

環境準備

  • 沒有 Dockerfile 或 Docker Compose 檔案
  • 沒有 Pull Request 範本
  • 閱讀貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

PowerShell/PowerShellEditorServices 的其他 Issue

查看 PowerShell/PowerShellEditorServices 的全部 Issue

相似的 Issue

更多 C# Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。