Skip to content
Open
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,19 @@ To make VS Code map the files on the server to the right files on your local mac
}
```

### Environment Variables in Path Mappings

You can use environment variables in `pathMappings` using the `${env:VARIABLE_NAME}` syntax. This is useful for dynamic paths that vary between environments or users:

```json
"pathMappings": {
"${env:DOCKER_WEB_ROOT}": "${workspaceFolder}",
"/app": "${env:PROJECT_PATH}"
}
```

The environment variables are resolved when the debug session starts. If a variable is not defined, the literal string (e.g., `${env:UNDEFINED_VAR}`) will be kept as-is.

Please also note that setting any of the CLI debugging options will not work with remote host debugging, because the script is always launched locally. If you want to debug a CLI script on a remote host, you need to launch it manually from the command line.

## Proxy support
Expand Down
11 changes: 11 additions & 0 deletions src/envResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Resolves environment variables in a string
* Supports: ${env:VAR_NAME}
*/
export function resolveEnvVariables(value: string): string {
// Replace ${env:VAR_NAME} with environment variable values
return value.replace(/\$\{env:([^}]+)\}/g, (match, envVar: string) => {
const envValue = process.env[envVar]
return envValue !== undefined ? envValue : match
})
}
44 changes: 24 additions & 20 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { WorkspaceFolder, DebugConfiguration, CancellationToken } from 'vscode'
import { EvaluateExtendedArguments, LaunchRequestArguments } from './phpDebug'
import * as which from 'which'
import * as path from 'path'
import { resolveEnvVariables } from './envResolver'
import { DebugProtocol } from '@vscode/debugprotocol'

export function activate(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -72,29 +73,32 @@ export function activate(context: vscode.ExtensionContext) {
}
}
}
if (folder && folder.uri.scheme !== 'file') {
// replace
if (debugConfiguration.pathMappings) {
for (const key in debugConfiguration.pathMappings) {
debugConfiguration.pathMappings[key] = debugConfiguration.pathMappings[key].replace(
'${workspaceFolder}',
folder.uri.toString()
)
if (debugConfiguration.pathMappings) {
const resolvedMappings: { [index: string]: string } = {}
for (const [serverPath, localPath] of Object.entries(debugConfiguration.pathMappings)) {
const resolvedServerPath = resolveEnvVariables(serverPath)
let resolvedLocalPath = resolveEnvVariables(localPath)

if (folder && folder.uri.scheme !== 'file') {
resolvedLocalPath = resolvedLocalPath.replace('${workspaceFolder}', folder.uri.toString())
}

resolvedMappings[resolvedServerPath] = resolvedLocalPath
}
// The following path are currently NOT mapped
/*
debugConfiguration.skipEntryPaths = debugConfiguration.skipEntryPaths?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
debugConfiguration.skipFiles = debugConfiguration.skipFiles?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
debugConfiguration.ignore = debugConfiguration.ignore?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
*/
debugConfiguration.pathMappings = resolvedMappings
}
// The following path are currently NOT mapped
/*
debugConfiguration.skipEntryPaths = debugConfiguration.skipEntryPaths?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
debugConfiguration.skipFiles = debugConfiguration.skipFiles?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
debugConfiguration.ignore = debugConfiguration.ignore?.map(v =>
v.replace('${workspaceFolder}', folder.uri.toString())
)
*/
return debugConfiguration
},
})
Expand Down
68 changes: 68 additions & 0 deletions src/test/envResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { assert } from 'chai'
import { describe, it, beforeEach, afterEach } from 'mocha'
import { resolveEnvVariables } from '../envResolver'

describe('Environment Variable Resolution', () => {
let originalEnv: NodeJS.ProcessEnv

beforeEach(() => {
originalEnv = { ...process.env }
})

afterEach(() => {
process.env = originalEnv
})

it('should resolve ${env:VAR_NAME} with existing environment variable', () => {
process.env.TEST_VAR = '/test/path'
const result = resolveEnvVariables('${env:TEST_VAR}/subdir')
assert.equal(result, '/test/path/subdir')
})

it('should keep ${env:VAR_NAME} if environment variable does not exist', () => {
delete process.env.NONEXISTENT_VAR
const result = resolveEnvVariables('${env:NONEXISTENT_VAR}/subdir')
assert.equal(result, '${env:NONEXISTENT_VAR}/subdir')
})

it('should resolve multiple environment variables', () => {
process.env.VAR1 = '/path1'
process.env.VAR2 = '/path2'
const result = resolveEnvVariables('${env:VAR1}/${env:VAR2}')
assert.equal(result, '/path1//path2')
})

it('should handle text without environment variables', () => {
const result = resolveEnvVariables('/var/www/html')
assert.equal(result, '/var/www/html')
})

it('should handle environment variables with underscores and numbers', () => {
process.env.MY_VAR_123 = '/custom/path'
const result = resolveEnvVariables('${env:MY_VAR_123}')
assert.equal(result, '/custom/path')
})

it('should handle empty environment variable value', () => {
process.env.EMPTY_VAR = ''
const result = resolveEnvVariables('${env:EMPTY_VAR}/test')
assert.equal(result, '/test')
})

it('should handle mixed content', () => {
process.env.DOCKER_ROOT = '/var/www/html'
const result = resolveEnvVariables('prefix/${env:DOCKER_ROOT}/suffix')
assert.equal(result, 'prefix//var/www/html/suffix')
})

it('should handle pathMapping use case', () => {
process.env.DOCKER_WEB_ROOT = '/var/www/html'
process.env.LOCAL_PROJECT = '/Users/developer/myproject'

const serverPath = '${env:DOCKER_WEB_ROOT}'
const localPath = '${env:LOCAL_PROJECT}'

assert.equal(resolveEnvVariables(serverPath), '/var/www/html')
assert.equal(resolveEnvVariables(localPath), '/Users/developer/myproject')
})
})