Skip to content

Commit

Permalink
feat(cli): add global config file
Browse files Browse the repository at this point in the history
  • Loading branch information
juanrgm committed Nov 21, 2023
1 parent 166af56 commit 6028a36
Show file tree
Hide file tree
Showing 20 changed files with 292 additions and 183 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-weeks-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rtpl/cli": minor
---

Add global config file
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ lib
node_modules
/resources
rtpl-lock.json
rtpl-secrets.json
rtpl-config.*
/rtpl.*
tsconfig.tsbuildinfo
11 changes: 7 additions & 4 deletions packages/cli/src/actions/backup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GlobalOptions, pkg } from "../cli.js";
import { parseConfigFile } from "../utils/self/config.js";
import * as lock from "../utils/self/lock.js";
import chalk from "chalk";
import { createHash } from "crypto";
Expand All @@ -14,10 +15,12 @@ export type BackupOptions = GlobalOptions & {
};

export default async function backup(options: BackupOptions) {
const config = await parseConfigFile(options.config);

const log = options.log ?? true;
const lockData = (lock.parseFile(options.lockPath, true) ?? {
templates: {},
}) as any as lock.LockData<{ contents: string }>;
const lockData = lock.parseFile(config.lock.path) as any as lock.LockData<{
contents: string;
}>;

let files = 0;

Expand All @@ -39,7 +42,7 @@ export default async function backup(options: BackupOptions) {
];

const file = [...header, yaml].join("\n");
const path = join(options.backupPath, `${Date.now()}.yaml`);
const path = join(config.backup.path, `${Date.now()}.yaml`);

await mkdir(dirname(path), { recursive: true });
await writeFile(path, file);
Expand Down
122 changes: 72 additions & 50 deletions packages/cli/src/actions/diff.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { GlobalOptions } from "../cli.js";
import { AbstractRes } from "../index.js";
import { readIfExists } from "../utils/fs.js";
import { readTplFile, resolveTpl } from "../utils/self/resolve.js";
import { parseConfigFile } from "../utils/self/config.js";
import {
ActionEnum,
getFileActions,
logAction,
} from "../utils/self/install.js";
import { parseFile } from "../utils/self/lock.js";
import { parseTplFile, resolveTpl } from "../utils/self/resolve.js";
import chalk from "chalk";
import { diffLines } from "diff";

Expand All @@ -10,69 +18,83 @@ export type DiffOptions = GlobalOptions & {
lines: number;
};
export default async function diff(options: DiffOptions) {
const tpl = await readTplFile(options.templatePath);
const config = await parseConfigFile(options.config);
const tpl = await parseTplFile(config.template.path);
const { resources } = await resolveTpl(tpl, {
config,
filter: options.filter,
lockPath: options.lockPath,
outPath: options.outPath,
});
let changes = 0;
for (const path in resources) {
const res = resources[path];
let resValue: string;
try {
resValue = res.toString();
} catch (error) {
console.info("\x1b[36m%s\x1b[0m", path);
throw error;
const lockData = parseFile(config.lock.path);
const selfLockData = lockData.templates[tpl.config.name] || {
files: {},
dirs: {},
};
const actions = await getFileActions(resources, selfLockData);
for (const path in actions) {
const action = actions[path];
if (action.type === ActionEnum.NONE) continue;
logAction(action.type, path);
changes++;
if (action.type === ActionEnum.ADD || action.type === ActionEnum.UPDATE) {
await showDiff(resources[path], path, options);
}
}

const old = (await readIfExists(path))?.toString();
const lines = diffLines(old ?? "", resValue);
const haveChanges =
typeof old !== "string" ||
lines.some((line) => line.added || line.removed);
if (!changes) process.stderr.write(`${chalk.grey("No changes")}\n`);
return { exitCode: changes ? 1 : 0, changes };
}

if (haveChanges) changes++;
if (!options.showAll && !haveChanges) continue;
async function showDiff(
res: AbstractRes<any, any>,
path: string,
options: DiffOptions,
) {
let resValue: string;
try {
resValue = res.toString();
} catch (error) {
console.info("\x1b[36m%s\x1b[0m", path);
throw error;
}
const old = (await readIfExists(path))?.toString();
const lines = diffLines(old ?? "", resValue);
const haveChanges =
typeof old !== "string" || lines.some((line) => line.added || line.removed);

let lineIndex = 0;
let removed = 0;
if (!options.showAll && !haveChanges) return;

for (const line of lines) {
const sublines = line.value.split(/\r?\n/).slice(0, line.count);
for (const subline of sublines) {
++lineIndex;
let result: string;
let lineIndex = 0;
let removed = 0;

if (line.added) {
lineIndex -= removed;
removed = 0;
result = chalk.green(subline);
} else if (line.removed) {
removed++;
result = chalk.red(subline);
} else {
result = subline;
}
for (const line of lines) {
const sublines = line.value.split(/\r?\n/).slice(0, line.count);
for (const subline of sublines) {
++lineIndex;
let result: string;

const haveLineChanges = line.added || line.removed;
if (options.lines === 1 && !haveLineChanges) continue;
if (line.added) {
lineIndex -= removed;
removed = 0;
result = chalk.green(subline);
} else if (line.removed) {
removed++;
result = chalk.red(subline);
} else {
result = subline;
}

const haveLineChanges = line.added || line.removed;
if (options.lines === 1 && !haveLineChanges) continue;

if (options.hideLines) {
console.info(result);
} else {
const lineIndexStr = lineIndex.toString().padStart(3, " ");
console.info(
`${chalk.grey(lineIndexStr)} ${chalk.white("|")} ${result}`,
);
}
if (options.hideLines) {
console.info(result);
} else {
const lineIndexStr = lineIndex.toString().padStart(3, " ");
console.info(
`${chalk.grey(lineIndexStr)} ${chalk.white("|")} ${result}`,
);
}
}
console.info();
}

if (!changes) process.stderr.write(`${chalk.grey("No changes")}\n`);
return { exitCode: changes ? 1 : 0, changes };
}
35 changes: 19 additions & 16 deletions packages/cli/src/actions/install.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GlobalOptions } from "../cli.js";
import { confirmPrompt } from "../utils/cli.js";
import { parseConfigFile } from "../utils/self/config.js";
import {
ActionEnum,
execFileAction,
Expand All @@ -8,12 +9,12 @@ import {
} from "../utils/self/install.js";
import * as lock from "../utils/self/lock.js";
import { splitGlobalOptions } from "../utils/self/options.js";
import { readTplFile, resolveTpl } from "../utils/self/resolve.js";
import { parseTplFile, resolveTpl } from "../utils/self/resolve.js";
import backup from "./backup.js";
import diff from "./diff.js";
import chalk from "chalk";
import { rmdir, writeFile } from "fs/promises";
import { dirname, join } from "path";
import { join } from "path";

export type InstallActionOptions = GlobalOptions & {
dryRun: boolean;
Expand All @@ -39,7 +40,9 @@ export default async function install(options: InstallActionOptions) {
console.info();
}

if (!options.noBackup) {
const config = await parseConfigFile(globalOptions.config);

if (!options.noBackup && config.backup.enabled) {
const backupResult = await backup({
...globalOptions,
log: false,
Expand All @@ -48,24 +51,19 @@ export default async function install(options: InstallActionOptions) {
return { exitCode: backupResult.exitCode, changes: 0 };
}

const tpl = await readTplFile(options.templatePath);
const tpl = await parseTplFile(config.template.path);
const { resources, secrets } = await resolveTpl(tpl, {
config,
filter: options.filter,
lockPath: options.lockPath,
outPath: options.outPath,
});

const secretsPath = join(dirname(options.lockPath), "rtpl.secrets.json");

await writeFile(secretsPath, JSON.stringify(secrets, null, 2));
if (secrets)
await writeFile(config.secrets.path, JSON.stringify(secrets, null, 2));

//await tpl.onBeforeInstall?.(options);

const lockData = lock.parseFile(options.lockPath, true) ?? {
templates: {},
};
const lockData = lock.parseFile(config.lock.path);

const lockDir = dirname(options.lockPath);
let changes = 0;
let errors = 0;

Expand All @@ -83,7 +81,12 @@ export default async function install(options: InstallActionOptions) {
changes++;
}
try {
const dirs = await execFileAction(action, lockDir, path, options.dryRun);
const dirs = await execFileAction(
action,
config.root,
path,
options.dryRun,
);
if (action.lock) {
selfLockData.files[path] = action.lock;
if (dirs) {
Expand All @@ -109,14 +112,14 @@ export default async function install(options: InstallActionOptions) {
const dirs = Object.keys(selfLockData.dirs).sort().reverse();
for (const dir of dirs) {
try {
await rmdir(join(lockDir, dir));
await rmdir(join(config.root, dir));
delete selfLockData.dirs[dir];
} catch (error) {}
}

lockData.templates[tpl.config.name] = selfLockData;

if (!options.dryRun) await lock.writeFile(options.lockPath, lockData);
if (!options.dryRun) await lock.writeFile(config.lock.path, lockData);

//await tpl.onInstall?.(options);

Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/actions/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GlobalOptions } from "../cli.js";
import { readTplFile } from "../utils/self/resolve.js";
import { parseConfigFile } from "../utils/self/config.js";
import { parseTplFile } from "../utils/self/resolve.js";
import camelCase from "lodash.camelcase";
import { stringify } from "yaml";

Expand All @@ -8,7 +9,8 @@ export type OptionsOptions = GlobalOptions & {
};

export default async function options(options: OptionsOptions) {
const tpl = await readTplFile(options.templatePath);
const config = await parseConfigFile(options.config);
const tpl = await parseTplFile(config.template.path);
const tplOptions = await tpl.options();

if (options.format === "yaml") {
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/actions/render.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { GlobalOptions } from "../cli.js";
import { readTplFile, resolveTpl } from "../utils/self/resolve.js";
import { parseConfigFile } from "../utils/self/config.js";
import { parseTplFile, resolveTpl } from "../utils/self/resolve.js";

export default async function render(options: GlobalOptions) {
const tpl = await readTplFile(options.templatePath);
const config = await parseConfigFile(options.config);
const tpl = await parseTplFile(config.template.path);
const { resources } = await resolveTpl(tpl, {
config,
filter: options.filter,
lockPath: options.lockPath,
outPath: options.outPath,
});
for (const path in resources) {
const res = resources[path];
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/actions/repair-lock.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { GlobalOptions } from "../cli.js";
import { confirmPrompt } from "../utils/cli.js";
import { parseConfigFile } from "../utils/self/config.js";
import * as lock from "../utils/self/lock.js";
import chalk from "chalk";
import { existsSync } from "fs";

export type RepairLockOptions = GlobalOptions & {};

export default async function repairLock(options: RepairLockOptions) {
const lockData = (lock.parseFile(options.lockPath, true) ?? {
templates: {},
}) as any as lock.LockData;
const config = await parseConfigFile(options.config);
const lockData = lock.parseFile(config.lock.path) as any as lock.LockData;

const notfounds: string[] = [];

Expand Down Expand Up @@ -38,7 +38,7 @@ export default async function repairLock(options: RepairLockOptions) {

if (!confirm) return { exitCode: 1 };

await lock.writeFile(options.lockPath, lockData);
await lock.writeFile(config.lock.path, lockData);

console.info(chalk.green("Lock file repared."));

Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/actions/restore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GlobalOptions } from "../cli.js";
import { confirmPrompt } from "../utils/cli.js";
import { parseConfigFile } from "../utils/self/config.js";
import { LockData } from "../utils/self/lock.js";
import chalk from "chalk";
import { existsSync } from "fs";
Expand Down Expand Up @@ -43,8 +44,9 @@ const normalizePath = (path: string) =>
relative(process.cwd(), path).replace(/\\/g, "/");

export default async function restore(options: RestoreOptions) {
const config = await parseConfigFile(options.config);
const path = normalizePath(
await findBackup(options.backupPath, options.input),
await findBackup(config.backup.path, options.input),
);
const yaml = (await readFile(path)).toString();
const lockData = parse(yaml, { version: "1.1" }) as LockData<{
Expand Down Expand Up @@ -74,7 +76,7 @@ export default async function restore(options: RestoreOptions) {
errorFiles.push(path);
};

if (existsSync(options.lockPath)) logError(options.lockPath);
if (existsSync(config.lock.path)) logError(config.lock.path);

for (const name in lockData.templates) {
const tpl = lockData.templates[name];
Expand Down Expand Up @@ -105,7 +107,7 @@ export default async function restore(options: RestoreOptions) {
}
}

await writeFile(options.lockPath, JSON.stringify(lockData, null, 2));
await writeFile(config.lock.path, JSON.stringify(lockData, null, 2));

console.info();
console.info(chalk.green("Backup restored successfully."));
Expand Down
Loading

0 comments on commit 6028a36

Please sign in to comment.