refactor(config): load JSON configs and emit JSON-only PCA output

This commit is contained in:
2026-02-08 10:44:04 +01:00
parent ec5b0906ba
commit b4cddcc50a
3 changed files with 50 additions and 47 deletions

View File

@@ -35,7 +35,7 @@ function runAz(args, options = {}) {
async function main() { async function main() {
const usageText = `Usage: ${path.basename(process.argv[1])} [options] <app-name> const usageText = `Usage: ${path.basename(process.argv[1])} [options] <app-name>
Options: Options:
-c, --config <path> Write config template to file (optional) -c, --config <path> Write JSON config to file (optional)
-h, --help Show this help message and exit`; -h, --help Show this help message and exit`;
let values; let values;
let positionals; let positionals;
@@ -51,7 +51,7 @@ Options:
})); }));
} catch (err) { } catch (err) {
console.error(`Error: ${err.message}`); console.error(`Error: ${err.message}`);
console.log(usageText); console.error(usageText);
process.exit(1); process.exit(1);
} }
@@ -64,7 +64,7 @@ Options:
console.error( console.error(
"Error: Too many positional arguments. Only one app name positional argument is allowed.", "Error: Too many positional arguments. Only one app name positional argument is allowed.",
); );
console.log(usageText); console.error(usageText);
process.exit(1); process.exit(1);
} }
@@ -73,7 +73,7 @@ Options:
if (!appName) { if (!appName) {
console.error("Error: Application name is required."); console.error("Error: Application name is required.");
console.log(usageText); console.error(usageText);
process.exit(1); process.exit(1);
} }
@@ -89,13 +89,12 @@ Options:
"tsv", "tsv",
]).stdout; ]).stdout;
let userConfirmation = "";
if (appId) { if (appId) {
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout, output: process.stderr,
}); });
userConfirmation = await new Promise((resolve) => { const answer = await new Promise((resolve) => {
rl.question( rl.question(
`Application '${appName}' already exists. Update it? [y/N]: `, `Application '${appName}' already exists. Update it? [y/N]: `,
(answer) => { (answer) => {
@@ -105,8 +104,8 @@ Options:
); );
}); });
if (!/^(yes|y)$/i.test(userConfirmation)) { if (!/^(yes|y)$/i.test(answer)) {
console.log("Canceled."); console.error("Canceled.");
process.exit(0); process.exit(0);
} }
} }
@@ -231,16 +230,14 @@ Options:
process.exit(1); process.exit(1);
} }
if (userConfirmation) { const configTemplate = JSON.stringify(
console.log(`Updated application '${appName}'`); {
} else { tenantId,
console.log(`Created application '${appName}'`); clientId: appId,
} },
const configTemplate = `export const config = { null,
"appName": "${appName}", 2,
"tenantId": "${tenantId}", );
"clientId": "${appId}"
};`;
if (configPath) { if (configPath) {
const targetPath = path.resolve(configPath); const targetPath = path.resolve(configPath);

View File

@@ -2,22 +2,22 @@
* Get an Azure application by its display name. * Get an Azure application by its display name.
* *
* @param { Object } client * @param { Object } client
* @param { string } appName * @param { string } displayName
* @returns * @returns
*/ */
export async function getApp(client, appName) { export async function getApp(client, displayName) {
const result = await client const result = await client
.api("/applications") .api("/applications")
.filter(`displayName eq '${appName}'`) .filter(`displayName eq '${displayName}'`)
.get(); .get();
// Return the first application found or null if none exists // Return the first application found or null if none exists
return result.value.length > 0 ? result.value[0] : null; return result.value.length > 0 ? result.value[0] : null;
} }
export async function createApp(client, appName) { export async function createApp(client, displayName) {
const app = await client.api("/applications").post({ const app = await client.api("/applications").post({
displayName: appName, displayName,
}); });
if (!app || !app.appId) { if (!app || !app.appId) {

View File

@@ -10,31 +10,37 @@ export function getUserConfigDir() {
return process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"); return process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
} }
export async function loadConfig(type) { async function loadConfig(configFileName) {
const configBaseDir = getUserConfigDir(); if (typeof configFileName !== "string" || configFileName.trim() === "") {
const configType = typeof type === "string" ? type.toLowerCase() : "";
let configFileName;
switch (configType) {
case "public":
case "p":
configFileName = "public-config.json";
break;
case "confidential":
case "c":
configFileName = "confidential-config.json";
break;
default:
configFileName = null;
}
if (!configFileName) {
throw new Error( throw new Error(
`Invalid config type: ${type}. Expected "public"|"p" or "confidential"|"c".`, 'Invalid config file name. Expected a non-empty string like "public-config.json" or "confidential-config.json".',
); );
} }
const configPath = path.join(configBaseDir, "sk-az-tools", configFileName); const config = {
const configJson = await readFile(configPath, "utf8"); tenantId: process.env.AZURE_TENANT_ID,
return JSON.parse(configJson); clientId: process.env.AZURE_CLIENT_ID,
};
const configPath = path.join(getUserConfigDir(), "sk-az-tools", configFileName);
return readFile(configPath, "utf8")
.then((configJson) => JSON.parse(configJson))
.catch((err) => {
if (err?.code === "ENOENT") {
return {};
}
throw err;
})
.then((json) => ({
tenantId: json.tenantId || config.tenantId,
clientId: json.clientId || config.clientId,
}));
}
export function loadPublicConfig() {
return loadConfig("public-config.json");
}
export function loadConfidentialConfig() {
return loadConfig("confidential-config.json");
} }