mirror of
https://github.com/sbrow/envr.git
synced 2026-08-26 18:03:32 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 674f23ad2f |
@@ -15,6 +15,5 @@ envr-prof
|
|||||||
findr/findr
|
findr/findr
|
||||||
findr/findr-prof
|
findr/findr-prof
|
||||||
findr/bench-*.md
|
findr/bench-*.md
|
||||||
gendocs
|
|
||||||
result
|
result
|
||||||
version.odin
|
version.odin
|
||||||
|
|||||||
@@ -1,38 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [1.0.0](https://github.com/sbrow/envr/compare/v0.4.0...v1.0.0) (2026-07-27)
|
|
||||||
|
|
||||||
|
|
||||||
### ⚠ BREAKING CHANGES
|
|
||||||
|
|
||||||
* Renamed `nushell-completion` command to `completion nushell`.
|
|
||||||
* Databases with a JSON formatted remotes column will no longer parse correctly. If you have an old database, make sure you upgrade to envr 0.4.0 and run `envr list` before upgrading to this version.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Added `uninstall` command. ([222a0a3](https://github.com/sbrow/envr/commit/222a0a3452b978d9bd14d6d2b91e40cd8f4959e3))
|
|
||||||
* Added bash completion support. ([e0a339e](https://github.com/sbrow/envr/commit/e0a339e8ec996c0e91e27357854abd9a78bd695a))
|
|
||||||
* Brought back the docs generator. ([86a4da6](https://github.com/sbrow/envr/commit/86a4da6cbaae698379c73b8345e6d238b2a11a46))
|
|
||||||
* Improved nushell wrapper output. ([209ec89](https://github.com/sbrow/envr/commit/209ec89addbed4718c5aed845ab20b386d1e425d))
|
|
||||||
* Scan results can now be filtered during selection. ([ae42d53](https://github.com/sbrow/envr/commit/ae42d53f029dd185143a645fb3f3a787051c1bb4))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Removed irrelevent flags from `nushell-completion` command help text. ([af1860c](https://github.com/sbrow/envr/commit/af1860c970604470992182bbfe9ecb15609a0acf))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Added `#no_bounds_check` to ssh parsers. ([f5624e9](https://github.com/sbrow/envr/commit/f5624e91f1236eecb263f28600ce7f4b2fda149f))
|
|
||||||
* Added a pre-alloc opportunity. ([997f11b](https://github.com/sbrow/envr/commit/997f11b257f1c981f73c6ae97d6e821147b5e2fc))
|
|
||||||
* Removed support for JSON formatted remotes. ([5282a44](https://github.com/sbrow/envr/commit/5282a4414b9a0211604553f2cb9434025aa5296b))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Renamed `nushell-completion` command to `completion nushell`. ([b0635e0](https://github.com/sbrow/envr/commit/b0635e035c44fb20f9b02265cff1ed8db30aa9d0))
|
|
||||||
|
|
||||||
## [0.4.0](https://github.com/sbrow/envr/compare/v0.3.0...v0.4.0) (2026-06-29)
|
## [0.4.0](https://github.com/sbrow/envr/compare/v0.3.0...v0.4.0) (2026-06-29)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
|
|
||||||
4. Test all command branches.
|
4. Test all command branches.
|
||||||
|
|
||||||
|
5. Generate md and man pages again.
|
||||||
|
|
||||||
6. Add tests for untested commands.
|
6. Add tests for untested commands.
|
||||||
|
|
||||||
7. Add purge command?
|
7. Add uninstall command.
|
||||||
|
|
||||||
|
8. Add purge command?
|
||||||
|
|
||||||
|
|
||||||
## Double-check AI output
|
## Double-check AI output
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ Positional_Arg :: struct {
|
|||||||
ntype: string, // nushell type: "path", "string". "" defaults to "path"
|
ntype: string, // nushell type: "path", "string". "" defaults to "path"
|
||||||
completion: string,
|
completion: string,
|
||||||
optional: bool,
|
optional: bool,
|
||||||
desc: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandInfo :: struct {
|
CommandInfo :: struct {
|
||||||
@@ -97,18 +96,14 @@ key somewhere, otherwise your data could be lost forever.`,
|
|||||||
short = "Import a .env file into envr",
|
short = "Import a .env file into envr",
|
||||||
aliases = {"add"},
|
aliases = {"add"},
|
||||||
flags = GLOBAL_FLAGS,
|
flags = GLOBAL_FLAGS,
|
||||||
args = {
|
args = {{name = "path", completion = "untracked-paths"}},
|
||||||
{name = "path", completion = "untracked-paths", desc = "Path to .env file to backup"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "restore",
|
name = "restore",
|
||||||
usage = "envr restore <path>",
|
usage = "envr restore <path>",
|
||||||
short = "Restore a .env file from the database",
|
short = "Restore a .env file from the database",
|
||||||
flags = GLOBAL_FLAGS,
|
flags = GLOBAL_FLAGS,
|
||||||
args = {
|
args = {{name = "path", completion = "tracked-paths"}},
|
||||||
{name = "path", completion = "tracked-paths", desc = "Path to .env file to restore"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "list",
|
name = "list",
|
||||||
@@ -121,22 +116,14 @@ key somewhere, otherwise your data could be lost forever.`,
|
|||||||
usage = "envr remove <path>",
|
usage = "envr remove <path>",
|
||||||
short = "Remove a .env file from your database",
|
short = "Remove a .env file from your database",
|
||||||
flags = GLOBAL_FLAGS,
|
flags = GLOBAL_FLAGS,
|
||||||
args = {
|
args = {{name = "path", completion = "tracked-paths"}},
|
||||||
{name = "path", completion = "tracked-paths", desc = "Path to .env file to remove"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "check",
|
name = "check",
|
||||||
usage = "envr check [path]",
|
usage = "envr check [path]",
|
||||||
short = "Check if files are backed up",
|
short = "Check if files are backed up",
|
||||||
flags = GLOBAL_FLAGS,
|
flags = GLOBAL_FLAGS,
|
||||||
args = {
|
args = {{name = "path", optional = true}},
|
||||||
{
|
|
||||||
name = "path",
|
|
||||||
optional = true,
|
|
||||||
desc = "Path to check (defaults to current directory)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{name = "version", usage = "envr version", short = "Show envr's version", flags = {.Help}},
|
{name = "version", usage = "envr version", short = "Show envr's version", flags = {.Help}},
|
||||||
{
|
{
|
||||||
@@ -154,14 +141,7 @@ key somewhere, otherwise your data could be lost forever.`,
|
|||||||
nushell
|
nushell
|
||||||
bash`,
|
bash`,
|
||||||
flags = {.Help},
|
flags = {.Help},
|
||||||
args = {
|
args = {{name = "shell", ntype = "string", completion = "shells"}},
|
||||||
{
|
|
||||||
name = "shell",
|
|
||||||
ntype = "string",
|
|
||||||
completion = "shells",
|
|
||||||
desc = "Shell to generate completions for",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = "uninstall",
|
name = "uninstall",
|
||||||
@@ -290,64 +270,6 @@ Flag_Field :: struct {
|
|||||||
completion: string,
|
completion: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
write_flags_table :: proc(tbl: ^table.Table, flags: bit_set[Flag_Type]) {
|
|
||||||
table.caption(tbl, "Flags:")
|
|
||||||
for ft in Flag_Type {
|
|
||||||
if ft not_in flags do continue
|
|
||||||
names, hint, desc := flag_field_info(ft)
|
|
||||||
if len(hint) > 0 {
|
|
||||||
display := table.format(
|
|
||||||
tbl,
|
|
||||||
"%s%s",
|
|
||||||
colorize(.Flag, names, tbl.format_allocator),
|
|
||||||
hint,
|
|
||||||
)
|
|
||||||
table.row(tbl, display, desc)
|
|
||||||
} else {
|
|
||||||
table.row(tbl, colorize(.Flag, names, tbl.format_allocator), desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flag_field_info :: proc(
|
|
||||||
ft: Flag_Type,
|
|
||||||
) -> (
|
|
||||||
names: string,
|
|
||||||
value_hint: string,
|
|
||||||
description: string,
|
|
||||||
) {
|
|
||||||
f := flag_field(ft)
|
|
||||||
|
|
||||||
if len(f.short_name) > 0 {
|
|
||||||
names = fmt.tprintf("-%s, --%s", f.short_name, f.long_name)
|
|
||||||
} else {
|
|
||||||
names = fmt.tprintf("--%s", f.long_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch f.kind {
|
|
||||||
case .Bool:
|
|
||||||
value_hint = ""
|
|
||||||
case .String:
|
|
||||||
value_hint = " <value>"
|
|
||||||
case .Enum:
|
|
||||||
value_hint = fmt.tprintf(" %s", f.enum_values)
|
|
||||||
}
|
|
||||||
|
|
||||||
description = f.usage
|
|
||||||
if len(f.default_val) > 0 {
|
|
||||||
switch f.kind {
|
|
||||||
case .Bool:
|
|
||||||
// do nothing
|
|
||||||
case .String:
|
|
||||||
description = fmt.tprintf(`%s (default "%s")`, f.usage, f.default_val)
|
|
||||||
case .Enum:
|
|
||||||
description = fmt.tprintf("%s (default '%s')", f.usage, f.default_val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
flag_field :: proc(ft: Flag_Type) -> Flag_Field {
|
flag_field :: proc(ft: Flag_Type) -> Flag_Field {
|
||||||
field := reflect.struct_field_at(Flags, int(ft))
|
field := reflect.struct_field_at(Flags, int(ft))
|
||||||
|
|
||||||
@@ -393,6 +315,64 @@ flag_field :: proc(ft: Flag_Type) -> Flag_Field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flag_field_info :: proc(
|
||||||
|
ft: Flag_Type,
|
||||||
|
) -> (
|
||||||
|
names: string,
|
||||||
|
value_hint: string,
|
||||||
|
description: string,
|
||||||
|
) {
|
||||||
|
f := flag_field(ft)
|
||||||
|
|
||||||
|
if len(f.short_name) > 0 {
|
||||||
|
names = fmt.tprintf("-%s, --%s", f.short_name, f.long_name)
|
||||||
|
} else {
|
||||||
|
names = fmt.tprintf("--%s", f.long_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch f.kind {
|
||||||
|
case .Bool:
|
||||||
|
value_hint = ""
|
||||||
|
case .String:
|
||||||
|
value_hint = " <value>"
|
||||||
|
case .Enum:
|
||||||
|
value_hint = fmt.tprintf(" %s", f.enum_values)
|
||||||
|
}
|
||||||
|
|
||||||
|
description = f.usage
|
||||||
|
if len(f.default_val) > 0 {
|
||||||
|
switch f.kind {
|
||||||
|
case .Bool:
|
||||||
|
// do nothing
|
||||||
|
case .String:
|
||||||
|
description = fmt.tprintf(`%s (default "%s")`, f.usage, f.default_val)
|
||||||
|
case .Enum:
|
||||||
|
description = fmt.tprintf("%s (default '%s')", f.usage, f.default_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
write_flags_table :: proc(tbl: ^table.Table, flags: bit_set[Flag_Type]) {
|
||||||
|
table.caption(tbl, "Flags:")
|
||||||
|
for ft in Flag_Type {
|
||||||
|
if ft not_in flags do continue
|
||||||
|
names, hint, desc := flag_field_info(ft)
|
||||||
|
if len(hint) > 0 {
|
||||||
|
display := table.format(
|
||||||
|
tbl,
|
||||||
|
"%s%s",
|
||||||
|
colorize(.Flag, names, tbl.format_allocator),
|
||||||
|
hint,
|
||||||
|
)
|
||||||
|
table.row(tbl, display, desc)
|
||||||
|
} else {
|
||||||
|
table.row(tbl, colorize(.Flag, names, tbl.format_allocator), desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
find_command :: proc(name: string) -> (CommandInfo, bool) {
|
find_command :: proc(name: string) -> (CommandInfo, bool) {
|
||||||
for c in COMMANDS {
|
for c in COMMANDS {
|
||||||
if c.name == name {
|
if c.name == name {
|
||||||
@@ -407,7 +387,11 @@ find_command :: proc(name: string) -> (CommandInfo, bool) {
|
|||||||
return CommandInfo{}, false
|
return CommandInfo{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
ENVR_DESCRIPTION :: `envr keeps your .env synced to a local, encrypted database.
|
// TODO: command args should be shown in usage.
|
||||||
|
write_usage :: proc(w: io.Writer) {
|
||||||
|
fmt.wprintf(
|
||||||
|
w,
|
||||||
|
`envr keeps your .env synced to a local, encrypted database.
|
||||||
Is a safe and easy way to gather all your .env files in one place where they can
|
Is a safe and easy way to gather all your .env files in one place where they can
|
||||||
easily be backed by another tool such as restic or git.
|
easily be backed by another tool such as restic or git.
|
||||||
|
|
||||||
@@ -436,14 +420,13 @@ Select the files you want to back up from the interactive list.
|
|||||||
5. If you lose a repository, after re-cloning the repo into the same path it was
|
5. If you lose a repository, after re-cloning the repo into the same path it was
|
||||||
at before, restore your backup with:
|
at before, restore your backup with:
|
||||||
|
|
||||||
> envr restore ~/<path to repository>/.env`
|
> envr restore ~/<path to repository>/.env
|
||||||
|
|
||||||
// TODO: command args should be shown in usage.
|
%s
|
||||||
write_usage :: proc(w: io.Writer) {
|
|
||||||
fmt.wprintf(
|
%s [command]
|
||||||
w,
|
|
||||||
"%s\n\n%s\n\n %s [command]\n\n",
|
`,
|
||||||
ENVR_DESCRIPTION,
|
|
||||||
colorize(.Heading, "Usage:"),
|
colorize(.Heading, "Usage:"),
|
||||||
colorize(.Flag, "envr"),
|
colorize(.Flag, "envr"),
|
||||||
flush = false,
|
flush = false,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:terminal/ansi"
|
||||||
|
|
||||||
cmd_init :: proc(cmd: ^Command) {
|
cmd_init :: proc(cmd: ^Command) {
|
||||||
force := cmd.flags.force
|
force := cmd.flags.force
|
||||||
|
|||||||
+5
-6
@@ -221,8 +221,7 @@ search_paths :: proc(cfg: Config, allocator := context.allocator) -> [dynamic]st
|
|||||||
include = expanded
|
include = expanded
|
||||||
} else {
|
} else {
|
||||||
// TODO: show errors?
|
// TODO: show errors?
|
||||||
resolved: string
|
resolved, err := filepath.abs(expanded, allocator)
|
||||||
resolved, err = filepath.abs(expanded, allocator)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
include = resolved
|
include = resolved
|
||||||
}
|
}
|
||||||
@@ -232,6 +231,10 @@ search_paths :: proc(cfg: Config, allocator := context.allocator) -> [dynamic]st
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envr_dir :: proc(config_path: string) -> string {
|
||||||
|
return filepath.dir(config_path)
|
||||||
|
}
|
||||||
|
|
||||||
// User is responsible for freeing the path
|
// User is responsible for freeing the path
|
||||||
data_path :: proc(
|
data_path :: proc(
|
||||||
config_path: string,
|
config_path: string,
|
||||||
@@ -243,7 +246,3 @@ data_path :: proc(
|
|||||||
return filepath.join([]string{envr_dir(config_path), "data.envr"}, allocator)
|
return filepath.join([]string{envr_dir(config_path), "data.envr"}, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
envr_dir :: proc(config_path: string) -> string {
|
|
||||||
return filepath.dir(config_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:crypto/hash"
|
import "core:crypto/hash"
|
||||||
import "core:encoding/hex"
|
import "core:encoding/hex"
|
||||||
import "core:encoding/ini"
|
import "core:encoding/ini"
|
||||||
|
|||||||
+10
-11
@@ -5,7 +5,7 @@ Manage your .env files.
|
|||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
envr keeps your .env synced to a local, encrypted database.
|
envr keeps your .env synced to a local, encrypted database.
|
||||||
Is a safe and easy way to gather all your .env files in one place where they can
|
Is a safe and eay way to gather all your .env files in one place where they can
|
||||||
easily be backed by another tool such as restic or git.
|
easily be backed by another tool such as restic or git.
|
||||||
|
|
||||||
All your data is stored in ~/.envr/data.envr
|
All your data is stored in ~/.envr/data.envr
|
||||||
@@ -33,26 +33,25 @@ Select the files you want to back up from the interactive list.
|
|||||||
5. If you lose a repository, after re-cloning the repo into the same path it was
|
5. If you lose a repository, after re-cloning the repo into the same path it was
|
||||||
at before, restore your backup with:
|
at before, restore your backup with:
|
||||||
|
|
||||||
> envr restore ~/<path to repository>/.env
|
> envr restore ~/<path to repository>/.env
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for envr
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [envr backup](envr_backup.md) - Import a .env file into envr
|
||||||
|
* [envr check](envr_check.md) - check if files in the current directory are backed up
|
||||||
|
* [envr edit-config](envr_edit-config.md) - Edit your config with your default editor
|
||||||
* [envr init](envr_init.md) - Set up envr
|
* [envr init](envr_init.md) - Set up envr
|
||||||
|
* [envr list](envr_list.md) - View your tracked files
|
||||||
|
* [envr nushell-completion](envr_nushell-completion.md) - Generate custom completions for nushell
|
||||||
|
* [envr remove](envr_remove.md) - Remove a .env file from your database
|
||||||
|
* [envr restore](envr_restore.md) - Install a .env file from the database into your file system
|
||||||
* [envr scan](envr_scan.md) - Find and select .env files for backup
|
* [envr scan](envr_scan.md) - Find and select .env files for backup
|
||||||
* [envr sync](envr_sync.md) - Update or restore your env backups
|
* [envr sync](envr_sync.md) - Update or restore your env backups
|
||||||
* [envr backup](envr_backup.md) - Import a .env file into envr
|
|
||||||
* [envr restore](envr_restore.md) - Restore a .env file from the database
|
|
||||||
* [envr list](envr_list.md) - View your tracked files
|
|
||||||
* [envr remove](envr_remove.md) - Remove a .env file from your database
|
|
||||||
* [envr check](envr_check.md) - Check if files are backed up
|
|
||||||
* [envr version](envr_version.md) - Show envr's version
|
* [envr version](envr_version.md) - Show envr's version
|
||||||
* [envr edit-config](envr_edit-config.md) - Edit your config with your default editor
|
|
||||||
* [envr completion](envr_completion.md) - Generate shell completion scripts
|
|
||||||
* [envr uninstall](envr_uninstall.md) - Remove envr configuration and database
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,13 @@ Import a .env file into envr
|
|||||||
envr backup <path> [flags]
|
envr backup <path> [flags]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arguments:**
|
|
||||||
|
|
||||||
* `path` — Path to .env file to backup
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for backup
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
*Alias: `add`*
|
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
## envr check
|
## envr check
|
||||||
|
|
||||||
Check if files are backed up
|
check if files in the current directory are backed up
|
||||||
|
|
||||||
```
|
```
|
||||||
envr check [path] [flags]
|
envr check [path] [flags]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arguments:**
|
|
||||||
|
|
||||||
* `path` — Path to check (defaults to current directory) (optional)
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for check
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
## envr completion
|
|
||||||
|
|
||||||
Generate shell completion scripts
|
|
||||||
|
|
||||||
### Synopsis
|
|
||||||
|
|
||||||
Supported shells:
|
|
||||||
|
|
||||||
nushell
|
|
||||||
bash
|
|
||||||
|
|
||||||
```
|
|
||||||
envr completion <shell> [flags]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Arguments:**
|
|
||||||
|
|
||||||
* `shell` — Shell to generate completions for
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
```
|
|
||||||
-h, --help show this documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
### SEE ALSO
|
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
|
||||||
@@ -9,11 +9,10 @@ envr edit-config [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for edit-config
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ Set up envr
|
|||||||
### Synopsis
|
### Synopsis
|
||||||
|
|
||||||
The init command generates your initial config and saves it to
|
The init command generates your initial config and saves it to
|
||||||
~/.envr/config in JSON format.\n\nDuring setup, you will be prompted to select one or more ssh keys with which to
|
~/.envr/config in JSON format.
|
||||||
|
|
||||||
|
During setup, you will be prompted to select one or more ssh keys with which to
|
||||||
encrypt your databse. **Make 100% sure** that you have **a remote copy** of this
|
encrypt your databse. **Make 100% sure** that you have **a remote copy** of this
|
||||||
key somewhere, otherwise your data could be lost forever.
|
key somewhere, otherwise your data could be lost forever.
|
||||||
|
|
||||||
@@ -16,12 +18,11 @@ envr init [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-f, --force Overwrite an existing config
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
-h, --help help for init
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
-f, --force Overwrite existing config
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ envr list [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for list
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
-o, --output the format of output data (default 'table')
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
## envr nushell-completion
|
||||||
|
|
||||||
|
Generate custom completions for nushell
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
At time of writing, cobra does not natively support nushell,
|
||||||
|
so a custom command had to be written
|
||||||
|
|
||||||
|
```
|
||||||
|
envr nushell-completion [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for nushell-completion
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
@@ -3,21 +3,16 @@
|
|||||||
Remove a .env file from your database
|
Remove a .env file from your database
|
||||||
|
|
||||||
```
|
```
|
||||||
envr remove <path> [flags]
|
envr remove [flags]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arguments:**
|
|
||||||
|
|
||||||
* `path` — Path to .env file to remove
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for remove
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
## envr restore
|
## envr restore
|
||||||
|
|
||||||
Restore a .env file from the database
|
Install a .env file from the database into your file system
|
||||||
|
|
||||||
```
|
```
|
||||||
envr restore <path> [flags]
|
envr restore [flags]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Arguments:**
|
|
||||||
|
|
||||||
* `path` — Path to .env file to restore
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for restore
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ envr scan [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for scan
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ envr sync [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for sync
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
-o, --output the format of output data (default 'table')
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
## envr uninstall
|
|
||||||
|
|
||||||
Remove envr configuration and database
|
|
||||||
|
|
||||||
### Synopsis
|
|
||||||
|
|
||||||
This will remove all files generated by envr from your system.
|
|
||||||
|
|
||||||
```
|
|
||||||
envr uninstall [flags]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
```
|
|
||||||
-h, --help show this documentation
|
|
||||||
-c, --config-file config file (default "~/.envr/config.json")
|
|
||||||
--color Whether or not to colorize output (default 'auto')
|
|
||||||
-f, --force Overwrite existing config
|
|
||||||
```
|
|
||||||
|
|
||||||
### SEE ALSO
|
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
|
||||||
@@ -9,9 +9,11 @@ envr version [flags]
|
|||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-h, --help show this documentation
|
-h, --help help for version
|
||||||
|
-l, --long Show all version information
|
||||||
```
|
```
|
||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
* [envr](envr.md) - Manage your .env files.
|
* [envr](envr.md) - Manage your .env files.
|
||||||
|
|
||||||
|
|||||||
+53
-58
@@ -4,12 +4,64 @@ import "base:runtime"
|
|||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
|
get_subtag :: proc(tag: string, id: string) -> (value: string, ok: bool) {
|
||||||
|
parts := strings.split(tag, ",", context.temp_allocator)
|
||||||
|
for part in parts {
|
||||||
|
trimmed := strings.trim_space(part)
|
||||||
|
if strings.has_prefix(trimmed, id) && len(trimmed) > len(id) && trimmed[len(id)] == '=' {
|
||||||
|
return trimmed[len(id) + 1:], true
|
||||||
|
}
|
||||||
|
if trimmed == id {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
is_bool_type :: proc(field: reflect.Struct_Field) -> bool {
|
||||||
|
base_ti := runtime.type_info_base(field.type)
|
||||||
|
_, is_bool := base_ti.variant.(runtime.Type_Info_Boolean)
|
||||||
|
return is_bool
|
||||||
|
}
|
||||||
|
|
||||||
|
set_field :: proc(model: rawptr, field: reflect.Struct_Field, value: string) -> bool {
|
||||||
|
ptr := rawptr(uintptr(model) + field.offset)
|
||||||
|
base_ti := runtime.type_info_base(field.type)
|
||||||
|
|
||||||
|
if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool {
|
||||||
|
(cast(^bool)ptr)^ = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string {
|
||||||
|
(cast(^string)ptr)^ = value
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum {
|
||||||
|
for name, i in enum_ti.names {
|
||||||
|
if strings.equal_fold(value, name) {
|
||||||
|
v := enum_ti.values[i]
|
||||||
|
switch base_ti.size {
|
||||||
|
case 1: (cast(^u8)ptr)^ = cast(u8)v
|
||||||
|
case 2: (cast(^u16)ptr)^ = cast(u16)v
|
||||||
|
case 4: (cast(^u32)ptr)^ = cast(u32)v
|
||||||
|
case 8: (cast(^u64)ptr)^ = cast(u64)v
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
parse_flags :: proc(model: ^$T, args: []string) -> (overflow: []string) {
|
parse_flags :: proc(model: ^$T, args: []string) -> (overflow: []string) {
|
||||||
field_count := reflect.struct_field_count(T)
|
field_count := reflect.struct_field_count(T)
|
||||||
long_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator)
|
long_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator)
|
||||||
short_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator)
|
short_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator)
|
||||||
|
|
||||||
for i in 0 ..< field_count {
|
for i in 0..<field_count {
|
||||||
field := reflect.struct_field_at(T, i)
|
field := reflect.struct_field_at(T, i)
|
||||||
|
|
||||||
name, _ := strings.replace(field.name, "_", "-", -1, context.temp_allocator)
|
name, _ := strings.replace(field.name, "_", "-", -1, context.temp_allocator)
|
||||||
@@ -80,60 +132,3 @@ parse_flags :: proc(model: ^$T, args: []string) -> (overflow: []string) {
|
|||||||
|
|
||||||
return overflow_dyn[:]
|
return overflow_dyn[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
get_subtag :: proc(tag: string, id: string) -> (value: string, ok: bool) {
|
|
||||||
parts := strings.split(tag, ",", context.temp_allocator)
|
|
||||||
for part in parts {
|
|
||||||
trimmed := strings.trim_space(part)
|
|
||||||
if strings.has_prefix(trimmed, id) && len(trimmed) > len(id) && trimmed[len(id)] == '=' {
|
|
||||||
return trimmed[len(id) + 1:], true
|
|
||||||
}
|
|
||||||
if trimmed == id {
|
|
||||||
return "", true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
is_bool_type :: proc(field: reflect.Struct_Field) -> bool {
|
|
||||||
base_ti := runtime.type_info_base(field.type)
|
|
||||||
_, is_bool := base_ti.variant.(runtime.Type_Info_Boolean)
|
|
||||||
return is_bool
|
|
||||||
}
|
|
||||||
|
|
||||||
set_field :: proc(model: rawptr, field: reflect.Struct_Field, value: string) -> bool {
|
|
||||||
ptr := rawptr(uintptr(model) + field.offset)
|
|
||||||
base_ti := runtime.type_info_base(field.type)
|
|
||||||
|
|
||||||
if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool {
|
|
||||||
(cast(^bool)ptr)^ = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string {
|
|
||||||
(cast(^string)ptr)^ = value
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum {
|
|
||||||
for name, i in enum_ti.names {
|
|
||||||
if strings.equal_fold(value, name) {
|
|
||||||
v := enum_ti.values[i]
|
|
||||||
switch base_ti.size {
|
|
||||||
case 1:
|
|
||||||
(cast(^u8)ptr)^ = cast(u8)v
|
|
||||||
case 2:
|
|
||||||
(cast(^u16)ptr)^ = cast(u16)v
|
|
||||||
case 4:
|
|
||||||
(cast(^u32)ptr)^ = cast(u32)v
|
|
||||||
case 8:
|
|
||||||
(cast(^u64)ptr)^ = cast(u64)v
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,11 +91,6 @@
|
|||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
echo '${version}' > version.txt
|
echo '${version}' > version.txt
|
||||||
|
|
||||||
# Generate man pages before main build (binary gets clobbered)
|
|
||||||
odin build . -define:GENDOCS=true -out:gendocs
|
|
||||||
./gendocs --man
|
|
||||||
|
|
||||||
odin build . -o:speed -out:${pname}
|
odin build . -o:speed -out:${pname}
|
||||||
runHook postBuild
|
runHook postBuild
|
||||||
'';
|
'';
|
||||||
@@ -103,9 +98,6 @@
|
|||||||
installPhase = ''
|
installPhase = ''
|
||||||
runHook preInstall
|
runHook preInstall
|
||||||
install -Dm755 ${pname} $out/bin/${pname}
|
install -Dm755 ${pname} $out/bin/${pname}
|
||||||
|
|
||||||
mkdir -p $out/share/man/man1
|
|
||||||
cp docs/man/*.1 $out/share/man/man1/
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|||||||
-289
@@ -1,289 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:os"
|
|
||||||
import "core:strings"
|
|
||||||
import "core:time"
|
|
||||||
|
|
||||||
when #config(GENDOCS, false) {
|
|
||||||
main :: proc() {
|
|
||||||
args := os.args
|
|
||||||
if len(args) < 2 {
|
|
||||||
fmt.eprintln("Usage: gendocs --man | --md")
|
|
||||||
os.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[1] {
|
|
||||||
case "--man":
|
|
||||||
generate_man_pages()
|
|
||||||
case "--md":
|
|
||||||
generate_markdown()
|
|
||||||
case:
|
|
||||||
fmt.eprintf("Unknown format: %s\n", args[1])
|
|
||||||
os.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_man_pages :: proc() {
|
|
||||||
os.mkdir_all("docs/man")
|
|
||||||
|
|
||||||
now := time.now()
|
|
||||||
month_year := fmt.tprintf("%s %d", time.month(now), time.year(now))
|
|
||||||
|
|
||||||
generate_man_main(month_year)
|
|
||||||
|
|
||||||
for &c in COMMANDS {
|
|
||||||
generate_man_command(&c, month_year)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("Generated man pages in docs/man/")
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_man_main :: proc(month_year: string) {
|
|
||||||
sb: strings.Builder
|
|
||||||
strings.builder_init(&sb, context.temp_allocator)
|
|
||||||
defer strings.builder_destroy(&sb)
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, ".TH ENVR 1 \"%s\" \"envr\" \"envr Manual\"\n", month_year)
|
|
||||||
fmt.sbprintf(&sb, ".SH NAME\nenvr \\- keep your .env files synced to an encrypted database\n")
|
|
||||||
fmt.sbprintf(&sb, ".SH SYNOPSIS\n\\fBenvr\\fR [\\fIcommand\\fR] [\\fIflags\\fR]\n")
|
|
||||||
fmt.sbprintf(
|
|
||||||
&sb,
|
|
||||||
".SH DESCRIPTION\nenvr keeps your .env synced to a local, encrypted database.\n",
|
|
||||||
)
|
|
||||||
fmt.sbprintf(
|
|
||||||
&sb,
|
|
||||||
"Is a safe and easy way to gather all your .env files in one place where they can\n",
|
|
||||||
)
|
|
||||||
fmt.sbprintf(&sb, "easily be backed by another tool such as restic or git.\n")
|
|
||||||
fmt.sbprintf(&sb, ".PP\nAll your data is stored in ~/.envr/data.envr\n")
|
|
||||||
fmt.sbprintf(&sb, ".SH COMMANDS\n")
|
|
||||||
for c in COMMANDS {
|
|
||||||
fmt.sbprintf(&sb, ".TP\n\\fB%s\\fR\n%s\n", c.name, c.short)
|
|
||||||
for a in c.aliases {
|
|
||||||
fmt.sbprintf(&sb, ".TP\n\\fB%s\\fR (alias for %s)\n%s\n", a, c.name, c.short)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, ".SH GLOBAL OPTIONS\n")
|
|
||||||
for ft in Flag_Type {
|
|
||||||
if ft not_in GLOBAL_FLAGS do continue
|
|
||||||
write_man_flag(&sb, ft)
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, ".SH SEE ALSO\n")
|
|
||||||
for c, i in COMMANDS {
|
|
||||||
if i > 0 do fmt.sbprintf(&sb, ",\n")
|
|
||||||
fmt.sbprintf(&sb, ".BR envr-%s (1)", c.name)
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, "\n")
|
|
||||||
|
|
||||||
path := "docs/man/envr.1"
|
|
||||||
err := os.write_entire_file(path, transmute([]u8)strings.to_string(sb))
|
|
||||||
if err != nil {
|
|
||||||
fmt.eprintf("Error writing %s: %v\n", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_man_command :: proc(c: ^CommandInfo, month_year: string) {
|
|
||||||
sb: strings.Builder
|
|
||||||
strings.builder_init(&sb, context.temp_allocator)
|
|
||||||
defer strings.builder_destroy(&sb)
|
|
||||||
|
|
||||||
upper_name := strings.to_upper(fmt.tprintf("envr-%s", c.name))
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, ".TH %s 1 \"%s\" \"envr\" \"envr Manual\"\n", upper_name, month_year)
|
|
||||||
fmt.sbprintf(&sb, ".SH NAME\nenvr-%s \\- %s\n", c.name, c.short)
|
|
||||||
fmt.sbprintf(&sb, ".SH SYNOPSIS\n\\fB%s\\fR", c.usage)
|
|
||||||
|
|
||||||
has_positionals := false
|
|
||||||
for arg in c.args {
|
|
||||||
has_positionals = true
|
|
||||||
if arg.optional {
|
|
||||||
fmt.sbprintf(&sb, " [\\fI%s\\fR]", arg.name)
|
|
||||||
} else {
|
|
||||||
fmt.sbprintf(&sb, " \\fI%s\\fR", arg.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, "\n")
|
|
||||||
|
|
||||||
if len(c.long) > 0 {
|
|
||||||
fmt.sbprintf(&sb, ".SH DESCRIPTION\n%s\n", c.long)
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_positionals {
|
|
||||||
fmt.sbprintf(&sb, ".SH ARGUMENTS\n")
|
|
||||||
for arg in c.args {
|
|
||||||
fmt.sbprintf(&sb, ".TP\n\\fB%s\\fR\n%s\n", arg.name, arg.desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, ".SH OPTIONS\n")
|
|
||||||
for ft in Flag_Type {
|
|
||||||
if ft not_in c.flags do continue
|
|
||||||
write_man_flag(&sb, ft)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.tprintf("docs/man/envr-%s.1", c.name)
|
|
||||||
err := os.write_entire_file(path, transmute([]u8)strings.to_string(sb))
|
|
||||||
if err != nil {
|
|
||||||
fmt.eprintf("Error writing %s: %v\n", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_man_flag :: proc(sb: ^strings.Builder, ft: Flag_Type) {
|
|
||||||
f := flag_field(ft)
|
|
||||||
|
|
||||||
fmt.sbprintf(sb, ".TP\n")
|
|
||||||
if len(f.short_name) > 0 {
|
|
||||||
fmt.sbprintf(sb, "\\fB-%s\\fR, \\fB--%s\\fR", f.short_name, f.long_name)
|
|
||||||
} else {
|
|
||||||
fmt.sbprintf(sb, "\\fB--%s\\fR", f.long_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
#partial switch f.kind {
|
|
||||||
case .String:
|
|
||||||
fmt.sbprintf(sb, " \\fIvalue\\fR")
|
|
||||||
case .Enum:
|
|
||||||
values, _ := strings.replace(f.enum_values, "'", "", -1)
|
|
||||||
fmt.sbprintf(sb, " \\fI%s\\fR", values)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(sb, "\n%s\n", f.usage)
|
|
||||||
|
|
||||||
if len(f.default_val) > 0 {
|
|
||||||
#partial switch f.kind {
|
|
||||||
case .String:
|
|
||||||
fmt.sbprintf(sb, "(default \"%s\")\n", f.default_val)
|
|
||||||
case .Enum:
|
|
||||||
fmt.sbprintf(sb, "(default '%s')\n", f.default_val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_markdown :: proc() {
|
|
||||||
os.mkdir_all("docs/cli")
|
|
||||||
|
|
||||||
generate_md_main()
|
|
||||||
|
|
||||||
for &c in COMMANDS {
|
|
||||||
generate_md_command(&c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("Generated markdown in docs/cli/")
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_md_main :: proc() {
|
|
||||||
sb: strings.Builder
|
|
||||||
strings.builder_init(&sb, context.temp_allocator)
|
|
||||||
defer strings.builder_destroy(&sb)
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, "## envr\n\n")
|
|
||||||
fmt.sbprintf(&sb, "Manage your .env files.\n\n")
|
|
||||||
fmt.sbprintf(&sb, "### Synopsis\n\n")
|
|
||||||
fmt.sbprint(&sb, ENVR_DESCRIPTION)
|
|
||||||
fmt.sbprintf(&sb, "\n\n### Options\n\n")
|
|
||||||
write_md_options(&sb, {.Help})
|
|
||||||
fmt.sbprintf(&sb, "\n### SEE ALSO\n\n")
|
|
||||||
for c in COMMANDS {
|
|
||||||
fmt.sbprintf(&sb, "* [envr %s](envr_%s.md)\t - %s\n", c.name, c.name, c.short)
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, "\n")
|
|
||||||
|
|
||||||
write_file("docs/cli/envr.md", strings.to_string(sb))
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_md_command :: proc(c: ^CommandInfo) {
|
|
||||||
sb: strings.Builder
|
|
||||||
strings.builder_init(&sb, context.temp_allocator)
|
|
||||||
defer strings.builder_destroy(&sb)
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, "## envr %s\n\n", c.name)
|
|
||||||
fmt.sbprintf(&sb, "%s\n\n", c.short)
|
|
||||||
|
|
||||||
if len(c.long) > 0 {
|
|
||||||
fmt.sbprintf(&sb, "### Synopsis\n\n")
|
|
||||||
fmt.sbprint(&sb, c.long)
|
|
||||||
fmt.sbprintf(&sb, "\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, "```\n%s [flags]\n```\n\n", c.usage)
|
|
||||||
|
|
||||||
if len(c.args) > 0 {
|
|
||||||
fmt.sbprintf(&sb, "**Arguments:**\n\n")
|
|
||||||
for arg in c.args {
|
|
||||||
optional := arg.optional ? " (optional)" : ""
|
|
||||||
fmt.sbprintf(&sb, "* `%s` — %s%s\n", arg.name, arg.desc, optional)
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&sb, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, "### Options\n\n")
|
|
||||||
write_md_options(&sb, c.flags)
|
|
||||||
|
|
||||||
for a in c.aliases {
|
|
||||||
fmt.sbprintf(&sb, "\n*Alias: `%s`*\n", a)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(&sb, "\n### SEE ALSO\n\n")
|
|
||||||
fmt.sbprintf(&sb, "* [envr](envr.md)\t - Manage your .env files.\n")
|
|
||||||
|
|
||||||
path := fmt.tprintf("docs/cli/envr_%s.md", c.name)
|
|
||||||
write_file(path, strings.to_string(sb))
|
|
||||||
}
|
|
||||||
|
|
||||||
write_md_options :: proc(sb: ^strings.Builder, flags: bit_set[Flag_Type]) {
|
|
||||||
displays := make([dynamic]string, 0, 5, context.temp_allocator)
|
|
||||||
defer delete(displays)
|
|
||||||
|
|
||||||
descs := make([dynamic]string, 0, 5, context.temp_allocator)
|
|
||||||
defer delete(descs)
|
|
||||||
|
|
||||||
max_width := 0
|
|
||||||
|
|
||||||
for ft in Flag_Type {
|
|
||||||
if ft not_in flags do continue
|
|
||||||
f := flag_field(ft)
|
|
||||||
|
|
||||||
display: string
|
|
||||||
if len(f.short_name) > 0 {
|
|
||||||
display = fmt.tprintf("-%s, --%s", f.short_name, f.long_name)
|
|
||||||
} else {
|
|
||||||
display = fmt.tprintf(" --%s", f.long_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
desc := f.usage
|
|
||||||
if len(f.default_val) > 0 {
|
|
||||||
#partial switch f.kind {
|
|
||||||
case .String:
|
|
||||||
desc = fmt.tprintf(`%s (default "%s")`, f.usage, f.default_val)
|
|
||||||
case .Enum:
|
|
||||||
desc = fmt.tprintf("%s (default '%s')", f.usage, f.default_val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
append(&displays, display)
|
|
||||||
append(&descs, desc)
|
|
||||||
if len(display) > max_width {
|
|
||||||
max_width = len(display)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.sbprintf(sb, "```\n")
|
|
||||||
pad_buf: [64]u8
|
|
||||||
for i in 0..<len(displays) {
|
|
||||||
padding := max_width - len(displays[i]) + 3
|
|
||||||
for j in 0..<padding {
|
|
||||||
pad_buf[j] = ' '
|
|
||||||
}
|
|
||||||
fmt.sbprintf(sb, " %s%s%s\n", displays[i], string(pad_buf[:padding]), descs[i])
|
|
||||||
}
|
|
||||||
fmt.sbprintf(sb, "```\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
write_file :: proc(path: string, content: string) {
|
|
||||||
err := os.write_entire_file(path, transmute([]u8)content)
|
|
||||||
if err != nil {
|
|
||||||
fmt.eprintf("Error writing %s: %v\n", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -14,81 +14,79 @@ when SPALL {
|
|||||||
spall_buffer: spall.Buffer
|
spall_buffer: spall.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
when !#config(GENDOCS, false) {
|
main :: proc() {
|
||||||
main :: proc() {
|
when SPALL {
|
||||||
when SPALL {
|
ctx, spall_ok := spall.context_create_with_scale("envr.spall", false, 1.0)
|
||||||
ctx, spall_ok := spall.context_create_with_scale("envr.spall", false, 1.0)
|
if !spall_ok {
|
||||||
if !spall_ok {
|
fmt.eprintln("Failed to create spall trace file")
|
||||||
fmt.eprintln("Failed to create spall trace file")
|
|
||||||
os.exit(1)
|
|
||||||
}
|
|
||||||
spall_ctx = ctx
|
|
||||||
defer spall.context_destroy(&spall_ctx)
|
|
||||||
|
|
||||||
spall_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
|
||||||
defer delete(spall_backing)
|
|
||||||
|
|
||||||
spall_buffer = spall.buffer_create(spall_backing, u32(sync.current_thread_id()))
|
|
||||||
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
when ODIN_DEBUG {
|
|
||||||
heap_track: mem.Tracking_Allocator
|
|
||||||
mem.tracking_allocator_init(&heap_track, context.allocator)
|
|
||||||
defer mem.tracking_allocator_destroy(&heap_track)
|
|
||||||
defer if len(heap_track.allocation_map) > 0 {
|
|
||||||
for _, leak in heap_track.allocation_map {
|
|
||||||
fmt.eprintf("LEAK: %v leaked %m\n", leak.location, leak.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.allocator = mem.tracking_allocator(&heap_track)
|
|
||||||
|
|
||||||
temp_track: mem.Tracking_Allocator
|
|
||||||
mem.tracking_allocator_init(&temp_track, context.temp_allocator)
|
|
||||||
defer mem.tracking_allocator_destroy(&temp_track)
|
|
||||||
context.temp_allocator = mem.tracking_allocator(&temp_track)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer free_all(context.temp_allocator)
|
|
||||||
|
|
||||||
cmd, ok := parse_args(os.args, os.to_writer(os.stdout), os.to_writer(os.stderr))
|
|
||||||
defer delete_command(&cmd) // delete flushes automatically
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch cmd.name {
|
|
||||||
case "init":
|
|
||||||
cmd_init(&cmd)
|
|
||||||
case "version":
|
|
||||||
cmd_version(&cmd)
|
|
||||||
case "list":
|
|
||||||
cmd_list(&cmd)
|
|
||||||
case "backup", "add":
|
|
||||||
cmd_backup(&cmd)
|
|
||||||
case "remove":
|
|
||||||
cmd_remove(&cmd)
|
|
||||||
case "restore":
|
|
||||||
cmd_restore(&cmd)
|
|
||||||
case "edit-config":
|
|
||||||
cmd_edit_config(&cmd)
|
|
||||||
case "check":
|
|
||||||
cmd_check(&cmd)
|
|
||||||
case "scan":
|
|
||||||
cmd_scan(&cmd)
|
|
||||||
case "sync":
|
|
||||||
cmd_sync(&cmd)
|
|
||||||
case "completion":
|
|
||||||
cmd_completion(&cmd)
|
|
||||||
case "uninstall":
|
|
||||||
cmd_uninstall(&cmd)
|
|
||||||
case:
|
|
||||||
fmt.wprintf(cmd.err, "Unknown command: %s\n", cmd.name)
|
|
||||||
write_usage(cmd.out)
|
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
}
|
}
|
||||||
|
spall_ctx = ctx
|
||||||
|
defer spall.context_destroy(&spall_ctx)
|
||||||
|
|
||||||
|
spall_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
||||||
|
defer delete(spall_backing)
|
||||||
|
|
||||||
|
spall_buffer = spall.buffer_create(spall_backing, u32(sync.current_thread_id()))
|
||||||
|
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
|
||||||
}
|
}
|
||||||
} // !GENDOCS
|
|
||||||
|
when ODIN_DEBUG {
|
||||||
|
heap_track: mem.Tracking_Allocator
|
||||||
|
mem.tracking_allocator_init(&heap_track, context.allocator)
|
||||||
|
defer mem.tracking_allocator_destroy(&heap_track)
|
||||||
|
defer if len(heap_track.allocation_map) > 0 {
|
||||||
|
for _, leak in heap_track.allocation_map {
|
||||||
|
fmt.eprintf("LEAK: %v leaked %m\n", leak.location, leak.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.allocator = mem.tracking_allocator(&heap_track)
|
||||||
|
|
||||||
|
temp_track: mem.Tracking_Allocator
|
||||||
|
mem.tracking_allocator_init(&temp_track, context.temp_allocator)
|
||||||
|
defer mem.tracking_allocator_destroy(&temp_track)
|
||||||
|
context.temp_allocator = mem.tracking_allocator(&temp_track)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer free_all(context.temp_allocator)
|
||||||
|
|
||||||
|
cmd, ok := parse_args(os.args, os.to_writer(os.stdout), os.to_writer(os.stderr))
|
||||||
|
defer delete_command(&cmd) // delete flushes automatically
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cmd.name {
|
||||||
|
case "init":
|
||||||
|
cmd_init(&cmd)
|
||||||
|
case "version":
|
||||||
|
cmd_version(&cmd)
|
||||||
|
case "list":
|
||||||
|
cmd_list(&cmd)
|
||||||
|
case "backup", "add":
|
||||||
|
cmd_backup(&cmd)
|
||||||
|
case "remove":
|
||||||
|
cmd_remove(&cmd)
|
||||||
|
case "restore":
|
||||||
|
cmd_restore(&cmd)
|
||||||
|
case "edit-config":
|
||||||
|
cmd_edit_config(&cmd)
|
||||||
|
case "check":
|
||||||
|
cmd_check(&cmd)
|
||||||
|
case "scan":
|
||||||
|
cmd_scan(&cmd)
|
||||||
|
case "sync":
|
||||||
|
cmd_sync(&cmd)
|
||||||
|
case "completion":
|
||||||
|
cmd_completion(&cmd)
|
||||||
|
case "uninstall":
|
||||||
|
cmd_uninstall(&cmd)
|
||||||
|
case:
|
||||||
|
fmt.wprintf(cmd.err, "Unknown command: %s\n", cmd.name)
|
||||||
|
write_usage(cmd.out)
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when SPALL {
|
when SPALL {
|
||||||
@(instrumentation_enter)
|
@(instrumentation_enter)
|
||||||
|
|||||||
+15
-15
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:text/table"
|
import "core:text/table"
|
||||||
|
|
||||||
@@ -17,6 +18,20 @@ decorations := table.Decorations {
|
|||||||
"─",
|
"─",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ansi_aware_width :: proc(str: string) -> int #no_bounds_check {
|
||||||
|
width := 0
|
||||||
|
for i := 0; i < len(str); {
|
||||||
|
if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' {
|
||||||
|
i += 2
|
||||||
|
for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}}
|
||||||
|
} else {
|
||||||
|
width += 1
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {
|
write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {
|
||||||
table.build(t, ansi_aware_width)
|
table.build(t, ansi_aware_width)
|
||||||
|
|
||||||
@@ -49,21 +64,6 @@ write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {
|
|||||||
write_table_separator(w, t)
|
write_table_separator(w, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
ansi_aware_width :: proc(str: string) -> int #no_bounds_check {
|
|
||||||
width := 0
|
|
||||||
for i := 0; i < len(str); {
|
|
||||||
if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' {
|
|
||||||
i += 2
|
|
||||||
for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}}
|
|
||||||
} else {
|
|
||||||
width += 1
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
table_reset :: proc(t: ^table.Table) {
|
table_reset :: proc(t: ^table.Table) {
|
||||||
clear(&t.cells)
|
clear(&t.cells)
|
||||||
clear(&t.colw)
|
clear(&t.colw)
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.0.0
|
0.4.0
|
||||||
|
|||||||
Reference in New Issue
Block a user