Skip to content

Commit 2695297

Browse files
committed
added env._cd
1 parent 1c7cbc6 commit 2695297

File tree

10 files changed

+219
-102
lines changed

10 files changed

+219
-102
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ You can also read the config file with `dev config read`.
176176

177177
- `shell.function` (default: `dev`): You can rename the [shell module](https://github.com/amir-s/dev/#install) function with `dev config set shell.function whatever`. After restarting your shell, you can use `whatever` instead of `dev`.
178178

179-
### `_cd scripts`
179+
### `dev.json`'s `scripts._cd`
180180

181181
`dev` can execute a script once you `cd` into your project. You can define a script in your `dev.json` file like this:
182182

@@ -190,6 +190,18 @@ You can also read the config file with `dev config read`.
190190

191191
Once you `cd` into the project, `dev` will ask for permission to run the script. If you want to persist the permission for this project, a `.dev.json` file will be created in the project root. This file should not be committed to the repository and is only for local use.
192192

193+
### `dev.json`'s `env._cd`
194+
195+
`dev` can automatically load environmant variables from a file when you `cd` into your project. You can define a file in your `dev.json` file like this:
196+
197+
```json
198+
{
199+
"env": {
200+
"_cd": ".env"
201+
}
202+
}
203+
```
204+
193205
### `dev update`
194206

195207
Run `dev update` to check for updates. You can select to automatially apply the updates, or run the install script manually.

main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const shellExec = (cmd: string): unknown => {
5555
};
5656

5757
const cd = (path: string): unknown => shellExec(`cd:${path}`);
58+
const loadEnvFile = (path: string): unknown => shellExec(`env:${path}`);
5859

5960
const source = (): unknown => {
6061
const currentShellType = (
@@ -145,6 +146,7 @@ const execute = async () => {
145146
writeConfig,
146147
cd,
147148
source,
149+
loadEnvFile,
148150
} as ModuleRunOptions);
149151
};
150152

modules/_cd/handleEnvFile.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import "colors";
2+
import report from "yurnalist";
3+
import inquirer from "inquirer";
4+
5+
import type { KV } from "./kv.ts";
6+
import type { DevJson } from "./index.ts";
7+
import { fs } from "zx";
8+
import type { ModuleRunOptions } from "../../utils/types.ts";
9+
10+
const CHOICES = ["no", "yes", "never", "always"] as const;
11+
12+
export const handleEnvFile = async (
13+
devJson: DevJson,
14+
kv: KV,
15+
loadEnvFile: ModuleRunOptions["loadEnvFile"]
16+
) => {
17+
const { env } = devJson;
18+
19+
if (!env || !env["_cd"]) {
20+
return;
21+
}
22+
23+
const envFile = env["_cd"];
24+
25+
const isAllowed = () => {
26+
const all = kv.read<string>("_cd", "auto load env file");
27+
28+
if (all === "never") {
29+
return false;
30+
}
31+
32+
if (all === "always") {
33+
return true;
34+
}
35+
36+
return null;
37+
};
38+
39+
const allowed = isAllowed();
40+
if (allowed === false) {
41+
return false;
42+
}
43+
44+
if (!fs.existsSync(envFile)) {
45+
report.error(
46+
`ENV file specified in ${"dev.json".green} not found: ${envFile.red}`
47+
);
48+
return false;
49+
}
50+
51+
if (allowed === true) {
52+
loadEnvFile(envFile);
53+
return true;
54+
}
55+
56+
report.info(`ENV variables are about to be loaded from ${envFile.green}`);
57+
58+
const { run } = (await inquirer.prompt([
59+
{
60+
type: "list",
61+
name: "run",
62+
message: "Do you accept?",
63+
choices: CHOICES,
64+
},
65+
])) as { run: (typeof CHOICES)[number] };
66+
67+
if (run === "no") {
68+
return false;
69+
}
70+
71+
if (run === "never") {
72+
kv.write("_cd", "auto load env file", "never");
73+
return false;
74+
}
75+
76+
if (run === "always") {
77+
kv.write("_cd", "auto load env file", "always");
78+
}
79+
80+
loadEnvFile(envFile);
81+
return true;
82+
};

modules/_cd/handleScript.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import "colors";
2+
import { $ } from "zx";
3+
import report from "yurnalist";
4+
import inquirer from "inquirer";
5+
6+
import type { KV } from "./kv.ts";
7+
import type { DevJson } from "./index.ts";
8+
9+
const CHOICES = [
10+
"no",
11+
"yes",
12+
"never this command",
13+
"always this command",
14+
"never for _cd commands for this project",
15+
"always for _cd commands for this project",
16+
] as const;
17+
18+
export const handleScript = async (devJson: DevJson, kv: KV) => {
19+
const { scripts } = devJson;
20+
21+
if (!scripts || !scripts["_cd"]) {
22+
return;
23+
}
24+
25+
const script = scripts["_cd"];
26+
27+
const allowedCommands =
28+
kv.read<Record<string, boolean>>("_cd", "allowed") || {};
29+
const bannedCommands =
30+
kv.read<Record<string, boolean>>("_cd", "banned") || {};
31+
32+
const isAllowed = (command: string) => {
33+
const all = kv.read<string>("_cd", "run all _cd commands for this project");
34+
35+
if (all === "never") {
36+
return false;
37+
}
38+
39+
if (all === "always") {
40+
return true;
41+
}
42+
43+
if (bannedCommands[command]) {
44+
return false;
45+
}
46+
47+
if (allowedCommands[command]) {
48+
return true;
49+
}
50+
51+
return null;
52+
};
53+
54+
const allowed = isAllowed(script);
55+
if (allowed === false) {
56+
return false;
57+
}
58+
59+
if (allowed === true) {
60+
await $`$SHELL -c ${script}`;
61+
return true;
62+
}
63+
64+
report.info(`This _cd command is about to run: ${script.green}`);
65+
66+
const { run } = (await inquirer.prompt([
67+
{
68+
type: "list",
69+
name: "run",
70+
message: "Do you want to run the script?",
71+
choices: CHOICES,
72+
},
73+
])) as { run: (typeof CHOICES)[number] };
74+
75+
if (run === "no") {
76+
return false;
77+
}
78+
79+
if (run === "never this command") {
80+
kv.write("_cd", "banned", { ...bannedCommands, [script]: true });
81+
return false;
82+
}
83+
84+
if (run === "never for _cd commands for this project") {
85+
kv.write("_cd", "run all _cd commands for this project", "never");
86+
return false;
87+
}
88+
89+
if (run === "always this command") {
90+
kv.write("_cd", "allowed", { ...allowedCommands, [script]: true });
91+
}
92+
93+
if (run === "always for _cd commands for this project") {
94+
kv.write("_cd", "run all _cd commands for this project", "always");
95+
}
96+
97+
await $`$SHELL -c ${script}`;
98+
return true;
99+
};

modules/_cd/help.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { HelpDocFN } from "../help/help.ts";
33

44
export const cmd = "_cd";
55
export const description =
6-
"This is not a command. It's an internal module that runs the :cd script defined in the dev.json file when you cd into your project.";
6+
"This is not a command. It's an internal module that runs the _cd script defined in the dev.json file when you cd into your project.";
77

88
export const help: HelpDocFN = () => ({
99
description: [description],

modules/_cd/index.ts

Lines changed: 16 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,26 @@
11
import "colors";
22
import { $ } from "zx";
3-
import report from "yurnalist";
4-
import inquirer from "inquirer";
3+
import process from "process";
54
import { kv } from "./kv.ts";
6-
7-
interface Scripts {
8-
_cd?: string;
9-
}
5+
import { handleScript } from "./handleScript.ts";
6+
import { handleEnvFile } from "./handleEnvFile.ts";
7+
import type { ModuleRunOptions } from "../../utils/types.ts";
108

119
$.verbose = true;
1210

13-
const CHOICES = [
14-
"no",
15-
"yes",
16-
"never this command",
17-
"always this command",
18-
"never for _cd commands for this project",
19-
"always for _cd commands for this project",
20-
] as const;
21-
22-
export const run = async () => {
23-
const { scripts }: { scripts?: Scripts } = JSON.parse(
24-
Deno.readTextFileSync("dev.json")
25-
);
26-
27-
if (!scripts || !scripts["_cd"]) {
28-
return;
29-
}
30-
31-
const script = scripts["_cd"];
32-
33-
const allowedCommands =
34-
kv.read<Record<string, boolean>>("_cd", "allowed") || {};
35-
const bannedCommands =
36-
kv.read<Record<string, boolean>>("_cd", "banned") || {};
37-
38-
const isAllowed = (command: string) => {
39-
const all = kv.read<string>("_cd", "run all _cd commands for this project");
40-
41-
if (all === "never") {
42-
return false;
43-
}
44-
45-
if (all === "always") {
46-
return true;
47-
}
48-
49-
if (bannedCommands[command]) {
50-
return false;
51-
}
52-
53-
if (allowedCommands[command]) {
54-
return true;
55-
}
56-
57-
return null;
11+
export interface DevJson {
12+
scripts?: {
13+
_cd?: string;
5814
};
15+
env?: {
16+
_cd?: string;
17+
};
18+
}
5919

60-
const allowed = isAllowed(script);
61-
62-
if (allowed === false) {
63-
Deno.exit(0);
64-
return;
65-
}
66-
67-
if (allowed === true) {
68-
await $`$SHELL -c ${script}`;
69-
Deno.exit(0);
70-
}
71-
72-
report.info(`This _cd command is about to run: ${script.green}`);
73-
74-
const { run } = (await inquirer.prompt([
75-
{
76-
type: "list",
77-
name: "run",
78-
message: "Do you want to run the script?",
79-
choices: CHOICES,
80-
},
81-
])) as { run: (typeof CHOICES)[number] };
82-
83-
if (run === "no") {
84-
Deno.exit(0);
85-
return;
86-
}
87-
88-
if (run === "never this command") {
89-
kv.write("_cd", "banned", { ...bannedCommands, [script]: true });
90-
Deno.exit(0);
91-
return;
92-
}
93-
94-
if (run === "never for _cd commands for this project") {
95-
kv.write("_cd", "run all _cd commands for this project", "never");
96-
Deno.exit(0);
97-
return;
98-
}
99-
100-
if (run === "always this command") {
101-
kv.write("_cd", "allowed", { ...allowedCommands, [script]: true });
102-
}
103-
104-
if (run === "always for _cd commands for this project") {
105-
kv.write("_cd", "run all _cd commands for this project", "always");
106-
}
20+
export const run = async ({ loadEnvFile }: ModuleRunOptions) => {
21+
const devJson = JSON.parse(Deno.readTextFileSync("dev.json")) as DevJson;
22+
await handleScript(devJson, kv);
23+
await handleEnvFile(devJson, kv, loadEnvFile);
10724

108-
await $`$SHELL -c ${script}`;
109-
Deno.exit(0);
25+
process.exit(0);
11026
};

modules/_cd/kv.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ export const kv = {
6565
read: readKV,
6666
write: writeKV,
6767
};
68+
69+
export type KV = typeof kv;

modules/shell/fish.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ function <$SHELL_FN_NAME$>
1212
cd (string replace -r "cd:" "" -- $cmd)
1313
case "source:*"
1414
source (string replace -r "source:" "" -- $cmd)
15+
case "env:*"
16+
set cmd (string replace -r "env:" "" -- $cmd)
1517
case "*"
1618
# echo do nothing
1719
end

modules/shell/sh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const script = `
1212
case "\${cmd}" in
1313
cd:*) cd "\${cmd//cd:/}" ;;
1414
source:*) source "\${cmd//source:/}" ;;
15+
env:*) export $(xargs < "\${cmd//env:/}") ;;
1516
*) ;;
1617
esac
1718
done < "\${tempfile}"

utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export interface ModuleRunOptions {
66
writeConfig: WriteConfigFN;
77
cd: (path: string) => unknown;
88
source: () => unknown;
9+
loadEnvFile: (path: string) => unknown;
910
}

0 commit comments

Comments
 (0)