Compare commits
25 Commits
v3.7.1
...
fb8bfa6a9c
Author | SHA1 | Date | |
---|---|---|---|
fb8bfa6a9c | |||
79adf18d9a | |||
7ce71dfca8 | |||
3bcd4f3f6d | |||
d4ef1c70e0 | |||
c9e107d2fd | |||
b6b4f2e5b8 | |||
432593a0f3 | |||
6629caf459 | |||
8bcc99b898 | |||
05694ed022 | |||
67bd712590 | |||
a90482de8c | |||
261bbee1a4 | |||
fcf492c661 | |||
a200c1eb22 | |||
f3e70c61cb | |||
72e831da33 | |||
a03885e8db | |||
6cc10e3f4f | |||
4a1208942d | |||
91780024f0 | |||
3ca4fe0c65 | |||
e6dfff255c | |||
15942bb08d |
16
.github/workflows/test.yaml
vendored
16
.github/workflows/test.yaml
vendored
@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Random Test
|
||||
on: push # yamllint disable-line rule:truthy
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: run a scwrypts thing
|
||||
uses: wrynegade/scwrypts@main
|
||||
with:
|
||||
scwrypt: --name hello-world --group scwrypts --type py
|
||||
args: --message "hello from github actions ci <3"
|
||||
|
||||
- name: cat config stuff
|
||||
shell: bash
|
||||
run: ls ~/.config/scwrypts; cat ~/.config/scwrypts/*
|
@ -6,6 +6,9 @@ In modern developer / dev-ops workflows, scripts require a complex configuration
|
||||
Without a better solution, the developer is cursed to copy lines-upon-lines of variables into terminals, create random text artifacts, or maybe even commit secure credentials into source.
|
||||
Scwrypts leverages ZSH to give hot-key access to run scripts in such environments.
|
||||
|
||||
## Major Version Upgrade Notice
|
||||
|
||||
Please refer to [Version 3 to Version 4 Upgrade Path](./docs/upgrade/v3-to-v4.md) when upgrading from scwrypts v3 to scwrypts v4!
|
||||
|
||||
## Dependencies
|
||||
Due to the wide variety of resources used by scripting libraries, the user is expected to manually resolve dependencies.
|
||||
@ -48,6 +51,8 @@ There are a few notable changes to this runtime:
|
||||
- User yes/no prompts will **always be YES**
|
||||
- Other user input will default to an empty string
|
||||
- Logs will not be captured
|
||||
- Setting the environment variable `SCWRYPTS_GROUP_LOADER__[a-z_]\+` will source the file indicated in the variable (this allows custom groups without needing to modify the `config.zsh` directly)
|
||||
- In GitHub actions, `*.scwrypts.zsh` groups are detected automatically from the `$GITHUB_WORKSPACE`; set `SCWRYPTS_GITHUB_NO_AUTOLOAD=true` to disable
|
||||
|
||||
|
||||
## Contributing
|
||||
|
@ -27,7 +27,7 @@ runs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: wrynegade/scwrypts
|
||||
path: $HOME/scwrypts
|
||||
path: ./wrynegade/scwrypts
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
- name: check dependencies
|
||||
@ -43,7 +43,7 @@ runs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes zsh fzf ripgrep
|
||||
|
||||
for D in $($HOME/scwrypts/scwrypts -n --name check-all-dependencies --group ci --type zsh)
|
||||
for D in $($GITHUB_WORKSPACE/wrynegade/scwrypts/scwrypts -n --name check-all-dependencies --group ci --type zsh)
|
||||
do
|
||||
echo "--- installing $D ---"
|
||||
( sudo apt-get install --yes $D; exit 0; )
|
||||
@ -51,7 +51,7 @@ runs:
|
||||
} > $HOME/.scwrypts.apt-get.log 2>&1
|
||||
|
||||
echo "updating virtual dependencies"
|
||||
$HOME/scwrypts/scwrypts -n \
|
||||
$GITHUB_WORKSPACE/wrynegade/scwrypts/scwrypts -n \
|
||||
--name scwrypts/virtualenv/update-all \
|
||||
--group scwrypts \
|
||||
--type zsh \
|
||||
@ -65,4 +65,4 @@ runs:
|
||||
env:
|
||||
CI: true
|
||||
SCWRYPTS_ENV: ${{ inputs.scwrypts-env }}
|
||||
run: $HOME/scwrypts/scwrypts ${{inputs.scwrypt}} -- ${{inputs.args}} || exit 1
|
||||
run: $GITHUB_WORKSPACE/wrynegade/scwrypts/scwrypts ${{inputs.scwrypt}} -- ${{inputs.args}} || exit 1
|
||||
|
254
docs/upgrade/v3-to-v4.md
Normal file
254
docs/upgrade/v3-to-v4.md
Normal file
@ -0,0 +1,254 @@
|
||||
# Scwrypts Upgrade v3 to v4 Notes
|
||||
|
||||
Scwrypts v4 brings a big update to the *runstring for `zsh`-type scwrypts*.
|
||||
I've found some of the boilerplate required by each individual script to be confusing and terse, and the goal here is to make it easier and safer to write and run scwrypts in this critical format.
|
||||
|
||||
Jump to [Technical Bits](#technical-bits) if you just want to get started with migration steps.
|
||||
The actual migration _should take less than a minute per script_.
|
||||
This document is deliberately verbose for future reference when I don't remember how any of this works.
|
||||
|
||||
## Ideology and History
|
||||
|
||||
Originally (scwrypts v2 and below) wanted to preserve the direct-use of individual scwrypts.
|
||||
In those versions, executable files could be executed directly (outside of the `scwrypts` function) and still operate with minimal, unwanted consequences.
|
||||
This resulted in a rigid structure which made code-sharing difficult at small scales and untenable in many non-trivial cases.
|
||||
|
||||
Scwrypts v3, begrudgingly introduced a pseudo-import syntax with `use`.
|
||||
This sought to combat the issues of code-sharing and open up the structure of executable scwrypts to the scwrypts-writer.
|
||||
Beyond just clarity, this allowed external libraries to be written and cross-referenced.
|
||||
Although "importing" is an odd (anti?)feature to shell scripting, the way libraries could be written and reused was too helpful and I succumbed to write the `import.driver.zsh` module.
|
||||
|
||||
|
||||
Throughout v3, I tried to maintain the "executability" of individual scwrypts.
|
||||
It's ugly though.
|
||||
Every individual scwrypt relies on `import.driver.zsh` and the context set up by the `scwrypts` executable.
|
||||
While you _could_ run the executable file directly, it would misbehave at-best and fail pretty reliably.
|
||||
|
||||
So... here's v4!
|
||||
Scwrypts v4 accepts the reality that, although `zsh` scwrypts are zsh, they do not stand alone without the proper context setup provided by `scwrypts`.
|
||||
To improve usability, I've abstracted much of the boilerplate so you never have to see it.
|
||||
I've injected safety mechanisms like `--help` arguments and utility mechanisms like flag separation (`-abc` is really `-a -b -c`) into all v4 zsh scwrypts.
|
||||
|
||||
You don't have to worry about checking the context, v4 does that for you!
|
||||
|
||||
You don't have to worry about execution, v4 does that for you!
|
||||
|
||||
So!
|
||||
|
||||
Are you coupling your zsh scripts to `scwrypts` when you write them? Yes.
|
||||
Is that a bad thing? I don't think so.
|
||||
Shell-scripting is such a critical coupler to real-life systems.
|
||||
High-risk-high-impact to SLAs means we cannot allow context mistakes by sysadmins and users.
|
||||
Reusability between local machine, cloud runtime, and CI pipelines is a must.
|
||||
And if you have a need to reign all that in to single, isolated executable files...
|
||||
|
||||
...then good luck <3
|
||||
|
||||
## Technical Bits
|
||||
|
||||
Big idea: let's get rid of v3 boilerplate and make things easy.
|
||||
|
||||
### Your executable must be in a MAIN function
|
||||
|
||||
A main function in shell scripts?
|
||||
Weird!
|
||||
Don't worry, it's easy.
|
||||
|
||||
Take your original scwrypt, and slap the executable stuff into a function called `MAIN` (yes, it must be _exactly_, all-caps `MAIN`):
|
||||
|
||||
```diff
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=(dep-function-a dep-function-b)
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use do/awesome/stuff --group my-custom-library
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
- echo "do some stuff here"
|
||||
- # ... etc ...
|
||||
- SUCCESS "completed the stuff"
|
||||
+ MAIN() {
|
||||
+ echo "do some stuff here"
|
||||
+ # ... etc ...
|
||||
+ SUCCESS "completed the stuff
|
||||
+ }
|
||||
```
|
||||
|
||||
**Don't invoke the function!**
|
||||
Scwrypts will now do that on your behalf.
|
||||
I've already written many scwrypts which _almost_ used this syntax.
|
||||
All I had to do in this case was delete the function invocation at the end:
|
||||
|
||||
```diff
|
||||
# ... top boilerplate ...
|
||||
MAIN() {
|
||||
SUCCESS "look at me I'm so cool I already wrote this in a main function"
|
||||
}
|
||||
-
|
||||
- #####################################################################
|
||||
- MAIN $@
|
||||
```
|
||||
|
||||
Again, **do not invoke the function**. Just name it `MAIN` and you're good-to-go!
|
||||
|
||||
### Great news!
|
||||
|
||||
Great news!
|
||||
We have finished with *all of the necessary steps* to migrate to v4!
|
||||
Easy!
|
||||
|
||||
While you're here, let's do a couple more things to cleanup your scwrypts (I promise they are also easy and will take less than a few seconds for each)!
|
||||
|
||||
### Remove the boilerplate
|
||||
|
||||
Were you confused by all that garbage at the top?
|
||||
Awesome!
|
||||
Just get rid of any of it you don't use.
|
||||
|
||||
While you _probably_ will still need whatever dependencies you already defined, feel free to get rid of empty config lists like `DEPENDENCIES+=()`.
|
||||
For non-empty lists, the syntax remains the same (use the `+=` and make sure it's an array-type `()` just like before!)
|
||||
|
||||
Also you can ditch the `CHECK_ENVIRONMENT`.
|
||||
While it won't hurt, v4 already does this, so just get rid of it.
|
||||
Here's my recommended formatting:
|
||||
```diff
|
||||
#!/bin/zsh
|
||||
- #####################################################################
|
||||
DEPENDENCIES+=(dep-function-a dep-function-b)
|
||||
- REQUIRED_ENV+=()
|
||||
|
||||
use do/awesome/stuff --group my-custom-library
|
||||
-
|
||||
- CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
echo "do some stuff here"
|
||||
# ... etc ...
|
||||
SUCCESS "completed the stuff
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Get rid of `--help` argument processing
|
||||
|
||||
Scwrypts v4 injects the `--help` argument into every zsh scwrypt.
|
||||
So there's no need to process it manually anymore!
|
||||
|
||||
We can now eliminate the help case from any MAIN body or library function:
|
||||
|
||||
```diff
|
||||
MAIN() {
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
# ... a bunch of cases ...
|
||||
- -h | --help ) USAGE; return 0 ;;
|
||||
# ... a bunch of cases ...
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
While you probably weren't doing this, you can also do the same for any logic which splits arguments input like `-abc` which should be read as `-a -b -c`.
|
||||
If you know how to do this, you know how to get rid of it.
|
||||
|
||||
### Write some help docs
|
||||
|
||||
Okay this one might take a little bit of time if you haven't done it already (but this is the last recommended step! hang in there and make your stuff better!).
|
||||
If you _have_ done it already, typically by writing a variable called "USAGE" in your code, maybe consider the _new and improved_ way to write your help strings.
|
||||
|
||||
Returning to our original `MAIN()` example, I'll add some options parsing so we should now look something like this:
|
||||
```sh
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=(dep-function-a dep-function-b)
|
||||
|
||||
use do/awesome/stuff --group my-custom-library
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
local A
|
||||
local B=false
|
||||
local ARGS=()
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
-a | --option-a ) A=$2; shift 1 ;;
|
||||
|
||||
-b | --allow-b ) B=true ;;
|
||||
|
||||
* ) ARGS+=($1) ;;
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
|
||||
echo "A : $A\nB : $B\nARGS : $ARGS"
|
||||
}
|
||||
```
|
||||
|
||||
All we have to do is add some usage variables and we're done!
|
||||
I want to call out a few specific ones:
|
||||
- `USAGE__options` provides descriptions for CLI flags like `-a` `--some-flag` and `-a <some value>` (reminder, you *don't* need '-h, --help' anymore!)
|
||||
- `USAGE__args` provides descriptions for non-flag CLI arguments, where order matters (e.g. `cat file-a file-b ... etc`)
|
||||
- `USAGE__description` provides the human-readable description of what your function does
|
||||
- `USAGE__usage` you probably don't need to adjust this one, but it will be everything after the `--` in the usage-line. Defaults to include `[...options...]`, but I suppose you might want to write `USAGE__usage+=' [...args...]` if you 1) have args and 2) are really specific about your help strings.
|
||||
|
||||
Just add another section to define these values before declaring `MAIN`:
|
||||
```sh
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=(dep-function-a dep-function-b)
|
||||
|
||||
use do/awesome/stuff --group my-custom-library
|
||||
#####################################################################
|
||||
|
||||
USAGE__options='
|
||||
-a, --option-a <string> sets the value of the A variable
|
||||
-b, --allow-b enables the B option
|
||||
'
|
||||
|
||||
# remember there's no specific formatting here, just write it nice
|
||||
USAGE__args='
|
||||
N-args All remaining args are inserted into the ARGS variable
|
||||
'
|
||||
|
||||
USAGE__description="
|
||||
This is my cool example function. It's really neato, but does
|
||||
very little.
|
||||
"
|
||||
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
# ... etc ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Now, when we run `scwrypts my sample -- --help`, we get:
|
||||
```txt
|
||||
usage: scwrypts my sample -- [...options...]
|
||||
|
||||
args:
|
||||
N-args All remaining args are inserted into the ARGS variable
|
||||
|
||||
options:
|
||||
-a, --option-a <string> sets the value of the A variable
|
||||
-b, --allow-b enables the B option
|
||||
|
||||
-h, --help display this message and exit
|
||||
|
||||
This is my cool example function. It's really neato, but does
|
||||
very little.
|
||||
```
|
||||
|
||||
### All done
|
||||
|
||||
No more recommendations at this time.
|
||||
Someday I'll have an auto-formatter and a language server to help with go-to-definition, but that's still for the future.
|
||||
|
||||
Thanks for your time and welcome to v4!
|
@ -1,9 +1,5 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
cd "$SCWRYPTS_ROOT__scwrypts/"
|
||||
|
||||
@ -35,6 +31,3 @@ MAIN() {
|
||||
echo $DEPENDENCIES | sed 's/ /\n/g'
|
||||
CHECK_ENVIRONMENT && SUCCESS "all dependencies satisfied"
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -2,30 +2,45 @@
|
||||
command -v compdef >/dev/null 2>&1 || return 0
|
||||
#####################################################################
|
||||
|
||||
_k() {
|
||||
local C=$(k meta get context)
|
||||
local NS=$(k meta get namespace)
|
||||
for CLI in kubectl helm flux
|
||||
do
|
||||
eval "_${CLI[1]}() {
|
||||
local SUBSESSION=0
|
||||
echo \${words[2]} | grep -q '^[0-9]\\+$' && SUBSESSION=\${words[2]}
|
||||
|
||||
local KUBEWORDS=(kubectl)
|
||||
[ $C ] && KUBEWORDS+=(--context $C)
|
||||
[ $NS ] && KUBEWORDS+=(--namespace $NS)
|
||||
local PASSTHROUGH_WORDS=($CLI)
|
||||
[[ \$CURRENT -gt 2 ]] && echo \${words[2]} | grep -qv '^[0-9]\\+$' && {
|
||||
local KUBECONTEXT=\$(k \$SUBSESSION meta get context)
|
||||
local NAMESPACE=\$(k \$SUBSESSION meta get namespace)
|
||||
|
||||
words="$KUBEWORDS ${words[@]:1}"
|
||||
_kubectl
|
||||
}
|
||||
[ \$KUBECONTEXT ] \
|
||||
&& PASSTHROUGH_WORDS+=($([[ $CLI =~ ^helm$ ]] && echo '--kube-context' || echo '--context') \$KUBECONTEXT) \
|
||||
;
|
||||
[ \$NAMESPACE ] \
|
||||
&& PASSTHROUGH_WORDS+=(--namespace \$NAMESPACE) \
|
||||
;
|
||||
}
|
||||
|
||||
compdef _k k
|
||||
local DELIMIT_COUNT=0
|
||||
local WORD
|
||||
for WORD in \${words[@]:1}
|
||||
do
|
||||
case \$WORD in
|
||||
[0-9]* ) continue ;;
|
||||
-- )
|
||||
echo \$words | grep -q 'exec' && ((DELIMIT_COUNT+=1))
|
||||
[[ \$DELIMIT_COUNT -eq 0 ]] && ((DELIMIT_COUNT+=1)) && continue
|
||||
;;
|
||||
esac
|
||||
PASSTHROUGH_WORDS+=(\"\$WORD\")
|
||||
done
|
||||
|
||||
#####################################################################
|
||||
_h() {
|
||||
local C=$(k meta get context)
|
||||
local NS=$(k meta get namespace)
|
||||
echo \"\$words\" | grep -q '\\s\\+$' && PASSTHROUGH_WORDS+=(' ')
|
||||
|
||||
local KUBEWORDS=(kubectl)
|
||||
[ $C ] && KUBEWORDS+=(--context $C)
|
||||
[ $NS ] && KUBEWORDS+=(--namespace $NS)
|
||||
words=\"\$PASSTHROUGH_WORDS\"
|
||||
_$CLI
|
||||
}
|
||||
"
|
||||
|
||||
words="$KUBEWORDS ${words[@]:1}"
|
||||
_helm
|
||||
}
|
||||
compdef _h h
|
||||
compdef _${CLI[1]} ${CLI[1]}
|
||||
done
|
||||
|
@ -3,6 +3,7 @@
|
||||
unalias k h >/dev/null 2>&1
|
||||
k() { _SCWRYPTS_KUBECTL_DRIVER kubectl $@; }
|
||||
h() { _SCWRYPTS_KUBECTL_DRIVER helm $@; }
|
||||
f() { _SCWRYPTS_KUBECTL_DRIVER flux $@; }
|
||||
|
||||
|
||||
_SCWRYPTS_KUBECTL_DRIVER() {
|
||||
@ -57,7 +58,7 @@ _SCWRYPTS_KUBECTL_DRIVER() {
|
||||
"
|
||||
|
||||
local USAGE__description="
|
||||
Provides 'k' (kubectl) and 'h' (helm) shorthands to the respective
|
||||
Provides 'k' (kubectl), 'h' (helm), and 'f' (flux) shorthands to the respective
|
||||
utility. These functions leverage redis and scwrypts environments to
|
||||
allow quick selection of contexts and namespaces usable across all
|
||||
active shell instances.
|
||||
@ -100,18 +101,20 @@ _SCWRYPTS_KUBECTL_DRIVER() {
|
||||
--subsession ) SUBSESSION=$2; shift 1 ;;
|
||||
|
||||
-n | --namespace )
|
||||
echo "TODO: set namespace ('$2')" >&2
|
||||
USER_ARGS+=(--namespace $2); shift 1
|
||||
;;
|
||||
|
||||
-k | --context | --kube-context )
|
||||
echo "TODO: set context ('$2')" >&2
|
||||
[[ $CLI =~ ^helm$ ]] && USER_ARGS+=(--kube-context $2)
|
||||
[[ $CLI =~ ^kubectl$ ]] && USER_ARGS+=(--context $2)
|
||||
_SCWRYPTS_KUBECTL_DRIVER kubectl meta set namespace $2
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-- ) shift 1; break ;;
|
||||
-k | --context | --kube-context )
|
||||
_SCWRYPTS_KUBECTL_DRIVER kubectl meta set context $2
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-- )
|
||||
echo $USER_ARGS | grep -q 'exec' && USER_ARGS+=(--)
|
||||
shift 1
|
||||
break
|
||||
;;
|
||||
|
||||
* )
|
||||
[ ! $CUSTOM_COMMAND ] && {
|
||||
@ -149,6 +152,7 @@ _SCWRYPTS_KUBECTL_DRIVER() {
|
||||
|
||||
[ $CONTEXT ] && [[ $CLI =~ ^helm$ ]] && CLI_ARGS+=(--kube-context $CONTEXT)
|
||||
[ $CONTEXT ] && [[ $CLI =~ ^kubectl$ ]] && CLI_ARGS+=(--context $CONTEXT)
|
||||
[ $CONTEXT ] && [[ $CLI =~ ^flux$ ]] && CLI_ARGS+=(--context $CONTEXT)
|
||||
|
||||
[[ $STRICT -eq 1 ]] && {
|
||||
[ $CONTEXT ] || ERROR "missing kubectl 'context'"
|
||||
|
@ -30,8 +30,15 @@ SCWRYPTS_KUBECTL_CUSTOM_COMMAND_PARSE__meta() {
|
||||
USAGE__args="set (namespace|context)"
|
||||
USAGE__description="interactively set a namespace or context for '$SCWRYPTS_ENV'"
|
||||
case $2 in
|
||||
namespace | context ) USER_ARGS+=($1 $2) ;;
|
||||
namespace | context ) USER_ARGS+=($1 $2 $3); [ $3 ] && shift 1 ;;
|
||||
-h | --help ) HELP=1 ;;
|
||||
'' )
|
||||
: \
|
||||
&& SCWRYPTS_KUBECTL_CUSTOM_COMMAND__meta set context \
|
||||
&& SCWRYPTS_KUBECTL_CUSTOM_COMMAND__meta set namespace \
|
||||
;
|
||||
return $?
|
||||
;;
|
||||
|
||||
* ) ERROR "cannot set '$2'" ;;
|
||||
esac
|
||||
@ -94,7 +101,7 @@ SCWRYPTS_KUBECTL_CUSTOM_COMMAND__meta() {
|
||||
;;
|
||||
|
||||
set )
|
||||
scwrypts -n --name set-$2 --type zsh --group kubectl -- --subsession $SUBSESSION >/dev/null \
|
||||
scwrypts -n --name set-$2 --type zsh --group kubectl -- $3 --subsession $SUBSESSION >/dev/null \
|
||||
&& SUCCESS "$2 set"
|
||||
;;
|
||||
|
||||
|
@ -1,16 +1,7 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use kubectl --group kubectl
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
KUBECTL__GET_CONTEXT
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -1,16 +1,7 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use kubectl --group kubectl
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
KUBECTL__GET_NAMESPACE
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -21,7 +21,6 @@ KUBECTL() {
|
||||
kubectl ${KUBECTL_ARGS[@]} $@
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
|
||||
KUBECTL__GET_CONTEXT() { REDIS get --prefix "current:context"; }
|
||||
@ -87,3 +86,73 @@ KUBECTL__LIST_NAMESPACES() {
|
||||
echo default
|
||||
KUBECTL get namespaces -o name | sed 's/^namespace\///' | sort
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
|
||||
KUBECTL__SERVE() {
|
||||
[ $CONTEXT ] || local CONTEXT=$(KUBECTL__GET_CONTEXT)
|
||||
[ $CONTEXT ] || ERROR 'must configure a context in which to serve'
|
||||
|
||||
[ $NAMESPACE ] || local NAMESPACE=$(KUBECTL__GET_NAMESPACE)
|
||||
[ $NAMESPACE ] || ERROR 'must configure a namespace in which to serve'
|
||||
|
||||
CHECK_ERRORS --no-fail --no-usage || return 1
|
||||
|
||||
[ $SERVICE ] && SERVICE=$(KUBECTL__LIST_SERVICES | jq -c "select (.service == \"$SERVICE\")" || echo $SERVICE)
|
||||
[ $SERVICE ] || local SERVICE=$(KUBECTL__SELECT_SERVICE)
|
||||
[ $SERVICE ] || ERROR 'must provide or select a service'
|
||||
|
||||
KUBECTL__LIST_SERVICES | grep -q "^$SERVICE$"\
|
||||
|| ERROR "no service '$SERVICE' in '$CONFIG/$NAMESPACE'"
|
||||
|
||||
CHECK_ERRORS --no-fail --no-usage || return 1
|
||||
|
||||
##########################################
|
||||
|
||||
SERVICE_PASSWORD="$(KUBECTL__GET_SERVICE_PASSWORD)"
|
||||
KUBECTL__SERVICE_PARSE
|
||||
|
||||
INFO "attempting to serve ${NAMESPACE}/${SERVICE_NAME}:${SERVICE_PORT}"
|
||||
[ $SERVICE_PASSWORD ] && INFO "password : $SERVICE_PASSWORD"
|
||||
|
||||
KUBECTL port-forward service/$SERVICE_NAME $SERVICE_PORT
|
||||
}
|
||||
|
||||
KUBECTL__SELECT_SERVICE() {
|
||||
[ $NAMESPACE ] || local NAMESPACE=$(KUBECTL__GET_NAMESPACE)
|
||||
[ $NAMESPACE ] || return 1
|
||||
|
||||
local SERVICES=$(KUBECTL__LIST_SERVICES)
|
||||
local SELECTED=$({
|
||||
echo "namespace service port"
|
||||
echo $SERVICES \
|
||||
| jq -r '.service + " " + .port' \
|
||||
| sed "s/^/$NAMESPACE /" \
|
||||
;
|
||||
} \
|
||||
| column -t \
|
||||
| FZF 'select a service' --header-lines=1 \
|
||||
| awk '{print $2;}' \
|
||||
)
|
||||
|
||||
echo $SERVICES | jq -c "select (.service == \"$SELECTED\")"
|
||||
}
|
||||
|
||||
KUBECTL__LIST_SERVICES() {
|
||||
KUBECTL get service --no-headers\
|
||||
| awk '{print "{\"service\":\""$1"\",\"ip\":\""$3"\",\"port\":\""$5"\"}"}' \
|
||||
| jq -c 'select (.ip != "None")' \
|
||||
;
|
||||
}
|
||||
|
||||
KUBECTL__GET_SERVICE_PASSWORD() {
|
||||
[ $PASSWORD_SECRET ] && [ $PASSWORD_KEY ] || return 0
|
||||
|
||||
KUBECTL get secret $PASSWORD_SECRET -o jsonpath="{.data.$PASSWORD_KEY}" \
|
||||
| base64 --decode
|
||||
}
|
||||
|
||||
KUBECTL__SERVICE_PARSE() {
|
||||
SERVICE_NAME=$(echo $SERVICE | jq -r .service)
|
||||
SERVICE_PORT=$(echo $SERVICE | jq -r .port | sed 's|/.*$||')
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use redis --group kubectl
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
echo $(REDIS --get-static-definition)
|
||||
MAIN() {
|
||||
echo $(REDIS --get-static-definition)
|
||||
}
|
||||
|
49
plugins/kubectl/serve
Executable file
49
plugins/kubectl/serve
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/zsh
|
||||
use kubectl --group kubectl
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
local USAGE="
|
||||
usage: [service] [...options...]
|
||||
|
||||
args:
|
||||
service (optional) name of the service to forward locally
|
||||
|
||||
options:
|
||||
--context override context
|
||||
--namespace override namespace
|
||||
--subsession REDIS subsession (default 0)
|
||||
|
||||
to show a required password on screen, use both:
|
||||
--password-secret Secret resource
|
||||
--password-key key within Secret's 'data'
|
||||
|
||||
-h, --help show this dialogue and exit
|
||||
"
|
||||
local CONTEXT NAMESPACE SERVICE
|
||||
local SUBSESSION=0
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
--context ) CONTEXT=$2; shift 1 ;;
|
||||
--namespace ) NAMESPACE=$2; shift 1 ;;
|
||||
--subsession ) SUBSESSION=$2; shift 1 ;;
|
||||
|
||||
--password-secret ) PASSWORD_SECRET=$2; shift 1 ;;
|
||||
--password-key ) PASSWORD_KEY=$2; shift 1 ;;
|
||||
|
||||
-h | --help ) USAGE; return 0 ;;
|
||||
|
||||
* )
|
||||
[ $SERVICE ] && ERROR "unexpected argument '$2'"
|
||||
SERVICE=$1
|
||||
;;
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
|
||||
CHECK_ERRORS
|
||||
|
||||
KUBECTL__SERVE
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use kubectl --group kubectl
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
@ -17,6 +11,8 @@ MAIN() {
|
||||
|
||||
options:
|
||||
--subsession REDIS subsession (default 0)
|
||||
|
||||
-h, --help show this dialogue and exit
|
||||
"
|
||||
local CONTEXT
|
||||
local SUBSESSION=0
|
||||
@ -26,6 +22,8 @@ MAIN() {
|
||||
case $1 in
|
||||
--subsession ) SUBSESSION=$2; shift 1 ;;
|
||||
|
||||
-h | --help ) USAGE; return 0 ;;
|
||||
|
||||
* )
|
||||
[ $CONTEXT ] && ERROR "unexpected argument '$2'"
|
||||
CONTEXT=$1
|
||||
@ -41,6 +39,3 @@ MAIN() {
|
||||
|
||||
KUBECTL__SET_CONTEXT $CONTEXT
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -1,11 +1,5 @@
|
||||
#!/bin/zsh
|
||||
#####################################################################
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use kubectl --group kubectl
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
@ -17,6 +11,8 @@ MAIN() {
|
||||
|
||||
options:
|
||||
--subsession REDIS subsession (default 0)
|
||||
|
||||
-h, --help show this dialogue and exit
|
||||
"
|
||||
local NAMESPACE
|
||||
local SUBSESSION=0
|
||||
@ -26,6 +22,8 @@ MAIN() {
|
||||
case $1 in
|
||||
--subsession ) SUBSESSION=$2; shift 1 ;;
|
||||
|
||||
-h | --help ) USAGE; return 0 ;;
|
||||
|
||||
* )
|
||||
[ $NAMESPACE ] && ERROR "unexpected argument '$2'"
|
||||
NAMESPACE=$1
|
||||
@ -41,6 +39,3 @@ MAIN() {
|
||||
|
||||
KUBECTL__SET_NAMESPACE $NAMESPACE
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert csv into json'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert csv into json',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert csv into yaml'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert csv into yaml',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert json into csv'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert json into csv',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert json into yaml'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert json into yaml',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert yaml into csv'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert yaml into csv',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.data.converter import convert
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.data import convert
|
||||
|
||||
|
||||
description = 'convert yaml into json'
|
||||
parse_args = []
|
||||
|
||||
def main(_args, stream):
|
||||
return convert(
|
||||
@ -18,7 +16,5 @@ def main(_args, stream):
|
||||
)
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'convert yaml into json',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,16 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from json import dumps
|
||||
|
||||
from py.lib.fzf import fzf, fzf_tail
|
||||
from py.lib.http import directus
|
||||
from py.lib.scwrypts import execute
|
||||
from scwrypts.fzf import fzf, fzf_tail
|
||||
from scwrypts.http import directus
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
description = 'interactive CLI to get data from directus'
|
||||
|
||||
#####################################################################
|
||||
parse_args = [
|
||||
( ['-c', '--collection'], {
|
||||
"dest" : 'collection',
|
||||
"default" : None,
|
||||
"help" : 'the name of the collection',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-f', '--filters'], {
|
||||
"dest" : 'filters',
|
||||
"default" : None,
|
||||
"help" : 'as a URL-suffix, filters for the query',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-d', '--fields'], {
|
||||
"dest" : 'fields',
|
||||
"default" : None,
|
||||
"help" : 'comma-separated list of fields to include',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-p', '--interactive-prompt'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'interactive',
|
||||
"default" : False,
|
||||
"help" : 'interactively generate filter prompts; implied if no flags are provided',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['--prompt-filters'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'generate_filters_prompt',
|
||||
"default" : False,
|
||||
"help" : '(superceded by -p) only generate filters interactively',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['--prompt-fields'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'generate_fields_prompt',
|
||||
"default" : False,
|
||||
"help" : '(superceded by -p) only generate filters interactively',
|
||||
"required" : False,
|
||||
}),
|
||||
]
|
||||
|
||||
def main(args, stream):
|
||||
if {None} == { args.collection, args.filters, args.fields }:
|
||||
@ -96,50 +135,6 @@ def _get_or_select_fields(args, collection):
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'interactive CLI to get data from directus',
|
||||
parse_args = [
|
||||
( ['-c', '--collection'], {
|
||||
"dest" : 'collection',
|
||||
"default" : None,
|
||||
"help" : 'the name of the collection',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-f', '--filters'], {
|
||||
"dest" : 'filters',
|
||||
"default" : None,
|
||||
"help" : 'as a URL-suffix, filters for the query',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-d', '--fields'], {
|
||||
"dest" : 'fields',
|
||||
"default" : None,
|
||||
"help" : 'comma-separated list of fields to include',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['-p', '--interactive-prompt'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'interactive',
|
||||
"default" : False,
|
||||
"help" : 'interactively generate filter prompts; implied if no flags are provided',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['--prompt-filters'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'generate_filters_prompt',
|
||||
"default" : False,
|
||||
"help" : '(superceded by -p) only generate filters interactively',
|
||||
"required" : False,
|
||||
}),
|
||||
( ['--prompt-fields'], {
|
||||
"action" : 'store_true',
|
||||
"dest" : 'generate_fields_prompt',
|
||||
"default" : False,
|
||||
"help" : '(superceded by -p) only generate filters interactively',
|
||||
"required" : False,
|
||||
}),
|
||||
]
|
||||
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,37 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from json import dumps
|
||||
from sys import stderr
|
||||
|
||||
from py.lib.http import discord
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
#####################################################################
|
||||
|
||||
def main(args, stream):
|
||||
if args.content is None:
|
||||
print(f'reading input from {stream.input.name}', file=stderr)
|
||||
args.content = ''.join(stream.readlines()).strip()
|
||||
|
||||
if len(args.content) == 0:
|
||||
args.content = 'PING'
|
||||
|
||||
response = discord.send_message(**vars(args))
|
||||
|
||||
stream.writeline(dumps({
|
||||
**(response.json() if response.text != '' else {'message': 'OK'}),
|
||||
'scwrypts_metadata': {},
|
||||
}))
|
||||
from scwrypts.http import discord
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'post a message to the indicated discord channel',
|
||||
parse_args = [
|
||||
description = 'post a message to the indicated discord channel'
|
||||
parse_args = [
|
||||
( ['-b', '--body'], {
|
||||
'dest' : 'content',
|
||||
'help' : 'message body',
|
||||
@ -58,4 +35,23 @@ execute(main,
|
||||
'required' : False,
|
||||
}),
|
||||
]
|
||||
)
|
||||
|
||||
def main(args, stream):
|
||||
if args.content is None:
|
||||
print(f'reading input from {stream.input.name}', file=stderr)
|
||||
args.content = ''.join(stream.readlines()).strip()
|
||||
|
||||
if len(args.content) == 0:
|
||||
args.content = 'PING'
|
||||
|
||||
response = discord.send_message(**vars(args))
|
||||
|
||||
stream.writeline(dumps({
|
||||
**(response.json() if response.text != '' else {'message': 'OK'}),
|
||||
'scwrypts_metadata': {},
|
||||
}))
|
||||
|
||||
|
||||
#####################################################################
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,27 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
|
||||
|
||||
def main(args, stream):
|
||||
stream.writeline(args.message)
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'a simple "Hello, World!" program',
|
||||
parse_args = [
|
||||
description = 'a simple "Hello, World!" program'
|
||||
parse_args = [
|
||||
( ['-m', '--message'], {
|
||||
'dest' : 'message',
|
||||
'default' : 'HELLO WORLD',
|
||||
'help' : 'message to print',
|
||||
'required' : False,
|
||||
}),
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
def main(args, stream):
|
||||
stream.writeline(args.message)
|
||||
|
||||
|
||||
#####################################################################
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
1
py/lib/.gitignore
vendored
Normal file
1
py/lib/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist/
|
3
py/lib/README.md
Normal file
3
py/lib/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Python Scwrypts
|
||||
[](https://python.org)
|
||||
<br>
|
@ -1,6 +0,0 @@
|
||||
import py.lib.data
|
||||
import py.lib.fzf
|
||||
import py.lib.http
|
||||
import py.lib.redis
|
||||
import py.lib.scwrypts
|
||||
import py.lib.twilio
|
@ -1 +0,0 @@
|
||||
import py.lib.data.converter
|
@ -1 +0,0 @@
|
||||
from py.lib.fzf.client import fzf, fzf_tail, fzf_head
|
@ -1,5 +0,0 @@
|
||||
from py.lib.http.client import get_request_client
|
||||
|
||||
import py.lib.http.directus
|
||||
import py.lib.http.discord
|
||||
import py.lib.http.linear
|
@ -1,2 +0,0 @@
|
||||
from py.lib.http.directus.client import *
|
||||
from py.lib.http.directus.constant import *
|
@ -1,2 +0,0 @@
|
||||
from py.lib.http.discord.client import *
|
||||
from py.lib.http.discord.send_message import *
|
@ -1 +0,0 @@
|
||||
from py.lib.http.linear.client import *
|
60
py/lib/pyproject.toml
Normal file
60
py/lib/pyproject.toml
Normal file
@ -0,0 +1,60 @@
|
||||
[project]
|
||||
name = 'scwrypts'
|
||||
description = 'scwrypts library and invoker'
|
||||
license = 'GPL-3.0-or-later'
|
||||
|
||||
readme = 'README.md'
|
||||
requires-python = '>=3.10'
|
||||
|
||||
authors = [
|
||||
{ name='yage', email='yage@yage.io' },
|
||||
]
|
||||
|
||||
|
||||
classifiers = [
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||
]
|
||||
|
||||
dynamic = ['version']
|
||||
|
||||
dependencies = [
|
||||
'bpython',
|
||||
'pyfzf',
|
||||
'pyyaml',
|
||||
'redis',
|
||||
'twilio',
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
'pylint',
|
||||
]
|
||||
|
||||
test = [
|
||||
'pytest',
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = 'https://github.com/wrynegade/scwrypts'
|
||||
issues = 'https://github.com/wrynegade/scwrypts/issues'
|
||||
|
||||
[build-system]
|
||||
requires = [
|
||||
'hatchling',
|
||||
'versioningit',
|
||||
]
|
||||
build-backend = 'hatchling.build'
|
||||
|
||||
[tool.hatch.version]
|
||||
source = 'versioningit'
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ['./']
|
||||
|
||||
[tool.versioningit]
|
||||
match = ['v*']
|
||||
|
@ -1 +0,0 @@
|
||||
from py.lib.redis.client import get_client
|
@ -1,6 +1,9 @@
|
||||
from py.lib.scwrypts.execute import execute
|
||||
from py.lib.scwrypts.getenv import getenv
|
||||
from py.lib.scwrypts.interactive import interactive
|
||||
from py.lib.scwrypts.run import run
|
||||
'''
|
||||
scwrypts
|
||||
|
||||
import py.lib.scwrypts.io
|
||||
python library functions and invoker for scwrypts
|
||||
'''
|
||||
|
||||
from .scwrypts.execute import execute
|
||||
from .scwrypts.interactive import interactive
|
||||
from .scwrypts.scwrypts import scwrypts
|
||||
|
1
py/lib/scwrypts/data/__init__.py
Normal file
1
py/lib/scwrypts/data/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .converter import convert
|
@ -4,9 +4,6 @@ import yaml
|
||||
|
||||
|
||||
def convert(input_stream, input_type, output_stream, output_type):
|
||||
if input_type == output_type:
|
||||
raise ValueError('input type and output type are the same')
|
||||
|
||||
data = convert_input(input_stream, input_type)
|
||||
write_output(output_stream, output_type, data)
|
||||
|
75
py/lib/scwrypts/data/test_converter.py
Normal file
75
py/lib/scwrypts/data/test_converter.py
Normal file
@ -0,0 +1,75 @@
|
||||
from io import StringIO
|
||||
#from string import ascii_letters, digits
|
||||
from unittest.mock import patch
|
||||
|
||||
from pytest import raises
|
||||
|
||||
from scwrypts.test import generate
|
||||
|
||||
from .converter import convert
|
||||
|
||||
GENERATE_OPTIONS = {
|
||||
'depth': 1,
|
||||
'minimum': -999999,
|
||||
'maximum': 999999,
|
||||
'dict_key_types': {str, int},
|
||||
'csv_columns_minimum': 10,
|
||||
'csv_columns_maximum': 64,
|
||||
'csv_rows_minimum': 10,
|
||||
'csv_rows_maximum': 64,
|
||||
}
|
||||
|
||||
INPUT_TYPES = {'csv', 'json', 'yaml'}
|
||||
OUTPUT_TYPES = {'csv', 'json', 'yaml'}
|
||||
|
||||
|
||||
def test_convert_to_csv():
|
||||
for input_type in INPUT_TYPES:
|
||||
input_stream = generate(input_type, {
|
||||
**GENERATE_OPTIONS,
|
||||
'data_types': {bool,int,float,str},
|
||||
})
|
||||
|
||||
if isinstance(input_stream, str):
|
||||
input_stream = StringIO(input_stream)
|
||||
|
||||
|
||||
convert(input_stream, input_type, StringIO(), 'csv')
|
||||
|
||||
def test_convert_to_json():
|
||||
for input_type in INPUT_TYPES:
|
||||
input_stream = generate(input_type, GENERATE_OPTIONS)
|
||||
|
||||
if isinstance(input_stream, str):
|
||||
input_stream = StringIO(input_stream)
|
||||
|
||||
convert(input_stream, input_type, StringIO(), 'json')
|
||||
|
||||
def test_convert_to_yaml():
|
||||
for input_type in INPUT_TYPES:
|
||||
input_stream = generate(input_type, GENERATE_OPTIONS)
|
||||
|
||||
if isinstance(input_stream, str):
|
||||
input_stream = StringIO(input_stream)
|
||||
|
||||
convert(input_stream, input_type, StringIO(), 'yaml')
|
||||
|
||||
|
||||
def test_convert_deep_json_to_yaml():
|
||||
input_stream = StringIO(generate('json', {**GENERATE_OPTIONS, 'depth': 4}))
|
||||
convert(input_stream, 'json', StringIO(), 'yaml')
|
||||
|
||||
def test_convert_deep_yaml_to_json():
|
||||
input_stream = generate('yaml', {**GENERATE_OPTIONS, 'depth': 4})
|
||||
convert(input_stream, 'yaml', StringIO(), 'json')
|
||||
|
||||
|
||||
def test_convert_output_unsupported():
|
||||
for input_type in list(INPUT_TYPES):
|
||||
with raises(ValueError):
|
||||
convert(StringIO(), input_type, StringIO(), generate(str))
|
||||
|
||||
def test_convert_input_unsupported():
|
||||
for output_type in list(OUTPUT_TYPES):
|
||||
with raises(ValueError):
|
||||
convert(StringIO(), generate(str), StringIO(), output_type)
|
@ -1,6 +1,6 @@
|
||||
from os import getenv as os_getenv
|
||||
|
||||
from py.lib.scwrypts.exceptions import MissingVariableError
|
||||
from .scwrypts.exceptions import MissingVariableError
|
||||
|
||||
|
||||
def getenv(name, required=True):
|
1
py/lib/scwrypts/fzf/__init__.py
Normal file
1
py/lib/scwrypts/fzf/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import fzf, fzf_tail, fzf_head
|
1
py/lib/scwrypts/http/__init__.py
Normal file
1
py/lib/scwrypts/http/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import get_request_client
|
@ -1,3 +1,5 @@
|
||||
from .client import *
|
||||
|
||||
FILTER_OPERATORS = {
|
||||
'_eq',
|
||||
'_neq',
|
@ -1,5 +1,6 @@
|
||||
from py.lib.http import get_request_client
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
from .. import get_request_client
|
||||
|
||||
|
||||
REQUEST = None
|
2
py/lib/scwrypts/http/discord/__init__.py
Normal file
2
py/lib/scwrypts/http/discord/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .client import *
|
||||
from .send_message import *
|
@ -1,5 +1,5 @@
|
||||
from py.lib.http import get_request_client
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
from scwrypts.http import get_request_client
|
||||
|
||||
REQUEST = None
|
||||
|
@ -1,5 +1,5 @@
|
||||
from py.lib.scwrypts import getenv
|
||||
from py.lib.http.discord import request
|
||||
from scwrypts.env import getenv
|
||||
from .client import request
|
||||
|
||||
def send_message(content, channel_id=None, webhook=None, username=None, avatar_url=None, **kwargs):
|
||||
if username is None:
|
1
py/lib/scwrypts/http/linear/__init__.py
Normal file
1
py/lib/scwrypts/http/linear/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import *
|
@ -1,5 +1,6 @@
|
||||
from py.lib.http import get_request_client
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
from .. import get_request_client
|
||||
|
||||
REQUEST = None
|
||||
|
1
py/lib/scwrypts/io/__init__.py
Normal file
1
py/lib/scwrypts/io/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .combined_io_stream import get_combined_stream, add_io_arguments
|
@ -2,7 +2,7 @@ from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from sys import stdin, stdout, stderr
|
||||
|
||||
from py.lib.scwrypts.getenv import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
|
||||
@contextmanager
|
||||
@ -34,8 +34,8 @@ def get_stream(filename=None, mode='r', encoding='utf-8', verbose=False, **kwarg
|
||||
stdout.flush()
|
||||
|
||||
|
||||
def add_io_arguments(parser, toggle_input=True, toggle_output=True):
|
||||
if toggle_input:
|
||||
def add_io_arguments(parser, allow_input=True, allow_output=True):
|
||||
if allow_input:
|
||||
parser.add_argument(
|
||||
'-i', '--input-file',
|
||||
dest = 'input_file',
|
||||
@ -44,7 +44,7 @@ def add_io_arguments(parser, toggle_input=True, toggle_output=True):
|
||||
required = False,
|
||||
)
|
||||
|
||||
if toggle_output:
|
||||
if allow_output:
|
||||
parser.add_argument(
|
||||
'-o', '--output-file',
|
||||
dest = 'output_file',
|
1
py/lib/scwrypts/redis/__init__.py
Normal file
1
py/lib/scwrypts/redis/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .client import get_client
|
@ -1,6 +1,6 @@
|
||||
from redis import StrictRedis
|
||||
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
CLIENT = None
|
||||
|
@ -1,22 +0,0 @@
|
||||
from os import getenv
|
||||
from pathlib import Path
|
||||
from subprocess import run as subprocess_run
|
||||
|
||||
|
||||
def run(scwrypt_name, *args):
|
||||
DEPTH = int(getenv('SUBSCWRYPT', '0'))
|
||||
DEPTH += 1
|
||||
|
||||
SCWRYPTS_EXE = Path(__file__).parents[3] / 'scwrypts'
|
||||
ARGS = ' '.join([str(x) for x in args])
|
||||
print(f'SUBSCWRYPT={DEPTH} {SCWRYPTS_EXE} {scwrypt_name} -- {ARGS}')
|
||||
|
||||
print(f'\n {"--"*DEPTH} ({DEPTH}) BEGIN SUBSCWRYPT : {Path(scwrypt_name).name}')
|
||||
subprocess_run(
|
||||
f'SUBSCWRYPT={DEPTH} {SCWRYPTS_EXE} {scwrypt_name} -- {ARGS}',
|
||||
shell=True,
|
||||
executable='/bin/zsh',
|
||||
check=False,
|
||||
)
|
||||
|
||||
print(f' {"--"*DEPTH} ({DEPTH}) END SUBSCWRYPT : {Path(scwrypt_name).name}\n')
|
0
py/lib/scwrypts/scwrypts/__init__.py
Normal file
0
py/lib/scwrypts/scwrypts/__init__.py
Normal file
@ -6,11 +6,11 @@ class MissingVariableError(EnvironmentError):
|
||||
super().__init__(f'Missing required environment variable "{name}"')
|
||||
|
||||
|
||||
class ImportedExecutableError(ImportError):
|
||||
def __init__(self):
|
||||
super().__init__('executable only; must run through scwrypts')
|
||||
|
||||
|
||||
class MissingFlagAndEnvironmentVariableError(EnvironmentError, ArgumentError):
|
||||
def __init__(self, flags, env_var):
|
||||
super().__init__(f'must provide at least one of : {{ flags: {flags} OR {env_var} }}')
|
||||
|
||||
|
||||
class MissingScwryptsExecutableError(EnvironmentError):
|
||||
def __init__(self):
|
||||
super().__init__(f'scwrypts must be installed and available on your PATH')
|
@ -1,9 +1,12 @@
|
||||
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
||||
|
||||
from py.lib.scwrypts.io import get_combined_stream, add_io_arguments
|
||||
from scwrypts.io import get_combined_stream, add_io_arguments
|
||||
|
||||
|
||||
def execute(main, description=None, parse_args=None, toggle_input=True, toggle_output=True):
|
||||
def execute(main, description=None, parse_args=None, allow_input=True, allow_output=True):
|
||||
'''
|
||||
API to initiate a python-based scwrypt
|
||||
'''
|
||||
if parse_args is None:
|
||||
parse_args = []
|
||||
|
||||
@ -12,7 +15,7 @@ def execute(main, description=None, parse_args=None, toggle_input=True, toggle_o
|
||||
formatter_class = ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
|
||||
add_io_arguments(parser, toggle_input, toggle_output)
|
||||
add_io_arguments(parser, allow_input, allow_output)
|
||||
|
||||
for a in parse_args:
|
||||
parser.add_argument(*a[0], **a[1])
|
@ -2,6 +2,9 @@ from bpython import embed
|
||||
|
||||
|
||||
def interactive(variable_descriptions):
|
||||
'''
|
||||
main() decorator to drop to interactive python environment upon completion
|
||||
'''
|
||||
def outer(function):
|
||||
|
||||
def inner(*args, **kwargs):
|
30
py/lib/scwrypts/scwrypts/scwrypts.py
Normal file
30
py/lib/scwrypts/scwrypts/scwrypts.py
Normal file
@ -0,0 +1,30 @@
|
||||
from os import getenv
|
||||
from shutil import which
|
||||
from subprocess import run
|
||||
|
||||
from .exceptions import MissingScwryptsExecutableError
|
||||
|
||||
|
||||
def scwrypts(name, group, _type, *args, log_level=None):
|
||||
'''
|
||||
invoke non-python scwrypts from python
|
||||
'''
|
||||
executable = which('scwrypts')
|
||||
if executable is None:
|
||||
raise MissingScwryptsExecutableError()
|
||||
|
||||
pre_args = ''
|
||||
|
||||
if log_level is not None:
|
||||
pre_args += '--log-level {log_level}'
|
||||
|
||||
depth = getenv('SUBSCWRYPT', '')
|
||||
if depth != '':
|
||||
depth = int(depth) + 1
|
||||
|
||||
return run(
|
||||
f'SUBSCWRYPT={depth} {executable} --name {name} --group {group} --type {_type} {pre_args} -- {" ".join(args)}',
|
||||
shell=True,
|
||||
executable='/bin/zsh',
|
||||
check=False,
|
||||
)
|
1
py/lib/scwrypts/test/__init__.py
Normal file
1
py/lib/scwrypts/test/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .random_generator import generate
|
10
py/lib/scwrypts/test/exceptions.py
Normal file
10
py/lib/scwrypts/test/exceptions.py
Normal file
@ -0,0 +1,10 @@
|
||||
class GeneratorError(Exception):
|
||||
pass
|
||||
|
||||
class NoDataTypeError(GeneratorError, ValueError):
|
||||
def __init__(self):
|
||||
super().__init__('must provide at least one data type (either "data_type" or "data_types")')
|
||||
|
||||
class BadGeneratorTypeError(GeneratorError, ValueError):
|
||||
def __init__(self, data_type):
|
||||
super().__init__(f'no generator exists for data type "{data_type}"')
|
240
py/lib/scwrypts/test/random_generator.py
Normal file
240
py/lib/scwrypts/test/random_generator.py
Normal file
@ -0,0 +1,240 @@
|
||||
from csv import writer, QUOTE_NONNUMERIC
|
||||
from io import StringIO
|
||||
from json import dumps
|
||||
from random import randint, uniform, choice
|
||||
from re import sub
|
||||
from string import printable
|
||||
from yaml import safe_dump
|
||||
|
||||
from .exceptions import NoDataTypeError, BadGeneratorTypeError
|
||||
|
||||
|
||||
SUPPORTED_DATA_TYPES = None
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
'data_types': None,
|
||||
'minimum': 0,
|
||||
'maximum': 64,
|
||||
'depth': 1,
|
||||
'character_set': None,
|
||||
'bool_nullable': False,
|
||||
'str_length': None,
|
||||
'str_minimum_length': 0,
|
||||
'str_maximum_length': 32,
|
||||
'list_length': 8,
|
||||
'set_length': 8,
|
||||
'dict_length': 8,
|
||||
'dict_key_types': {int, float, chr, str},
|
||||
'csv_columns': None,
|
||||
'csv_columns_minimum': 1,
|
||||
'csv_columns_maximum': 16,
|
||||
'csv_rows': None,
|
||||
'csv_rows_minimum': 2,
|
||||
'csv_rows_maximum': 16,
|
||||
'json_initial_type': dict,
|
||||
'yaml_initial_type': dict,
|
||||
}
|
||||
|
||||
def generate(data_type=None, options=None):
|
||||
'''
|
||||
generate random data with the call of a function
|
||||
use data_type to generate a single value
|
||||
|
||||
use options to set generation options (key = type, value = kwargs)
|
||||
|
||||
use options.data_types and omit data_type to generate a random type
|
||||
'''
|
||||
if options is None:
|
||||
options = {**DEFAULT_OPTIONS}
|
||||
else:
|
||||
options = DEFAULT_OPTIONS | options
|
||||
|
||||
if data_type is None and options['data_types'] is None:
|
||||
raise NoDataTypeError()
|
||||
|
||||
if data_type is None and options['data_types'] is not None:
|
||||
return generate(data_type=choice(list(options['data_types'])), options=options)
|
||||
|
||||
if not isinstance(data_type, str):
|
||||
data_type = data_type.__name__
|
||||
|
||||
if data_type not in Generator.get_supported_data_types():
|
||||
raise BadGeneratorTypeError(data_type)
|
||||
|
||||
return getattr(Generator, f'_{data_type}')(options)
|
||||
|
||||
#####################################################################
|
||||
|
||||
class Generator:
|
||||
|
||||
@classmethod
|
||||
def get_supported_data_types(cls):
|
||||
global SUPPORTED_DATA_TYPES # pylint: disable=global-statement
|
||||
if SUPPORTED_DATA_TYPES is None:
|
||||
SUPPORTED_DATA_TYPES = {
|
||||
sub('^_', '', data_type)
|
||||
for data_type, method in Generator.__dict__.items()
|
||||
if isinstance(method, staticmethod)
|
||||
}
|
||||
|
||||
return SUPPORTED_DATA_TYPES
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _bool(options):
|
||||
return choice([True, False, None]) if options['bool_nullable'] else choice([True, False])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _int(options):
|
||||
return randint(options['minimum'], options['maximum'])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _float(options):
|
||||
return uniform(options['minimum'], options['maximum'])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _chr(options):
|
||||
character_set = options['character_set']
|
||||
return choice(character_set) if character_set is not None else chr(randint(0,65536))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _str(options):
|
||||
length = options['str_length']
|
||||
if length is None:
|
||||
length = generate(int, {
|
||||
'minimum': options['str_minimum_length'],
|
||||
'maximum': options['str_maximum_length'],
|
||||
})
|
||||
return ''.join((generate(chr, options) for _ in range(length)))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _list(options):
|
||||
if options['depth'] <= 0:
|
||||
return []
|
||||
|
||||
options['depth'] -= 1
|
||||
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {bool, int, float, chr, str}
|
||||
|
||||
return [ generate(None, options) for _ in range(options['list_length']) ]
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _set(options):
|
||||
if options['depth'] <= 0:
|
||||
return set()
|
||||
|
||||
options['depth'] -= 1
|
||||
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {bool, int, float, chr, str}
|
||||
|
||||
set_options = options | {'data_types': options['data_types'] - {list, dict, set}}
|
||||
|
||||
return { generate(None, set_options) for _ in range(options['set_length']) }
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _dict(options):
|
||||
if options['depth'] <= 0:
|
||||
return {}
|
||||
|
||||
options['depth'] -= 1
|
||||
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {bool, int, float, chr, str, list, set, dict}
|
||||
|
||||
if len(options['data_types']) == 0:
|
||||
return {}
|
||||
|
||||
key_options = options | {'data_types': options['dict_key_types']}
|
||||
return {
|
||||
generate(None, key_options): generate(None, options)
|
||||
for _ in range(options['dict_length'])
|
||||
}
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _csv(options):
|
||||
'''
|
||||
creates a StringIO object containing csv data
|
||||
'''
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {int, float, str}
|
||||
|
||||
columns = options['csv_columns']
|
||||
if columns is None:
|
||||
columns = max(1, generate(int, {
|
||||
'minimum': options['csv_columns_minimum'],
|
||||
'maximum': options['csv_columns_maximum'],
|
||||
}))
|
||||
|
||||
rows = options['csv_rows']
|
||||
if rows is None:
|
||||
rows = max(1, generate(int, {
|
||||
'minimum': options['csv_rows_minimum'],
|
||||
'maximum': options['csv_rows_maximum'],
|
||||
}))
|
||||
|
||||
if options['character_set'] is None:
|
||||
options['character_set'] = printable
|
||||
|
||||
csv = StringIO()
|
||||
csv_writer = writer(csv, quoting=QUOTE_NONNUMERIC)
|
||||
|
||||
options['list_length'] = columns
|
||||
|
||||
for line in [ generate(list, options) for _ in range(rows) ]:
|
||||
csv_writer.writerow(line)
|
||||
|
||||
csv.seek(0)
|
||||
return csv
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _json(options):
|
||||
'''
|
||||
creates a str containing json data
|
||||
'''
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {bool, int, float, str, list, dict}
|
||||
|
||||
if options['character_set'] is None:
|
||||
options['character_set'] = printable
|
||||
|
||||
options['dict_key_types'] = { int, float, str }
|
||||
|
||||
data = generate(options['json_initial_type'], options)
|
||||
return dumps(data)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _yaml(options):
|
||||
'''
|
||||
creates a StringIO object containing yaml data
|
||||
'''
|
||||
if options['data_types'] is None:
|
||||
options['data_types'] = {bool, int, float, str, list, dict}
|
||||
|
||||
if options['character_set'] is None:
|
||||
options['character_set'] = printable
|
||||
|
||||
options['dict_key_types'] = { int, float, str }
|
||||
|
||||
yaml = StringIO()
|
||||
safe_dump(generate(options['yaml_initial_type'], options), yaml, default_flow_style=False)
|
||||
|
||||
yaml.seek(0)
|
||||
return yaml
|
||||
|
||||
|
||||
#####################################################################
|
||||
if __name__ == '__main__':
|
||||
print(generate('json', {'depth': 3}))
|
44
py/lib/scwrypts/test/test_random_generator.py
Normal file
44
py/lib/scwrypts/test/test_random_generator.py
Normal file
@ -0,0 +1,44 @@
|
||||
from os import getenv
|
||||
from random import randint
|
||||
|
||||
from .random_generator import generate, Generator
|
||||
|
||||
|
||||
ITERATIONS = int(getenv('PYTEST_ITERATIONS__scwrypts__test__random_generator', getenv('PYTEST_ITERATIONS', '999')))
|
||||
|
||||
|
||||
def test_generate(): # generators should be quick and "just work" (no Exceptions)
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
for _ in range(ITERATIONS):
|
||||
generate(data_type)
|
||||
|
||||
|
||||
def test_generate_depth_deep():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'depth': 4})
|
||||
|
||||
def test_generate_depth_shallow():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'depth': randint(-999, 0)})
|
||||
|
||||
|
||||
def test_generate_range_all():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'minimum': -99, 'maximum': 99})
|
||||
|
||||
def test_generate_range_positive():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'minimum': 1, 'maximum': 99})
|
||||
|
||||
def test_generate_range_zero():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'minimum': 3, 'maximum': 3})
|
||||
|
||||
def test_generate_range_negative():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'minimum': -99, 'maximum': -1})
|
||||
|
||||
|
||||
def test_generate_bool_nullable():
|
||||
for data_type in Generator.get_supported_data_types():
|
||||
generate(data_type, {'bool': {'nullable': True}})
|
2
py/lib/scwrypts/twilio/__init__.py
Normal file
2
py/lib/scwrypts/twilio/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .client import get_client
|
||||
from .send_sms import send_sms
|
@ -1,6 +1,6 @@
|
||||
from twilio.rest import Client
|
||||
|
||||
from py.lib.scwrypts import getenv
|
||||
from scwrypts.env import getenv
|
||||
|
||||
CLIENT = None
|
||||
|
@ -1,7 +1,7 @@
|
||||
from json import dumps
|
||||
from time import sleep
|
||||
|
||||
from py.lib.twilio.client import get_client
|
||||
from .client import get_client
|
||||
|
||||
|
||||
def send_sms(to, from_, body, max_char_count=300, stream=None):
|
@ -1,2 +0,0 @@
|
||||
from py.lib.twilio.client import get_client
|
||||
from py.lib.twilio.send_sms import send_sms
|
@ -1,13 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.http.linear import graphql
|
||||
from py.lib.scwrypts import execute
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts.http.linear import graphql
|
||||
|
||||
|
||||
description = 'comment on an issue in linear.app'
|
||||
parse_args = [
|
||||
( ['-d', '--issue-id'], {
|
||||
'dest' : 'issue_id',
|
||||
'help' : 'issue short-code (e.g. CLOUD-319)',
|
||||
'required' : True,
|
||||
}),
|
||||
( ['-m', '--message'], {
|
||||
'dest' : 'message',
|
||||
'help' : 'comment to post to the target issue',
|
||||
'required' : True,
|
||||
}),
|
||||
]
|
||||
|
||||
|
||||
def get_query(args):
|
||||
@ -26,20 +35,6 @@ def main(args, stream):
|
||||
response = graphql(get_query(args))
|
||||
stream.writeline(response)
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'comment on an inssue in linear.app',
|
||||
parse_args = [
|
||||
( ['-d', '--issue-id'], {
|
||||
'dest' : 'issue_id',
|
||||
'help' : 'issue short-code (e.g. CLOUD-319)',
|
||||
'required' : True,
|
||||
}),
|
||||
( ['-m', '--message'], {
|
||||
'dest' : 'message',
|
||||
'help' : 'comment to post to the target issue',
|
||||
'required' : True,
|
||||
}),
|
||||
]
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,15 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
from py.lib.redis import get_client
|
||||
from py.lib.scwrypts import execute, interactive, getenv
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from scwrypts import interactive
|
||||
from scwrypts.env import getenv
|
||||
from scwrypts.redis import get_client
|
||||
|
||||
|
||||
description = 'establishes a redis client in an interactive python shell'
|
||||
parse_args = []
|
||||
|
||||
@interactive([
|
||||
f'r = StrictRedis(\'{getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}\')',
|
||||
])
|
||||
@ -20,7 +19,5 @@ def main(_args, _stream):
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'establishes a redis client in an interactive python shell',
|
||||
parse_args = [],
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
@ -1,5 +1 @@
|
||||
bpython
|
||||
pyfzf
|
||||
pyyaml
|
||||
redis
|
||||
twilio
|
||||
./lib
|
||||
|
@ -1,15 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
from scwrypts import execute
|
||||
#####################################################################
|
||||
from sys import stderr
|
||||
|
||||
from py.lib.scwrypts import execute, getenv
|
||||
from py.lib.twilio import send_sms
|
||||
from scwrypts.env import getenv
|
||||
from scwrypts.twilio import send_sms
|
||||
|
||||
from py.lib.scwrypts.exceptions import ImportedExecutableError, MissingFlagAndEnvironmentVariableError
|
||||
|
||||
if __name__ != '__main__':
|
||||
raise ImportedExecutableError()
|
||||
|
||||
#####################################################################
|
||||
description = 'send a simple SMS through twilio'
|
||||
parse_args = [
|
||||
( ['-t', '--to'], {
|
||||
'dest' : 'to',
|
||||
'help' : 'phone number of the receipient',
|
||||
'required' : False,
|
||||
'default' : getenv('TWILIO__DEFAULT_PHONE_TO', required=False),
|
||||
}),
|
||||
( ['-f', '--from'], {
|
||||
'dest' : 'from_',
|
||||
'help' : 'phone number of the receipient',
|
||||
'required' : False,
|
||||
'default' : getenv('TWILIO__DEFAULT_PHONE_FROM', required=False),
|
||||
}),
|
||||
( ['-b', '--body'], {
|
||||
'dest' : 'body',
|
||||
'help' : 'message body',
|
||||
'required' : False,
|
||||
}),
|
||||
( ['--max-char-count'], {
|
||||
'dest' : 'max_char_count',
|
||||
'help' : 'separate message into parts by character count (1 < N <= 1500)',
|
||||
'required' : False,
|
||||
'default' : 300,
|
||||
}),
|
||||
]
|
||||
|
||||
def main(args, stream):
|
||||
if args.body is None:
|
||||
@ -35,31 +58,5 @@ def main(args, stream):
|
||||
|
||||
|
||||
#####################################################################
|
||||
execute(main,
|
||||
description = 'send a simple SMS through twilio',
|
||||
parse_args = [
|
||||
( ['-t', '--to'], {
|
||||
'dest' : 'to',
|
||||
'help' : 'phone number of the receipient',
|
||||
'required' : False,
|
||||
'default' : getenv('TWILIO__DEFAULT_PHONE_TO', required=False),
|
||||
}),
|
||||
( ['-f', '--from'], {
|
||||
'dest' : 'from_',
|
||||
'help' : 'phone number of the receipient',
|
||||
'required' : False,
|
||||
'default' : getenv('TWILIO__DEFAULT_PHONE_FROM', required=False),
|
||||
}),
|
||||
( ['-b', '--body'], {
|
||||
'dest' : 'body',
|
||||
'help' : 'message body',
|
||||
'required' : False,
|
||||
}),
|
||||
( ['--max-char-count'], {
|
||||
'dest' : 'max_char_count',
|
||||
'help' : 'separate message into parts by character count (1 < N <= 1500)',
|
||||
'required' : False,
|
||||
'default' : 300,
|
||||
}),
|
||||
]
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
execute(main, description, parse_args)
|
||||
|
388
run
388
run
@ -1,151 +1,189 @@
|
||||
#!/bin/zsh
|
||||
export EXECUTION_DIR=$(pwd)
|
||||
source "${0:a:h}/zsh/lib/import.driver.zsh" || exit 42
|
||||
|
||||
#####################################################################
|
||||
|
||||
__RUN() {
|
||||
() {
|
||||
cd "$SCWRYPTS_ROOT__scwrypts"
|
||||
GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; }
|
||||
local ERRORS=0
|
||||
local USAGE='
|
||||
usage: scwrypts [OPTIONS ...] SCRIPT -- [SCRIPT OPTIONS ...]
|
||||
usage: scwrypts [...options...] [...patterns...] -- [...script options...]
|
||||
|
||||
OPTIONS
|
||||
-g, --group <group-name> only use scripts from the indicated group
|
||||
-t, --type <type-name> only use scripts of the indicated type
|
||||
options:
|
||||
selection
|
||||
-m, --name <scwrypt-name> only run the script if there is an exact match
|
||||
(requires type and group)
|
||||
-g, --group <group-name> only use scripts from the indicated group
|
||||
-t, --type <type-name> only use scripts of the indicated type
|
||||
|
||||
runtime
|
||||
-y, --yes auto-accept all [yn] prompts through current scwrypt
|
||||
-e, --env <env-name> set environment; overwrites SCWRYPTS_ENV
|
||||
-n, --no-log skip logging and run in quiet mode
|
||||
-n shorthand for "--log-level 0"
|
||||
-v, --log-level [0-4] set scwrypts log level to one of the following:
|
||||
0 : only command output and critical failures; skips logfile
|
||||
1 : add success / failure messages
|
||||
2 : (default) include status update messages
|
||||
3 : (CI default) include warning messages
|
||||
4 : include debug messages
|
||||
|
||||
--update update scwrypts library to latest version
|
||||
|
||||
-v, --version print out scwrypts version and exit
|
||||
-l, --list print out command list and exit
|
||||
alternate commands
|
||||
-h, --help display this message and exit
|
||||
-l, --list print out command list and exit
|
||||
--list-envs print out environment list and exit
|
||||
--update update scwrypts library to latest version
|
||||
--version print out scwrypts version and exit
|
||||
|
||||
patterns:
|
||||
- a list of glob patterns to loose-match a scwrypt by name
|
||||
|
||||
script options:
|
||||
- everything after "--" is forwarded to the scwrypt you run
|
||||
("-- --help" will provide more information)
|
||||
'
|
||||
cd "$SCWRYPTS_ROOT"
|
||||
|
||||
#####################################################################
|
||||
### cli argument parsing and global configuration ###################
|
||||
#####################################################################
|
||||
|
||||
local ENV_NAME="$SCWRYPTS_ENV"
|
||||
local SEARCH_PATTERNS=()
|
||||
|
||||
local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME
|
||||
|
||||
local ERROR=0
|
||||
[ ! $SCWRYPTS_LOG_LEVEL ] && {
|
||||
local SCWRYPTS_LOG_LEVEL
|
||||
[ $CI ] && SCWRYPTS_LOG_LEVEL=3 || SCWRYPTS_LOG_LEVEL=2
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
-t | --type )
|
||||
[ ! $2 ] && ERROR "missing value for argument $1" && break
|
||||
SEARCH_TYPE=$2
|
||||
shift 2
|
||||
;;
|
||||
-g | --group )
|
||||
[ ! $2 ] && ERROR "missing value for argument $1" && break
|
||||
SEARCH_GROUP=$2
|
||||
shift 2
|
||||
;;
|
||||
-m | --name )
|
||||
[ ! $2 ] && ERROR "missing value for argument $1" && break
|
||||
SEARCH_NAME=$2
|
||||
shift 2
|
||||
;;
|
||||
|
||||
-[a-z][a-z]* )
|
||||
VARSPLIT=$(echo "$1 " | sed 's/^\(-.\)\(.*\) /\1 -\2/')
|
||||
set -- $(echo " $VARSPLIT ") ${@:2}
|
||||
;;
|
||||
|
||||
### alternate commands ###################
|
||||
|
||||
-h | --help )
|
||||
USAGE
|
||||
return 0
|
||||
;;
|
||||
-n | --no-log )
|
||||
[ ! $SUBSCWRYPT ] && SUBSCWRYPT=0
|
||||
shift 1
|
||||
;;
|
||||
-e | --env )
|
||||
[ ! $2 ] && ERROR "missing value for argument $1" && break
|
||||
[ ! $SUBSCWRYPTS ] \
|
||||
&& [ $ENV_NAME ] \
|
||||
&& WARNING 'overwriting session environment' \
|
||||
;
|
||||
|
||||
ENV_NAME="$2"
|
||||
STATUS "using CLI environment '$ENV_NAME'"
|
||||
shift 2
|
||||
;;
|
||||
-l | --list )
|
||||
SCWRYPTS__GET_AVAILABLE_SCWRYPTS
|
||||
return 0
|
||||
;;
|
||||
-v | --version )
|
||||
echo scwrypts $(cd "$SCWRYPTS__ROOT__scwrypts"; git describe --tags)
|
||||
|
||||
--list-envs )
|
||||
SCWRYPTS__GET_ENV_NAMES
|
||||
return 0
|
||||
;;
|
||||
|
||||
--version )
|
||||
echo scwrypts $(GIT_SCWRYPTS describe --tags)
|
||||
return 0
|
||||
;;
|
||||
|
||||
--update )
|
||||
cd "$SCWRYPTS__ROOT__scwrypts"
|
||||
git fetch --quiet origin main
|
||||
git fetch --quiet origin main --tags
|
||||
GIT_SCWRYPTS fetch --quiet origin main
|
||||
GIT_SCWRYPTS fetch --quiet origin main --tags
|
||||
local SYNC_STATUS=$?
|
||||
|
||||
git diff --exit-code origin/main -- . >&2
|
||||
GIT_SCWRYPTS diff --exit-code origin/main -- . >/dev/null 2>&1
|
||||
local DIFF_STATUS=$?
|
||||
|
||||
[[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && {
|
||||
SUCCESS 'already up-to-date with origin/main'
|
||||
} || {
|
||||
git rebase --autostash origin/main \
|
||||
GIT_SCWRYPTS rebase --autostash origin/main \
|
||||
&& SUCCESS 'up-to-date with origin/main' \
|
||||
&& GIT_SCWRYPTS log -n1 \
|
||||
|| {
|
||||
git rebase --abort
|
||||
GIT_SCWRYPTS rebase --abort
|
||||
ERROR 'unable to update scwrypts; please try manual upgrade'
|
||||
REMINDER "installation in '$(pwd)'"
|
||||
}
|
||||
}
|
||||
return 0
|
||||
;;
|
||||
-- )
|
||||
shift 1
|
||||
break # pass arguments after '--' to the scwrypt
|
||||
;;
|
||||
--* )
|
||||
ERROR "unrecognized argument '$1'"
|
||||
|
||||
### scwrypts filters #####################
|
||||
|
||||
-m | --name )
|
||||
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
||||
SEARCH_NAME=$2
|
||||
shift 1
|
||||
;;
|
||||
* )
|
||||
SEARCH_PATTERNS+=($1)
|
||||
|
||||
-g | --group )
|
||||
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
||||
SEARCH_GROUP=$2
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-t | --type )
|
||||
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
||||
SEARCH_TYPE=$2
|
||||
shift 1
|
||||
;;
|
||||
|
||||
### runtime settings #####################
|
||||
|
||||
-y | --yes ) export __SCWRYPTS_YES=1 ;;
|
||||
|
||||
-n | --no-log )
|
||||
SCWRYPTS_LOG_LEVEL=0
|
||||
[[ $1 =~ ^--no-log$ ]] && WARNING 'the --no-log flag is deprecated and will be removed in scwrypts v4.2'
|
||||
;;
|
||||
|
||||
-v | --log-level )
|
||||
[[ $2 =~ ^[0-4]$ ]] || ERROR "invalid setting for log-level '$2'"
|
||||
SCWRYPTS_LOG_LEVEL=$2
|
||||
shift 1
|
||||
;;
|
||||
|
||||
-e | --env )
|
||||
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
||||
|
||||
[ $ENV_NAME ] && DEBUG 'overwriting session environment'
|
||||
|
||||
ENV_NAME="$2"
|
||||
STATUS "using CLI environment '$ENV_NAME'"
|
||||
shift 1
|
||||
;;
|
||||
|
||||
##########################################
|
||||
|
||||
-- ) shift 1; break ;; # pass arguments after '--' to the scwrypt
|
||||
--* ) ERROR "unrecognized argument '$1'" ;;
|
||||
* ) SEARCH_PATTERNS+=($1) ;;
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
|
||||
[ $SEARCH_NAME ] && {
|
||||
[ ! $SEARCH_TYPE ] && ERROR '--name requires --type argument'
|
||||
[ ! $SEARCH_GROUP ] && ERROR '--name requires --group argument'
|
||||
[ $SEARCH_TYPE ] || ERROR '--name requires --type argument'
|
||||
[ $SEARCH_GROUP ] || ERROR '--name requires --group argument'
|
||||
}
|
||||
|
||||
CHECK_ERRORS
|
||||
|
||||
##########################################
|
||||
#####################################################################
|
||||
### scwrypts selection / filtering ##################################
|
||||
#####################################################################
|
||||
|
||||
local SCWRYPTS_AVAILABLE
|
||||
local POTENTIAL_ERROR="no such scwrypt exists:"
|
||||
|
||||
SCWRYPTS_AVAILABLE=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS)
|
||||
|
||||
[ $SEARCH_NAME ] && {
|
||||
POTENTIAL_ERROR+="\n NAME : '$SEARCH_NAME'"
|
||||
POTENTIAL_ERROR+="\n TYPE : '$SEARCH_TYPE'"
|
||||
POTENTIAL_ERROR+="\n GROUP : '$SEARCH_GROUP'"
|
||||
SCWRYPTS_AVAILABLE=$({
|
||||
##########################################
|
||||
|
||||
[ $SEARCH_NAME ] && SCWRYPTS_AVAILABLE=$({
|
||||
echo $SCWRYPTS_AVAILABLE | head -n1
|
||||
echo $SCWRYPTS_AVAILABLE | sed -e 's/\x1b\[[0-9;]*m//g' | grep "^$SEARCH_NAME *$SEARCH_TYPE *$SEARCH_GROUP\$"
|
||||
})
|
||||
}
|
||||
|
||||
[ ! $SEARCH_NAME ] && {
|
||||
}) || {
|
||||
[ $SEARCH_TYPE ] && {
|
||||
POTENTIAL_ERROR+="\n TYPE : '$SEARCH_TYPE'"
|
||||
SCWRYPTS_AVAILABLE=$(\
|
||||
{
|
||||
echo $SCWRYPTS_AVAILABLE | head -n1
|
||||
@ -158,7 +196,6 @@ __RUN() {
|
||||
}
|
||||
|
||||
[ $SEARCH_GROUP ] && {
|
||||
POTENTIAL_ERROR+="\n GROUP : '$SEARCH_GROUP'"
|
||||
SCWRYPTS_AVAILABLE=$(
|
||||
{
|
||||
echo $SCWRYPTS_AVAILABLE | head -n1
|
||||
@ -185,39 +222,49 @@ __RUN() {
|
||||
}
|
||||
}
|
||||
|
||||
[[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -lt 2 ]] && ERROR "$POTENTIAL_ERROR"
|
||||
|
||||
CHECK_ERRORS
|
||||
[[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -lt 2 ]] && {
|
||||
FAIL 1 "$(echo "
|
||||
no such scwrypt exists
|
||||
NAME : '$SEARCH_NAME'
|
||||
TYPE : '$SEARCH_TYPE'
|
||||
GROUP : '$SEARCH_GROUP'
|
||||
PATTERNS : '$SEARCH_PATTERNS'
|
||||
" | sed "1d; \$d; /''$/d")"
|
||||
}
|
||||
|
||||
##########################################
|
||||
|
||||
local NAME="$SEARCH_NAME"
|
||||
local TYPE="$SEARCH_TYPE"
|
||||
local GROUP="$SEARCH_GROUP"
|
||||
|
||||
[[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -eq 2 ]] \
|
||||
&& SCWRYPT_SELECTION=$(echo $SCWRYPTS_AVAILABLE | tail -n1) \
|
||||
|| SCWRYPT_SELECTION=$(echo $SCWRYPTS_AVAILABLE | FZF "select a script to run" --header-lines 1)
|
||||
|| SCWRYPT_SELECTION=$(echo $SCWRYPTS_AVAILABLE | FZF "select a script to run" --header-lines 1) \
|
||||
;
|
||||
|
||||
[ $SCWRYPT_SELECTION ] || exit 2
|
||||
|
||||
##########################################
|
||||
|
||||
local NAME TYPE GROUP
|
||||
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
|
||||
|
||||
export SCWRYPT_NAME=$NAME
|
||||
export SCWRYPT_TYPE=$TYPE
|
||||
export SCWRYPT_GROUP=$GROUP
|
||||
|
||||
##########################################
|
||||
#####################################################################
|
||||
### environment variables and configuration validation ##############
|
||||
#####################################################################
|
||||
|
||||
local ENV_REQUIRED=true \
|
||||
&& [ ! $CI ] \
|
||||
&& [[ ! $SCWRYPT_NAME =~ scwrypts/logs ]] \
|
||||
&& [[ ! $SCWRYPT_NAME =~ scwrypts/environment ]] \
|
||||
|| ENV_REQUIRED=false
|
||||
|
||||
local ENV_REQUIRED=$(__CHECK_ENV_REQUIRED && echo 1 || echo 0)
|
||||
local REQUIRED_ENVIRONMENT_REGEX=$(eval echo '$SCWRYPTS_REQUIRED_ENVIRONMENT_REGEX__'$SCWRYPT_GROUP)
|
||||
|
||||
[ $REQUIRED_ENVIRONMENT_REGEX ] && {
|
||||
[[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \
|
||||
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
||||
}
|
||||
|
||||
[[ $ENV_REQUIRED -eq 1 ]] && {
|
||||
[[ $ENV_REQUIRED =~ true ]] && {
|
||||
[ ! $ENV_NAME ] && ENV_NAME=$(SCWRYPTS__SELECT_ENV)
|
||||
|
||||
for GROUP in ${SCWRYPTS_GROUPS[@]}
|
||||
do
|
||||
local ENV_FILE=$(SCWRYPTS__GET_ENV_FILE "$ENV_NAME" "$GROUP")
|
||||
@ -232,104 +279,99 @@ __RUN() {
|
||||
export ENV_NAME
|
||||
}
|
||||
|
||||
##########################################
|
||||
|
||||
[ $REQUIRED_ENVIRONMENT_REGEX ] && {
|
||||
[[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \
|
||||
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
||||
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
||||
}
|
||||
|
||||
##########################################
|
||||
|
||||
[ ! $SUBSCWRYPT ] \
|
||||
&& [[ $ENV_NAME =~ prod ]] \
|
||||
&& { __VALIDATE_UPSTREAM_TIMELINE || ABORT; }
|
||||
|
||||
##########################################
|
||||
|
||||
local RUN_STRING=$(SCWRYPTS__GET_RUNSTRING $SCWRYPT_NAME $SCWRYPT_TYPE $SCWRYPT_GROUP)
|
||||
[ ! $RUN_STRING ] && exit 3
|
||||
|
||||
##########################################
|
||||
|
||||
local LOGFILE=$(__GET_LOGFILE)
|
||||
|
||||
local HEADER=$(
|
||||
[ $SUBSCWRYPT ] && return 0
|
||||
echo '====================================================================='
|
||||
echo "script : $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME"
|
||||
echo "run at : $(date)"
|
||||
echo "config : $ENV_NAME"
|
||||
[ ! $LOGFILE ] && echo '\033[1;33m------------------------------------------\033[0m'
|
||||
)
|
||||
|
||||
[ ! $LOGFILE ] && {
|
||||
[ $HEADER ] && echo $HEADER
|
||||
[ $SUBSCWRYPT ] && {
|
||||
eval "$RUN_STRING $(printf "%q " "$@")"
|
||||
exit $?
|
||||
} || {
|
||||
eval "$RUN_STRING $(printf "%q " "$@")" </dev/tty >/dev/tty 2>&1
|
||||
exit $?
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
[ $HEADER ] && echo $HEADER
|
||||
echo '\033[1;33m--- BEGIN OUTPUT -------------------------\033[0m'
|
||||
(eval "$RUN_STRING $(printf "%q " "$@")")
|
||||
EXIT_CODE=$?
|
||||
echo '\033[1;33m--- END OUTPUT ---------------------------\033[0m'
|
||||
|
||||
[[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m'
|
||||
|
||||
echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m"
|
||||
} 2>&1 | tee --append "$LOGFILE"
|
||||
|
||||
exit $(\
|
||||
sed -n 's/^terminated with.*code \([0-9]*\).*$/\1/p' $LOGFILE \
|
||||
| tail -n1
|
||||
)
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
|
||||
__CHECK_ENV_REQUIRED() {
|
||||
[ $CI ] && return 1
|
||||
|
||||
echo $SCWRYPT_NAME | grep -q 'scwrypts/logs/' && return 1
|
||||
echo $SCWRYPT_NAME | grep -q 'scwrypts/environment/' && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
__VALIDATE_UPSTREAM_TIMELINE() {
|
||||
[ ! $SUBSCWRYPT ] && [[ $ENV_NAME =~ prod ]] && {
|
||||
STATUS "on '$ENV_NAME'; checking diff against origin/main"
|
||||
|
||||
git fetch --quiet origin main
|
||||
GIT_SCWRYPTS fetch --quiet origin main
|
||||
local SYNC_STATUS=$?
|
||||
|
||||
git diff --exit-code origin/main -- . >&2
|
||||
GIT_SCWRYPTS diff --exit-code origin/main -- . >&2
|
||||
local DIFF_STATUS=$?
|
||||
|
||||
[[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && {
|
||||
SUCCESS 'up-to-date with origin/main'
|
||||
} || {
|
||||
WARNING
|
||||
[[ $SYNC_STATUS -ne 0 ]] && WARNING 'unable to synchronize with origin/main'
|
||||
[[ $DIFF_STATUS -ne 0 ]] && WARNING 'your branch differs from origin/main (diff listed above)'
|
||||
WARNING
|
||||
SCWRYPTS_LOG_LEVEL=3 WARNING "you are trying to run in ${__BRIGHT_RED}production${__YELLOW} but $([[ $SYNC_STATUS -ne 0 ]] && echo 'I am unable to verify your scwrypts version')$([[ $DIFF_STATUS -ne 0 ]] && echo 'your scwrypts is out-of-date (diff listed above)')"
|
||||
|
||||
yN 'continue?' || return 1
|
||||
yN 'continue?' || {
|
||||
REMINDER "you can use 'scwrypts --update' to quickly update scwrypts to latest"
|
||||
ABORT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__GET_LOGFILE() {
|
||||
[ $SUBSCWRYPT ] \
|
||||
|| [[ $SCWRYPT_NAME =~ scwrypts/logs ]] \
|
||||
|| [[ $SCWRYPT_NAME =~ interactive ]] \
|
||||
&& return 0
|
||||
##########################################
|
||||
|
||||
echo "$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log"
|
||||
}
|
||||
local RUN_STRING=$(SCWRYPTS__GET_RUNSTRING $SCWRYPT_NAME $SCWRYPT_TYPE $SCWRYPT_GROUP)
|
||||
[ "$RUN_STRING" ] || return 42
|
||||
|
||||
#####################################################################
|
||||
__RUN $@
|
||||
#####################################################################
|
||||
### logging and pretty header/footer setup ##########################
|
||||
#####################################################################
|
||||
|
||||
local LOGFILE \
|
||||
&& [[ $SCWRYPTS_LOG_LEVEL -gt 0 ]] \
|
||||
&& [ ! $SUBSCWRYPT ] \
|
||||
&& [[ ! $SCWRYPT_NAME =~ scwrypts/logs ]] \
|
||||
&& [[ ! $SCWRYPT_NAME =~ interactive ]] \
|
||||
&& LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log" \
|
||||
|| LOGFILE='/dev/null' \
|
||||
;
|
||||
|
||||
local HEADER FOOTER
|
||||
|
||||
[[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && {
|
||||
HEADER=$(
|
||||
echo "
|
||||
=====================================================================
|
||||
script : $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME
|
||||
run at : $(date)
|
||||
config : $ENV_NAME
|
||||
log level : $SCWRYPTS_LOG_LEVEL
|
||||
\\033[1;33m--- SCWRYPT BEGIN ---------------------------------------------------\\033[0m
|
||||
" | sed 's/^\s\+//; 1d'
|
||||
)
|
||||
|
||||
FOOTER="\\033[1;33m--- SCWRYPT END ---------------------------------------------------\\033[0m"
|
||||
}
|
||||
|
||||
[ $SUBSCWRYPT ] && {
|
||||
HEADER="\\033[0;33m--- ($SUBSCWRYPT) BEGIN $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME ---"
|
||||
FOOTER="\\033[0;33m--- ($SUBSCWRYPT) END $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME ---"
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
### run the scwrypt #################################################
|
||||
#####################################################################
|
||||
|
||||
[ ! $SUBSCWRYPT ] && export SUBSCWRYPT=0
|
||||
|
||||
set -o pipefail
|
||||
{
|
||||
[ $HEADER ] && echo $HEADER
|
||||
[[ $LOGFILE =~ ^/dev/null$ ]] && {
|
||||
eval "$RUN_STRING $(printf "%q " "$@")" </dev/tty >/dev/tty 2>&1
|
||||
EXIT_CODE=$?
|
||||
} || {
|
||||
(eval "$RUN_STRING $(printf "%q " "$@")")
|
||||
EXIT_CODE=$?
|
||||
}
|
||||
[ $FOOTER ] && echo $FOOTER
|
||||
[[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m'
|
||||
|
||||
[[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && [ ! $SUBSCWRYPT ] \
|
||||
&& echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m"
|
||||
|
||||
return $EXIT_CODE
|
||||
} 2>&1 | tee --append "$LOGFILE"
|
||||
|
||||
} $@
|
||||
|
@ -6,7 +6,7 @@ SCWRYPTS__ZSH_PLUGIN() {
|
||||
local NAME
|
||||
local TYPE
|
||||
local GROUP
|
||||
zle clear-command-line
|
||||
LBUFFER= RBUFFER=
|
||||
[ ! $SCWRYPT_SELECTION ] && { zle accept-line; return 0; }
|
||||
|
||||
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
|
||||
@ -21,6 +21,33 @@ SCWRYPTS__ZSH_PLUGIN() {
|
||||
zle -N scwrypts SCWRYPTS__ZSH_PLUGIN
|
||||
bindkey $SCWRYPTS_SHORTCUT scwrypts
|
||||
|
||||
#####################################################################
|
||||
SCWRYPTS__ZSH_BUILDER_PLUGIN() {
|
||||
local SCWRYPT_SELECTION=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS | FZF 'select a script' --header-lines 1)
|
||||
local NAME
|
||||
local TYPE
|
||||
local GROUP
|
||||
LBUFFER= RBUFFER=
|
||||
[ ! $SCWRYPT_SELECTION ] && { zle accept-line; return 0; }
|
||||
|
||||
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
|
||||
|
||||
scwrypts --name $NAME --group $GROUP --type $TYPE -- --help >&2 || {
|
||||
zle accept-line
|
||||
return 0
|
||||
}
|
||||
echo
|
||||
|
||||
zle reset-prompt
|
||||
which scwrypts >/dev/null 2>&1\
|
||||
&& LBUFFER="scwrypts" || LBUFFER="$SCWRYPTS_ROOT/scwrypts"
|
||||
|
||||
LBUFFER+=" --name $NAME --group $GROUP --type $TYPE -- "
|
||||
}
|
||||
|
||||
zle -N scwrypts-builder SCWRYPTS__ZSH_BUILDER_PLUGIN
|
||||
bindkey $SCWRYPTS_BUILDER_SHORTCUT scwrypts-builder
|
||||
|
||||
#####################################################################
|
||||
SCWRYPTS__ZSH_PLUGIN_ENV() {
|
||||
local RESET='reset'
|
||||
|
@ -1,10 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/ecr
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
ECR_LOGIN $@
|
||||
MAIN() {
|
||||
ECR_LOGIN $@
|
||||
}
|
||||
|
@ -3,11 +3,9 @@ DEPENDENCIES+=(jq)
|
||||
REQUIRED_ENV+=(AWS__EFS__LOCAL_MOUNT_POINT)
|
||||
|
||||
use cloud/aws/cli
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
EFS_CONNECT() {
|
||||
MAIN() {
|
||||
GETSUDO || exit 1
|
||||
[ ! -d $AWS__EFS__LOCAL_MOUNT_POINT ] && {
|
||||
sudo mkdir $AWS__EFS__LOCAL_MOUNT_POINT \
|
||||
@ -64,6 +62,3 @@ EFS_CONNECT() {
|
||||
FAIL 2 "unable to mount '$FS_ID'"
|
||||
}
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
EFS_CONNECT $@
|
||||
|
@ -3,11 +3,9 @@ DEPENDENCIES+=(jq)
|
||||
REQUIRED_ENV+=(AWS__EFS__LOCAL_MOUNT_POINT)
|
||||
|
||||
use cloud/aws/cli
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
EFS_DISCONNECT() {
|
||||
MAIN() {
|
||||
[ ! -d "$AWS__EFS__LOCAL_MOUNT_POINT" ] && {
|
||||
STATUS 'no efs currently mounted'
|
||||
exit 0
|
||||
@ -32,6 +30,3 @@ EFS_DISCONNECT() {
|
||||
&& SUCCESS "done" \
|
||||
|| FAIL 2 "failed to unmount '$EFS'"
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
EFS_DISCONNECT $@
|
||||
|
@ -1,10 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/eks
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
EKS_CLUSTER_LOGIN $@
|
||||
MAIN() {
|
||||
EKS__CLUSTER_LOGIN $@
|
||||
}
|
||||
|
@ -1,19 +1,11 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/rds
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
CREATE_BACKUP() {
|
||||
MAIN() {
|
||||
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
|
||||
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
|
||||
|
||||
PG_DUMP
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
CREATE_BACKUP $@
|
||||
|
@ -1,20 +1,11 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/rds
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
RDS_INTERACTIVE_LOGIN() {
|
||||
MAIN() {
|
||||
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
|
||||
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
|
||||
|
||||
POSTGRES__LOGIN_INTERACTIVE
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
RDS_INTERACTIVE_LOGIN $@
|
||||
|
@ -1,20 +1,11 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/rds
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
LOAD_BACKUP() {
|
||||
MAIN() {
|
||||
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
|
||||
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
|
||||
|
||||
PG_RESTORE
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
LOAD_BACKUP $@
|
||||
|
@ -1,11 +1,9 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=(cli53)
|
||||
REQUIRED_ENV+=(AWS_PROFILE)
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
ROUTE53_BACKUP() {
|
||||
MAIN() {
|
||||
local BACKUP_PATH="$SCWRYPTS_OUTPUT_PATH/$ENV_NAME/aws-dns-backup/$(date '+%Y-%m-%d')"
|
||||
mkdir -p $BACKUP_PATH >/dev/null 2>&1
|
||||
|
||||
@ -25,12 +23,11 @@ ROUTE53_BACKUP() {
|
||||
for P in ${JOBS[@]}; do wait $P >/dev/null 2>&1; done
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
|
||||
ROUTE53_GET_DOMAINS() {
|
||||
cli53 list --profile $AWS_PROFILE \
|
||||
| awk '{print $2;}' \
|
||||
| sed '1d; s/\.$//'\
|
||||
;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
ROUTE53_BACKUP
|
||||
|
@ -1,10 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/media-sync
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MEDIA_SYNC__PULL $@
|
||||
MAIN() {
|
||||
MEDIA_SYNC__PULL $@
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/media-sync
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MEDIA_SYNC__PUSH $@
|
||||
MAIN() {
|
||||
MEDIA_SYNC__PUSH $@
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
POSTGRES__LOGIN_INTERACTIVE $@
|
||||
|
||||
MAIN() {
|
||||
POSTGRES__LOGIN_INTERACTIVE $@
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
PG_DUMP $@
|
||||
|
||||
MAIN() {
|
||||
PG_DUMP $@
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
PG_RESTORE $@
|
||||
|
||||
MAIN() {
|
||||
PG_RESTORE $@
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use db/postgres
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
RUN_SQL_POSTGRES() {
|
||||
MAIN() {
|
||||
WARNING " \nthis function is in a beta state\n "
|
||||
local _PASS _ARGS=()
|
||||
POSTGRES__SET_LOGIN_ARGS $@
|
||||
|
||||
@ -43,9 +39,3 @@ RUN_SQL_POSTGRES() {
|
||||
&& SUCCESS "finished running '$INPUT_FILE'" \
|
||||
|| FAIL 3 "something went wrong running '$INPUT_FILE' (see above)"
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
WARNING
|
||||
WARNING 'this function is in a beta state'
|
||||
WARNING
|
||||
RUN_SQL_POSTGRES $@
|
||||
|
@ -1,11 +1,8 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=(docker)
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
DOCKER_CLEAN() {
|
||||
MAIN() {
|
||||
WARNING 'this will prune all docker resources from the current machine'
|
||||
WARNING 'pruned resources are PERMANENTLY DELETED'
|
||||
yN 'continue?' || return 1
|
||||
@ -14,6 +11,3 @@ DOCKER_CLEAN() {
|
||||
SUCCESS "IMAGE : $(docker image prune -f 2>/dev/null | tail -n 1)"
|
||||
SUCCESS "VOLUME : $(docker volume prune -f 2>/dev/null | tail -n 1)"
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
DOCKER_CLEAN $@
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
SUCCESS 'hello world!'
|
||||
MAIN() {
|
||||
SUCCESS 'Hello, World!'
|
||||
}
|
||||
|
@ -1,17 +1,9 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use helm
|
||||
use scwrypts
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
unset USAGE
|
||||
HELM__TEMPLATE__GET $@
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -1,17 +1,9 @@
|
||||
#!/bin/zsh
|
||||
DEPENDENCIES+=()
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use helm
|
||||
use scwrypts
|
||||
|
||||
CHECK_ENVIRONMENT
|
||||
#####################################################################
|
||||
|
||||
MAIN() {
|
||||
unset USAGE
|
||||
HELM__DEPENDENCY__UPDATE $@
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
MAIN $@
|
||||
|
@ -1,19 +1,44 @@
|
||||
#####################################################################
|
||||
|
||||
DEPENDENCIES+=(
|
||||
kubectl
|
||||
)
|
||||
|
||||
REQUIRED_ENV+=(
|
||||
AWS_ACCOUNT
|
||||
AWS_REGION
|
||||
)
|
||||
DEPENDENCIES+=(kubectl yq)
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/cli
|
||||
|
||||
#####################################################################
|
||||
|
||||
EKS_CLUSTER_LOGIN() {
|
||||
EKS__KUBECTL() { EKS kubectl $@; }
|
||||
EKS__FLUX() { EKS flux $@; }
|
||||
|
||||
#####################################################################
|
||||
|
||||
EKS() {
|
||||
local USAGE="
|
||||
usage: cli [...kubectl args...]
|
||||
|
||||
args:
|
||||
cli a kubectl-style CLI (e.g. kubectl, helm, flux, etc)
|
||||
|
||||
Allows access to kubernetes CLI commands by configuring environment
|
||||
to point to a specific cluster.
|
||||
"
|
||||
|
||||
REQUIRED_ENV=(AWS_REGION AWS_ACCOUNT CLUSTER_NAME) DEPENDENCIES=(kubectl $1) CHECK_ENVIRONMENT || return 1
|
||||
|
||||
local CONTEXT="arn:aws:eks:${AWS_REGION}:${AWS_ACCOUNT}:cluster/${CLUSTER_NAME}"
|
||||
|
||||
local CONTEXT_ARGS=()
|
||||
case $1 in
|
||||
helm ) CONTEXT_ARGS+=(--kube-context $CONTEXT) ;;
|
||||
* ) CONTEXT_ARGS+=(--context $CONTEXT) ;;
|
||||
esac
|
||||
|
||||
$1 ${CONTEXT_ARGS[@]} ${@:2}
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
|
||||
EKS__CLUSTER_LOGIN() {
|
||||
local USAGE="
|
||||
usage: [...options...]
|
||||
|
||||
@ -25,6 +50,7 @@ EKS_CLUSTER_LOGIN() {
|
||||
cluster in EKS. Also creates the kubeconfig entry if it does not
|
||||
already exist.
|
||||
"
|
||||
REQUIRED_ENV=(AWS_ACCOUNT AWS_REGION) CHECK_ENVIRONMENT || return 1
|
||||
|
||||
local CLUSTER_NAME
|
||||
|
||||
|
116
zsh/lib/cloud/aws/eksctl.module.zsh
Normal file
116
zsh/lib/cloud/aws/eksctl.module.zsh
Normal file
@ -0,0 +1,116 @@
|
||||
#####################################################################
|
||||
|
||||
DEPENDENCIES+=(eksctl)
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use cloud/aws/eks
|
||||
|
||||
#####################################################################
|
||||
|
||||
EKSCTL() {
|
||||
REQUIRED_ENV=(AWS_PROFILE AWS_REGION) CHECK_ENVIRONMENT || return 1
|
||||
|
||||
AWS_PROFILE=$AWS_PROFILE AWS_REGION=$AWS_REGION \
|
||||
eksctl $@
|
||||
}
|
||||
|
||||
EKSCTL__CREATE_IAMSERVICEACCOUNT() {
|
||||
local USAGE="
|
||||
usage: serviceaccount-name namespace [...options...] -- [...'eksctl create iamserviceaccount' args...]
|
||||
|
||||
options:
|
||||
--serviceaccount (required) target k8s:ServiceAccount
|
||||
--namespace (required) target k8s:Namespace
|
||||
--role-name (required) name of the IAM role to assign
|
||||
|
||||
--force don't check for existing serviceaccount and override any existing configuration
|
||||
|
||||
eksctl create iamserviceaccount args:
|
||||
$(eksctl create iamserviceaccount --help 2>&1 | grep -v -- '--name' | grep -v -- '--namespace' | grep -v -- '--role-name' | sed 's/^/ /')
|
||||
"
|
||||
REQUIRED_ENV=(AWS_REGION AWS_ACCOUNT CLUSTER_NAME) CHECK_ENVIRONMENT || return 1
|
||||
|
||||
local SERVICEACCOUNT NAMESPACE ROLE_NAME
|
||||
local FORCE=0
|
||||
local EKSCTL_ARGS=()
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
case $1 in
|
||||
--serviceaccount ) SERVICEACCOUNT=$2; shift 1 ;;
|
||||
--namespace ) NAMESPACE=$2; shift 1 ;;
|
||||
--role-name ) ROLE_NAME=$2; shift 1 ;;
|
||||
|
||||
--force ) FORCE=1 ;;
|
||||
|
||||
-- ) shift 1; break ;;
|
||||
|
||||
* ) ERROR "unknown argument '$1'" ;;
|
||||
esac
|
||||
shift 1
|
||||
done
|
||||
|
||||
while [[ $# -gt 0 ]]; do EKSCTL_ARGS+=($1); shift 1; done
|
||||
|
||||
[ $SERVICEACCOUNT ] || ERROR "--serviceaccount is required"
|
||||
[ $NAMESPACE ] || ERROR "--namespace is required"
|
||||
[ $ROLE_NAME ] || ERROR "--role-name is required"
|
||||
|
||||
CHECK_ERRORS --no-fail || return 1
|
||||
|
||||
##########################################
|
||||
|
||||
[[ $FORCE -eq 0 ]] && {
|
||||
_EKS__CHECK_IAMSERVICEACCOUNT_EXISTS
|
||||
local EXISTS_STATUS=$?
|
||||
case $EXISTS_STATUS in
|
||||
0 )
|
||||
SUCCESS "'$NAMESPACE/$SERVICEACCOUNT' already configured with '$ROLE_NAME'"
|
||||
return 0
|
||||
;;
|
||||
1 ) ;; # role does not exist yet; continue with rollout
|
||||
2 )
|
||||
ERROR "'$NAMESPACE/$SERVICEACCOUNT' has been configured with a different role than '$ROLE_NAME'"
|
||||
REMINDER "must use --force flag to overwrite"
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
STATUS "creating iamserviceaccount" \
|
||||
&& EKSCTL create iamserviceaccount \
|
||||
--cluster $CLUSTER_NAME \
|
||||
--namespace $NAMESPACE \
|
||||
--name $SERVICEACCOUNT \
|
||||
--role-name $ROLE_NAME \
|
||||
--override-existing-serviceaccounts \
|
||||
--approve \
|
||||
${EKSCTL_ARGS[@]} \
|
||||
&& SUCCESS "successfully configured '$NAMESPACE/$SERVICEACCOUNT' with IAM role '$ROLE_NAME'" \
|
||||
|| { ERROR "unable to configure '$NAMESPACE/$SERVICEACCOUNT' with IAM role '$ROLE_NAME' (check cloudformation dashboard for details)"; return 3; }
|
||||
}
|
||||
|
||||
_EKS__CHECK_IAMSERVICEACCOUNT_EXISTS() {
|
||||
STATUS "checking for existing role-arn"
|
||||
local CURRENT_ROLE_ARN=$(
|
||||
EKS__KUBECTL --namespace $NAMESPACE get serviceaccount $SERVICEACCOUNT -o yaml \
|
||||
| YQ -r '.metadata.annotations["eks.amazonaws.com/role-arn"]' \
|
||||
| grep -v '^null$' \
|
||||
)
|
||||
|
||||
[ $CURRENT_ROLE_ARN ] || {
|
||||
STATUS "serviceaccount does not exist or has no configured role"
|
||||
return 1
|
||||
}
|
||||
|
||||
[[ $CURRENT_ROLE_ARN =~ "$ROLE_NAME$" ]] || {
|
||||
STATUS "serviceaccount current role does not match desired role:
|
||||
CURRENT : $CURRENT_ROLE_ARN
|
||||
DESIRED : arn:aws:iam::${AWS_ACCOUNT}:role/$ROLE_NAME
|
||||
"
|
||||
return 2
|
||||
}
|
||||
|
||||
STATUS "serviceaccount current role matches desired role"
|
||||
return 0
|
||||
}
|
@ -86,7 +86,7 @@ RDS__GET_DATABASE_CREDENTIALS() {
|
||||
user-input ) _RDS_AUTH__userinput ;;
|
||||
esac
|
||||
|
||||
[[ $PRINT_PASSWORD -eq 1 ]] && INFO "password : $DB_PASS"
|
||||
[[ $PRINT_PASSWORD -eq 1 ]] && DEBUG "password : $DB_PASS"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
Binary file not shown.
@ -47,6 +47,26 @@ do
|
||||
}
|
||||
done
|
||||
|
||||
#####################################################################
|
||||
|
||||
for GROUP_LOADER in $(env | sed -n 's/^SCWRYPTS_GROUP_LOADER__[a-z_]\+=//p')
|
||||
do
|
||||
[ -f "$GROUP_LOADER" ] && source "$GROUP_LOADER"
|
||||
done
|
||||
|
||||
: \
|
||||
&& [ ! "$SCWRYPTS_AUTODETECT_GROUP_BASEDIR" ] \
|
||||
&& [ $GITHUB_WORKSPACE ] \
|
||||
&& [ ! $SCWRYPTS_GITHUB_NO_AUTOLOAD ] \
|
||||
&& SCWRYPTS_AUTODETECT_GROUP_BASEDIR="$GITHUB_WORKSPACE"
|
||||
|
||||
[ "$SCWRYPTS_AUTODETECT_GROUP_BASEDIR" ] && [ -d "$SCWRYPTS_AUTODETECT_GROUP_BASEDIR" ] && {
|
||||
for GROUP_LOADER in $(find "$SCWRYPTS_AUTODETECT_GROUP_BASEDIR" -type f -name \*scwrypts.zsh)
|
||||
do
|
||||
[ -f "$GROUP_LOADER" ] && source "$GROUP_LOADER"
|
||||
done
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
[ $NO_EXPORT_CONFIG ] || __SCWRYPT=1 # arbitrary; indicates currently inside a scwrypt
|
||||
true
|
||||
|
@ -4,6 +4,7 @@ DEPENDENCIES+=(helm kubeval)
|
||||
REQUIRED_ENV+=()
|
||||
|
||||
use helm/validate
|
||||
use scwrypts
|
||||
|
||||
#####################################################################
|
||||
|
||||
@ -17,6 +18,8 @@ HELM__TEMPLATE__GET() {
|
||||
--colorize use 'bat' to colorize output
|
||||
--raw remove scwrypts-added fluff and only output helm template details
|
||||
|
||||
-h, --help show this help dialogue
|
||||
|
||||
Smart helm-template generator which auto-detects the chart
|
||||
and sample values for testing and developing helm charts.
|
||||
"
|
||||
@ -35,8 +38,11 @@ HELM__TEMPLATE__GET() {
|
||||
;;
|
||||
|
||||
--raw ) RAW=1 ;;
|
||||
|
||||
-h | --help ) USAGE; return 0 ;;
|
||||
-- ) shift 1; break ;;
|
||||
|
||||
|
||||
* ) HELM_ARGS+=($1) ;;
|
||||
esac
|
||||
shift 1
|
||||
@ -60,6 +66,7 @@ HELM__TEMPLATE__GET() {
|
||||
|
||||
[ ! $TEMPLATE_OUTPUT ] && EXIT_CODE=1
|
||||
|
||||
|
||||
[[ $RAW -eq 1 ]] && {
|
||||
[ $USE_CHART_ROOT ] && [[ $USE_CHART_ROOT -eq 1 ]] || HELM_ARGS+=(--show-only $(echo $TEMPLATE_FILENAME | sed "s|^$CHART_ROOT/||"))
|
||||
[[ $COLORIZE -eq 1 ]] \
|
||||
|
@ -27,13 +27,20 @@ HELM__VALIDATE() {
|
||||
return 1
|
||||
}
|
||||
|
||||
CHART_NAME=$(yq -r .name "$CHART_ROOT/Chart.yaml")
|
||||
CHART_NAME=$(YQ -r .name "$CHART_ROOT/Chart.yaml")
|
||||
|
||||
[[ $TEMPLATE_FILENAME =~ values*.yaml$ ]] && {
|
||||
[[ $TEMPLATE_FILENAME =~ values.*.yaml$ ]] && {
|
||||
HELM_ARGS+=(--values $TEMPLATE_FILENAME)
|
||||
USE_CHART_ROOT=1
|
||||
}
|
||||
|
||||
[[ $TEMPLATE_FILENAME =~ tests/.*.yaml$ ]] && {
|
||||
HELM_ARGS+=(--values $TEMPLATE_FILENAME)
|
||||
USE_CHART_ROOT=1
|
||||
}
|
||||
[[ $TEMPLATE_FILENAME =~ .tpl$ ]] \
|
||||
&& USE_CHART_ROOT=1
|
||||
|
||||
[[ $(dirname $TEMPLATE_FILENAME) =~ ^$CHART_ROOT$ ]] \
|
||||
&& USE_CHART_ROOT=1
|
||||
|
||||
@ -55,11 +62,34 @@ _HELM__GET_CHART_ROOT() {
|
||||
|
||||
_HELM__GET_DEFAULT_VALUES_ARGS() {
|
||||
for F in \
|
||||
"$CHART_ROOT/values.yaml" \
|
||||
"$CHART_ROOT/values.test.yaml" \
|
||||
"$CHART_ROOT/tests/default.yaml" \
|
||||
"$CHART_ROOT/values.test.yaml" \
|
||||
"$CHART_ROOT/values.yaml" \
|
||||
;
|
||||
do
|
||||
[ -f "$F" ] && HELM_ARGS+=(--values "$F")
|
||||
[ -f "$F" ] && HELM_ARGS=(--values "$F" $HELM_ARGS)
|
||||
done
|
||||
|
||||
for LOCAL_REPOSITORY in $(\
|
||||
cat "$CHART_ROOT/Chart.yaml" \
|
||||
| YQ -r '.dependencies[] | .repository' \
|
||||
| grep '^file://' \
|
||||
| sed 's|file://||' \
|
||||
)
|
||||
do
|
||||
[[ $LOCAL_REPOSITORY =~ ^[/~] ]] \
|
||||
&& LOCAL_REPOSITORY_ROOT="$LOCAL_REPOSITORY" \
|
||||
|| LOCAL_REPOSITORY_ROOT="$CHART_ROOT/$LOCAL_REPOSITORY" \
|
||||
;
|
||||
|
||||
for F in \
|
||||
"$LOCAL_REPOSITORY_ROOT/tests/default.yaml" \
|
||||
"$LOCAL_REPOSITORY_ROOT/values.test.yaml" \
|
||||
"$LOCAL_REPOSITORY_ROOT/values.yaml" \
|
||||
;
|
||||
do
|
||||
[ -f "$F" ] && HELM_ARGS=(--values "$F" $HELM_ARGS)
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user