Skip to content

Commit

Permalink
feat: add status, push and pull commands
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurMialon committed Dec 13, 2024
1 parent dfdaf18 commit f85b18b
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 27 deletions.
48 changes: 43 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
[Cliffy](https://cliffy.io/) inspired by
[GNU Stow](https://www.gnu.org/software/stow/)


## Table of Contents

- [Demo](#demo)
Expand All @@ -19,6 +18,9 @@
- [Config](#config)
- [Add](#add)
- [Edit](#edit)
- [Status](#status)
- [Push](#push)
- [Pull](#pull)
- [Upgrade](#upgrade)
- [Ignore](#ignore)

Expand Down Expand Up @@ -70,9 +72,8 @@ Examples of structure:
└── .zshrc
```

Dot CLI automatically symlinked all files to your `$HOME` directory
(or any directory you want to target). It follows the structure inside each
package.
Dot CLI automatically symlinked all files to your `$HOME` directory (or any
directory you want to target). It follows the structure inside each package.

Example for the **ZSH** package:

Expand Down Expand Up @@ -126,7 +127,8 @@ Basic setup. Ask you to set locations to your dotfiles and the target location.
dot init
```

**With arguments:** You can clone your dotfiles repository and link all packages.
**With arguments:** You can clone your dotfiles repository and link all
packages.

```bash
dot init [email protected]:ArthurMialon/dotfiles.git
Expand Down Expand Up @@ -251,6 +253,42 @@ dot edit

---

### Status

Check status of your dotfiles repository.

**Basic:**

```bash
dot status
```

---

### Push

Push updates to your remote dotfiles repository.

**Basic:**

```bash
dot push
```

---

### Pull

Pull updates from your remote dotfiles repository and link files.

**Basic:**

```bash
dot pull
```

---

### Upgrade

You can upgrade the Dot CLI with the following command.
Expand Down
4 changes: 2 additions & 2 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Dot from "../dot.ts";
import { blue, bold } from "@std/fmt/colors";
import { Command } from "@cliffy/command";
import { Confirm, Input } from "@cliffy/prompt";
import { Confirm } from "@cliffy/prompt";
import list from "./list.ts";
import link from "./link.ts";
import configEditPrompt from "../prompt/config-edit.ts";
Expand All @@ -10,7 +10,7 @@ import * as log from "../tools/logging.ts";
import * as git from "../tools/git.ts";

export default new Command()
.description("Initialize dot CLI with valid configuration.")
.description(`Initialize ${Dot.title} with valid configuration.`)
.arguments("[repository]")
.action(async (_flags, remoteRepository) => {
log.info(blue(`👋 Welcome to ${Dot.title}.`));
Expand Down
48 changes: 48 additions & 0 deletions src/commands/pull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as log from "../tools/logging.ts";
import * as config from "../tools/config.ts";
import { Command } from "@cliffy/command";
import { bold } from "@std/fmt/colors";
import * as git from "../tools/git.ts";
import { Confirm } from "@cliffy/prompt";
import link from "./link.ts";

export default new Command()
.description("Pull new version of your dotfiles")
.option(
"-f, --force",
"Force link after pull",
{
default: false,
},
)
.action(async ({ force }) => {
const { repo } = await config.get();

const branch = await git.getCurrentBranch(repo);

if (!branch) {
log.error("Cannot read current branch of repository", bold(repo));
Deno.exit(1);
}

const success = await git.pull(repo, branch);

if (!success) {
log.error("Cannot pull your repository in:", bold(repo));
log.error("Please fix conflicts and/or rebase.");
Deno.exit(1);
}

log.info("\n");

const confirm = force ? true : (await Confirm.prompt({
message: `Do you want to link new changes?`,
}));

if (!confirm) {
log.info("Apply changes with `dot link` whenever you want.");
Deno.exit(0);
}

await link.parse(force ? ["-f"] : []);
});
52 changes: 52 additions & 0 deletions src/commands/push.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as log from "../tools/logging.ts";
import * as config from "../tools/config.ts";
import { Command } from "@cliffy/command";
import status from "./status.ts";
import { bold } from "@std/fmt/colors";
import * as git from "../tools/git.ts";
import { Confirm } from "@cliffy/prompt";

function toISODate(date: Date): string {
return date.toISOString().split("T")[0];
}

export default new Command()
.description("Publish new version of your dotfiles")
.action(async () => {
const { repo } = await config.get();

await status.parse([]);

const changes = await git.hasChange(repo);

if (!changes) {
log.info("No changes to push");
Deno.exit(0);
}

const branch = await git.getCurrentBranch(repo);

if (!branch) {
log.error("Cannot read current branch of repository", bold(repo));
Deno.exit(1);
}

const date = toISODate(new Date());
const commit = `chore: updated on ${date} from Dot CLI`;

log.info("\nCommit message", bold(commit));

const confirm = await Confirm.prompt({
message: `Do you want to commit and push the changes?`,
});

if (!confirm) {
log.info("Commit aborted");
Deno.exit(0);
}

await git.commit(repo, commit);
await git.push(repo, branch);

log.success("Dotfiles pushed to remote repository.");
});
16 changes: 16 additions & 0 deletions src/commands/status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as log from "../tools/logging.ts";
import * as config from "../tools/config.ts";
import { Command } from "@cliffy/command";
import { bold } from "@std/fmt/colors";
import * as git from "../tools/git.ts";

export default new Command()
.description("Check status of your dotfiles repository")
.action(async () => {
const configuration = await config.get();

log.info("Status of your dotfiles in:");
log.info(bold(configuration.repo), "\n");

await git.status(configuration.repo);
});
2 changes: 1 addition & 1 deletion src/commands/unlink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default new Command()
const configuration = await config.get();

const pkgs = await packages.list(configuration.repo)
.catch(() => [])
.catch(() => []);

const filteredPkgs = pkgs
.filter((pkg) => {
Expand Down
11 changes: 8 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Dot from "./dot.ts";
import { Command } from "@cliffy/command";

import Dot from "./dot.ts";
import config from "./commands/config/index.ts";
import init from "./commands/init.ts";
import list from "./commands/list.ts";
Expand All @@ -9,7 +9,9 @@ import link from "./commands/link.ts";
import unlink from "./commands/unlink.ts";
import add from "./commands/add.ts";
import upgrade from "./commands/upgrade.ts";

import status from "./commands/status.ts";
import push from "./commands/push.ts";
import pull from "./commands/pull.ts";
import * as log from "./tools/logging.ts";

const command = new Command()
Expand All @@ -24,6 +26,9 @@ const command = new Command()
.command("link", link)
.command("unlink", unlink)
.command("upgrade", upgrade)
.command("add", add);
.command("add", add)
.command("status", status)
.command("pull", pull)
.command("push", push);

await command.parse(Deno.args);
112 changes: 107 additions & 5 deletions src/tools/git.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,121 @@
import { copy, readerFromStreamReader } from "@std/io";
import * as log from "../tools/logging.ts";

export const clone = async (
repository: string,
targetFolder: string,
): Promise<boolean> => {
const clone = new Deno.Command("git", {
args: ["clone", repository, targetFolder],
const command = new Deno.Command("git", {
args: ["command", repository, targetFolder],
stdout: "piped",
stderr: "piped",
}).spawn();

copy(readerFromStreamReader(clone.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(clone.stderr.getReader()), Deno.stderr);
copy(readerFromStreamReader(command.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(command.stderr.getReader()), Deno.stderr);

const { success } = await clone.status;
const { success } = await command.status;

return !!success;
};

const statusCommand = (repository: string) => {
return new Deno.Command("git", {
args: ["-C", repository, "status", "--short"],
stdout: "piped",
stderr: "piped",
}).spawn();
};

export const status = async (
repository: string,
): Promise<boolean> => {
const command = statusCommand(repository);

copy(readerFromStreamReader(command.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(command.stderr.getReader()), Deno.stderr);

const { success } = await command.status;

return !!success;
};

export const hasChange = async (
repository: string,
): Promise<boolean> => {
const command = statusCommand(repository);

const output = await command.output();

const status = new TextDecoder().decode(output.stdout);

return !!Number(status.trim().length);
};

export const commit = async (
repository: string,
message: string,
): Promise<boolean> => {
const command = new Deno.Command("git", {
args: ["-C", repository, "commit", "-am", message],
stdout: "piped",
stderr: "piped",
}).spawn();

copy(readerFromStreamReader(command.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(command.stderr.getReader()), Deno.stderr);

const { success } = await command.status;

return !!success;
};

export const getCurrentBranch = async (repository: string) => {
const command = new Deno.Command("git", {
args: ["-C", repository, "rev-parse", "--abbrev-ref", "HEAD"],
stdout: "piped",
stderr: "piped",
}).spawn();

const output = await command.output();

const branch = new TextDecoder().decode(output.stdout);

return branch.trim();
};

export const push = async (
repository: string,
branch: string,
): Promise<boolean> => {
const command = new Deno.Command("git", {
args: ["-C", repository, "push", "origin", branch],
stdout: "piped",
stderr: "piped",
}).spawn();

copy(readerFromStreamReader(command.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(command.stderr.getReader()), Deno.stderr);

const { success } = await command.status;

return !!success;
};

export const pull = async (
repository: string,
branch: string,
): Promise<boolean> => {
const command = new Deno.Command("git", {
args: ["-C", repository, "pull", "origin", branch],
stdout: "piped",
stderr: "piped",
}).spawn();

copy(readerFromStreamReader(command.stdout.getReader()), Deno.stdout);
copy(readerFromStreamReader(command.stderr.getReader()), Deno.stderr);

const { success } = await command.status;

return !!success;
};
Loading

0 comments on commit f85b18b

Please sign in to comment.