Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce includeInDmnoConfig option #182

Merged
merged 1 commit into from
Dec 16, 2024
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
11 changes: 11 additions & 0 deletions .changeset/few-rings-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@dmno/encrypted-vault-plugin": patch
"@dmno/remix-integration": patch
"@dmno/vite-integration": patch
"@dmno/1password-plugin": patch
"@dmno/bitwarden-plugin": patch
"@dmno/infisical-plugin": patch
"dmno": patch
---

add `includeInDmnoConfig` option so items can be exluded from DMNO_CONFIG
12 changes: 12 additions & 0 deletions example-repo/packages/astro-web/.dmno/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ export default defineDmnoService({
'SOME_API_KEY',
],
schema: {

INTERNAL_ITEM: {
description: 'will not be included in DMNO_CONFIG',
value: 'dont-include-me',
includeInDmnoConfig: false,
},

FN_INTERNAL_CHECK: {
value: () => DMNO_CONFIG.INTERNAL_ITEM,
},


OP_TOKEN: { extends: OnePasswordTypes.serviceAccountToken },

FOO: {
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/cli/commands/printenv.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ program.action(async (itemPath: string, opts: {}, thisCommand) => {
checkForSchemaErrors(workspace);
checkForConfigErrors(service);


const injectedEnv = await ctx.dmnoServer.makeRequest('getInjectedJson', service.serviceName);
// TODO: process env version or dmno env? what if the key is renamed as an env var? flag to select?
// TODO: could be smarter about not caring about errors unless they affect the item(s) being printed
// TODO: support nested paths
// TODO: do we want to support multiple items?

if (!injectedEnv[itemPath]) {
const { injectedProcessEnv } = await ctx.dmnoServer.makeRequest('getServiceResolvedConfig', service.serviceName);

if (!(itemPath in injectedProcessEnv)) {
throw new CliExitError(`Config item ${itemPath} not found in config schema`, {
details: [
'Perhaps you meant one of:',
Expand All @@ -49,10 +50,9 @@ program.action(async (itemPath: string, opts: {}, thisCommand) => {
});
}


// TODO: what to do about formatting of arrays/objects/etc
// now just print the resolved value
ctx.logOutput(injectedEnv[itemPath].value);
ctx.logOutput(injectedProcessEnv[itemPath]);
});

export const PrintEnvCommand = program;
9 changes: 7 additions & 2 deletions packages/core/src/cli/commands/resolve.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ program.action(async (opts: {
checkForSchemaErrors(workspace);
checkForConfigErrors(service, { showAll: opts?.showAll });

// this logic could probably move to the service itself?
function getExposedConfigValues() {
const values = {} as Record<string, any>;
for (const itemKey in service.configNodes) {
Expand All @@ -71,14 +72,18 @@ program.action(async (opts: {
return values;
}

// when we are using the non default format (which includes everything) we have the same questions
// here about what values to include, and how to handle env var keys that may be renamed
// probably need a flag to select which we are talking about

// console.log(service.config);
if (opts.format === 'json') {
console.log(JSON.stringify(getExposedConfigValues()));
} else if (opts.format === 'json-full') {
console.dir(service, { depth: null });
} else if (opts.format === 'json-injected') {
const injectedJson = await ctx.dmnoServer.makeRequest('getInjectedJson', ctx.selectedService.serviceName);
console.log(JSON.stringify(injectedJson));
const { injectedDmnoEnv } = await ctx.dmnoServer.makeRequest('getServiceResolvedConfig', ctx.selectedService.serviceName);
console.log(JSON.stringify(injectedDmnoEnv));
} else if (opts.format === 'env') {
console.log(stringifyObjectAsEnvFile(getExposedConfigValues()));
} else {
Expand Down
21 changes: 7 additions & 14 deletions packages/core/src/cli/commands/run.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,18 @@ program.action(async (_command, opts: {
//! await workspace.resolveConfig();
checkForConfigErrors(service);

const injectedJson = await ctx.dmnoServer.makeRequest('getInjectedJson', ctx.selectedService.serviceName);
const {
injectedProcessEnv,
injectedDmnoEnv,
} = await ctx.dmnoServer.makeRequest('getServiceResolvedConfig', ctx.selectedService.serviceName);

const fullInjectedEnv = {
// TODO: not sure if we want to overwrite or not
...process.env,
...injectedProcessEnv,
};
// we need to add any config items that are defined in dmno config, but we dont want to modify existing items
for (const key in injectedJson) {
// must skip $SETTINGS
if (key.startsWith('$')) continue;

// TODO: need to think about how we deal with nested items
// TODO: let config nodes expose themselves in inject env vars with aliases
if (!Object.hasOwn(process.env, key)) {
const strVal = injectedJson[key]?.value?.toString();
if (strVal !== undefined) fullInjectedEnv[key] = strVal;
}
}

fullInjectedEnv.DMNO_INJECTED_ENV = JSON.stringify(injectedJson);
fullInjectedEnv.DMNO_INJECTED_ENV = JSON.stringify(injectedDmnoEnv);
// this is what signals to the child process that is has a parent dmno server to use
fullInjectedEnv.DMNO_PARENT_SERVER = ctx.dmnoServer.parentServerInfo;

Expand Down
22 changes: 18 additions & 4 deletions packages/core/src/config-engine/config-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,24 @@ export class DmnoService {
return this.configraphEntity.isValid;
}

getEnv() {
const env: Record<string, any> = _.mapValues(this.configraphEntity.configNodes, (node) => {
return node.resolvedValue;
});
/**
* key/value object of resolved config ready to be injected as actual environment variables (process.env)
*/
getInjectedProcessEnv() {
const env: Record<string, string | undefined> = {};
for (const node of _.values(this.configraphEntity.configNodes)) {
// TODO: handle key renaming
// TODO: better handling of nested keys / object
const val = node.resolvedValue;
// not sure about this handling of _empty_ items
if (val === null || val === undefined || val === '') {
env[node.key] = undefined;
} else if (_.isPlainObject(val)) {
env[node.key] = JSON.stringify(val);
} else {
env[node.key] = val.toString();
}
}
return env;
}

Expand Down
13 changes: 12 additions & 1 deletion packages/core/src/config-engine/configraph-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export type DmnoDataTypeMetadata = {

/** opt in/out of build-type code replacements - default is false unless changed at the service level */
dynamic?: boolean;

/** set to false to keep this item out of DMNO_CONFIG */
includeInDmnoConfig?: boolean,
};

// way more legible, but super weird that we are involving a class like this
Expand Down Expand Up @@ -142,7 +145,11 @@ DmnoEntityMetadata, DmnoDataTypeMetadata, DmnoConfigraphNode
getInjectedEnvJSON(): InjectedDmnoEnv {
// some funky ts stuff going on here... doesn't like how I set the values,
// but otherwise the type seems to work ok?
const env: any = _.mapValues(this.configNodes, (item) => item.toInjectedJSON());
const env: any = {};
_.each(this.configNodes, (item, itemKey) => {
if (!item.includeInDmnoConfig) return;
env[itemKey] = item.toInjectedJSON();
});
// simple way to get settings passed through to injected stuff - we may want
env.$SETTINGS = this.settings;
return env as any;
Expand All @@ -159,6 +166,10 @@ export class DmnoConfigraphNode extends ConfigraphNode<DmnoDataTypeMetadata> {
return !!this.type.getMetadata('sensitive');
}

get includeInDmnoConfig() {
return this.type.getMetadata('includeInDmnoConfig') !== false;
}

get isDynamic() {
// this resolves whether the item should actually be treated as static or dynamic
// which takes into account the specific item's `dynamic` override
Expand Down
26 changes: 17 additions & 9 deletions packages/core/src/config-engine/type-generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ export async function generateServiceTypes(service: DmnoService, writeToFile = f
// write global file which defines a DMNO_CONFIG global
// this used in our config.mts files and in front-end apps where we inject rollup rewrites
await fs.promises.writeFile(`${typeGenFolderPath}/global.d.ts`, `${AUTOGENERATED_FILE_BANNER}
import type { DmnoGeneratedConfigSchema } from './schema.d.ts';
import type { DmnoConfigSchema } from './schema.d.ts';

declare global {
/** ${service.serviceName} config global obj */
const DMNO_CONFIG: DmnoGeneratedConfigSchema;
const DMNO_CONFIG: DmnoConfigSchema;
}
`, 'utf-8');

// write global file which defines a DMNO_CONFIG global
// this used in our config.mts files and in front-end apps where we inject rollup rewrites
await fs.promises.writeFile(`${typeGenFolderPath}/global-public.d.ts`, `${AUTOGENERATED_FILE_BANNER}
import type { DmnoGeneratedPublicConfigSchema } from './schema.d.ts';
import type { DmnoPublicConfigSchema } from './schema.d.ts';

declare global {
/** ${service.serviceName} config global obj - public (non-sensitive) items only */
const DMNO_PUBLIC_CONFIG: DmnoGeneratedPublicConfigSchema;
const DMNO_PUBLIC_CONFIG: DmnoPublicConfigSchema;
}
`, 'utf-8');
}
Expand All @@ -53,20 +53,28 @@ declare global {
export async function generateTypescriptTypes(service: DmnoService) {
const tsSrc = [
AUTOGENERATED_FILE_BANNER,
'export type DmnoGeneratedConfigSchema = {',
'export type FullDmnoConfigSchema = {',
];
const publicKeys: Array<string> = [];
const dmnoConfigKeys: Array<string> = [];
const dmnoPublicConfigKeys: Array<string> = [];
for (const itemKey in service.config) {
const configItem = service.config[itemKey];
if (!configItem.isSensitive) publicKeys.push(itemKey);
// generate the TS type for the item in the full schema
tsSrc.push(...await getTsDefinitionForNode(configItem, 1));
// then include in DMNO_CONFIG and DMNO_PUBLIC_CONFIG based on settings
if (configItem.includeInDmnoConfig) {
dmnoConfigKeys.push(itemKey);
if (!configItem.isSensitive) dmnoPublicConfigKeys.push(itemKey);
}
}
tsSrc.push('}');
tsSrc.push('\n');

const publicKeysForPick = _.map(publicKeys, JSON.stringify).join(' | ');
tsSrc.push(`export type DmnoGeneratedPublicConfigSchema = Pick<DmnoGeneratedConfigSchema, ${publicKeysForPick || 'never'}>`);

const keysForPick = _.map(dmnoConfigKeys, JSON.stringify).join(' | ');
tsSrc.push(`export type DmnoConfigSchema = Pick<FullDmnoConfigSchema, ${keysForPick || 'never'}>`);
const publicKeysForPick = _.map(dmnoPublicConfigKeys, JSON.stringify).join(' | ');
tsSrc.push(`export type DmnoPublicConfigSchema = Pick<FullDmnoConfigSchema, ${publicKeysForPick || 'never'}>`);
return tsSrc.join('\n');
}
export async function getPublicConfigKeys(service: DmnoService) {
Expand Down
12 changes: 8 additions & 4 deletions packages/core/src/config-loader/dmno-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,13 @@ export class DmnoServer {
// TODO: ideally resolution would be a second step that we could trigger as needed
return workspace.toJSON();
},
getInjectedJson: async (serviceId: string) => {
getServiceResolvedConfig: async (serviceId: string) => {
const workspace = await this.configLoader?.getWorkspace()!;
const service = workspace.getService(serviceId);
return service.getInjectedEnvJSON();
return {
injectedProcessEnv: service.getInjectedProcessEnv(),
injectedDmnoEnv: service.getInjectedEnvJSON(),
};
},
clearCache: async () => {
const workspace = await this.configLoader?.getWorkspace()!;
Expand Down Expand Up @@ -485,11 +488,12 @@ export class DmnoServer {
throw new Error(`Unable to select service by package name - ${packageName}`);
}

const injectedEnv = await this.makeRequest('getInjectedJson', service.serviceName);
const { injectedProcessEnv, injectedDmnoEnv } = await this.makeRequest('getServiceResolvedConfig', service.serviceName);

return {
serviceDetails: service,
injectedEnv,
injectedProcessEnv,
injectedDmnoEnv,
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/astro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async function reloadDmnoConfig() {
(process as any).dmnoServer ||= new DmnoServer({ watch: true });
dmnoServer = (process as any).dmnoServer;
const resolvedService = await dmnoServer.getCurrentPackageConfig();
const injectedConfig = resolvedService.injectedEnv;
const injectedConfig = resolvedService.injectedDmnoEnv;
dmnoConfigValid = resolvedService.serviceDetails.isValid;
configItemKeysAccessed = {};

Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/remix/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async function reloadDmnoConfig() {
(process as any).dmnoServer ||= new DmnoServer({ watch: true });
dmnoServer = (process as any).dmnoServer;
const resolvedService = await dmnoServer.getCurrentPackageConfig();
const injectedConfig = resolvedService.injectedEnv;
const injectedConfig = resolvedService.injectedDmnoEnv;
dmnoConfigValid = resolvedService.serviceDetails.isValid;
configItemKeysAccessed = {};

Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function reloadDmnoConfig() {
(process as any).dmnoServer ||= new DmnoServer({ watch: true });
dmnoServer = (process as any).dmnoServer;
const resolvedService = await dmnoServer.getCurrentPackageConfig();
const injectedConfig = resolvedService.injectedEnv;
const injectedConfig = resolvedService.injectedDmnoEnv;
dmnoConfigValid = resolvedService.serviceDetails.isValid;
configItemKeysAccessed = {};

Expand Down
1 change: 1 addition & 0 deletions packages/plugins/1password/src/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const OnePasswordServiceAccountToken = createDmnoDataType({
sensitive: {
redactMode: 'show_last_2',
},
includeInDmnoConfig: false,
});

const OnePasswordUUID = createDmnoDataType({
Expand Down
3 changes: 3 additions & 0 deletions packages/plugins/bitwarden/src/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const BitwardenMachineIdentityAccessToken = createDmnoDataType({
icon: BITWARDEN_ICON,
},
sensitive: true,
includeInDmnoConfig: false,
});

const BitwardenSecretId = createDmnoDataType({
Expand All @@ -29,6 +30,7 @@ const BitwardenSecretId = createDmnoDataType({
ui: {
icon: BITWARDEN_ICON,
},
includeInDmnoConfig: false,
});

const BitwardenProjectId = createDmnoDataType({
Expand All @@ -42,6 +44,7 @@ const BitwardenProjectId = createDmnoDataType({
ui: {
icon: BITWARDEN_ICON,
},
includeInDmnoConfig: false,
});


Expand Down
1 change: 1 addition & 0 deletions packages/plugins/encrypted-vault/src/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const DmnoEncryptionKey = createDmnoDataType({
icon: 'material-symbols:key',
},
sensitive: true,
includeInDmnoConfig: false,
});

export const EncryptedVaultTypes = {
Expand Down
6 changes: 5 additions & 1 deletion packages/plugins/infisical/src/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const InfisicalClientId = createDmnoDataType({
ui: {
icon: INFISICAL_ICON,
},
sensitive: true,
includeInDmnoConfig: false,
});

const InfisicalClientSecret = createDmnoDataType({
Expand All @@ -29,6 +29,8 @@ const InfisicalClientSecret = createDmnoDataType({
ui: {
icon: INFISICAL_ICON,
},
sensitive: true,
includeInDmnoConfig: false,
});

const InfisicalEnvironment = createDmnoDataType({
Expand All @@ -42,6 +44,7 @@ const InfisicalEnvironment = createDmnoDataType({
ui: {
icon: INFISICAL_ICON,
},
includeInDmnoConfig: false,
});

const InfisicalProjectId = createDmnoDataType({
Expand All @@ -55,6 +58,7 @@ const InfisicalProjectId = createDmnoDataType({
ui: {
icon: INFISICAL_ICON,
},
includeInDmnoConfig: false,
});


Expand Down
Loading