Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions docs/externally-managed-environment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Externally Managed Environment Support

This document describes the new feature that allows Zephyr IDE to work with externally managed environments, such as Docker containers or pre-configured development environments.

## Overview

Zephyr IDE now supports using system environment variables instead of managing them automatically. This is particularly useful for:

- Docker/container-based development workflows
- Pre-configured development environments
- DevContainers
- CI/CD pipelines
- Any scenario where Zephyr tools and SDK are already installed and configured

## Features

### 1. Configuration Setting: `use-system-environment`

You can enable externally managed environment mode by adding this to your `.vscode/settings.json`:

```json
{
"zephyr-ide.use-system-environment": true
}
```

When enabled, Zephyr IDE will not override the following environment variables:
- `ZEPHYR_BASE`
- `ZEPHYR_SDK_INSTALL_DIR`
- `VIRTUAL_ENV`
- `PATH`

### 2. Externally Managed Workspace Mode

You can also explicitly select "Externally Managed" mode from the West Workspace panel:

1. Open the Zephyr IDE sidebar
2. Navigate to the "West Workspaces" panel
3. Click the target icon next to "Externally Managed"
4. Confirm the switch

Or use the command palette:
- Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS)
- Type "Zephyr IDE: Use Externally Managed West Workspace"
- Press Enter

## How It Works

### Configuration Setting Method

When `use-system-environment` is set to `true`:
- Zephyr IDE clears its environment variable collection
- The extension description shows "Using system environment variables"
- All environment variables (`ZEPHYR_BASE`, `ZEPHYR_SDK_INSTALL_DIR`, etc.) come from the system

### Externally Managed Workspace Method

When you select "Externally Managed" workspace:
- A special setup state is created with `externallyManaged: true`
- The `ZEPHYR_BASE` is read from your system's `ZEPHYR_BASE` environment variable
- All west operations use the system's configured environment
- Build and flash operations work with your existing tools

## Use Cases

### Docker/DevContainer Workflow

```dockerfile
# Dockerfile
FROM zephyrprojectrtos/ci:v0.26.6

ENV ZEPHYR_BASE=/workspaces/zephyrproject/zephyr
ENV ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-0.16.8
```

`.vscode/settings.json`:
```json
{
"zephyr-ide.use-system-environment": true
}
```

### Pre-configured Environment

If you have a custom Zephyr installation:

1. Set environment variables in your shell profile:
```bash
export ZEPHYR_BASE=/path/to/zephyrproject/zephyr
export ZEPHYR_SDK_INSTALL_DIR=/path/to/zephyr-sdk-0.16.8
```

2. Choose one of:
- Enable `use-system-environment` in settings
- Select "Externally Managed" from West Workspaces panel

### CI/CD Pipeline

In GitHub Actions or other CI systems:

```yaml
env:
ZEPHYR_BASE: ${{ github.workspace }}/zephyr
ZEPHYR_SDK_INSTALL_DIR: /opt/zephyr-sdk
```

Then enable `use-system-environment` in your settings.

## Migration from Managed to Externally Managed

If you're currently using Zephyr IDE's managed environment and want to switch:

1. Note your current `ZEPHYR_BASE` and `ZEPHYR_SDK_INSTALL_DIR` paths
2. Set these as system environment variables
3. Enable `use-system-environment` setting or select "Externally Managed"
4. Restart VS Code to apply changes

## Troubleshooting

### Environment Variables Not Found

If you see errors about missing Zephyr tools:

1. Verify environment variables are set:
```bash
echo $ZEPHYR_BASE
echo $ZEPHYR_SDK_INSTALL_DIR
```

2. Ensure the paths exist and contain valid Zephyr installations

3. Restart VS Code after setting environment variables

### Switching Back to Managed Mode

To return to Zephyr IDE's managed environment:

1. Disable `use-system-environment` in settings, or
2. Select a different workspace from the West Workspaces panel
3. Restart VS Code

## Technical Details

- The `ZEPHYR_SDK_INSTALL_DIR` update is now only performed when a managed setup state exists
- A new `externallyManaged` flag in `SetupState` interface identifies external environments
- The `generateExternallyManagedSetupState()` function creates a valid setup state that works with existing code
- All existing checks for `activeSetupState` remain compatible

