@tolgamorf/env2op-cli 0.2.1 → 0.2.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/README.md +7 -3
- package/dist/cli.js +322 -38
- package/dist/index.js +1 -1
- package/dist/op2env-cli.js +310 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Or in a single command:
|
|
|
19
19
|
brew install tolgamorf/tap/env2op-cli
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
### Package Managers (
|
|
22
|
+
### Package Managers (macOS/Linux/Windows)
|
|
23
23
|
|
|
24
24
|
#### Global installation
|
|
25
25
|
|
|
@@ -97,8 +97,10 @@ env2op .env.production Personal "MyApp" -f
|
|
|
97
97
|
| `-f, --force` | Skip confirmation prompts |
|
|
98
98
|
| `--dry-run` | Preview actions without executing |
|
|
99
99
|
| `--secret` | Store all fields as 'password' type (default: 'text') |
|
|
100
|
-
|
|
|
100
|
+
| `--verbose` | Show op CLI output |
|
|
101
|
+
| `--update` | Check for and install updates |
|
|
101
102
|
| `-v, --version` | Show version |
|
|
103
|
+
| `-h, --help` | Show help |
|
|
102
104
|
|
|
103
105
|
## op2env (Pull)
|
|
104
106
|
|
|
@@ -131,8 +133,10 @@ op2env .env.tpl -f
|
|
|
131
133
|
| `-o, --output` | Output .env path (default: template without `.tpl`) |
|
|
132
134
|
| `-f, --force` | Overwrite without prompting |
|
|
133
135
|
| `--dry-run` | Preview actions without executing |
|
|
134
|
-
|
|
|
136
|
+
| `--verbose` | Show op CLI output |
|
|
137
|
+
| `--update` | Check for and install updates |
|
|
135
138
|
| `-v, --version` | Show version |
|
|
139
|
+
| `-h, --help` | Show help |
|
|
136
140
|
|
|
137
141
|
## How It Works
|
|
138
142
|
|
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
22
22
|
var require_package = __commonJS((exports, module) => {
|
|
23
23
|
module.exports = {
|
|
24
24
|
name: "@tolgamorf/env2op-cli",
|
|
25
|
-
version: "0.2.
|
|
25
|
+
version: "0.2.2",
|
|
26
26
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
27
27
|
type: "module",
|
|
28
28
|
main: "dist/index.js",
|
|
@@ -102,7 +102,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
// src/cli.ts
|
|
105
|
-
import
|
|
105
|
+
import pc5 from "picocolors";
|
|
106
106
|
|
|
107
107
|
// src/commands/convert.ts
|
|
108
108
|
import { basename, dirname, join } from "node:path";
|
|
@@ -788,6 +788,274 @@ async function runConvert(options) {
|
|
|
788
788
|
}
|
|
789
789
|
}
|
|
790
790
|
|
|
791
|
+
// src/commands/update.ts
|
|
792
|
+
import * as p4 from "@clack/prompts";
|
|
793
|
+
import pc4 from "picocolors";
|
|
794
|
+
|
|
795
|
+
// src/lib/package-manager.ts
|
|
796
|
+
var UPDATE_COMMANDS = {
|
|
797
|
+
homebrew: "brew upgrade tolgamorf/tap/env2op-cli",
|
|
798
|
+
npm: "npm update -g @tolgamorf/env2op-cli",
|
|
799
|
+
bun: "bun update -g @tolgamorf/env2op-cli",
|
|
800
|
+
pnpm: "pnpm update -g @tolgamorf/env2op-cli",
|
|
801
|
+
unknown: "npm update -g @tolgamorf/env2op-cli"
|
|
802
|
+
};
|
|
803
|
+
var DISPLAY_NAMES = {
|
|
804
|
+
homebrew: "Homebrew",
|
|
805
|
+
npm: "npm",
|
|
806
|
+
bun: "Bun",
|
|
807
|
+
pnpm: "pnpm",
|
|
808
|
+
unknown: "npm (default)"
|
|
809
|
+
};
|
|
810
|
+
function detectFromPath() {
|
|
811
|
+
const binPath = process.argv[1] ?? "";
|
|
812
|
+
if (binPath.includes("/Cellar/") || binPath.includes("/homebrew/") || binPath.includes("/opt/homebrew/") || binPath.includes("/home/linuxbrew/")) {
|
|
813
|
+
return "homebrew";
|
|
814
|
+
}
|
|
815
|
+
if (binPath.includes("/.bun/")) {
|
|
816
|
+
return "bun";
|
|
817
|
+
}
|
|
818
|
+
if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
|
|
819
|
+
return "pnpm";
|
|
820
|
+
}
|
|
821
|
+
if (binPath.includes("/node_modules/")) {
|
|
822
|
+
return "npm";
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
async function detectFromCommands() {
|
|
827
|
+
const brewResult = await exec("brew", ["list", "env2op-cli"], { verbose: false });
|
|
828
|
+
if (brewResult.exitCode === 0) {
|
|
829
|
+
return "homebrew";
|
|
830
|
+
}
|
|
831
|
+
return "npm";
|
|
832
|
+
}
|
|
833
|
+
async function detectPackageManager() {
|
|
834
|
+
const fromPath = detectFromPath();
|
|
835
|
+
if (fromPath) {
|
|
836
|
+
return {
|
|
837
|
+
type: fromPath,
|
|
838
|
+
updateCommand: UPDATE_COMMANDS[fromPath],
|
|
839
|
+
displayName: DISPLAY_NAMES[fromPath]
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
const fromCommands = await detectFromCommands();
|
|
843
|
+
return {
|
|
844
|
+
type: fromCommands,
|
|
845
|
+
updateCommand: UPDATE_COMMANDS[fromCommands],
|
|
846
|
+
displayName: DISPLAY_NAMES[fromCommands]
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/lib/update.ts
|
|
851
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
852
|
+
import { homedir } from "node:os";
|
|
853
|
+
import { join as join2 } from "node:path";
|
|
854
|
+
var CACHE_DIR = join2(homedir(), ".env2op");
|
|
855
|
+
var CACHE_FILE = join2(CACHE_DIR, "update-check.json");
|
|
856
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
857
|
+
function getCliVersion() {
|
|
858
|
+
try {
|
|
859
|
+
const pkg2 = require_package();
|
|
860
|
+
return pkg2.version ?? "0.0.0";
|
|
861
|
+
} catch {
|
|
862
|
+
return "0.0.0";
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function loadCache() {
|
|
866
|
+
try {
|
|
867
|
+
if (existsSync(CACHE_FILE)) {
|
|
868
|
+
const content = readFileSync(CACHE_FILE, "utf-8");
|
|
869
|
+
return JSON.parse(content);
|
|
870
|
+
}
|
|
871
|
+
} catch {}
|
|
872
|
+
return { lastCheck: 0, latestVersion: null };
|
|
873
|
+
}
|
|
874
|
+
function saveCache(cache) {
|
|
875
|
+
try {
|
|
876
|
+
if (!existsSync(CACHE_DIR)) {
|
|
877
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
878
|
+
}
|
|
879
|
+
writeFileSync2(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
880
|
+
} catch {}
|
|
881
|
+
}
|
|
882
|
+
function shouldCheckForUpdate(cache) {
|
|
883
|
+
const now = Date.now();
|
|
884
|
+
return now - cache.lastCheck > CHECK_INTERVAL_MS;
|
|
885
|
+
}
|
|
886
|
+
async function fetchLatestVersion() {
|
|
887
|
+
try {
|
|
888
|
+
const response = await fetch("https://registry.npmjs.org/@tolgamorf/env2op-cli/latest");
|
|
889
|
+
if (!response.ok)
|
|
890
|
+
return null;
|
|
891
|
+
const data = await response.json();
|
|
892
|
+
return data.version ?? null;
|
|
893
|
+
} catch {
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function compareVersions(v1, v2) {
|
|
898
|
+
const parts1 = v1.split(".").map(Number);
|
|
899
|
+
const parts2 = v2.split(".").map(Number);
|
|
900
|
+
for (let i = 0;i < 3; i++) {
|
|
901
|
+
const p1 = parts1[i] || 0;
|
|
902
|
+
const p22 = parts2[i] || 0;
|
|
903
|
+
if (p1 < p22)
|
|
904
|
+
return -1;
|
|
905
|
+
if (p1 > p22)
|
|
906
|
+
return 1;
|
|
907
|
+
}
|
|
908
|
+
return 0;
|
|
909
|
+
}
|
|
910
|
+
async function checkForUpdate(forceCheck = false) {
|
|
911
|
+
const currentVersion = getCliVersion();
|
|
912
|
+
const cache = loadCache();
|
|
913
|
+
if (!forceCheck && !shouldCheckForUpdate(cache) && cache.latestVersion) {
|
|
914
|
+
const updateAvailable2 = compareVersions(currentVersion, cache.latestVersion) < 0;
|
|
915
|
+
const isSkipped2 = cache.skipVersion === cache.latestVersion;
|
|
916
|
+
return {
|
|
917
|
+
currentVersion,
|
|
918
|
+
latestVersion: cache.latestVersion,
|
|
919
|
+
updateAvailable: updateAvailable2,
|
|
920
|
+
isSkipped: isSkipped2,
|
|
921
|
+
fromCache: true
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
const latestVersion = await fetchLatestVersion();
|
|
925
|
+
saveCache({
|
|
926
|
+
...cache,
|
|
927
|
+
lastCheck: Date.now(),
|
|
928
|
+
latestVersion
|
|
929
|
+
});
|
|
930
|
+
if (!latestVersion) {
|
|
931
|
+
return {
|
|
932
|
+
currentVersion,
|
|
933
|
+
latestVersion: null,
|
|
934
|
+
updateAvailable: false,
|
|
935
|
+
isSkipped: false,
|
|
936
|
+
fromCache: false
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
const updateAvailable = compareVersions(currentVersion, latestVersion) < 0;
|
|
940
|
+
const isSkipped = cache.skipVersion === latestVersion;
|
|
941
|
+
return {
|
|
942
|
+
currentVersion,
|
|
943
|
+
latestVersion,
|
|
944
|
+
updateAvailable,
|
|
945
|
+
isSkipped,
|
|
946
|
+
fromCache: false
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
async function performUpdate(pm) {
|
|
950
|
+
const packageManager = pm ?? await detectPackageManager();
|
|
951
|
+
try {
|
|
952
|
+
const [command, ...args] = packageManager.updateCommand.split(" ");
|
|
953
|
+
const result = await exec(command, args, { verbose: false });
|
|
954
|
+
if (result.exitCode !== 0) {
|
|
955
|
+
return {
|
|
956
|
+
success: false,
|
|
957
|
+
error: result.stderr || `Command exited with code ${result.exitCode}`
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
return { success: true };
|
|
961
|
+
} catch (error) {
|
|
962
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
963
|
+
return { success: false, error: message };
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
function skipVersion(version) {
|
|
967
|
+
const cache = loadCache();
|
|
968
|
+
cache.skipVersion = version;
|
|
969
|
+
saveCache(cache);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// src/lib/update-prompts.ts
|
|
973
|
+
import * as p3 from "@clack/prompts";
|
|
974
|
+
import pc3 from "picocolors";
|
|
975
|
+
var S_BAR_START = "┌";
|
|
976
|
+
var S_BAR_END = "└";
|
|
977
|
+
function showUpdateNotification(result, cliName = "env2op") {
|
|
978
|
+
console.log();
|
|
979
|
+
console.log(`${pc3.gray(S_BAR_START)}${pc3.gray("─")} ${pc3.yellow("Update available:")} ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
|
|
980
|
+
console.log(`${pc3.gray(S_BAR_END)}${pc3.gray("─")} Run ${pc3.cyan(`'${cliName} update'`)} to update`);
|
|
981
|
+
}
|
|
982
|
+
async function askToUpdate(result) {
|
|
983
|
+
const response = await p3.select({
|
|
984
|
+
message: "Would you like to update?",
|
|
985
|
+
options: [
|
|
986
|
+
{ value: "update", label: "Update now", hint: "Download and install the latest version" },
|
|
987
|
+
{ value: "later", label: "Remind me later", hint: "Ask again next time" },
|
|
988
|
+
{ value: "skip", label: "Skip this version", hint: `Don't ask about ${result.latestVersion} again` }
|
|
989
|
+
]
|
|
990
|
+
});
|
|
991
|
+
if (p3.isCancel(response)) {
|
|
992
|
+
return "later";
|
|
993
|
+
}
|
|
994
|
+
return response;
|
|
995
|
+
}
|
|
996
|
+
function showUpdateAvailable(result) {
|
|
997
|
+
p3.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
|
|
998
|
+
}
|
|
999
|
+
function showUpToDate(currentVersion) {
|
|
1000
|
+
p3.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
|
|
1001
|
+
}
|
|
1002
|
+
function showPackageManagerInfo(pm) {
|
|
1003
|
+
console.log();
|
|
1004
|
+
console.log(` ${pc3.dim("Detected:")} ${pm.displayName} installation`);
|
|
1005
|
+
console.log(` ${pc3.dim("Command:")} ${pc3.cyan(pm.updateCommand)}`);
|
|
1006
|
+
console.log();
|
|
1007
|
+
}
|
|
1008
|
+
function showUpdateSuccess(newVersion) {
|
|
1009
|
+
p3.log.success(`Updated to version ${pc3.green(newVersion)}`);
|
|
1010
|
+
p3.log.info("Please restart to use the new version.");
|
|
1011
|
+
}
|
|
1012
|
+
function showUpdateError(error, pm) {
|
|
1013
|
+
if (error) {
|
|
1014
|
+
p3.log.error(pc3.dim(error));
|
|
1015
|
+
}
|
|
1016
|
+
p3.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/commands/update.ts
|
|
1020
|
+
async function runUpdate(options) {
|
|
1021
|
+
const { force = false, cliName = "env2op" } = options;
|
|
1022
|
+
p4.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
|
|
1023
|
+
const spinner4 = p4.spinner();
|
|
1024
|
+
spinner4.start("Checking for updates...");
|
|
1025
|
+
const result = await checkForUpdate(true);
|
|
1026
|
+
spinner4.stop("Checked for updates");
|
|
1027
|
+
if (!result.updateAvailable || !result.latestVersion) {
|
|
1028
|
+
showUpToDate(result.currentVersion);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
showUpdateAvailable(result);
|
|
1032
|
+
const pm = await detectPackageManager();
|
|
1033
|
+
showPackageManagerInfo(pm);
|
|
1034
|
+
if (!force) {
|
|
1035
|
+
const choice = await askToUpdate(result);
|
|
1036
|
+
if (choice === "skip") {
|
|
1037
|
+
skipVersion(result.latestVersion);
|
|
1038
|
+
p4.log.info(`Skipped version ${result.latestVersion}`);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (choice === "later") {
|
|
1042
|
+
p4.log.info("Update postponed");
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
const updateSpinner = p4.spinner();
|
|
1047
|
+
updateSpinner.start(`Updating to ${result.latestVersion}...`);
|
|
1048
|
+
const updateResult = await performUpdate(pm);
|
|
1049
|
+
if (updateResult.success) {
|
|
1050
|
+
updateSpinner.stop("Update completed");
|
|
1051
|
+
showUpdateSuccess(result.latestVersion);
|
|
1052
|
+
} else {
|
|
1053
|
+
updateSpinner.stop("Update failed");
|
|
1054
|
+
showUpdateError(updateResult.error, pm);
|
|
1055
|
+
process.exit(1);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
791
1059
|
// src/cli.ts
|
|
792
1060
|
var pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
|
|
793
1061
|
var args = process.argv.slice(2);
|
|
@@ -814,10 +1082,19 @@ for (let i = 0;i < args.length; i++) {
|
|
|
814
1082
|
}
|
|
815
1083
|
var hasHelp = flags.has("h") || flags.has("help");
|
|
816
1084
|
var hasVersion = flags.has("v") || flags.has("version");
|
|
1085
|
+
var hasUpdate = flags.has("update");
|
|
817
1086
|
if (hasVersion) {
|
|
818
1087
|
console.log(pkg2.version);
|
|
819
1088
|
process.exit(0);
|
|
820
1089
|
}
|
|
1090
|
+
if (hasUpdate) {
|
|
1091
|
+
await runUpdate({
|
|
1092
|
+
force: flags.has("f") || flags.has("force"),
|
|
1093
|
+
verbose: flags.has("verbose"),
|
|
1094
|
+
cliName: "env2op"
|
|
1095
|
+
});
|
|
1096
|
+
process.exit(0);
|
|
1097
|
+
}
|
|
821
1098
|
if (hasHelp || positional.length === 0) {
|
|
822
1099
|
showHelp();
|
|
823
1100
|
process.exit(0);
|
|
@@ -837,48 +1114,55 @@ await runConvert({
|
|
|
837
1114
|
force: flags.has("f") || flags.has("force"),
|
|
838
1115
|
verbose: flags.has("verbose")
|
|
839
1116
|
});
|
|
1117
|
+
try {
|
|
1118
|
+
const updateResult = await checkForUpdate();
|
|
1119
|
+
if (updateResult.updateAvailable && !updateResult.isSkipped) {
|
|
1120
|
+
showUpdateNotification(updateResult, "env2op");
|
|
1121
|
+
}
|
|
1122
|
+
} catch {}
|
|
840
1123
|
function showHelp() {
|
|
841
|
-
const name =
|
|
842
|
-
const version =
|
|
1124
|
+
const name = pc5.bold(pc5.cyan("env2op"));
|
|
1125
|
+
const version = pc5.dim(`v${pkg2.version}`);
|
|
843
1126
|
console.log(`
|
|
844
1127
|
${name} ${version}
|
|
845
1128
|
${pkg2.description}
|
|
846
1129
|
|
|
847
|
-
${
|
|
848
|
-
${
|
|
1130
|
+
${pc5.bold("USAGE")}
|
|
1131
|
+
${pc5.cyan("$")} env2op ${pc5.yellow("<env_file>")} ${pc5.yellow("<vault>")} ${pc5.yellow("<item_name>")} ${pc5.dim("[options]")}
|
|
849
1132
|
|
|
850
|
-
${
|
|
851
|
-
${
|
|
852
|
-
${
|
|
853
|
-
${
|
|
1133
|
+
${pc5.bold("ARGUMENTS")}
|
|
1134
|
+
${pc5.yellow("env_file")} Path to .env file
|
|
1135
|
+
${pc5.yellow("vault")} 1Password vault name
|
|
1136
|
+
${pc5.yellow("item_name")} Name for the Secure Note in 1Password
|
|
854
1137
|
|
|
855
|
-
${
|
|
856
|
-
${
|
|
857
|
-
${
|
|
858
|
-
${
|
|
859
|
-
${
|
|
860
|
-
${
|
|
861
|
-
${
|
|
862
|
-
${
|
|
1138
|
+
${pc5.bold("OPTIONS")}
|
|
1139
|
+
${pc5.cyan("-o, --output")} Output template path (default: <env_file>.tpl)
|
|
1140
|
+
${pc5.cyan("-f, --force")} Skip confirmation prompts
|
|
1141
|
+
${pc5.cyan(" --dry-run")} Preview actions without executing
|
|
1142
|
+
${pc5.cyan(" --secret")} Store all fields as password type (hidden)
|
|
1143
|
+
${pc5.cyan(" --verbose")} Show op CLI output
|
|
1144
|
+
${pc5.cyan(" --update")} Check for and install updates
|
|
1145
|
+
${pc5.cyan("-v, --version")} Show version
|
|
1146
|
+
${pc5.cyan("-h, --help")} Show this help message
|
|
863
1147
|
|
|
864
|
-
${
|
|
865
|
-
${
|
|
866
|
-
${
|
|
1148
|
+
${pc5.bold("EXAMPLES")}
|
|
1149
|
+
${pc5.dim("# Basic usage")}
|
|
1150
|
+
${pc5.cyan("$")} env2op .env.production Personal "MyApp - Production"
|
|
867
1151
|
|
|
868
|
-
${
|
|
869
|
-
${
|
|
1152
|
+
${pc5.dim("# Custom output path")}
|
|
1153
|
+
${pc5.cyan("$")} env2op .env Personal "MyApp" -o secrets.tpl
|
|
870
1154
|
|
|
871
|
-
${
|
|
872
|
-
${
|
|
1155
|
+
${pc5.dim("# Preview without making changes")}
|
|
1156
|
+
${pc5.cyan("$")} env2op .env Personal "MyApp" --dry-run
|
|
873
1157
|
|
|
874
|
-
${
|
|
875
|
-
${
|
|
1158
|
+
${pc5.dim("# Store as hidden password fields")}
|
|
1159
|
+
${pc5.cyan("$")} env2op .env Personal "MyApp" --secret
|
|
876
1160
|
|
|
877
|
-
${
|
|
878
|
-
${
|
|
1161
|
+
${pc5.dim("# Skip confirmation prompts (for CI/scripts)")}
|
|
1162
|
+
${pc5.cyan("$")} env2op .env Personal "MyApp" -f
|
|
879
1163
|
|
|
880
|
-
${
|
|
881
|
-
${
|
|
1164
|
+
${pc5.bold("DOCUMENTATION")}
|
|
1165
|
+
${pc5.dim("https://github.com/tolgamorf/env2op-cli")}
|
|
882
1166
|
`);
|
|
883
1167
|
}
|
|
884
1168
|
function showMissingArgsError(provided) {
|
|
@@ -890,17 +1174,17 @@ function showMissingArgsError(provided) {
|
|
|
890
1174
|
if (provided.length < 3)
|
|
891
1175
|
missing.push("item_name");
|
|
892
1176
|
console.log(`
|
|
893
|
-
${
|
|
1177
|
+
${pc5.red(pc5.bold("Error:"))} Missing required arguments
|
|
894
1178
|
|
|
895
|
-
${
|
|
1179
|
+
${pc5.bold("Usage:")} env2op ${pc5.yellow("<env_file>")} ${pc5.yellow("<vault>")} ${pc5.yellow("<item_name>")} ${pc5.dim("[options]")}
|
|
896
1180
|
|
|
897
|
-
${
|
|
898
|
-
${missing.map((arg) => ` ${
|
|
1181
|
+
${pc5.bold("Missing:")}
|
|
1182
|
+
${missing.map((arg) => ` ${pc5.red("•")} ${pc5.yellow(arg)}`).join(`
|
|
899
1183
|
`)}
|
|
900
1184
|
|
|
901
|
-
${
|
|
902
|
-
${
|
|
1185
|
+
${pc5.bold("Example:")}
|
|
1186
|
+
${pc5.cyan("$")} env2op .env.production Personal "MyApp - Production"
|
|
903
1187
|
|
|
904
|
-
Run ${
|
|
1188
|
+
Run ${pc5.cyan("env2op --help")} for more information.
|
|
905
1189
|
`);
|
|
906
1190
|
}
|
package/dist/index.js
CHANGED
|
@@ -361,7 +361,7 @@ import { writeFileSync } from "node:fs";
|
|
|
361
361
|
// package.json
|
|
362
362
|
var package_default = {
|
|
363
363
|
name: "@tolgamorf/env2op-cli",
|
|
364
|
-
version: "0.2.
|
|
364
|
+
version: "0.2.2",
|
|
365
365
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
366
366
|
type: "module",
|
|
367
367
|
main: "dist/index.js",
|
package/dist/op2env-cli.js
CHANGED
|
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
22
22
|
var require_package = __commonJS((exports, module) => {
|
|
23
23
|
module.exports = {
|
|
24
24
|
name: "@tolgamorf/env2op-cli",
|
|
25
|
-
version: "0.2.
|
|
25
|
+
version: "0.2.2",
|
|
26
26
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
27
27
|
type: "module",
|
|
28
28
|
main: "dist/index.js",
|
|
@@ -102,7 +102,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
// src/op2env-cli.ts
|
|
105
|
-
import
|
|
105
|
+
import pc5 from "picocolors";
|
|
106
106
|
|
|
107
107
|
// src/commands/inject.ts
|
|
108
108
|
import { existsSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -733,6 +733,274 @@ async function runInject(options) {
|
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
+
// src/commands/update.ts
|
|
737
|
+
import * as p4 from "@clack/prompts";
|
|
738
|
+
import pc4 from "picocolors";
|
|
739
|
+
|
|
740
|
+
// src/lib/package-manager.ts
|
|
741
|
+
var UPDATE_COMMANDS = {
|
|
742
|
+
homebrew: "brew upgrade tolgamorf/tap/env2op-cli",
|
|
743
|
+
npm: "npm update -g @tolgamorf/env2op-cli",
|
|
744
|
+
bun: "bun update -g @tolgamorf/env2op-cli",
|
|
745
|
+
pnpm: "pnpm update -g @tolgamorf/env2op-cli",
|
|
746
|
+
unknown: "npm update -g @tolgamorf/env2op-cli"
|
|
747
|
+
};
|
|
748
|
+
var DISPLAY_NAMES = {
|
|
749
|
+
homebrew: "Homebrew",
|
|
750
|
+
npm: "npm",
|
|
751
|
+
bun: "Bun",
|
|
752
|
+
pnpm: "pnpm",
|
|
753
|
+
unknown: "npm (default)"
|
|
754
|
+
};
|
|
755
|
+
function detectFromPath() {
|
|
756
|
+
const binPath = process.argv[1] ?? "";
|
|
757
|
+
if (binPath.includes("/Cellar/") || binPath.includes("/homebrew/") || binPath.includes("/opt/homebrew/") || binPath.includes("/home/linuxbrew/")) {
|
|
758
|
+
return "homebrew";
|
|
759
|
+
}
|
|
760
|
+
if (binPath.includes("/.bun/")) {
|
|
761
|
+
return "bun";
|
|
762
|
+
}
|
|
763
|
+
if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
|
|
764
|
+
return "pnpm";
|
|
765
|
+
}
|
|
766
|
+
if (binPath.includes("/node_modules/")) {
|
|
767
|
+
return "npm";
|
|
768
|
+
}
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
async function detectFromCommands() {
|
|
772
|
+
const brewResult = await exec("brew", ["list", "env2op-cli"], { verbose: false });
|
|
773
|
+
if (brewResult.exitCode === 0) {
|
|
774
|
+
return "homebrew";
|
|
775
|
+
}
|
|
776
|
+
return "npm";
|
|
777
|
+
}
|
|
778
|
+
async function detectPackageManager() {
|
|
779
|
+
const fromPath = detectFromPath();
|
|
780
|
+
if (fromPath) {
|
|
781
|
+
return {
|
|
782
|
+
type: fromPath,
|
|
783
|
+
updateCommand: UPDATE_COMMANDS[fromPath],
|
|
784
|
+
displayName: DISPLAY_NAMES[fromPath]
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const fromCommands = await detectFromCommands();
|
|
788
|
+
return {
|
|
789
|
+
type: fromCommands,
|
|
790
|
+
updateCommand: UPDATE_COMMANDS[fromCommands],
|
|
791
|
+
displayName: DISPLAY_NAMES[fromCommands]
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/lib/update.ts
|
|
796
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
797
|
+
import { homedir } from "node:os";
|
|
798
|
+
import { join } from "node:path";
|
|
799
|
+
var CACHE_DIR = join(homedir(), ".env2op");
|
|
800
|
+
var CACHE_FILE = join(CACHE_DIR, "update-check.json");
|
|
801
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
802
|
+
function getCliVersion() {
|
|
803
|
+
try {
|
|
804
|
+
const pkg2 = require_package();
|
|
805
|
+
return pkg2.version ?? "0.0.0";
|
|
806
|
+
} catch {
|
|
807
|
+
return "0.0.0";
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function loadCache() {
|
|
811
|
+
try {
|
|
812
|
+
if (existsSync2(CACHE_FILE)) {
|
|
813
|
+
const content = readFileSync2(CACHE_FILE, "utf-8");
|
|
814
|
+
return JSON.parse(content);
|
|
815
|
+
}
|
|
816
|
+
} catch {}
|
|
817
|
+
return { lastCheck: 0, latestVersion: null };
|
|
818
|
+
}
|
|
819
|
+
function saveCache(cache) {
|
|
820
|
+
try {
|
|
821
|
+
if (!existsSync2(CACHE_DIR)) {
|
|
822
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
823
|
+
}
|
|
824
|
+
writeFileSync3(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
825
|
+
} catch {}
|
|
826
|
+
}
|
|
827
|
+
function shouldCheckForUpdate(cache) {
|
|
828
|
+
const now = Date.now();
|
|
829
|
+
return now - cache.lastCheck > CHECK_INTERVAL_MS;
|
|
830
|
+
}
|
|
831
|
+
async function fetchLatestVersion() {
|
|
832
|
+
try {
|
|
833
|
+
const response = await fetch("https://registry.npmjs.org/@tolgamorf/env2op-cli/latest");
|
|
834
|
+
if (!response.ok)
|
|
835
|
+
return null;
|
|
836
|
+
const data = await response.json();
|
|
837
|
+
return data.version ?? null;
|
|
838
|
+
} catch {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function compareVersions(v1, v2) {
|
|
843
|
+
const parts1 = v1.split(".").map(Number);
|
|
844
|
+
const parts2 = v2.split(".").map(Number);
|
|
845
|
+
for (let i = 0;i < 3; i++) {
|
|
846
|
+
const p1 = parts1[i] || 0;
|
|
847
|
+
const p22 = parts2[i] || 0;
|
|
848
|
+
if (p1 < p22)
|
|
849
|
+
return -1;
|
|
850
|
+
if (p1 > p22)
|
|
851
|
+
return 1;
|
|
852
|
+
}
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
async function checkForUpdate(forceCheck = false) {
|
|
856
|
+
const currentVersion = getCliVersion();
|
|
857
|
+
const cache = loadCache();
|
|
858
|
+
if (!forceCheck && !shouldCheckForUpdate(cache) && cache.latestVersion) {
|
|
859
|
+
const updateAvailable2 = compareVersions(currentVersion, cache.latestVersion) < 0;
|
|
860
|
+
const isSkipped2 = cache.skipVersion === cache.latestVersion;
|
|
861
|
+
return {
|
|
862
|
+
currentVersion,
|
|
863
|
+
latestVersion: cache.latestVersion,
|
|
864
|
+
updateAvailable: updateAvailable2,
|
|
865
|
+
isSkipped: isSkipped2,
|
|
866
|
+
fromCache: true
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
const latestVersion = await fetchLatestVersion();
|
|
870
|
+
saveCache({
|
|
871
|
+
...cache,
|
|
872
|
+
lastCheck: Date.now(),
|
|
873
|
+
latestVersion
|
|
874
|
+
});
|
|
875
|
+
if (!latestVersion) {
|
|
876
|
+
return {
|
|
877
|
+
currentVersion,
|
|
878
|
+
latestVersion: null,
|
|
879
|
+
updateAvailable: false,
|
|
880
|
+
isSkipped: false,
|
|
881
|
+
fromCache: false
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
const updateAvailable = compareVersions(currentVersion, latestVersion) < 0;
|
|
885
|
+
const isSkipped = cache.skipVersion === latestVersion;
|
|
886
|
+
return {
|
|
887
|
+
currentVersion,
|
|
888
|
+
latestVersion,
|
|
889
|
+
updateAvailable,
|
|
890
|
+
isSkipped,
|
|
891
|
+
fromCache: false
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
async function performUpdate(pm) {
|
|
895
|
+
const packageManager = pm ?? await detectPackageManager();
|
|
896
|
+
try {
|
|
897
|
+
const [command, ...args] = packageManager.updateCommand.split(" ");
|
|
898
|
+
const result = await exec(command, args, { verbose: false });
|
|
899
|
+
if (result.exitCode !== 0) {
|
|
900
|
+
return {
|
|
901
|
+
success: false,
|
|
902
|
+
error: result.stderr || `Command exited with code ${result.exitCode}`
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
return { success: true };
|
|
906
|
+
} catch (error) {
|
|
907
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
908
|
+
return { success: false, error: message };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function skipVersion(version) {
|
|
912
|
+
const cache = loadCache();
|
|
913
|
+
cache.skipVersion = version;
|
|
914
|
+
saveCache(cache);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/lib/update-prompts.ts
|
|
918
|
+
import * as p3 from "@clack/prompts";
|
|
919
|
+
import pc3 from "picocolors";
|
|
920
|
+
var S_BAR_START = "┌";
|
|
921
|
+
var S_BAR_END = "└";
|
|
922
|
+
function showUpdateNotification(result, cliName = "env2op") {
|
|
923
|
+
console.log();
|
|
924
|
+
console.log(`${pc3.gray(S_BAR_START)}${pc3.gray("─")} ${pc3.yellow("Update available:")} ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
|
|
925
|
+
console.log(`${pc3.gray(S_BAR_END)}${pc3.gray("─")} Run ${pc3.cyan(`'${cliName} update'`)} to update`);
|
|
926
|
+
}
|
|
927
|
+
async function askToUpdate(result) {
|
|
928
|
+
const response = await p3.select({
|
|
929
|
+
message: "Would you like to update?",
|
|
930
|
+
options: [
|
|
931
|
+
{ value: "update", label: "Update now", hint: "Download and install the latest version" },
|
|
932
|
+
{ value: "later", label: "Remind me later", hint: "Ask again next time" },
|
|
933
|
+
{ value: "skip", label: "Skip this version", hint: `Don't ask about ${result.latestVersion} again` }
|
|
934
|
+
]
|
|
935
|
+
});
|
|
936
|
+
if (p3.isCancel(response)) {
|
|
937
|
+
return "later";
|
|
938
|
+
}
|
|
939
|
+
return response;
|
|
940
|
+
}
|
|
941
|
+
function showUpdateAvailable(result) {
|
|
942
|
+
p3.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
|
|
943
|
+
}
|
|
944
|
+
function showUpToDate(currentVersion) {
|
|
945
|
+
p3.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
|
|
946
|
+
}
|
|
947
|
+
function showPackageManagerInfo(pm) {
|
|
948
|
+
console.log();
|
|
949
|
+
console.log(` ${pc3.dim("Detected:")} ${pm.displayName} installation`);
|
|
950
|
+
console.log(` ${pc3.dim("Command:")} ${pc3.cyan(pm.updateCommand)}`);
|
|
951
|
+
console.log();
|
|
952
|
+
}
|
|
953
|
+
function showUpdateSuccess(newVersion) {
|
|
954
|
+
p3.log.success(`Updated to version ${pc3.green(newVersion)}`);
|
|
955
|
+
p3.log.info("Please restart to use the new version.");
|
|
956
|
+
}
|
|
957
|
+
function showUpdateError(error, pm) {
|
|
958
|
+
if (error) {
|
|
959
|
+
p3.log.error(pc3.dim(error));
|
|
960
|
+
}
|
|
961
|
+
p3.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/commands/update.ts
|
|
965
|
+
async function runUpdate(options) {
|
|
966
|
+
const { force = false, cliName = "env2op" } = options;
|
|
967
|
+
p4.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
|
|
968
|
+
const spinner4 = p4.spinner();
|
|
969
|
+
spinner4.start("Checking for updates...");
|
|
970
|
+
const result = await checkForUpdate(true);
|
|
971
|
+
spinner4.stop("Checked for updates");
|
|
972
|
+
if (!result.updateAvailable || !result.latestVersion) {
|
|
973
|
+
showUpToDate(result.currentVersion);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
showUpdateAvailable(result);
|
|
977
|
+
const pm = await detectPackageManager();
|
|
978
|
+
showPackageManagerInfo(pm);
|
|
979
|
+
if (!force) {
|
|
980
|
+
const choice = await askToUpdate(result);
|
|
981
|
+
if (choice === "skip") {
|
|
982
|
+
skipVersion(result.latestVersion);
|
|
983
|
+
p4.log.info(`Skipped version ${result.latestVersion}`);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (choice === "later") {
|
|
987
|
+
p4.log.info("Update postponed");
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const updateSpinner = p4.spinner();
|
|
992
|
+
updateSpinner.start(`Updating to ${result.latestVersion}...`);
|
|
993
|
+
const updateResult = await performUpdate(pm);
|
|
994
|
+
if (updateResult.success) {
|
|
995
|
+
updateSpinner.stop("Update completed");
|
|
996
|
+
showUpdateSuccess(result.latestVersion);
|
|
997
|
+
} else {
|
|
998
|
+
updateSpinner.stop("Update failed");
|
|
999
|
+
showUpdateError(updateResult.error, pm);
|
|
1000
|
+
process.exit(1);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
736
1004
|
// src/op2env-cli.ts
|
|
737
1005
|
var pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
|
|
738
1006
|
var args = process.argv.slice(2);
|
|
@@ -759,10 +1027,19 @@ for (let i = 0;i < args.length; i++) {
|
|
|
759
1027
|
}
|
|
760
1028
|
var hasHelp = flags.has("h") || flags.has("help");
|
|
761
1029
|
var hasVersion = flags.has("v") || flags.has("version");
|
|
1030
|
+
var hasUpdate = flags.has("update");
|
|
762
1031
|
if (hasVersion) {
|
|
763
1032
|
console.log(pkg2.version);
|
|
764
1033
|
process.exit(0);
|
|
765
1034
|
}
|
|
1035
|
+
if (hasUpdate) {
|
|
1036
|
+
await runUpdate({
|
|
1037
|
+
force: flags.has("f") || flags.has("force"),
|
|
1038
|
+
verbose: flags.has("verbose"),
|
|
1039
|
+
cliName: "op2env"
|
|
1040
|
+
});
|
|
1041
|
+
process.exit(0);
|
|
1042
|
+
}
|
|
766
1043
|
if (hasHelp || positional.length === 0) {
|
|
767
1044
|
showHelp();
|
|
768
1045
|
process.exit(0);
|
|
@@ -775,41 +1052,48 @@ await runInject({
|
|
|
775
1052
|
force: flags.has("f") || flags.has("force"),
|
|
776
1053
|
verbose: flags.has("verbose")
|
|
777
1054
|
});
|
|
1055
|
+
try {
|
|
1056
|
+
const updateResult = await checkForUpdate();
|
|
1057
|
+
if (updateResult.updateAvailable && !updateResult.isSkipped) {
|
|
1058
|
+
showUpdateNotification(updateResult, "op2env");
|
|
1059
|
+
}
|
|
1060
|
+
} catch {}
|
|
778
1061
|
function showHelp() {
|
|
779
|
-
const name =
|
|
780
|
-
const version =
|
|
1062
|
+
const name = pc5.bold(pc5.cyan("op2env"));
|
|
1063
|
+
const version = pc5.dim(`v${pkg2.version}`);
|
|
781
1064
|
console.log(`
|
|
782
1065
|
${name} ${version}
|
|
783
1066
|
Pull secrets from 1Password to generate .env files
|
|
784
1067
|
|
|
785
|
-
${
|
|
786
|
-
${
|
|
1068
|
+
${pc5.bold("USAGE")}
|
|
1069
|
+
${pc5.cyan("$")} op2env ${pc5.yellow("<template_file>")} ${pc5.dim("[options]")}
|
|
787
1070
|
|
|
788
|
-
${
|
|
789
|
-
${
|
|
1071
|
+
${pc5.bold("ARGUMENTS")}
|
|
1072
|
+
${pc5.yellow("template_file")} Path to .env.tpl template file
|
|
790
1073
|
|
|
791
|
-
${
|
|
792
|
-
${
|
|
793
|
-
${
|
|
794
|
-
${
|
|
795
|
-
${
|
|
796
|
-
${
|
|
797
|
-
${
|
|
1074
|
+
${pc5.bold("OPTIONS")}
|
|
1075
|
+
${pc5.cyan("-o, --output")} Output .env path (default: template without .tpl)
|
|
1076
|
+
${pc5.cyan("-f, --force")} Overwrite without prompting
|
|
1077
|
+
${pc5.cyan(" --dry-run")} Preview actions without executing
|
|
1078
|
+
${pc5.cyan(" --verbose")} Show op CLI output
|
|
1079
|
+
${pc5.cyan(" --update")} Check for and install updates
|
|
1080
|
+
${pc5.cyan("-v, --version")} Show version
|
|
1081
|
+
${pc5.cyan("-h, --help")} Show this help message
|
|
798
1082
|
|
|
799
|
-
${
|
|
800
|
-
${
|
|
801
|
-
${
|
|
1083
|
+
${pc5.bold("EXAMPLES")}
|
|
1084
|
+
${pc5.dim("# Basic usage - generates .env from .env.tpl")}
|
|
1085
|
+
${pc5.cyan("$")} op2env .env.tpl
|
|
802
1086
|
|
|
803
|
-
${
|
|
804
|
-
${
|
|
1087
|
+
${pc5.dim("# Custom output path")}
|
|
1088
|
+
${pc5.cyan("$")} op2env .env.tpl -o .env.local
|
|
805
1089
|
|
|
806
|
-
${
|
|
807
|
-
${
|
|
1090
|
+
${pc5.dim("# Preview without making changes")}
|
|
1091
|
+
${pc5.cyan("$")} op2env .env.tpl --dry-run
|
|
808
1092
|
|
|
809
|
-
${
|
|
810
|
-
${
|
|
1093
|
+
${pc5.dim("# Overwrite existing .env without prompting")}
|
|
1094
|
+
${pc5.cyan("$")} op2env .env.tpl -f
|
|
811
1095
|
|
|
812
|
-
${
|
|
813
|
-
${
|
|
1096
|
+
${pc5.bold("DOCUMENTATION")}
|
|
1097
|
+
${pc5.dim("https://github.com/tolgamorf/env2op-cli")}
|
|
814
1098
|
`);
|
|
815
1099
|
}
|