@clickup/pg-mig
Version:
PostgreSQL schema migration tool with microsharding and clustering support
82 lines (68 loc) • 2.66 kB
text/typescript
import { existsSync, readFileSync, writeFileSync } from "fs";
import { dirname } from "path";
import type { MigrateOptions } from "../cli";
import { unindent } from "../internal/helpers/unindent";
import type { Registry } from "../internal/Registry";
const WARNING =
"IF THERE IS MERGE CONFLICT HERE, DO NOT RESOLVE IT MANUALLY! READ ABOVE!";
const HEADER_FILES = [
"pg-mig.chain_template.md",
".github/pg-mig.chain_template.md",
];
const HEADER_DEFAULT = unindent(`
If you pulled, and a merge conflict occurs in this file, it means the
migration version file that you recently created appears in the middle
of already-applied migrations, instead of at the end.
Migration versions MUST form a strict "blockchain-like" list, where
new versions are ALWAYS appended in the end ("append-only" principle).
DO NOT resolve this Git merge conflict manually!!!
Instead, do the following:
1. First, make sure the current file matches the upstream version
(equivalent to "accept theirs" in Git conflict resolution).
2. UNDO your migration version that you created recently.
3. Rename your migration version file so that its timestamp becomes the
highest (i.e. the latest) among all migrations.
4. Regenerate the chain file with: pg-mig --chain
5. APPLY your migration version locally with: pg-mig
6. Resubmit the Pull Request. It will not have any merge conflicts
anymore, because your migration version file is now in the end.
`);
/**
* Overwrites the chain file for the migration versions. Chain file ensures that
* the migration versions are appended strictly in the end (so a migration file
* appeared in the middle will produce a git merge conflict).
*/
export async function actionChain(
options: MigrateOptions,
registry: Registry,
): Promise<boolean> {
const lines: string[] = [];
lines.push(
("\n" + readHeader().trimEnd() + "\n")
.replace(/^/gm, "# ")
.replace(/ +$/gm, ""),
);
lines.push("");
lines.push(`digest=${registry.getDigest("short")} # ${WARNING}`);
lines.push("");
let prevVersion = "(init)";
for (const version of registry.getVersions()) {
lines.push(`${prevVersion} -> ${version} # ${WARNING}`);
prevVersion = version;
}
writeFileSync(`${options.migDir}/chain.txt`, lines.join("\n") + "\n", {
mode: 0o644,
});
return true;
}
function readHeader(): string {
for (let dir = process.cwd(); dirname(dir) !== dir; dir = dirname(dir)) {
for (const file of HEADER_FILES) {
const path = `${dir}/${file}`;
if (existsSync(path)) {
return readFileSync(path, "utf8");
}
}
}
return HEADER_DEFAULT;
}