@cyberalien/git-utils 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/git/clone.js CHANGED
@@ -1,16 +1,11 @@
1
1
  import { execAsync } from "../misc/exec.js";
2
2
  import { doesGitRepoExist } from "./exists.js";
3
- import { rm } from "node:fs/promises";
3
+ import { rmDir } from "../misc/rm.js";
4
4
  /**
5
5
  * Clone Git repository to target directory
6
6
  */
7
7
  async function cloneGitRepo(repo, target) {
8
- try {
9
- await rm(target, {
10
- recursive: true,
11
- force: true
12
- });
13
- } catch {}
8
+ await rmDir(target, false);
14
9
  try {
15
10
  await execAsync(`git clone ${repo.shallow ? "--depth 1" : ""} --branch ${repo.branch} ${repo.repo} ${target}`);
16
11
  } catch {
package/lib/git/commit.js CHANGED
@@ -3,12 +3,26 @@ import { getCurrentGitBranch } from "./branch.js";
3
3
  async function commitGitRepoChanges(target, message, push = true) {
4
4
  try {
5
5
  await execAsync(`git add -A`, { cwd: target });
6
- await execAsync(`git commit -m "${message}"`, { cwd: target });
7
- if (!(await execAsync(`git status`, { cwd: target })).stdout.includes("Your branch is ahead of")) {
8
- console.error("Failed to commit changes");
6
+ const commitResult = await execAsync(`git commit -m "${message}"`, { cwd: target });
7
+ if (!commitResult.stdout.includes(message)) {
8
+ console.error("Failed to commit changes:", commitResult);
9
9
  return false;
10
10
  }
11
- if (push) await execAsync(`git push origin "${await getCurrentGitBranch(target)}"`, { cwd: target });
11
+ if (!commitResult.stdout.includes("(root-commit)")) {
12
+ const statusResult = await execAsync(`git status`, { cwd: target });
13
+ if (!statusResult.stdout.includes("Your branch is ahead of")) {
14
+ if (statusResult.stdout.includes("nothing to commit, working tree clean")) {
15
+ console.error("Failed to commit changes: nothing to commit");
16
+ return false;
17
+ }
18
+ console.error("Failed to commit changes:", statusResult);
19
+ return false;
20
+ }
21
+ }
22
+ if (push) {
23
+ const pushResult = await execAsync(`git push origin "${await getCurrentGitBranch(target)}"`, { cwd: target });
24
+ console.log("pushResult:", pushResult.stdout);
25
+ }
12
26
  } catch {
13
27
  return false;
14
28
  }
package/lib/git/exists.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { join } from "node:path";
1
2
  import { lstat } from "node:fs/promises";
2
3
  /**
3
4
  * Check if Git repository exists in target directory
4
5
  */
5
6
  async function doesGitRepoExist(target) {
6
7
  try {
7
- return (await lstat(`${target}/.git`)).isDirectory();
8
+ return (await lstat(join(target, ".git"))).isDirectory();
8
9
  } catch {
9
10
  return false;
10
11
  }
@@ -0,0 +1,12 @@
1
+ import { GitRepoConfig } from "./types.js";
2
+ interface CreateGitRepoResult {
3
+ packageJSON?: Record<string, unknown>;
4
+ bumpVersion?: string | ((currentVersion: string) => string);
5
+ files?: Record<string, string | Record<string, unknown>>;
6
+ commit?: string;
7
+ }
8
+ /**
9
+ * Create or update git repository
10
+ */
11
+ declare function createGitRepo(repo: GitRepoConfig, target: string, callback: (isNew: boolean) => Promise<CreateGitRepoResult>, dryRun?: boolean): Promise<boolean>;
12
+ export { CreateGitRepoResult, createGitRepo };
@@ -0,0 +1,42 @@
1
+ import { cloneGitRepo } from "./clone.js";
2
+ import { commitGitRepoChanges } from "./commit.js";
3
+ import { initGitRepo } from "./init.js";
4
+ import { createFile } from "../misc/write.js";
5
+ import { readJsonFile, writeJsonFile } from "../misc/json.js";
6
+ import { updateVersionNumber } from "../misc/version.js";
7
+ import { scanDirectory } from "../misc/scan.js";
8
+ import { isGitRepoUpdated } from "./updated.js";
9
+ import { join } from "node:path";
10
+ import { rm } from "node:fs/promises";
11
+ /**
12
+ * Create or update git repository
13
+ */
14
+ async function createGitRepo(repo, target, callback, dryRun = false) {
15
+ const cloneResult = await cloneGitRepo(repo, target);
16
+ if (!cloneResult) await initGitRepo(repo, target);
17
+ const oldFiles = await scanDirectory(target);
18
+ const filesToRemove = new Set(oldFiles.filter((file) => !file.startsWith(".git/")));
19
+ const result = await callback(!cloneResult);
20
+ if (result.packageJSON) {
21
+ if ("version" in result.packageJSON && oldFiles.includes("package.json")) try {
22
+ const oldFile = await readJsonFile(target, "package.json");
23
+ if (oldFile.version) {
24
+ const oldVersion = oldFile.version;
25
+ const newVersion = updateVersionNumber(oldVersion);
26
+ result.packageJSON.version = newVersion;
27
+ console.log("Bumped version number:", oldVersion, "->", newVersion, "of", result.packageJSON.name ?? "unknown");
28
+ }
29
+ } catch {}
30
+ await writeJsonFile(target, "package.json", result.packageJSON);
31
+ filesToRemove.delete("package.json");
32
+ }
33
+ if (result.files) for (const [name, content] of Object.entries(result.files)) {
34
+ if (typeof content === "string") await createFile(join(target, name), content);
35
+ else await writeJsonFile(target, name, content);
36
+ filesToRemove.delete(name);
37
+ }
38
+ for (const file of filesToRemove) await rm(join(target, file));
39
+ if (!await isGitRepoUpdated(target)) return true;
40
+ return await commitGitRepoChanges(target, result.commit ?? "chore: update files", !dryRun);
41
+ }
42
+ export { createGitRepo };
@@ -0,0 +1,6 @@
1
+ import { GitRepoConfig } from "./types.js";
2
+ /**
3
+ * Clone Git repository to target directory
4
+ */
5
+ declare function initGitRepo(repo: GitRepoConfig, target: string): Promise<boolean>;
6
+ export { initGitRepo };
@@ -0,0 +1,30 @@
1
+ import { execAsync } from "../misc/exec.js";
2
+ import { doesGitRepoExist } from "./exists.js";
3
+ import { rmDir } from "../misc/rm.js";
4
+ /**
5
+ * Clone Git repository to target directory
6
+ */
7
+ async function initGitRepo(repo, target) {
8
+ await rmDir(target, true);
9
+ try {
10
+ const initResult = await execAsync(`git init`, { cwd: target });
11
+ if (!initResult.stdout.includes("Initialized empty Git repository")) {
12
+ console.error("Failed to init repo:", initResult);
13
+ return false;
14
+ }
15
+ } catch {
16
+ return false;
17
+ }
18
+ try {
19
+ await execAsync(`git remote add origin ${repo.repo}`, { cwd: target });
20
+ } catch {
21
+ return false;
22
+ }
23
+ try {
24
+ await execAsync(`git checkout -b "${repo.branch}"`, { cwd: target });
25
+ } catch {
26
+ return false;
27
+ }
28
+ return await doesGitRepoExist(target);
29
+ }
30
+ export { initGitRepo };
@@ -5,6 +5,5 @@ async function isGitRepoUpdated(target) {
5
5
  } catch {
6
6
  return null;
7
7
  }
8
- return false;
9
8
  }
10
9
  export { isGitRepoUpdated };
package/lib/misc/json.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { createFile } from "./write.js";
1
2
  import { join } from "node:path";
2
- import { readFile, writeFile } from "node:fs/promises";
3
+ import { readFile } from "node:fs/promises";
3
4
  /**
4
5
  * Read and parse JSON file
5
6
  */
@@ -11,6 +12,6 @@ async function readJsonFile(target, filename) {
11
12
  * Write JSON file
12
13
  */
13
14
  async function writeJsonFile(target, filename, data) {
14
- await writeFile(join(target, filename), JSON.stringify(data, null, 2) + "\n", "utf-8");
15
+ await createFile(join(target, filename), JSON.stringify(data, null, 2) + "\n");
15
16
  }
16
17
  export { readJsonFile, writeJsonFile };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Remove directory, create empty if needed
3
+ */
4
+ declare function rmDir(target: string, createEmpty?: boolean): Promise<void>;
5
+ export { rmDir };
package/lib/misc/rm.js ADDED
@@ -0,0 +1,14 @@
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ /**
3
+ * Remove directory, create empty if needed
4
+ */
5
+ async function rmDir(target, createEmpty = false) {
6
+ try {
7
+ await rm(target, {
8
+ recursive: true,
9
+ force: true
10
+ });
11
+ } catch {}
12
+ if (createEmpty) await mkdir(target, { recursive: true });
13
+ }
14
+ export { rmDir };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Find all files in directory
3
+ */
4
+ declare function scanDirectory(path: string): Promise<string[]>;
5
+ export { scanDirectory };
@@ -0,0 +1,29 @@
1
+ import { join } from "node:path";
2
+ import { lstat, readdir } from "node:fs/promises";
3
+ /**
4
+ * Find all files in directory
5
+ */
6
+ async function scanDirectory(path) {
7
+ const results = [];
8
+ async function scan(subdir) {
9
+ const files = await readdir(join(path, subdir));
10
+ for (let i = 0; i < files.length; i++) {
11
+ const filename = join(subdir, files[i]);
12
+ let stat;
13
+ try {
14
+ stat = await lstat(join(path, filename));
15
+ } catch {
16
+ continue;
17
+ }
18
+ if (stat.isDirectory()) {
19
+ await scan(filename);
20
+ continue;
21
+ }
22
+ if (!stat.isFile()) continue;
23
+ results.push(filename);
24
+ }
25
+ }
26
+ await scan("");
27
+ return results;
28
+ }
29
+ export { scanDirectory };
@@ -1,5 +1,9 @@
1
+ /**
2
+ * Update version number using optional callback
3
+ */
4
+ declare function updateVersionNumber(oldVersion: string, newVersion?: string | ((currentVersion: string) => string)): string;
1
5
  /**
2
6
  * Bump version in package.json file, return new version
3
7
  */
4
8
  declare function bumpPackageVersion(target: string, newVersion?: string | ((currentVersion: string) => string)): Promise<string>;
5
- export { bumpPackageVersion };
9
+ export { bumpPackageVersion, updateVersionNumber };
@@ -1,5 +1,8 @@
1
1
  import { readJsonFile, writeJsonFile } from "./json.js";
2
- function update(oldVersion, newVersion) {
2
+ /**
3
+ * Update version number using optional callback
4
+ */
5
+ function updateVersionNumber(oldVersion, newVersion) {
3
6
  if (typeof newVersion === "string") return newVersion;
4
7
  if (newVersion) return newVersion(oldVersion);
5
8
  const parts = oldVersion.split(".");
@@ -14,11 +17,11 @@ function update(oldVersion, newVersion) {
14
17
  async function bumpPackageVersion(target, newVersion) {
15
18
  const content = await readJsonFile(target, "package.json");
16
19
  const oldVersion = content.version;
17
- const version = update(oldVersion, newVersion);
20
+ const version = updateVersionNumber(oldVersion, newVersion);
18
21
  if (version !== oldVersion) {
19
22
  content.version = version;
20
23
  writeJsonFile(target, "package.json", content);
21
24
  }
22
25
  return version;
23
26
  }
24
- export { bumpPackageVersion };
27
+ export { bumpPackageVersion, updateVersionNumber };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Create file, including creating directory if it doesn't exist
3
+ */
4
+ declare function createFile(target: string, content: string): Promise<void>;
5
+ export { createFile };
@@ -0,0 +1,13 @@
1
+ import { dirname } from "node:path";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ /**
4
+ * Create file, including creating directory if it doesn't exist
5
+ */
6
+ async function createFile(target, content) {
7
+ const dir = dirname(target);
8
+ try {
9
+ await mkdir(dir, { recursive: true });
10
+ } catch {}
11
+ await writeFile(target, content, "utf8");
12
+ }
13
+ export { createFile };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Functions for working with git repos.",
5
5
  "author": "Vjacheslav Trushkin",
6
- "version": "0.0.1",
6
+ "version": "0.0.2",
7
7
  "license": "UNLICENSED",
8
8
  "bugs": "https://github.com/cyberalien/svg-utils/issues",
9
9
  "homepage": "https://cyberalien.dev/",