MAIN MENU
Le blogue Devolutions

Annonces, mises à jour et analyses de Devolutions

Devolutions PowerShell Universal VS Code debugger integration illustration for the blog.

Débogage PowerShell amélioré dans VS Code grâce à l’intégration du débogueur Universal

PowerShell Universal 4.2 et l’extension VS Code ajoutent le débogage distant via PowerShell Editor Services et le Debug Adapter Protocol, pour s’attacher à un processus et un runspace depuis l’arbre Platform.

PowerShell Universal (et son prédécesseur Universal Dashboard) ont toujours été délicats à déboguer. Presque toute la fonctionnalité passe par des runspaces en arrière-plan, donc avancer pas à pas avec un débogueur demande un peu de gymnastique d’outils. Ça ne fonctionnait pas non plus à distance: jusqu’à maintenant.

Débogueur PowerShell Universal pour VS Code

Livré avec la version 4.2 de PowerShell Universal et l’extension Universal pour VS Code, nous ajoutons la possibilité de déboguer des scripts, des Apps et des API directement depuis VS Code. Vous pouvez poser des points d’arrêt (en général via Wait-Debugger), avancer pas à pas dans le code et inspecter les variables. Ça fonctionne à distance via une connexion WebSocket vers le serveur PowerShell Universal.

Une nouvelle vue Processes sous l’arbre Platform offre une connexion à tous les runspaces du serveur gérés par PowerShell Universal. Vous pouvez sélectionner un processus et cliquer sur Attach pour y attacher le débogueur.

PowerShell Universal VS Code Processes view listing managed runspaces ready to attach.
Processus et runspaces dans l’extension PowerShell Universal pour VS Code

Une fois attaché, vous avez l’expérience complète de débogage PowerShell dans Visual Studio Code. Vous pouvez voir votre code, avancer pas à pas et inspecter les variables.

Visual Studio Code debugger stepping through a PowerShell Universal script with variables.
Débogage d’un script PowerShell Universal dans Visual Studio Code

Même si l’expérience est assez impressionnante, elle repose surtout sur l’hébergement d’une instance de PowerShell Editor Services dans le service PSU et une communication directe via WebSocket. Nous entrons dans les détails techniques ci-dessous.

Debug Adapter Protocol

Le Debug Adapter Protocol est une spécification pour implémenter des débogueurs. Il est intégré à Visual Studio Code et à Visual Studio, de sorte qu’une seule implémentation du protocole fonctionne dans les deux éditeurs. Dans le monde PowerShell, c’est PowerShell Editor Services.

VS Code envoie des requêtes JSON au processus PowerShell Editor Services, et PowerShell Editor Services répond avec des réponses JSON. Plutôt que de réécrire un débogueur de A à Z, nous pouvons maintenant nous en servir pour offrir des services de débogage.

PowerShell Editor Services

PowerShell Editor Services est une implémentation serveur du Debug Adapter Protocol. Il se livre effectivement comme un module PowerShell chargé dans un processus PowerShell, puis communique via RPC. C’est la même implémentation que celle de l’extension PowerShell pour Visual Studio Code. Pour travailler avec les services, on démarre un processus PowerShell avec les arguments de ligne de commande appropriés d’editor services.

Ça ressemble typiquement à ceci.

pwsh -NoLogo -NoProfile -Command "$PSES_BUNDLE_PATH/PowerShellEditorServices/Start-EditorServices.ps1 -BundledModulesPath $PSES_BUNDLE_PATH -LogPath $SESSION_TEMP_PATH/logs.log -SessionDetailsPath $SESSION_TEMP_PATH/session.json -FeatureFlags @() -AdditionalModules @() -HostName 'My Client' -HostProfileId 'myclient' -HostVersion 1.0.0 -LogLevel Normal"

Ensuite, nous pouvons lire le fichier session.json écrit par le service. Par défaut, il contient des noms de named pipes utilisables pour le Language Service Protocol et le Debug Adapter Protocol. Une fois un named pipe connecté, nous pouvons envoyer et recevoir des messages JSON pour contrôler le débogueur et obtenir l’état actuel du runspace.

Brancher PowerShell Universal