## See Also

- [Main Documentation](../README.md)
- [Manual](MANUAL.md)
- [Issue #86](https://github.com/mylonics/zephyr-ide/issues/86)
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
],
"default": false,
"description": "If true, uses 'west update --narrow'. If false, uses 'west update' without --narrow."
},
"zephyr-ide.use-system-environment": {
"type": [
"boolean"
],
"default": false,
"description": "If true, uses system environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR, etc.) instead of managing them. Useful for containerized or pre-configured environments."
}
}
},
Expand Down Expand Up @@ -200,6 +207,10 @@
"title": "Zephyr IDE: Refresh West Workspaces",
"icon": "$(refresh)"
},
{
"command": "zephyr-ide.use-externally-managed-west-workspace",
"title": "Zephyr IDE: Use Externally Managed West Workspace"
},
{
"command": "zephyr-ide.reset-workspace",
"title": "Zephyr IDE: Reset Workspace"
Expand Down
12 changes: 12 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,18 @@ export async function activate(context: vscode.ExtensionContext) {
})
);

context.subscriptions.push(
vscode.commands.registerCommand("zephyr-ide.use-externally-managed-west-workspace", async () => {
const { generateExternallyManagedSetupState } = await import("./setup_utilities/types.js");
const externalSetupState = generateExternallyManagedSetupState();
wsConfig.activeSetupState = externalSetupState;
await setWorkspaceState(context, wsConfig);
reloadEnvironmentVariables(context, externalSetupState);
westWorkspaceView.updateWebView(wsConfig, globalConfig);
vscode.window.showInformationMessage("Using externally managed west workspace. Environment variables from system will be used.");
})
);

