Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/disable-bun-env-autoloading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@kidd-cli/config': minor
'@kidd-cli/bundler': minor
---

Disable Bun's automatic `.env` and `bunfig.toml` loading in compiled binaries by default. Adds `autoloadDotenv` option to compile config for opt-in `.env` loading. `bunfig.toml` loading is always disabled.
6 changes: 5 additions & 1 deletion packages/bundler/src/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ export async function createBundler(params: CreateBundlerParams): Promise<Bundle
watch: async (overrides: WatchOverrides = {}): AsyncBundlerResult<void> => {
const lifecycle = resolveLifecycle(baseLifecycle, overrides)
await lifecycle.onStart({ phase: 'watch' })
const result = await watch({ onSuccess: overrides.onSuccess, resolved, verbose: overrides.verbose })
const result = await watch({
onSuccess: overrides.onSuccess,
resolved,
verbose: overrides.verbose,
})
await lifecycle.onFinish({ phase: 'watch' })
return result
},
Expand Down
58 changes: 58 additions & 0 deletions packages/bundler/src/compile/compile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const noopLifecycle = {
function makeResolved(overrides?: {
readonly targets?: readonly string[]
readonly name?: string
readonly autoloadDotenv?: boolean
}): Parameters<typeof compile>[0]['resolved'] {
return {
entry: '/project/src/index.ts',
Expand All @@ -42,6 +43,7 @@ function makeResolved(overrides?: {
define: {},
},
compile: {
autoloadDotenv: overrides?.autoloadDotenv ?? false,
targets: (overrides?.targets ?? []) as readonly CompileTarget[],
name: overrides?.name ?? 'cli',
},
Expand Down Expand Up @@ -257,6 +259,62 @@ describe('compile operation', () => {
expect(stepFinishes).toContain('linux-x64')
})

it('should always pass --no-compile-autoload-bunfig', async () => {
await compile({
resolved: makeResolved({ targets: ['linux-x64'], name: 'my-app' }),
lifecycle: noopLifecycle,
})

expect(mockProcessExec).toHaveBeenCalledWith({
cmd: 'bun',
args: expect.arrayContaining(['--no-compile-autoload-bunfig']),
cwd: '/project',
})
})

it('should pass --no-compile-autoload-dotenv by default when autoloadDotenv is not configured', async () => {
await compile({
resolved: makeResolved({ targets: ['linux-x64'], name: 'my-app' }),
lifecycle: noopLifecycle,
})

expect(mockProcessExec).toHaveBeenCalledWith({
cmd: 'bun',
args: expect.arrayContaining(['--no-compile-autoload-dotenv']),
cwd: '/project',
})
})

it('should pass --no-compile-autoload-dotenv when autoloadDotenv is explicitly false', async () => {
await compile({
resolved: makeResolved({ targets: ['linux-x64'], name: 'my-app', autoloadDotenv: false }),
lifecycle: noopLifecycle,
})

expect(mockProcessExec).toHaveBeenCalledWith({
cmd: 'bun',
args: expect.arrayContaining(['--no-compile-autoload-dotenv']),
cwd: '/project',
})
})

it('should not pass --no-compile-autoload-dotenv when autoloadDotenv is true', async () => {
await compile({
resolved: makeResolved({
targets: ['linux-x64'],
name: 'my-app',
autoloadDotenv: true,
}),
lifecycle: noopLifecycle,
})

expect(mockProcessExec).toHaveBeenCalledWith({
cmd: 'bun',
args: expect.not.arrayContaining(['--no-compile-autoload-dotenv']),
cwd: '/project',
})
})

it('should invoke bun with --compile and --outfile args', async () => {
await compile({
resolved: makeResolved({ name: 'my-app' }),
Expand Down
25 changes: 25 additions & 0 deletions packages/bundler/src/compile/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export async function compile(params: {
const isMultiTarget = targets.length > 1

const results = await compileTargetsSequentially({
autoloadDotenv: params.resolved.compile.autoloadDotenv,
bundledEntry,
cwd: params.resolved.cwd,
isMultiTarget,
Expand Down Expand Up @@ -99,6 +100,7 @@ export function resolveTargetLabel(target: CompileTarget): string {
* @returns The accumulated result tuples for each target.
*/
async function compileTargetsSequentially(params: {
readonly autoloadDotenv: boolean
readonly bundledEntry: string
readonly cwd: string
readonly isMultiTarget: boolean
Expand All @@ -118,6 +120,7 @@ async function compileTargetsSequentially(params: {
}

const result = await compileSingleTarget({
autoloadDotenv: params.autoloadDotenv,
bundledEntry: params.bundledEntry,
cwd: params.cwd,
isMultiTarget: params.isMultiTarget,
Expand All @@ -143,6 +146,7 @@ async function compileTargetsSequentially(params: {
* @returns A result tuple with the compiled binary info or an error.
*/
async function compileSingleTarget(params: {
readonly autoloadDotenv: boolean
readonly bundledEntry: string
readonly cwd: string
readonly outDir: string
Expand All @@ -162,6 +166,7 @@ async function compileSingleTarget(params: {
const args = [
'build',
'--compile',
...resolveAutoloadFlags({ autoloadDotenv: params.autoloadDotenv }),
params.bundledEntry,
'--outfile',
outfile,
Expand Down Expand Up @@ -258,6 +263,26 @@ function formatCompileError(target: CompileTarget, execError: Error, verbose: bo
return header
}

/**
* Build the CLI flags that control Bun's compile-time config autoloading.
*
* `bunfig.toml` loading is always disabled — kidd CLIs should never load
* Bun runtime config. `.env` loading is controlled by the `autoloadDotenv`
* option (disabled by default).
*
* @private
* @param params - The autoload settings.
* @returns An array of CLI flag strings.
*/
function resolveAutoloadFlags(params: { readonly autoloadDotenv: boolean }): readonly string[] {
const candidates = [
{ enabled: false, flag: '--no-compile-autoload-bunfig' },
{ enabled: params.autoloadDotenv, flag: '--no-compile-autoload-dotenv' },
] as const

return candidates.filter((c) => !c.enabled).map((c) => c.flag)
}

/**
* Remove temporary `.bun-build` files that `bun build --compile` leaves behind.
*
Expand Down
1 change: 1 addition & 0 deletions packages/bundler/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface ResolvedBuildOptions {
* Fully resolved compile options with all defaults applied.
*/
export interface ResolvedCompileOptions {
readonly autoloadDotenv: boolean
readonly targets: readonly CompileTarget[]
readonly name: string
}
Expand Down
9 changes: 9 additions & 0 deletions packages/bundler/src/utils/resolve-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ describe('config resolution', () => {
})
})

it('should default autoloadDotenv to false', () => {
expect(resolved.compile.autoloadDotenv).toBeFalsy()
})

it('should use binaryName as compile name when no config name', () => {
expect(resolved.compile.name).toBe('cli')
})
Expand Down Expand Up @@ -75,6 +79,7 @@ describe('config resolution', () => {
},
commands: './src/commands',
compile: {
autoloadDotenv: true,
name: 'my-cli',
out: './bin',
targets: ['darwin-arm64'],
Expand Down Expand Up @@ -114,6 +119,10 @@ describe('config resolution', () => {
})
})

it('should use custom autoloadDotenv value', () => {
expect(resolved.compile.autoloadDotenv).toBeTruthy()
})

it('should prefer config compile name over binaryName', () => {
expect(resolved.compile.name).toBe('my-cli')
})
Expand Down
1 change: 1 addition & 0 deletions packages/bundler/src/utils/resolve-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function resolveConfig(params: {
buildOutDir,
commands,
compile: {
autoloadDotenv: compileOpts.autoloadDotenv ?? false,
name: compileOpts.name ?? params.binaryName,
targets: compileOpts.targets ?? [],
},
Expand Down
8 changes: 8 additions & 0 deletions packages/config/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ export interface CompileOptions {
* Binary name. Defaults to cli name.
*/
name?: string
/**
* Load `.env` files at runtime in the compiled binary. Default: false.
*
* When disabled, the compiled binary will not auto-load `.env` files from
* the working directory. Use the kidd auth dotenv strategy for explicit
* `.env` loading instead.
*/
readonly autoloadDotenv?: boolean
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/config/src/utils/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('KiddConfigSchema schema', () => {
const result = KiddConfigSchema.safeParse({
build: { external: ['pg'], minify: true, out: './dist', sourcemap: false, target: 'node20' },
commands: './commands',
compile: { name: 'my-cli', out: './bin', targets: ['linux-x64', 'darwin-arm64'] },
compile: { autoloadDotenv: true, name: 'my-cli', out: './bin', targets: ['linux-x64', 'darwin-arm64'] },
entry: './src/index.ts',
include: ['assets/**'],
})
Expand Down
1 change: 1 addition & 0 deletions packages/config/src/utils/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const BuildOptionsSchema = z
*/
const CompileOptionsSchema = z
.object({
autoloadDotenv: z.boolean().optional(),
name: z.string().optional(),
out: z.string().optional(),
targets: z.array(CompileTargetSchema).optional(),
Expand Down
Loading