Compare commits
51 Commits
Author | SHA1 | Date |
---|---|---|
Dmitriy Pleshevskiy | be61316444 | |
Dmitriy Pleshevskiy | 9eebd41db0 | |
Dmitriy Pleshevskiy | f4d4d46390 | |
Dmitriy Pleshevskiy | 4f583c160d | |
Dmitriy Pleshevskiy | 1f9e1d0e9e | |
Dmitriy Pleshevskiy | c4e30fb179 | |
Dmitriy Pleshevskiy | 542d7f1d5c | |
Dmitriy Pleshevskiy | 8f81bddeaa | |
Dmitriy Pleshevskiy | bbd1d47ba5 | |
Dmitriy Pleshevskiy | 4e9ea6c683 | |
Dmitriy Pleshevskiy | 2be25a330f | |
Dmitriy Pleshevskiy | 25d28531a1 | |
Dmitriy Pleshevskiy | 6eddd4c2a0 | |
Dmitriy Pleshevskiy | 7983beaea3 | |
Dmitriy Pleshevskiy | 5f0c94af77 | |
Dmitriy Pleshevskiy | dc66d1a643 | |
Dmitriy Pleshevskiy | 1f57ca0c97 | |
Dmitriy Pleshevskiy | 97b2897edc | |
Dmitriy Pleshevskiy | ac3f245a1c | |
Dmitriy Pleshevskiy | 21e9e89de2 | |
Dmitriy Pleshevskiy | 38be5079ff | |
Dmitriy Pleshevskiy | c0b010e279 | |
Dmitriy Pleshevskiy | 570a39734b | |
Dmitriy Pleshevskiy | 6095b4cd3c | |
Dmitriy Pleshevskiy | b67662f6ac | |
Dmitriy Pleshevskiy | ca299ad81d | |
Dmitriy Pleshevskiy | 1a6dfb8d46 | |
Dmitriy Pleshevskiy | 146c0697f2 | |
Dmitriy Pleshevskiy | 8f786cfd36 | |
Dmitriy Pleshevskiy | c6a7217ebd | |
Dmitriy Pleshevskiy | 6b23168d84 | |
Dmitriy Pleshevskiy | e8f17add36 | |
Dmitriy Pleshevskiy | 73dd2d4883 | |
Dmitriy Pleshevskiy | 925f446cb1 | |
Dmitriy Pleshevskiy | 5bd659ab46 | |
Dmitriy Pleshevskiy | d46da89a47 | |
Dmitriy Pleshevskiy | 53b99905f8 | |
Dmitriy Pleshevskiy | 5736fe2ea8 | |
Dmitriy Pleshevskiy | ddfe229f9d | |
Dmitriy Pleshevskiy | 34744cb5c8 | |
Dmitriy Pleshevskiy | c599e624f3 | |
Dmitriy Pleshevskiy | 00c1fc2954 | |
Dmitriy Pleshevskiy | 9a7f3bde5a | |
Dmitriy Pleshevskiy | 832de949b6 | |
Dmitriy Pleshevskiy | 2a6eab45c5 | |
Dmitriy Pleshevskiy | 8fed9f979b | |
Dmitriy Pleshevskiy | d6e7b03d35 | |
Dmitriy Pleshevskiy | 4cd7087f13 | |
Dmitriy Pleshevskiy | e94a9ca351 | |
Dmitriy Pleshevskiy | dd8f120152 | |
Dmitriy Pleshevskiy | 3972001328 |
25
.env.example
25
.env.example
|
@ -1,8 +1,17 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
# staging / production
|
||||
### local,staging
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
# DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
|
@ -10,13 +19,21 @@ CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
|||
|
||||
### local
|
||||
VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
VITE_API_BASE_URL='http://localhost:3000'
|
||||
VITE_SITE_URL='http://localhost:3000'
|
||||
CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
DEBUG=1
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
# VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
# VITE_BMM_SITE_URL='https://www.develop.staging.example.com'
|
||||
# VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
# CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug
|
||||
# DEBUG_VAR=1
|
||||
|
||||
### debug:on,local
|
||||
# DEBUG=1
|
||||
### debug:off,staging
|
||||
# DEBUG=
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
# build
|
||||
/target
|
||||
/result
|
||||
|
||||
.env
|
||||
# environments
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
# direnv
|
||||
.envrc
|
||||
.direnv
|
||||
|
|
|
@ -2,6 +2,232 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44bbe24bbd31a185bc2c4f7c2abe80bea13a20d57ee4e55be70ac512bdc76417"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vnetod"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,9 +1,10 @@
|
|||
[package]
|
||||
name = "vnetod"
|
||||
description = "Dotenv state switcher"
|
||||
version = "0.1.0"
|
||||
description = "Are you still switching sections in your dotenv file manually? Try this dotenv section switcher"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0+"
|
||||
homepage = "https://github.com/pleshevskiy/vnetod/discussions"
|
||||
repository = "https://git.pleshevski.ru/pleshevskiy/vnetod.git"
|
||||
keywords = ["env", "dotenv", "switcher", "change"]
|
||||
categories = ["command-line-interface", "config", "development-tools"]
|
||||
|
@ -11,3 +12,9 @@ categories = ["command-line-interface", "config", "development-tools"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2.15", default-features = false, features = ["std", "env", "derive"] }
|
||||
atty = { version = "0.2.14", optional = true }
|
||||
termcolor = { version = "1.1.3", optional = true }
|
||||
|
||||
[features]
|
||||
color = ["clap/color", "dep:atty", "dep:termcolor"]
|
||||
|
|
88
README.md
88
README.md
|
@ -1,13 +1,20 @@
|
|||
# vnetod
|
||||
# vnetod*
|
||||
|
||||
Dotenv state switcher
|
||||
<small><strong>*</strong> inverted word "dotenv"</small>
|
||||
|
||||
You can create many states in your `.env` and switch between them.
|
||||
Are you still switching sections in your dotenv file manually? Try this dotenv
|
||||
section switcher!
|
||||
|
||||
You can create many sections in your `.env` and switch between them.
|
||||
|
||||
Rules:
|
||||
|
||||
- State name starts on a new line with `###` symbols (Ex. `### local`)
|
||||
- State ends if line is empty or contains a new state name.
|
||||
- Section name starts on a new line with `###` symbols (Ex. `### local`)
|
||||
- Section can contain multiple comma-separated names (Ex.
|
||||
`### local,staging`)
|
||||
- Each section name may specify a namespace (Ex. `### debug:on,dev:on`). If a
|
||||
section doesn't contain a namespace, it's a global namespace.
|
||||
- Section ends if line is empty or contains a new section name.
|
||||
|
||||
You can see the [full example].
|
||||
|
||||
|
@ -15,17 +22,80 @@ You can see the [full example].
|
|||
|
||||
# Usage
|
||||
|
||||
Basic usage
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
vnetod local # choose local state
|
||||
vnetod staging # choose staging state
|
||||
vnetod - # disable all states
|
||||
vnetod local # enable local section
|
||||
vnetod staging # enable staging section
|
||||
vnetod local debug # enable local and debug sections
|
||||
vnetod # disable all sections
|
||||
```
|
||||
|
||||
This tool uses `.env` from your current location, but you can change this
|
||||
behavior with the `-f` (`--file`) flag.
|
||||
|
||||
```sh
|
||||
cp .env.example .env.properties
|
||||
vnetod -f .env.properties local
|
||||
```
|
||||
|
||||
And you can also change the output file with the `-o` (`--output`) flag, if you
|
||||
don't want to overwrite the input file.
|
||||
|
||||
```sh
|
||||
vnetod -f .env.example -o .env local
|
||||
```
|
||||
|
||||
You can also use variables from namespaces
|
||||
|
||||
```sh
|
||||
vnetod db:staging debug:off
|
||||
```
|
||||
|
||||
You can switch between states and overwrite from namespaces at the same time.
|
||||
|
||||
```sh
|
||||
vnetod local db:staging debug:off
|
||||
```
|
||||
|
||||
For more information, see the help.
|
||||
|
||||
```sh
|
||||
vnetod --help
|
||||
```
|
||||
|
||||
# Install
|
||||
|
||||
## Cargo
|
||||
|
||||
```sh
|
||||
cargo install vnetod
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/data pleshevskiy/vnetod --help
|
||||
```
|
||||
|
||||
## Nix
|
||||
|
||||
```sh
|
||||
nix run git+https://git.pleshevski.ru/pleshevskiy/vnetod -- --help
|
||||
```
|
||||
|
||||
# Contact me
|
||||
|
||||
- [send feedback](https://github.com/pleshevskiy/vnetod/discussions)
|
||||
- [make an issue](https://github.com/pleshevskiy/vnetod/issues)
|
||||
- matrix: @pleshevskiy:matrix.org
|
||||
- email: dmitriy@pleshevski.ru
|
||||
|
||||
# License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See [COPYING](./COPYING) to see the full text.
|
||||
See [COPYING] to see the full text.
|
||||
|
||||
[COPYING]: https://git.pleshevski.ru/pleshevskiy/vnetod/src/branch/main/COPYING
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
(import (
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
|
||||
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
|
||||
) {
|
||||
src = ./.;
|
||||
}).defaultNix
|
|
@ -0,0 +1,38 @@
|
|||
# Supported tags
|
||||
|
||||
- latest
|
||||
- 0.4
|
||||
|
||||
# vnetod*
|
||||
|
||||
<small><strong>*</strong> inverted word "dotenv"</small>
|
||||
|
||||
Are you still switching sections in your dotenv file manually? Try this dotenv
|
||||
section switcher!
|
||||
|
||||
You can create many sections in your `.env` and switch between them.
|
||||
|
||||
Rules:
|
||||
|
||||
- Section name starts on a new line with `###` symbols (Ex. `### local`)
|
||||
- Section can contain multiple comma-separated names (Ex.
|
||||
`### local,staging`)
|
||||
- Each section name may specify a namespace (Ex. `### debug:on,dev:on`). If a
|
||||
section doesn't contain a namespace, it's a global namespace.
|
||||
- Section ends if line is empty or contains a new section name.
|
||||
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/data pleshevskiy/vnetod -f .env.example -o .env staging db:local debug:on
|
||||
```
|
||||
|
||||
For more details see [the repo](https://git.pleshevski.ru/pleshevskiy/vnetod)
|
||||
|
||||
# License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See [COPYING] to see the full text.
|
||||
|
||||
[COPYING]: https://git.pleshevski.ru/pleshevskiy/vnetod/src/branch/main/COPYING
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1659190188,
|
||||
"narHash": "sha256-LudYrDFPFaQMW0l68TYkPWRPKmqpxIFU1nWfylIp9AQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a3fddd46a7f3418d7e3940ded94701aba569161d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
let
|
||||
inherit (builtins) fromTOML readFile substring;
|
||||
|
||||
cargoToml = fromTOML (readFile ./Cargo.toml);
|
||||
version = "${cargoToml.package.version}+${substring 0 8 self.lastModifiedDate}.${self.shortRev or "dirty"}";
|
||||
|
||||
mkVnetod = { lib, rustPlatform, vnetodFeatures ? [ ], ... }:
|
||||
rustPlatform.buildRustPackage {
|
||||
name = "vnetod-${version}";
|
||||
|
||||
src = lib.cleanSource ./.;
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
|
||||
buildFeatures = vnetodFeatures;
|
||||
|
||||
doCheck = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
overlays = {
|
||||
minimal = final: prev: {
|
||||
vnetod = final.callPackage mkVnetod { };
|
||||
};
|
||||
default = final: prev: {
|
||||
vnetod = final.callPackage mkVnetod {
|
||||
vnetodFeatures = [ "color" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
// flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
|
||||
vnetod = pkgs.callPackage mkVnetod { vnetodFeatures = [ "color" ]; };
|
||||
minimalVnetod = pkgs.callPackage mkVnetod { };
|
||||
|
||||
docker = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "pleshevskiy/vnetod";
|
||||
tag = cargoToml.package.version;
|
||||
config = {
|
||||
Volumes."/data" = { };
|
||||
WorkingDir = "/data";
|
||||
Entrypoint = [ "${vnetod}/bin/vnetod" ];
|
||||
};
|
||||
};
|
||||
|
||||
mkApp = prog: {
|
||||
type = "app";
|
||||
program = "${vnetod}/bin/vnetod";
|
||||
};
|
||||
in
|
||||
{
|
||||
apps = {
|
||||
default = mkApp vnetod;
|
||||
minimal = mkApp minimalVnetod;
|
||||
};
|
||||
|
||||
packages = {
|
||||
inherit docker vnetod;
|
||||
default = vnetod;
|
||||
minimal = minimalVnetod;
|
||||
};
|
||||
|
||||
devShell = pkgs.mkShell {
|
||||
packages = with pkgs; [ cargo rustc rustfmt clippy rust-analyzer ];
|
||||
RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc;
|
||||
};
|
||||
});
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
(import (
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
|
||||
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
|
||||
) {
|
||||
src = ./.;
|
||||
}).shellNix
|
|
@ -0,0 +1,124 @@
|
|||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
//!
|
||||
//! vnetod is free software: you can redistribute it and/or modify
|
||||
//! it under the terms of the GNU General Public License as published by
|
||||
//! the Free Software Foundation, either version 3 of the License, or
|
||||
//! (at your option) any later version.
|
||||
//!
|
||||
//! vnetod is distributed in the hope that it will be useful,
|
||||
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//! GNU General Public License for more details.
|
||||
//!
|
||||
//! You should have received a copy of the GNU General Public License
|
||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
|
||||
pub mod switch;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(
|
||||
author,
|
||||
version,
|
||||
about = "\
|
||||
Dotenv state switcher
|
||||
---------------------------------------------------------------------
|
||||
vnetod Copyright (C) 2022 Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
This program comes with ABSOLUTELY NO WARRANTY;
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions;
|
||||
---------------------------------------------------------------------
|
||||
"
|
||||
)]
|
||||
pub struct Args {
|
||||
#[clap(
|
||||
short = 'f',
|
||||
long,
|
||||
default_value = ".env",
|
||||
env = "VNETOD_FILE",
|
||||
help = "Change source file with environment variables."
|
||||
)]
|
||||
pub file: PathBuf,
|
||||
|
||||
#[clap(
|
||||
short = 'o',
|
||||
long,
|
||||
help = "Change output file with modified environment variables. It uses `file` argument by default if the output is not specified."
|
||||
)]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
help = "Writes modified environment variables to stdout instead of a file. `Output` argument will be ignored."
|
||||
)]
|
||||
pub dry_run: bool,
|
||||
|
||||
#[clap(
|
||||
value_parser,
|
||||
help = "Environment varible sections that will be enabled."
|
||||
)]
|
||||
pub sections: Vec<String>,
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
#[clap(
|
||||
long,
|
||||
value_enum,
|
||||
default_value = "auto",
|
||||
help = "This flag controls when to use colors.",
|
||||
long_help = "
|
||||
This flag controls when to use colors. The default setting is 'auto', which
|
||||
means vnetod will try to guess when to use colors. For example, if vnetod is
|
||||
printing to a terminal, then it will use colors, but if it is redirected to a
|
||||
file or a pipe, then it will suppress color output. vnetod will suppress color
|
||||
output in some other circumstances as well. For example, if the TERM
|
||||
environment variable is not set or set to 'dumb', then vnetod will not use
|
||||
colors.
|
||||
|
||||
The possible values for this flag are:
|
||||
|
||||
never Colors will never be used.
|
||||
auto The default. vnetod tries to be smart.
|
||||
always Colors will always be used regardless of where output is sent.
|
||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).
|
||||
"
|
||||
)]
|
||||
pub color: ColorVariant,
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||
pub enum ColorVariant {
|
||||
Auto,
|
||||
Always,
|
||||
Ansi,
|
||||
Never,
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
impl From<ColorVariant> for termcolor::ColorChoice {
|
||||
fn from(col: ColorVariant) -> Self {
|
||||
match col {
|
||||
ColorVariant::Never => Self::Never,
|
||||
ColorVariant::Always => Self::Always,
|
||||
ColorVariant::Ansi => Self::AlwaysAnsi,
|
||||
ColorVariant::Auto => {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
// Otherwise let's `termcolor` decide by inspecting the environment. From the [doc]:
|
||||
// - If `NO_COLOR` is set to any value, then colors will be suppressed.
|
||||
// - If `TERM` is set to dumb, then colors will be suppressed.
|
||||
// - In non-Windows environments, if `TERM` is not set, then colors will be suppressed.
|
||||
//
|
||||
// [doc]: https://github.com/BurntSushi/termcolor#automatic-color-selection
|
||||
Self::Auto
|
||||
} else {
|
||||
// Colors should be deactivated if the terminal is not a tty.
|
||||
Self::Never
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
//!
|
||||
//! vnetod is free software: you can redistribute it and/or modify
|
||||
//! it under the terms of the GNU General Public License as published by
|
||||
//! the Free Software Foundation, either version 3 of the License, or
|
||||
//! (at your option) any later version.
|
||||
//!
|
||||
//! vnetod is distributed in the hope that it will be useful,
|
||||
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//! GNU General Public License for more details.
|
||||
//!
|
||||
//! You should have received a copy of the GNU General Public License
|
||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
use crate::{cli::Args, domain};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
OpenFile,
|
||||
WriteFile,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::OpenFile => f.write_str("Cannot open file"),
|
||||
Error::WriteFile => f.write_str("Cannot write file"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
pub fn execute(args: &Args) -> Result<(), Error> {
|
||||
let content = std::fs::read_to_string(&args.file).map_err(|_| Error::OpenFile)?;
|
||||
|
||||
if args.dry_run {
|
||||
println!("Your file will be changed to the following")
|
||||
}
|
||||
|
||||
let fs_writer = (!args.dry_run)
|
||||
.then(|| {
|
||||
File::create(args.output.as_ref().unwrap_or(&args.file)).map_err(|_| Error::OpenFile)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
let color = args.color.clone();
|
||||
|
||||
println!();
|
||||
|
||||
let new_content = domain::switch::execute(domain::switch::Request {
|
||||
content: &content.trim(),
|
||||
sections: &args.sections,
|
||||
on_line: Some(Box::new(move |line| {
|
||||
#[cfg(feature = "color")]
|
||||
print_line(line, color.clone());
|
||||
#[cfg(not(feature = "color"))]
|
||||
print!("{}", line)
|
||||
})),
|
||||
});
|
||||
|
||||
println!();
|
||||
|
||||
if let Some(mut fs_writer) = fs_writer {
|
||||
fs_writer
|
||||
.write_all(new_content.as_bytes())
|
||||
.map_err(|_| Error::WriteFile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
fn print_line(line: &String, color: crate::cli::ColorVariant) {
|
||||
let mut stdout = StandardStream::stdout(color.into());
|
||||
let color = line
|
||||
.starts_with("###")
|
||||
.then_some(Color::Yellow)
|
||||
.or_else(|| (!line.starts_with("#")).then_some(Color::Green));
|
||||
stdout.set_color(ColorSpec::new().set_fg(color)).ok();
|
||||
write!(&mut stdout, "{}", line).unwrap();
|
||||
stdout.reset().ok();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
//!
|
||||
//! vnetod is free software: you can redistribute it and/or modify
|
||||
//! it under the terms of the GNU General Public License as published by
|
||||
//! the Free Software Foundation, either version 3 of the License, or
|
||||
//! (at your option) any later version.
|
||||
//!
|
||||
//! vnetod is distributed in the hope that it will be useful,
|
||||
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//! GNU General Public License for more details.
|
||||
//!
|
||||
//! You should have received a copy of the GNU General Public License
|
||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
|
||||
pub mod switch;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Section {
|
||||
namespace: Option<String>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
namespace: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_namespace(namespace: &str, name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
namespace: Some(namespace.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(s: &str) -> Self {
|
||||
let s = s.trim();
|
||||
s.split_once(':')
|
||||
.map_or_else(|| Self::new(s), |(ns, name)| Self::with_namespace(ns, name))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SectionInfo {
|
||||
enable_variable: bool,
|
||||
disable_variable: bool,
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
//!
|
||||
//! vnetod is free software: you can redistribute it and/or modify
|
||||
//! it under the terms of the GNU General Public License as published by
|
||||
//! the Free Software Foundation, either version 3 of the License, or
|
||||
//! (at your option) any later version.
|
||||
//!
|
||||
//! vnetod is distributed in the hope that it will be useful,
|
||||
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//! GNU General Public License for more details.
|
||||
//!
|
||||
//! You should have received a copy of the GNU General Public License
|
||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
|
||||
use super::{Section, SectionInfo};
|
||||
|
||||
pub type OnLineFn = Box<dyn Fn(&String)>;
|
||||
|
||||
pub struct Request<'args> {
|
||||
pub content: &'args str,
|
||||
pub sections: &'args [String],
|
||||
pub on_line: Option<OnLineFn>,
|
||||
}
|
||||
|
||||
pub fn execute(req: Request) -> String {
|
||||
let choose_sections = req
|
||||
.sections
|
||||
.iter()
|
||||
.map(|s| Section::parse(s.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut current_section: Option<SectionInfo> = None;
|
||||
let mut new_content = String::new();
|
||||
|
||||
for line in req.content.split_inclusive('\n') {
|
||||
let new_line = if is_section_end(line) {
|
||||
current_section = None;
|
||||
line.to_string()
|
||||
} else if let Some(section_info) = line.strip_prefix("### ") {
|
||||
current_section = section_info.split_whitespace().next().map(|r| {
|
||||
let current_sections = r.split(',').map(Section::parse).collect::<Vec<_>>();
|
||||
SectionInfo {
|
||||
enable_variable: should_enable_variable(&choose_sections, ¤t_sections),
|
||||
disable_variable: should_disable_variable(&choose_sections, ¤t_sections),
|
||||
}
|
||||
});
|
||||
line.to_string()
|
||||
} else if let Some(section_info) = current_section.as_ref() {
|
||||
let trimmed_line = line.trim_start_matches(['#', ' ']);
|
||||
let is_var = is_variable(trimmed_line);
|
||||
if is_var && section_info.enable_variable {
|
||||
String::from(trimmed_line)
|
||||
} else if is_var && section_info.disable_variable {
|
||||
format!("# {}", trimmed_line)
|
||||
} else {
|
||||
line.to_string()
|
||||
}
|
||||
} else {
|
||||
line.to_string()
|
||||
};
|
||||
|
||||
new_content.push_str(&new_line);
|
||||
if let Some(on_line) = req.on_line.as_ref() {
|
||||
on_line(&new_line);
|
||||
}
|
||||
}
|
||||
|
||||
new_content
|
||||
}
|
||||
|
||||
fn is_variable(trimmed_line: &str) -> bool {
|
||||
trimmed_line
|
||||
.chars()
|
||||
.filter(|ch| (*ch != '_' && ch.is_ascii_punctuation()) || ch.is_whitespace())
|
||||
.enumerate()
|
||||
.find(|(_, ch)| *ch == '=')
|
||||
.map_or(false, |(i, _)| i == 0)
|
||||
}
|
||||
|
||||
fn is_section_end(line: &str) -> bool {
|
||||
line.trim().is_empty()
|
||||
}
|
||||
|
||||
fn should_enable_variable(choose_sections: &[Section], current_sections: &[Section]) -> bool {
|
||||
let cross_sections = choose_sections
|
||||
.iter()
|
||||
.filter(|s| {
|
||||
s.namespace.is_some()
|
||||
&& current_sections
|
||||
.iter()
|
||||
.any(|s2| s.namespace == s2.namespace)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if cross_sections.is_empty() {
|
||||
choose_sections.iter().any(|s| {
|
||||
if s.namespace.is_none() {
|
||||
current_sections.iter().any(|s2| s.name == s2.name)
|
||||
} else {
|
||||
current_sections.contains(s)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cross_sections.iter().any(|s| current_sections.contains(s))
|
||||
}
|
||||
}
|
||||
|
||||
fn should_disable_variable(choose_sections: &[Section], current_sections: &[Section]) -> bool {
|
||||
choose_sections.is_empty()
|
||||
|| choose_sections.iter().any(|s| s.namespace.is_none())
|
||||
|| choose_sections
|
||||
.iter()
|
||||
.filter(|s| s.namespace.is_some())
|
||||
.any(|s| {
|
||||
current_sections
|
||||
.iter()
|
||||
.any(|s2| s.namespace == s2.namespace && s.name != s2.name)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const BASE_ENV: &str = include_str!("../../test_data/base_env");
|
||||
|
||||
fn make_test(input: &str, expected_output: &str, sections: Vec<&str>) {
|
||||
let output_data = execute(Request {
|
||||
content: input,
|
||||
sections: §ions.into_iter().map(String::from).collect::<Vec<_>>(),
|
||||
on_line: None,
|
||||
});
|
||||
assert_eq!(
|
||||
output_data.lines().collect::<Vec<_>>(),
|
||||
expected_output.lines().collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_disable_all_sections() {
|
||||
make_test(include_str!("../../test_data/all_env"), BASE_ENV, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_enable_local_sections() {
|
||||
make_test(
|
||||
BASE_ENV,
|
||||
include_str!("../../test_data/should_enable_local"),
|
||||
vec!["local"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_enable_staging_sections() {
|
||||
make_test(
|
||||
BASE_ENV,
|
||||
include_str!("../../test_data/should_enable_staging"),
|
||||
vec!["staging"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_use_debug_in_staging_section() {
|
||||
make_test(
|
||||
BASE_ENV,
|
||||
include_str!("../../test_data/should_use_debug_in_staging"),
|
||||
vec!["staging", "debug:on"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_use_staging_db_in_local_section() {
|
||||
make_test(
|
||||
BASE_ENV,
|
||||
include_str!("../../test_data/should_use_staging_db_in_local"),
|
||||
vec!["local", "db:staging"],
|
||||
);
|
||||
}
|
||||
|
||||
mod utils {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_not_enable_variables() {
|
||||
assert!(!should_enable_variable(
|
||||
&[Section::new("local")],
|
||||
&[Section::new("staging")]
|
||||
));
|
||||
assert!(!should_enable_variable(
|
||||
&[Section::with_namespace("db", "local")],
|
||||
&[Section::new("local")]
|
||||
));
|
||||
assert!(!should_enable_variable(
|
||||
&[Section::with_namespace("db", "local")],
|
||||
&[Section::with_namespace("db", "staging")]
|
||||
));
|
||||
assert!(!should_enable_variable(
|
||||
&[
|
||||
Section::new("staging"),
|
||||
Section::with_namespace("debug", "on")
|
||||
],
|
||||
&[
|
||||
Section::with_namespace("debug", "off"),
|
||||
Section::new("staging")
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_enable_variables() {
|
||||
assert!(should_enable_variable(
|
||||
&[Section::new("local")],
|
||||
&[Section::new("local")]
|
||||
));
|
||||
assert!(should_enable_variable(
|
||||
&[Section::new("local")],
|
||||
&[Section::with_namespace("db", "local")]
|
||||
));
|
||||
assert!(should_enable_variable(
|
||||
&[Section::with_namespace("db", "local")],
|
||||
&[Section::with_namespace("db", "local")]
|
||||
));
|
||||
assert!(should_enable_variable(
|
||||
&[
|
||||
Section::new("local"),
|
||||
Section::with_namespace("debug", "on")
|
||||
],
|
||||
&[
|
||||
Section::with_namespace("debug", "on"),
|
||||
Section::new("staging")
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_disable_variables() {
|
||||
assert!(!should_disable_variable(
|
||||
&[Section::with_namespace("debug", "on")],
|
||||
&[Section::new("local")]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_disable_variables() {
|
||||
assert!(should_disable_variable(
|
||||
&[Section::new("local")],
|
||||
&[Section::with_namespace("debug", "off")]
|
||||
));
|
||||
assert!(should_disable_variable(
|
||||
&[],
|
||||
&[Section::with_namespace("debug", "off")]
|
||||
));
|
||||
assert!(should_disable_variable(
|
||||
&[Section::with_namespace("debug", "on")],
|
||||
&[Section::with_namespace("debug", "off")]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_check_line_on_variable() {
|
||||
let test_cases = [
|
||||
("VAR=10", true),
|
||||
("THIS_IS_MY_VAR='hello world'", true),
|
||||
("hello world", false),
|
||||
("staging/production", false),
|
||||
("staging/production=value", false),
|
||||
];
|
||||
|
||||
for (input, expected) in test_cases {
|
||||
assert_eq!(is_variable(input), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
80
src/main.rs
80
src/main.rs
|
@ -1,4 +1,4 @@
|
|||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
||||
//!
|
||||
//! vnetod is free software: you can redistribute it and/or modify
|
||||
//! it under the terms of the GNU General Public License as published by
|
||||
|
@ -13,75 +13,17 @@
|
|||
//! You should have received a copy of the GNU General Public License
|
||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||
//!
|
||||
#![deny(clippy::all, clippy::pedantic)]
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
};
|
||||
mod cli;
|
||||
mod domain;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
fn main() -> Result<(), cli::switch::Error> {
|
||||
let cli = cli::Args::parse();
|
||||
|
||||
cli::switch::execute(&cli)?;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
match std::env::args().nth(1) {
|
||||
Some(name) => change_env_layout(&name)?,
|
||||
_ => print_help(),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn change_env_layout(name: &str) -> Result<(), Error> {
|
||||
let content = std::fs::read_to_string(".env").map_err(|_| Error::OpenFile)?;
|
||||
|
||||
let mut writer = File::create(".env")
|
||||
.map_err(|_| Error::OpenFile)
|
||||
.map(BufWriter::new)?;
|
||||
|
||||
let mut current_section_name: Option<String> = None;
|
||||
|
||||
for line in content.split_inclusive('\n') {
|
||||
let new_line = if is_section_end(line) {
|
||||
current_section_name = None;
|
||||
line.to_string()
|
||||
} else if let Some(section_info) = line.strip_prefix("### ") {
|
||||
current_section_name = section_info.split_whitespace().next().map(String::from);
|
||||
line.to_string()
|
||||
} else if let Some(cur_name) = current_section_name.clone() {
|
||||
if cur_name == name {
|
||||
String::from(line.trim_start_matches(['#', ' ']))
|
||||
} else {
|
||||
format!("# {}", line.trim_start_matches(['#', ' ']))
|
||||
}
|
||||
} else {
|
||||
line.to_string()
|
||||
};
|
||||
|
||||
writer
|
||||
.write_all(new_line.as_bytes())
|
||||
.map_err(|_| Error::WriteData)?;
|
||||
}
|
||||
|
||||
writer.flush().map_err(|_| Error::WriteData)
|
||||
}
|
||||
|
||||
fn is_section_end(line: &str) -> bool {
|
||||
line.trim().is_empty()
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
eprintln!(
|
||||
"\
|
||||
USAGE
|
||||
vnetod [STATE_NAME] - switch environment variable state by name
|
||||
|
||||
---
|
||||
vnetod Copyright (C) 2022 Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
"
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
OpenFile,
|
||||
WriteData,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
VITE_SITE_URL='http://localhost:3000'
|
||||
CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
DEBUG=1
|
||||
### debug:off,staging
|
||||
DEBUG=
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
# VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
# DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
# DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
# VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
# VITE_SITE_URL='http://localhost:3000'
|
||||
# CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
# VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
# VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
# CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
# DEBUG=1
|
||||
### debug:off,staging
|
||||
# DEBUG=
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
# DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
VITE_SITE_URL='http://localhost:3000'
|
||||
CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
# VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
# VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
# CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
DEBUG=1
|
||||
### debug:off,staging
|
||||
# DEBUG=
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
# DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
# VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
# VITE_SITE_URL='http://localhost:3000'
|
||||
# CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
# DEBUG=1
|
||||
### debug:off,staging
|
||||
DEBUG=
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
# DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
# VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
# VITE_SITE_URL='http://localhost:3000'
|
||||
# CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
DEBUG=1
|
||||
### debug:off,staging
|
||||
# DEBUG=
|
|
@ -0,0 +1,37 @@
|
|||
VITE_STRIPE_PK=
|
||||
VITE_SENTRY_ENABLED=
|
||||
VITE_SENTRY_DSN=
|
||||
### local,staging
|
||||
# staging/production
|
||||
VITE_SENTRY_ENV=staging
|
||||
### prod
|
||||
# VITE_SENTRY_ENV=production
|
||||
|
||||
### db:local
|
||||
# DATABASE_URL='postgres://user:password@localhost:5432/core'
|
||||
### db:staging
|
||||
DATABASE_URL='postgres://user:password@localhost:5555/core'
|
||||
### db:prod
|
||||
# DATABASE_URL='postgres://user:password@localhost:6666/core'
|
||||
|
||||
CYPRESS_CRED_EMAIL='owner@cypress.test'
|
||||
CYPRESS_CRED_PASSWORD='12345678'
|
||||
CYPRESS_TEST_PRIVATE_TOKEN='12345678'
|
||||
|
||||
### local
|
||||
VITE_API_BASE_URL='http://localhost:1337/api'
|
||||
VITE_SITE_URL='http://localhost:3000'
|
||||
CYPRESS_API_BASE_URL='http://localhost:1337/api'
|
||||
|
||||
# this variable will not switch
|
||||
GRAPHQL_PLAYGROUND=1
|
||||
|
||||
### staging
|
||||
# VITE_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
# VITE_SITE_URL='https://www.develop.staging.example.com'
|
||||
# CYPRESS_API_BASE_URL='https://app.develop.staging.example.com/api'
|
||||
|
||||
### debug:on,local
|
||||
DEBUG=1
|
||||
### debug:off,staging
|
||||
# DEBUG=
|
Loading…
Reference in New Issue