// Kick an initial refresh shortly after activation so views render even if no command ran yet
setTimeout(() => {
try {
Expand Down
46 changes: 46 additions & 0 deletions src/panels/west_workspace_view/WestWorkspaceView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,38 @@ export class WestWorkspaceView implements vscode.WebviewViewProvider {
}
data.push(globalData);

// Add special "Externally Managed" option
const isExternallyManaged = wsConfig.activeSetupState?.externallyManaged === true;

const externalData: any = {
icons: {
open: 'database',
closed: 'database'
},
label: 'Externally Managed',
description: 'Use system environment variables',
tooltip: 'Use existing ZEPHYR_BASE and ZEPHYR_SDK_INSTALL_DIR from system environment',
value: { installPath: 'externally-managed' }
};

if (isExternallyManaged) {
// Active externally managed workspace
externalData['selected'] = true;
externalData['open'] = false; // No sub-items, so don't open dropdown
externalData['actions'] = [{
icon: 'close',
actionId: 'deselect',
tooltip: 'Deselect Workspace'
}];
} else {
externalData['actions'] = [{
icon: 'target',
actionId: 'activate',
tooltip: 'Set as Active'
}];
}
data.push(externalData);

if (globalConfig.setupStateDictionary) {
for (const installPath in globalConfig.setupStateDictionary) {
// Skip global path as it's already added above
Expand Down Expand Up @@ -329,6 +361,20 @@ export class WestWorkspaceView implements vscode.WebviewViewProvider {

private async handleActivate(installPath: string) {
try {
// Handle special case for externally managed workspace
if (installPath === 'externally-managed') {
const confirm = await vscode.window.showWarningMessage(
'Switch to externally managed workspace? This will use system environment variables (ZEPHYR_BASE, ZEPHYR_SDK_INSTALL_DIR, etc.).',
'Switch',
'Cancel'
);

if (confirm === 'Switch') {
vscode.commands.executeCommand('zephyr-ide.use-externally-managed-west-workspace');
}
return;
}

const installName = path.basename(installPath);

// Show confirmation prompt (non-modal warning)
Expand Down
13 changes: 13 additions & 0 deletions src/setup_utilities/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface SetupState {
zephyrVersion?: ZephyrVersionNumber,
env: { [name: string]: string | undefined },
setupPath: string,
externallyManaged?: boolean,
}

export type SetupStateDictionary = { [name: string]: SetupState };
Expand Down Expand Up @@ -70,3 +71,15 @@ export function generateSetupState(setupPath: string): SetupState {
setupPath: setupPath
};
}

export function generateExternallyManagedSetupState(): SetupState {
return {
pythonEnvironmentSetup: true,
westUpdated: true,
packagesInstalled: true,
zephyrDir: process.env.ZEPHYR_BASE || '',
env: {},
setupPath: 'externally-managed',
externallyManaged: true
};
}
72 changes: 72 additions & 0 deletions src/test/externally-managed.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2024 mylonics
Author Rijesh Augustine

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import * as assert from "assert";
import { generateExternallyManagedSetupState } from "../setup_utilities/types";

suite("Externally Managed Setup State Test Suite", () => {

test("Creates externally managed setup state correctly", () => {
const setupState = generateExternallyManagedSetupState();

// Verify all required fields are set correctly
assert.strictEqual(setupState.pythonEnvironmentSetup, true, "pythonEnvironmentSetup should be true");
assert.strictEqual(setupState.westUpdated, true, "westUpdated should be true");
assert.strictEqual(setupState.packagesInstalled, true, "packagesInstalled should be true");
assert.strictEqual(setupState.setupPath, 'externally-managed', "setupPath should be 'externally-managed'");
assert.strictEqual(setupState.externallyManaged, true, "externallyManaged flag should be true");

// Verify env is an empty object
assert.deepStrictEqual(setupState.env, {}, "env should be an empty object");
});

test("Uses ZEPHYR_BASE from environment if available", () => {
// Save original value
const originalZephyrBase = process.env.ZEPHYR_BASE;

// Set test value
process.env.ZEPHYR_BASE = "/test/zephyr/path";

const setupState = generateExternallyManagedSetupState();

assert.strictEqual(setupState.zephyrDir, "/test/zephyr/path", "zephyrDir should use ZEPHYR_BASE from environment");

// Restore original value
if (originalZephyrBase !== undefined) {
process.env.ZEPHYR_BASE = originalZephyrBase;
} else {
delete process.env.ZEPHYR_BASE;
}
});

test("Sets empty zephyrDir when ZEPHYR_BASE is not set", () => {
// Save original value
const originalZephyrBase = process.env.ZEPHYR_BASE;

// Remove ZEPHYR_BASE
delete process.env.ZEPHYR_BASE;

const setupState = generateExternallyManagedSetupState();

assert.strictEqual(setupState.zephyrDir, '', "zephyrDir should be empty when ZEPHYR_BASE is not set");

// Restore original value
if (originalZephyrBase !== undefined) {
process.env.ZEPHYR_BASE = originalZephyrBase;
}
});
});
14 changes: 12 additions & 2 deletions src/utilities/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,20 @@ export function reloadEnvironmentVariables(context: vscode.ExtensionContext, set
context.environmentVariableCollection.persistent = false;
context.environmentVariableCollection.clear();

context.environmentVariableCollection.description = "Zephyr IDE adds `ZEPHYR_SDK_INSTALL_DIR`";
context.environmentVariableCollection.replace("ZEPHYR_SDK_INSTALL_DIR", getToolchainDir(), { applyAtProcessCreation: true, applyAtShellIntegration: true });
// Check if user wants to use system environment variables instead
const configuration = vscode.workspace.getConfiguration();
const useSystemEnvironment: boolean | undefined = configuration.get("zephyr-ide.use-system-environment");

if (useSystemEnvironment) {
// User prefers system environment variables, don't override
context.environmentVariableCollection.description = "Using system environment variables";
return;
}

if (setupState) {
context.environmentVariableCollection.description = "Zephyr IDE adds `ZEPHYR_SDK_INSTALL_DIR`";
context.environmentVariableCollection.replace("ZEPHYR_SDK_INSTALL_DIR", getToolchainDir(), { applyAtProcessCreation: true, applyAtShellIntegration: true });

if (setupState.env["VIRTUAL_ENV"]) {
context.environmentVariableCollection.description += ", `VIRTUAL_ENV`";
context.environmentVariableCollection.replace("VIRTUAL_ENV", setupState.env["VIRTUAL_ENV"], { applyAtProcessCreation: true, applyAtShellIntegration: true });
Expand Down