Skip to content

Commit

Permalink
feat: add show command to view environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
joelcox22 committed Jun 20, 2021
1 parent 9b5395f commit e825d32
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"standard.enable": true,
"standard.autoFixOnSave": true,
"standard.engine": "ts-standard"
}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ use-lambda-env --region ap-southeast-2 your-function-name node index.js

I'd recommend putting the above in a script in package.json, so that developers don't need to remember it, and so you can update the function name for all developers easily when needed.

You can also just `show` the environment variables as a JSON blob by doing

```bash
use-lambda-env show --region ap-southeast-2 your-function-name
```

## Contributing

This repository uses `semantic-release`. Please use the [semantic commit message format] for any contributions.
Expand Down
43 changes: 31 additions & 12 deletions src/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { cli } from './cli'
import * as useLambdaEnv from './use-lambda-env'

const spy = jest.spyOn(useLambdaEnv, 'useLambdaEnv')
spy.mockImplementation(jest.fn())
const use = jest.spyOn(useLambdaEnv, 'useLambdaEnv')
const show = jest.spyOn(useLambdaEnv, 'showLambdaEnv')
const consoleLog = jest.spyOn(console, 'log')
const processExit = jest.spyOn(process, 'exit')
use.mockImplementation(jest.fn())
show.mockImplementation(jest.fn())
consoleLog.mockImplementation(jest.fn())
processExit.mockImplementation(jest.fn() as any)