Plutôt que d’exposer un named pipe à l’extérieur, il est beaucoup plus simple d’exposer un WebSocket qui réutilise l’authentification, l’autorisation et le port du serveur web. Nous avons ajouté un hub SignalR qui reçoit des chaînes d’un client, puis les envoie au processus PowerShell Editor Services.

Nous canalisons effectivement les messages. Il a fallu un peu de travail pour s’adapter au protocole DAP et aux communications named pipe, mais c’était minime. Nous démarrons PSES dans un nouveau processus et seulement le service débogueur, pas le service de langage.

Brancher le débogueur VS Code

Maintenant que nous avons un WebSocket et un processus PowerShell Editor Services, nous pouvons l’intégrer à des outils comme VS Code. En pratique, nous avons implémenté un adaptateur de débogage personnalisé. Ce serait habituellement beaucoup de travail, mais comme nous communiquons réellement avec PSES via le WebSocket, nous pouvons faire transiter requêtes et réponses sans implémenter tout le protocole.

export class UniversalDebugAdapter implements vscode.DebugAdapter {

    constructor(context: vscode.ExtensionContext) {
        const connectionName = context.globalState.get("universal.connection");
        const settings = load();

        var appToken = settings.appToken;
        var url = settings.url;
        var rejectUnauthorized = true;
        var windowsAuth = false;

        if (connectionName && connectionName !== 'Default') {
            const connection = settings.connections.find(m => m.name === connectionName);
            if (connection) {
                appToken = connection.appToken;
                url = connection.url;
                rejectUnauthorized = !connection.allowInvalidCertificate;
            }
        }

        this.hubConnection = new HubConnectionBuilder()
            .withUrl(`${url}/debuggerhub`, { accessTokenFactory: () => appToken })
            .configureLogging(LogLevel.Information)
            .build();

        this.hubConnection.on("message", (message: string) => {
            const protocolMessage = JSON.parse(message) as DebugProtocol.ProtocolMessage;
            this.handleMessage(protocolMessage);
        });

        this.hubConnection.onclose(() => {
            vscode.window.showInformationMessage("Disconnected from PowerShell Universal Debugger.");
        });
    }

    private hubConnection: HubConnection;
    private sendMessage = new vscode.EventEmitter<DebugProtocol.ProtocolMessage>();

    readonly onDidSendMessage: vscode.Event<DebugProtocol.ProtocolMessage> = this.sendMessage.event;

    handleMessage(message: DebugProtocol.ProtocolMessage): void {
        if (this.hubConnection.state === 'Disconnected') {
            this.hubConnection.start().then(() => {
                this.handleMessage(message);
            });
            return;
        }

        switch (message.type) {
            case 'request':
                this.hubConnection.send("message", JSON.stringify(message));
                break;
            case 'response':
                this.sendMessage.fire(message);
                break;
            case 'event':
                this.sendMessage.fire(message);
                break;
        }
    }

    dispose() {
        this.hubConnection.stop();
    }
}

Une fois l’adaptateur de débogage en place, nous avons ajouté une vue arborescente pour choisir un processus et un runspace, puis s’y attacher. Nous exposions déjà cette information dans la console d’administration de PowerShell Universal, donc l’afficher aussi dans l’arbre VS Code a été trivial.

Quand l’utilisateur sélectionne un processus et clique sur attacher, nous envoyons une requête à l’adaptateur pour s’attacher au processus, et PSES prend le relais.

export const attachRunspace = async (runspace: RunspaceTreeItem, context: vscode.ExtensionContext) => {
    await vscode.debug.startDebugging(undefined, {
        name: "PowerShell Universal",
        type: "powershelluniversal",
        request: "attach",
        processId: runspace.runspace.processId,
        runspaceId: runspace.runspace.id
    });
};

Conclusion

Cette nouvelle fonction sera très utile pour quiconque travaille avec un script PowerShell dans Universal. Même si l’usage est un peu avancé, l’expérience de débogage est nettement meilleure que ce qui existait. Cette fonction sera livrée comme plugin dans PowerShell Universal v4.2. Nous allons déprécier l’expérience de débogage dans le navigateur et la retirer dans une version future.

Il serait possible d’étendre cette fonctionnalité au-delà des scripts PowerShell Universal. Ça pourrait ouvrir tout un univers de débogage PowerShell distant via WebSockets.

Prêt à construire? Téléchargez PowerShell Universal.