afterEach(() => {
jest.resetAllMocks()
Expand All @@ -16,48 +22,61 @@ const exec = (...args: string[]): any => cli.parse(['node', 'use-lambda-env', ..

test('basic usage', () => {
exec('my-function', 'env')
expect(spy).toHaveBeenCalledWith('my-function', ['env'], undefined)
expect(use).toHaveBeenCalledWith('my-function', ['env'], undefined)
})

test('complex command', () => {
exec('test-function', 'bash', '-c', 'env | grep test')
expect(spy).toHaveBeenCalledWith('test-function', ['bash', '-c', 'env | grep test'], undefined)
expect(use).toHaveBeenCalledWith('test-function', ['bash', '-c', 'env | grep test'], undefined)
})

test('region argument', () => {
exec('--region', 'ap-southeast-2', 'region-function', 'hello')
expect(spy).toHaveBeenCalledWith('region-function', ['hello'], 'ap-southeast-2')
expect(use).toHaveBeenCalledWith('region-function', ['hello'], 'ap-southeast-2')
})

test('region ignored from command', () => {
exec('region-function', 'hello', '--region', 'ap-southeast-2')
expect(spy).toHaveBeenCalledWith('region-function', ['hello', '--region', 'ap-southeast-2'], undefined)
expect(use).toHaveBeenCalledWith('region-function', ['hello', '--region', 'ap-southeast-2'], undefined)
})

test('region from environment variable', () => {
process.env.AWS_REGION = 'us-east-1'
exec('abc', 'def')
expect(spy).toHaveBeenCalledWith('abc', ['def'], 'us-east-1')
expect(use).toHaveBeenCalledWith('abc', ['def'], 'us-east-1')
})

test('region flag takes priority over environment variable', () => {
process.env.AWS_REGION = 'us-east-1'
exec('--region', 'ap-southeast-2', 'abc', 'def')
expect(spy).toHaveBeenCalledWith('abc', ['def'], 'ap-southeast-2')
expect(use).toHaveBeenCalledWith('abc', ['def'], 'ap-southeast-2')
})

test('conflicting region option and command', () => {
exec('--region', 'us-east-1', 'test', 'abc', '--region', 'us-east-2')
expect(spy).toHaveBeenCalledWith('test', ['abc', '--region', 'us-east-2'], 'us-east-1')
expect(use).toHaveBeenCalledWith('test', ['abc', '--region', 'us-east-2'], 'us-east-1')
})

test('handling an error', () => {
const consoleLog = jest.spyOn(console, 'log').mockImplementation(jest.fn())
const processExit = jest.spyOn(process, 'exit').mockImplementation(jest.fn() as any)
spy.mockImplementation(() => {
use.mockImplementation(() => {
throw new Error('example error')
})
exec('a', 'b')
expect(consoleLog).toHaveBeenCalledWith('example error')
expect(processExit).toHaveBeenCalledWith(1)
})

test('show environment variables', () => {
exec('show', 'my-function')
expect(show).toHaveBeenCalledWith('my-function', undefined)
expect(use).not.toHaveBeenCalled()
})

test('show failing', () => {
show.mockImplementation(() => {
throw new Error('test error')
})
exec('show', 'a')
expect(consoleLog).toHaveBeenCalledWith('test error')
expect(processExit).toHaveBeenCalledWith(1)
})
51 changes: 36 additions & 15 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cac from 'cac'
import * as fs from 'fs'
import { useLambdaEnv } from './use-lambda-env'
import { useLambdaEnv, showLambdaEnv } from './use-lambda-env'

export const cli = cac('use-lambda-env')

Expand All @@ -9,30 +9,51 @@ const { version } = JSON.parse(fs.readFileSync(`${__dirname}/../package.json`, '
cli.help()
cli.version(version)

function customParse (functionName: string): {
command: string[]
region?: string
} {
// a little bit of tricky option parsing, because
// variadic arguments makes things dificult :(
const functionNameIndex = cli.rawArgs.indexOf(functionName)
const command = cli.rawArgs.slice(functionNameIndex + 1)
const optionArgs = cli.rawArgs.slice(0, functionNameIndex)
let region: string | undefined
optionArgs.forEach((arg, index) => {
if (arg === '--region') {
region = optionArgs[index + 1]
}
})
return {
command,
region: region ?? process.env.AWS_REGION
}
}

cli.option('--region <region>', 'Which region your lambda function lives in')

cli
.command('<lambdaFunction> <...command>')
.option('--region <region>', 'Which region your lambda function lives in')
.allowUnknownOptions()
.action(async (functionName: string) => {
// a little bit of tricky option parsing, because
// variadic arguments makes things dificult :(
const functionNameIndex = cli.rawArgs.indexOf(functionName)
const command = cli.rawArgs.slice(functionNameIndex + 1)
const optionArgs = cli.rawArgs.slice(0, functionNameIndex)
let region: string | undefined
optionArgs.forEach((arg, index) => {
if (arg === '--region') {
region = optionArgs[index + 1]
}
})

try {
await useLambdaEnv(functionName, command, region ?? process.env.AWS_REGION)
const { command, region } = customParse(functionName)
await useLambdaEnv(functionName, command, region)
} catch (err) {
console.log(err.message)
process.exit(1)
}
})

cli.command('show <lambdaFunction>').action(async (functionName: string) => {
try {
const { region } = customParse(functionName)
await showLambdaEnv(functionName, region)
} catch (err) {
console.log(err.message)
process.exit(1)
}
})

cli.example('use-lambda-env my-function node index.js')
cli.example('use-lambda-env --region ap-southeast-2 my-function node index.js')
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@

import { cli } from './cli'

cli.parse()
try {
cli.parse()
} catch (err) {
console.log(err.message)
process.exit(1)
}
36 changes: 31 additions & 5 deletions src/use-lambda-env.test.ts → src/lambda-env.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import nock from 'nock'
import cp from 'child_process'
import { useLambdaEnv } from './use-lambda-env'
import { showLambdaEnv, useLambdaEnv } from './use-lambda-env'

nock.disableNetConnect()

const spawnSync = jest.spyOn(cp, 'spawnSync').mockReturnValue({
status: 0
} as any)
const spawnSync = jest.spyOn(cp, 'spawnSync')
const consoleLog = jest.spyOn(console, 'log')
spawnSync.mockReturnValue({ status: 0 } as any)
consoleLog.mockImplementation(jest.fn())

afterEach(() => {
jest.resetAllMocks()
})

const processExit = jest.spyOn(process, 'exit').mockImplementation(jest.fn() as any)

Expand All @@ -27,7 +32,6 @@ test('success', async () => {
stdio: 'inherit',
env: {
AWS_REGION: 'ap-southeast-2',
...process.env,
abc: 'def',
test: '123'
}
Expand Down Expand Up @@ -56,3 +60,25 @@ test('missing region config', async () => {
delete process.env.AWS_REGION
await expect(useLambdaEnv('test', ['command'])).rejects.toThrow('Region is missing')
})

test('show environment variables', async () => {
nock('https://lambda.ap-southeast-2.amazonaws.com')
.get('/2015-03-31/functions/my-function').reply(200, {
Configuration: {
Environment: {
Variables: {
abc: 'def',
test: '123'
}
}
}
})
await showLambdaEnv('my-function', 'ap-southeast-2')
expect(spawnSync).not.toHaveBeenCalled()
expect(consoleLog).toHaveBeenCalledWith({
AWS_REGION: 'ap-southeast-2',
abc: 'def',
test: '123'
})
expect(processExit).toHaveBeenCalledWith(0)
})
19 changes: 12 additions & 7 deletions src/use-lambda-env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as cp from 'child_process'
import * as lambda from '@aws-sdk/client-lambda'

export async function useLambdaEnv (functionName: string, command: string[], region?: string): Promise<never> {
async function getLambdaEnv (functionName: string, region?: string): Promise<NodeJS.ProcessEnv> {
const clientOptions: lambda.LambdaClientConfig = {}
if (typeof region === 'string') {
clientOptions.region = region
Expand All @@ -17,16 +17,21 @@ export async function useLambdaEnv (functionName: string, command: string[], reg
throw new Error(err)
}

const env: NodeJS.ProcessEnv = {
AWS_REGION: region ?? process.env.AWS_REGION,
...process.env,
...(data.Configuration?.Environment?.Variables ?? {})
return {
AWS_REGION: region,
...(data.Configuration?.Environment?.Variables)
}
}

export async function useLambdaEnv (functionName: string, command: string[], region?: string): Promise<never> {
const result = cp.spawnSync(command[0], command.slice(1), {
stdio: 'inherit',
env
env: await getLambdaEnv(functionName, region)
})

process.exit(result.status ?? 1)
}

export async function showLambdaEnv (functionName: string, region?: string): Promise<never> {
console.log(await getLambdaEnv(functionName, region))
process.exit(0)
}

0 comments on commit e825d32

Please sign in to comment.