Compare commits

..

22 Commits

Author SHA1 Message Date
9783119a7d v3.0.2
=====================================================================

--- Bug fixes ----------------------------

- interactive scripts work again

- color works on zx again
2023-06-22 17:58:55 -06:00
a94d6bc197 v3.0.1
=====================================================================

--- Bug fixes ----------------------------

- fixed variable reference in media-sync

- fixed bug with multi-line list environment variables sometimes gets
  appended to a previous variable line
2023-06-22 15:48:57 -06:00
76a746a53e v3.0.0 "The Great Overhaul"
=====================================================================

Notice the major version change which comes with breaking changes to
2.x! Reconstructs "library" functions for both python and zsh scwrypts,
with changes to virtualenv naming conventions (you'll need to refresh
all virtualenv with the appropriate scwrypt).

--- Changes ------------------------------

- changed a naming convention across zsh scripts, particularly
  removing underscores where there is no need to avoid naming clash
  (e.g. 'zsh/lib/utils/io.zsh' renames '__STATUS' to 'STATUS')

- moved clients reliant on py.lib.http to the py.lib.http module

- python scripts now rely on py.lib.scwrypts.execute

- updated package.json in zx scripts to include `type = module`

- 'scwrypts --list' commandline argument now includes additional
  relevant data for each scwrypt

- environment variables no longer add themselves to be staged in the
  '.env.template'

--- New Features -------------------------

- new 'use' syntax for disjoint import within zsh scripts; took me
  a very long time to convince myself this would be necessary

- introduced scwrypt "groups" to allow portable module creation;
  (i.e. ability add your own scripts from another repo!)

- py.lib.scwrypts.io provides a combined IO stream for quick, hybrid
  use of input/output files and stdin/stdout

- py.lib.fzf provides a wrapper to provide similar functionality to
  zsh/utils/io.zsh including fzf_(head|tail)

- improved efficiency of various scwrypts; notably reducing runtime
  of scwrypts/environment sync

- improved scwrypts CLI by adding new options for exact scwrypt
  matching, better filtering, and prettier/more-detailed interfaces

--- New Scripts --------------------------

- py/twilio )
    basic SMS integration with twilio
     - send-sms

- py/directus )
    interactive directus GET query
     - get-items

- py/discord )
    post message to discord channel or webhook
     - post-message
2023-06-21 20:04:30 -06:00
7617c938b1 v2.8.0
=====================================================================

--- Changes ------------------------------

- python library functions moved to `py/lib`
- python scwrypts renamed in kebob-case to help prevent import
- __name__ == '__main__' enforced on all python scwrypts

--- New Features -------------------------

- `__override` variables now allow values to be force-overwritten
- py.lib.http.client provides a slim `requests.request` wrapper

--- New Scripts --------------------------

py/data/convert )
  quick data converters
   - csv-to-json
   - csv-to-yaml
   - json-to-csv
   - json-to-yaml
   - yaml-to-csv
   - yaml-to-json

py/linear )
  uses the linear.app graphql API for PM tasks
   - comment

--- Bug Fixes ----------------------------

- `scwrypts` handles arguments with quotes and special characters
2023-02-13 21:57:17 -07:00
a1256bb0af v2.6.3
=====================================================================

--- Bug fixes ----------------------------

- s3/media-sync/push now pushes (instead of pull -- oops!)
- fixed a typo in the launch-or-show help
- environment actions no longer overwrite overwrite symlinks
2022-09-30 20:23:37 -06:00
73e26a2ecb v2.6.2
=====================================================================

--- Bug fixes ----------------------------

- VARIABLE__select actually sets the variable value after selection
2022-09-01 07:40:06 -06:00
20b7cc32eb v2.6.1
=====================================================================

--- Bug fixes ----------------------------

- i3/create-local-font-override uses the correct variables for the
  correct settings now
2022-08-24 12:01:24 -06:00
22dd6f8112 v2.6.0
=====================================================================

--- New Scripts --------------------------

- i3 window manager scrypts (see --help for more info)
   - zsh/i3/create-local-font-override
   - zsh/i3/launch-or-show

--- New Features -------------------------

Now support `__select` syntax in environment files!
(see zsh/scwrypts/README.md for more detail)

--- Changes ------------------------------

- moved some rogue configuration files under the scwrypts config
   - ~/.vim/bundle/build.zsh >> ~/.config/scwrypts/vundle.zsh
   - ~/.config/scwrypts/config.dotfile.zsh >> ~/.config/scwrypts/dotfiles.zsh

- __FZF, __FZF_TAIL, and __FZF_HEAD now create prompt+response logs

--- Bug Fixes ----------------------------

- zsh/config/symlinks
   - don't fail when trying to symlink a directory
   - no longer fails when trying to replace a broken symlink

- scwrypts now detects environments which are symlinked

- USAGE syntax now correctly shows the position of the '--' argument
  delimiter

support __select in env files; ignore __lower_case suffix in env files; put blank line before comments in env files

added i3 scripts
2022-08-22 21:47:46 -06:00
710d42e248 v2.5.3
=====================================================================

--- Bug Fixes ----------------------------

- __READ and __EDIT no longer fail on CI, just skip
2022-08-18 19:48:10 -06:00
244c188deb v2.5.2
=====================================================================

--- Changes ------------------------------

- added __GET_RANDOM_COLOR for funsies
2022-08-16 23:29:10 -06:00
5c882597da v2.5.1
=====================================================================

--- Bug Fixes ----------------------------

- git/package/install didn't create build dir if it did not exist
2022-08-16 18:48:56 -06:00
4047aad29e fixed credits bug with dashes 2022-08-16 18:44:25 -06:00
6333a2f6b8 v2.5.0
=====================================================================

--- New Scripts --------------------------

zsh )
  vundle-vim helpers
   - vim/vundle/edit-build-actions
   - vim/vundle/install
   - vim/vundle/rebuild

  quick install-from-source scripts
   - git/package/install
   - git/package/build    (install --only-build)
   - git/package/download (install --only-pull)
   - git/package/update   (install --update)

  config sym-linker (for source-controlled .configs)
   - config/update    (symlinks + terminfo)
   - config/symlinks  (set all symlinks from settings)
   - config/settings  (edit settings which aren't scwrypts env variables)
   - config/terminfo  (load all terminfo from settings)

--- Changes ------------------------------

- helper comments are now inserted in all env files
- removed underscore prefix for standard AWS environment variables
- added "opening" and "finished editing" comments to __EDIT util
- added __USAGE to format $USAGE variable to std:err
- added __ERROR_CHECK to exit if any __ERROR(s) were called
2022-08-16 18:26:55 -06:00
a740a66870 v2.4.0
=====================================================================

--- New Scripts --------------------------

zsh )
  memo program; quickly take notes!
   - memo/open
   - memo/remove
2022-08-10 00:46:07 -06:00
96992e9344 v2.3.0
=====================================================================

--- New Scripts --------------------------

zsh )
  latex + latex template engine
   - latex/build-pdf
   - latex/cleanup
   - latex/create-new
   - latex/get-pdf
   - latex/open-pdf

  beta SQL script -- got tired of floating this; works, but only OK
   - db/run-sql/postgres

--- Changes ------------------------------

- Added 'math', 'basic', and 'times-new-roman' templates to latex
- Added 'readlink' to list of required coreutils
- Added __INPUT to read into a variable with prompt (zsh/utils/io)
- Added $EXECUTION_DIR to interact with the user's working directory

--- Bug Fixes ----------------------------

- subscwrypts no longer force stdout/stderr to tty
2022-08-09 21:51:31 -06:00
e8bb889789 v2.2.0
=====================================================================

--- New Scripts --------------------------

zsh )
  amazon EKS
   - aws/eks/login

--- Changes ------------------------------

- moved global .config to global/config.zsh
- moved various global configurations to global/

--- Bug Fixes ----------------------------

- REDIS_AUTH no longer required to attempt connection
- global configurations now propagate to non-zsh scripts
2022-08-09 13:18:41 -06:00
89e899d49d v2.1.0
=====================================================================

--- New Scripts --------------------------

zsh )
  database backup/restore
   - db/postgres/pg_dump
   - db/postgres/pg_restore
   - aws/rds/create-backup
   - aws/rds/load-backup

  redis-cached curl commands
   - redis/curl

  youtube download
   - youtube/download
   - youtube/get-audio-clip

--- Changes ------------------------------

- 'scwrypts' executable now reloads upon execution to prevent staleness
- added various options to improve api/cli; see 'scwrypts --help' for more

--- Bug Fixes ----------------------------

- fixed an issue with .config settings' visibility to non-zsh scripts
- fixed an issue with command arguments globbing too early
2022-08-02 00:24:29 -06:00
f7eec633ef v2.0.3
=====================================================================

--- Bug Fixes ----------------------------

- rds/interactive-login now escapes special password characters
2022-07-30 11:02:47 -06:00
db0d0092db v2.0.2
=====================================================================

--- Changes ------------------------------

- improved colors readability by naming color variables in zsh/utils

--- Bug Fixes ----------------------------

- aws/efs/mount wouldn't mount if the previous session was not
  explicitly unmounted. now it does :)
2022-07-01 22:17:23 -06:00
f30eb7fb9e v2.0.1
=====================================================================

--- Bug Fixes ----------------------------

- rogue source on empty variable in main executable
2022-07-01 22:17:20 -06:00
eaefc99774 v2.0.0
=====================================================================

Subscwrypts + Environment Inheritance

--- Release Notes ------------------------

- added support for environment inheritance
- added support for arbitrarily nested scripts (subscwrypts)
- added support for CI mode

- improved modularity of zsh/utils module

- refactored to move some data from ~/.config/scwrypts to ~/.local/share/scwrypts

- refactored various scripts to use new subscwrypt api

--- New Scripts --------------------------

zsh )
  - db/interactive/postgres
  - aws/rds/interactive-login
2022-07-01 22:17:15 -06:00
2dcf94199b v1.1.0
=====================================================================

Changed environment dependency checker to be more automatic
2022-07-01 22:17:12 -06:00
185 changed files with 5084 additions and 1048 deletions

14
.config
View File

@ -1,14 +0,0 @@
#
# scwrypts config
#
# resource paths
SCWRYPTS_CONFIG_PATH="$HOME/.config/scwrypts"
SCWRYPTS_ENV_PATH="$SCWRYPTS_CONFIG_PATH/env"
SCWRYPTS_LOG_PATH="$SCWRYPTS_CONFIG_PATH/logs"
SCWRYPTS_OUTPUT_PATH="$HOME/SCWRYPTS"
# ZLE hotkeys
SCWRYPTS_SHORTCUT='' # CTRL + W
SCWRYPTS_ENV_SHORTCUT='' # CTRL + /

25
.env.template Normal file
View File

@ -0,0 +1,25 @@
#!/bin/zsh
export AWS_ACCOUNT=
export AWS_PROFILE=
export AWS_REGION=
export AWS__EFS__LOCAL_MOUNT_POINT=
export DIRECTUS__API_TOKEN=
export DIRECTUS__BASE_URL=
export DISCORD__BOT_TOKEN=
export DISCORD__DEFAULT_AVATAR_URL=
export DISCORD__DEFAULT_CHANNEL_ID=
export I3__BORDER_PIXEL_SIZE=
export I3__DMENU_FONT_SIZE=
export I3__GLOBAL_FONT_SIZE=
export I3__MODEL_CONFIG=
export LINEAR__API_TOKEN=
export MEDIA_SYNC__S3_BUCKET
export MEDIA_SYNC__TARGETS
export REDIS_AUTH=
export REDIS_HOST=
export REDIS_PORT=
export TWILIO__ACCOUNT_SID=
export TWILIO__API_KEY=
export TWILIO__API_SECRET=
export TWILIO__DEFAULT_PHONE_FROM=
export TWILIO__DEFAULT_PHONE_TO=

View File

@ -0,0 +1,32 @@
AWS_ACCOUNT | standard AWS environment variables used by awscli and other tools
AWS_PROFILE |
AWS_REGION |
AWS__EFS__LOCAL_MOUNT_POINT | fully-qualified path to mount the EFS drive
DIRECTUS__API_TOKEN | details for a directus instance
DIRECTUS__BASE_URL |
DISCORD__BOT_TOKEN | details for discord bot
DISCORD__DEFAULT_AVATAR_URL |
DISCORD__DEFAULT_CHANNEL_ID |
I3__BORDER_PIXEL_SIZE | custom i3 configuration settings
I3__DMENU_FONT_SIZE |
I3__GLOBAL_FONT_SIZE |
I3__MODEL_CONFIG |
LINEAR__API_TOKEN | linear.app project management configuration
MEDIA_SYNC__S3_BUCKET | s3 bucket name and filesystem targets for media backups
MEDIA_SYNC__TARGETS |
REDIS_AUTH | redis connection credentials
REDIS_HOST |
REDIS_PORT |
TWILIO__ACCOUNT_SID | twilio account / credentials
TWILIO__API_KEY |
TWILIO__API_SECRET |
TWILIO__DEFAULT_PHONE_FROM |
TWILIO__DEFAULT_PHONE_TO |

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
*.zsh diff
.config diff

View File

@ -1,10 +0,0 @@
#!/bin/zsh
export AWS__EFS__LOCAL_MOUNT_POINT=
export AWS__S3__MEDIA_BUCKET=
export AWS__S3__MEDIA_TARGETS=
export REDIS_AUTH=
export REDIS_HOST=
export REDIS_PORT=
export _AWS_ACCOUNT=
export _AWS_PROFILE=
export _AWS_REGION=

View File

@ -28,12 +28,9 @@ Check out [Meta Scwrypts](./zsh/scwrypts) to quickly set up environments and adj
### No Install / API Usage
Alternatively, the `scwrypts` API can be used directly:
```zsh
./scwrypts (environment-name) (...script-patterns)
./scwrypts [--env environment-name] (...script-name-patterns...) [-- ...passthrough arguments... ]
```
If not already set with `$SCWRYPTS_ENV`, Scwrypts will try to load `$1` as an environment.
If no environment with the name `$1` is found, `$1` is assumed to be a script pattern.
Given one or more script patterns, Scwrypts will filter the commands by pattern conjunction.
If only one command is found which matches the pattern(s), it will immediately begin execution.
If multiple commands match, the user will be prompted to select from the filtered list.
@ -44,6 +41,15 @@ Given no script patterns, Scwrypts becomes an interactive CLI, prompting the use
After determining which script to run, if no environment has been specified, Scwrypts prompts the user to choose one.
### Using in CI/CD or Automated Workflows
Set environment variable `CI=true` (and use the no install method) to run in an automated pipeline.
There are a few notable changes to this runtime:
- **The Scwrypts sandbox environment will not load.** All variables will be read from context.
- User yes/no prompts will **always be YES**
- Other user input will default to an empty string
- Logs will not be captured
## Contributing
Before contributing an issue, idea, or pull request, check out the [super-brief contributing guide](./docs/CONTRIBUTING.md)

1
py/.gitignore vendored
View File

@ -1,4 +1,3 @@
__pycache__/
*.py[cod]
*.so
.env/

0
py/data/__init__.py Normal file
View File

View File

24
py/data/convert/csv-to-json.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'csv',
output_stream = stream.output,
output_type = 'json',
)
#####################################################################
execute(main,
description = 'convert csv into json',
parse_args = [],
)

24
py/data/convert/csv-to-yaml.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'csv',
output_stream = stream.output,
output_type = 'yaml',
)
#####################################################################
execute(main,
description = 'convert csv into yaml',
parse_args = [],
)

24
py/data/convert/json-to-csv.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'json',
output_stream = stream.output,
output_type = 'csv',
)
#####################################################################
execute(main,
description = 'convert json into csv',
parse_args = [],
)

24
py/data/convert/json-to-yaml.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'json',
output_stream = stream.output,
output_type = 'yaml',
)
#####################################################################
execute(main,
description = 'convert json into yaml',
parse_args = [],
)

24
py/data/convert/yaml-to-csv.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'yaml',
output_stream = stream.output,
output_type = 'csv',
)
#####################################################################
execute(main,
description = 'convert yaml into csv',
parse_args = [],
)

24
py/data/convert/yaml-to-json.py Executable file
View File

@ -0,0 +1,24 @@
#!/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()
#####################################################################
def main(_args, stream):
return convert(
input_stream = stream.input,
input_type = 'yaml',
output_stream = stream.output,
output_type = 'json',
)
#####################################################################
execute(main,
description = 'convert yaml into json',
parse_args = [],
)

0
py/directus/__init__.py Normal file
View File

145
py/directus/get-items.py Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/env python
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 py.lib.scwrypts.exceptions import ImportedExecutableError
if __name__ != '__main__':
raise ImportedExecutableError()
#####################################################################
def main(args, stream):
if {None} == { args.collection, args.filters, args.fields }:
args.interactive = True
if args.interactive:
args.generate_filters_prompt = True
args.generate_fields_prompt = True
collection = _get_or_select_collection(args)
filters = _get_or_select_filters(args, collection)
fields = _get_or_select_fields(args, collection)
query = '&'.join([
param for param in [
fields,
filters,
]
if param
])
endpoint = f'items/{collection}?{query}'
response = directus.request('GET', endpoint)
stream.writeline(dumps({
**response.json(),
'scwrypts_metadata': {
'endpoint': endpoint,
'repeat_with': f'scwrypts -n py/directus/get-items -- -c {collection} -f \'{query}\'',
},
}))
def _get_or_select_collection(args):
collection = args.collection
if collection is None:
collection = fzf(
prompt = 'select a collection',
choices = directus.get_collections(),
)
if not collection:
raise ValueError('collection required for query')
return collection
def _get_or_select_filters(args, collection):
filters = args.filters or ''
if filters == '' and args.generate_filters_prompt:
filters = '&'.join([
f'filter[{filter}][' + (
operator := fzf(
prompt = f'select operator for {filter}',
choices = directus.FILTER_OPERATORS,
)
) + ']=' + fzf_tail(prompt = f'filter[{filter}][{operator}]')
for filter in fzf(
prompt = 'select filter(s) [C^c to skip]',
fzf_options = '--multi',
force_list = True,
choices = directus.get_fields(collection),
)
])
return filters
def _get_or_select_fields(args, collection):
fields = args.fields or ''
if fields == '' and args.generate_fields_prompt:
fields = ','.join(fzf(
prompt = 'select return field(s) [C^c to get all]',
fzf_options = '--multi',
choices = directus.get_fields(collection),
force_list = True,
))
if fields:
fields = f'fields[]={fields}'
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,
}),
]
)

0
py/discord/__init__.py Normal file
View File

61
py/discord/post-message.py Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
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.body is None:
print(f'reading input from {stream.input.name}', file=stderr)
args.body = ''.join(stream.readlines()).strip()
if len(args.body) == 0:
args.body = 'PING'
response = discord.send_message(
content = args.body,
channel_id = args.channel_id,
webhook = args.webhook,
avatar_url = args.avatar_url,
)
stream.writeline(dumps({
**(response.json() if response.text != '' else {'message': 'OK'}),
'scwrypts_metadata': {},
}))
#####################################################################
execute(main,
description = 'post a message to the indicated discord channel',
parse_args = [
( ['-b', '--body'], {
'dest' : 'body',
'help' : 'message body',
'required' : False,
}),
( ['-c', '--channel-id'], {
'dest' : 'channel_id',
'help' : 'target channel id',
'required' : False,
}),
( ['-w', '--webhook'], {
'dest' : 'webhook',
'help' : 'target webhook (takes precedence over -c)',
'required' : False,
}),
( ['--avatar-url'], {
'dest' : 'avatar_url',
'help' : 'replace default avatar_url',
'required' : False,
}),
]
)

27
py/hello-world.py Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env python
from py.lib.scwrypts import execute
from py.lib.scwrypts.exceptions import ImportedExecutableError
if __name__ != '__main__':
raise ImportedExecutableError()
#####################################################################
def main(args, stream):
stream.writeline(args.message)
#####################################################################
execute(main,
description = 'a simple "Hello, World!" program',
parse_args = [
( ['-m', '--message'], {
'dest' : 'message',
'default' : 'HELLO WORLD',
'help' : 'message to print',
'required' : False,
}),
],
)

View File

@ -1,7 +0,0 @@
#!/usr/bin/env python
def main():
print('HELLO WORLD')
if __name__ == '__main__':
main()

6
py/lib/__init__.py Normal file
View File

@ -0,0 +1,6 @@
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
py/lib/data/__init__.py Normal file
View File

@ -0,0 +1 @@
import py.lib.data.converter

73
py/lib/data/converter.py Normal file
View File

@ -0,0 +1,73 @@
import csv
import json
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)
def convert_input(stream, input_type):
supported_input_types = {'csv', 'json', 'yaml'}
if input_type not in supported_input_types:
raise ValueError(f'input_type "{input_type}" not supported; must be one of {supported_input_types}')
return {
'csv': _read_csv,
'json': _read_json,
'yaml': _read_yaml,
}[input_type](stream)
def write_output(stream, output_type, data):
supported_output_types = {'csv', 'json', 'yaml'}
if output_type not in supported_output_types:
raise ValueError(f'output_type "{output_type}" not supported; must be one of {supported_output_types}')
return {
'csv': _write_csv,
'json': _write_json,
'yaml': _write_yaml,
}[output_type](stream, data)
#####################################################################
def _read_csv(stream):
return [dict(line) for line in csv.DictReader(stream)]
def _write_csv(stream, data):
writer = csv.DictWriter(stream, fieldnames=list({
key
for dictionary in data
for key in dictionary.keys()
}))
writer.writeheader()
for value in data:
writer.writerow(value)
#####################################################################
def _read_json(stream):
data = json.loads(stream.read())
return data if isinstance(data, list) else [data]
def _write_json(stream, data):
stream.write(json.dumps(data))
#####################################################################
def _read_yaml(stream):
data = yaml.safe_load(stream)
return data if isinstance(data, list) else [data]
def _write_yaml(stream, data):
yaml.dump(data, stream, default_flow_style=False)

1
py/lib/fzf/__init__.py Normal file
View File

@ -0,0 +1 @@
from py.lib.fzf.client import fzf, fzf_tail, fzf_head

61
py/lib/fzf/client.py Normal file
View File

@ -0,0 +1,61 @@
from pyfzf.pyfzf import FzfPrompt
FZF_PROMPT = None
def fzf( # pylint: disable=too-many-arguments
choices=None,
prompt=None,
fzf_options='',
delimiter='\n',
return_type=str,
force_list=False,
):
global FZF_PROMPT # pylint: disable=global-statement
if choices is None:
choices = []
if not isinstance(return_type, type):
raise ValueError(f'return_type must be a valid python type; "{return_type}" is not a type')
if FZF_PROMPT is None:
FZF_PROMPT = FzfPrompt()
options = ' '.join({
'-i',
'--layout=reverse',
'--ansi',
'--height=30%',
f'--prompt "{prompt} : "' if prompt is not None else '',
fzf_options,
})
selections = [
return_type(selection)
for selection in FZF_PROMPT.prompt(choices, options, delimiter)
]
if not force_list:
if len(selections) == 0:
return None
if len(selections) == 1:
return selections[0]
return selections
def fzf_tail(*args, **kwargs):
return _fzf_print(*args, **kwargs)[-1]
def fzf_head(*args, **kwargs):
return _fzf_print(*args, **kwargs)[0]
def _fzf_print(*args, fzf_options='', **kwargs):
return fzf(
*args,
**kwargs,
fzf_options = f'--print-query {fzf_options}',
force_list = True,
)

5
py/lib/http/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from py.lib.http.client import get_request_client
import py.lib.http.directus
import py.lib.http.discord
import py.lib.http.linear

20
py/lib/http/client.py Normal file
View File

@ -0,0 +1,20 @@
from requests import request
def get_request_client(base_url, headers=None):
if headers is None:
headers = {}
return lambda method, endpoint, **kwargs: request(
method = method,
url = f'{base_url}/{endpoint}',
headers = {
**headers,
**kwargs.get('headers', {}),
},
**{
key: value
for key, value in kwargs.items()
if key != 'headers'
},
)

View File

@ -0,0 +1,2 @@
from py.lib.http.directus.client import *
from py.lib.http.directus.constant import *

View File

@ -0,0 +1,56 @@
from py.lib.http import get_request_client
from py.lib.scwrypts import getenv
REQUEST = None
COLLECTIONS = None
FIELDS = {}
def request(method, endpoint, **kwargs):
global REQUEST # pylint: disable=global-statement
if REQUEST is None:
REQUEST = get_request_client(
base_url = getenv("DIRECTUS__BASE_URL"),
headers = {
'Authorization': f'bearer {getenv("DIRECTUS__API_TOKEN")}',
}
)
return REQUEST(method, endpoint, **kwargs)
def graphql(query, system=False):
return request(
'POST',
'graphql' if system is True else 'graphql/system',
json={'query': query},
)
def get_collections():
global COLLECTIONS # pylint: disable=global-statement
if COLLECTIONS is None:
COLLECTIONS = [
item['collection']
for item in request(
'GET',
'collections?limit=-1&fields[]=collection',
).json()['data']
]
return COLLECTIONS
def get_fields(collection):
if FIELDS.get(collection) is None:
FIELDS[collection] = [
item['field']
for item in request(
'GET',
f'fields/{collection}?limit=-1&fields[]=field',
).json()['data']
]
return FIELDS[collection]

View File

@ -0,0 +1,25 @@
FILTER_OPERATORS = {
'_eq',
'_neq',
'_lt',
'_lte',
'_gt',
'_gte',
'_in',
'_nin',
'_null',
'_nnull',
'_contains',
'_ncontains',
'_starts_with',
'_ends_with',
'_nends_with',
'_between',
'_nbetween',
'_empty',
'_nempty',
'_intersects',
'_nintersects',
'_intersects_bbox',
'_nintersects_bbox',
}

View File

@ -0,0 +1,2 @@
from py.lib.http.discord.client import *
from py.lib.http.discord.send_message import *

View File

@ -0,0 +1,20 @@
from py.lib.http import get_request_client
from py.lib.scwrypts import getenv
REQUEST = None
def request(method, endpoint, **kwargs):
global REQUEST # pylint: disable=global-statement
if REQUEST is None:
headers = {}
if (token := getenv("DISCORD__BOT_TOKEN", required = False)) is not None:
headers['Authorization'] = f'Bot {token}'
REQUEST = get_request_client(
base_url = 'https://discord.com/api',
headers = headers,
)
return REQUEST(method, endpoint, **kwargs)

View File

@ -0,0 +1,34 @@
from py.lib.scwrypts import getenv
from py.lib.http.discord import request
def send_message(content, channel_id=None, webhook=None, avatar_url=None, **kwargs):
if channel_id is None:
channel_id = getenv('DISCORD__DEFAULT_CHANNEL_ID', required=False)
if avatar_url is None:
avatar_url = getenv('DISCORD__DEFAULT_AVATAR_URL', required=False)
endpoint = None
if webhook is not None:
endpoint = f'webhooks/{webhook}'
elif channel_id is not None:
endpoint = f'channels/{channel_id}/messages'
else:
raise ValueError('must provide target channel_id or webhook')
return request(
method = 'POST',
endpoint = endpoint,
json = {
key: value
for key, value in {
'content': content,
'username': 'wrobot',
'avatar_url': avatar_url,
**kwargs,
}.items()
if value is not None
},
)

View File

@ -0,0 +1 @@
from py.lib.http.linear.client import *

View File

@ -0,0 +1,20 @@
from py.lib.http import get_request_client
from py.lib.scwrypts import getenv
REQUEST = None
def request(method, endpoint, **kwargs):
global REQUEST # pylint: disable=global-statement
if REQUEST is None:
REQUEST = get_request_client(
base_url = 'https://api.linear.app',
headers = {
'Authorization': f'bearer {getenv("LINEAR__API_TOKEN")}',
}
)
return REQUEST(method, endpoint, **kwargs)
def graphql(query):
return request('POST', 'graphql', json={'query': query})

1
py/lib/redis/__init__.py Normal file
View File

@ -0,0 +1 @@
from py.lib.redis.client import get_client

19
py/lib/redis/client.py Normal file
View File

@ -0,0 +1,19 @@
from redis import StrictRedis
from py.lib.scwrypts import getenv
CLIENT = None
def get_client():
global CLIENT # pylint: disable=global-statement
if CLIENT is None:
print('getting redis client')
CLIENT = StrictRedis(
host = getenv('REDIS_HOST'),
port = getenv('REDIS_PORT'),
password = getenv('REDIS_AUTH', required=False),
decode_responses = True,
)
return CLIENT

View File

@ -0,0 +1,6 @@
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
import py.lib.scwrypts.io

View File

@ -0,0 +1,16 @@
from argparse import ArgumentError
class MissingVariableError(EnvironmentError):
def init(self, name):
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} }}')

View File

@ -0,0 +1,23 @@
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from py.lib.scwrypts.io import get_combined_stream, add_io_arguments
def execute(main, description=None, parse_args=None, toggle_input=True, toggle_output=True):
if parse_args is None:
parse_args = []
parser = ArgumentParser(
description = description,
formatter_class = ArgumentDefaultsHelpFormatter,
)
add_io_arguments(parser, toggle_input, toggle_output)
for a in parse_args:
parser.add_argument(*a[0], **a[1])
args = parser.parse_args()
with get_combined_stream(args.input_file, args.output_file) as stream:
return main(args, stream)

15
py/lib/scwrypts/getenv.py Normal file
View File

@ -0,0 +1,15 @@
from os import getenv as os_getenv
from py.lib.scwrypts.exceptions import MissingVariableError
def getenv(name, required=True):
value = os_getenv(name, None)
if required and not value:
raise MissingVariableError(name)
if value == '':
value = None
return value

View File

@ -0,0 +1,22 @@
from bpython import embed
def interactive(variable_descriptions):
def outer(function):
def inner(*args, **kwargs):
print('\npreparing interactive environment...\n')
local_vars = function(*args, **kwargs)
print('\n\n'.join([
f'>>> {x}' for x in variable_descriptions
]))
print('\nenvironment ready; user, GO! :)\n')
embed(local_vars)
return inner
return outer

86
py/lib/scwrypts/io.py Normal file
View File

@ -0,0 +1,86 @@
from contextlib import contextmanager
from pathlib import Path
from sys import stdin, stdout, stderr
from py.lib.scwrypts.getenv import getenv
@contextmanager
def get_stream(filename=None, mode='r', encoding='utf-8', verbose=False, **kwargs):
allowed_modes = {'r', 'w', 'w+'}
if mode not in allowed_modes:
raise ValueError(f'mode "{mode}" not supported modes (must be one of {allowed_modes})')
is_read = mode == 'r'
if filename is not None:
if verbose:
print(f'opening file {filename} for {"read" if is_read else "write"}', file=stderr)
if filename[0] not in {'/', '~'}:
filename = Path(f'{getenv("EXECUTION_DIR")}/{filename}').resolve()
with open(filename, mode=mode, encoding=encoding, **kwargs) as stream:
yield stream
else:
if verbose:
print('using stdin for read' if is_read else 'using stdout for write', file=stderr)
yield stdin if is_read else stdout
if not is_read:
stdout.flush()
def add_io_arguments(parser, toggle_input=True, toggle_output=True):
if toggle_input:
parser.add_argument(
'-i', '--input-file',
dest = 'input_file',
default = None,
help = 'path to input file; omit for stdin',
required = False,
)
if toggle_output:
parser.add_argument(
'-o', '--output-file',
dest = 'output_file',
default = None,
help = 'path to output file; omit for stdout',
required = False,
)
@contextmanager
def get_combined_stream(input_file=None, output_file=None):
with get_stream(input_file, 'r') as input_stream, get_stream(output_file, 'w+') as output_stream:
yield CombinedStream(input_stream, output_stream)
class CombinedStream:
def __init__(self, input_stream, output_stream):
self.input = input_stream
self.output = output_stream
def read(self, *args, **kwargs):
return self.input.read(*args, **kwargs)
def readline(self, *args, **kwargs):
return self.input.readline(*args, **kwargs)
def readlines(self, *args, **kwargs):
return self.input.readlines(*args, **kwargs)
def write(self, *args, **kwargs):
return self.output.write(*args, **kwargs)
def writeline(self, line):
x = self.output.write(f'{line}\n')
self.output.flush()
return x
def writelines(self, *args, **kwargs):
return self.output.writelines(*args, **kwargs)

22
py/lib/scwrypts/run.py Normal file
View File

@ -0,0 +1,22 @@
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')

View File

@ -0,0 +1,2 @@
from py.lib.twilio.client import get_client
from py.lib.twilio.send_sms import send_sms

18
py/lib/twilio/client.py Normal file
View File

@ -0,0 +1,18 @@
from twilio.rest import Client
from py.lib.scwrypts import getenv
CLIENT = None
def get_client():
global CLIENT # pylint: disable=global-statement
if CLIENT is None:
print('loading client')
CLIENT = Client(
username = getenv('TWILIO__API_KEY'),
password = getenv('TWILIO__API_SECRET'),
account_sid = getenv('TWILIO__ACCOUNT_SID'),
)
return CLIENT

57
py/lib/twilio/send_sms.py Normal file
View File

@ -0,0 +1,57 @@
from json import dumps
from time import sleep
from py.lib.twilio.client import get_client
def send_sms(to, from_, body, max_char_count=300, stream=None):
'''
abstraction for twilio.client.messages.create which will break
messages into multi-part SMS rather than throwing an error or
requiring the use of MMS data
@param to messages.create parameter
@param from_ messages.create parameter
@param body messages.create parameter
@param max_char_count 1 ≤ N ≤ 1500 (default 300)
@param stream used to report success/failure (optional)
@return a list of twilio MessageInstance objects
'''
client = get_client()
messages = []
max_char_count = max(1, min(max_char_count, 1500))
total_sms_parts = 1 + len(body) // max_char_count
contains_multiple_parts = total_sms_parts > 1
for i in range(0, len(body), max_char_count):
msg_body = body[i:i+max_char_count]
current_part = 1 + i // max_char_count
if contains_multiple_parts:
msg_body = f'{current_part}/{total_sms_parts}\n{msg_body}'
message = client.messages.create(
to = to,
from_ = from_,
body = msg_body,
)
messages.append(message)
if stream is not None:
stream.writeline(
dumps({
'sid': message.sid,
'to': to,
'from': from_,
'body': msg_body,
})
)
if contains_multiple_parts:
sleep(2 if max_char_count <= 500 else 5)
return messages

0
py/linear/__init__.py Normal file
View File

45
py/linear/comment.py Executable file
View File

@ -0,0 +1,45 @@
#!/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()
#####################################################################
def get_query(args):
body = f'"""from wrobot:\n```\n{args.message}\n```\n"""'
return f'''
mutation CommentCreate {{
commentCreate(
input: {{
issueId: "{args.issue_id}"
body: {body}
}}
) {{ success }}
}}'''
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,
}),
]
)

View File

@ -1,15 +0,0 @@
from redis import StrictRedis
from py.scwrypts import getenv
class RedisClient(StrictRedis):
def __init__(self):
super().__init__(
host = getenv('REDIS_HOST'),
port = getenv('REDIS_PORT'),
password = getenv('REDIS_AUTH'),
decode_responses = True,
)
Client = RedisClient()

View File

@ -1,20 +1,26 @@
#!/usr/bin/env python
from os import getenv
from py.lib.redis import get_client
from py.lib.scwrypts import execute, interactive, getenv
from py.redis.client import Client
from py.scwrypts import interactive
from py.lib.scwrypts.exceptions import ImportedExecutableError
if __name__ != '__main__':
raise ImportedExecutableError()
#####################################################################
@interactive
def main():
r = Client
print('''
r = StrictRedis("{getenv("REDIS_HOST")}")
''')
@interactive([
f'r = StrictRedis(\'{getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}\')',
])
def main(_args, _stream):
# pylint: disable=possibly-unused-variable
r = get_client()
return locals()
if __name__ == '__main__':
main()
#####################################################################
execute(main,
description = 'establishes a redis client in an interactive python shell',
parse_args = [],
)

View File

@ -1,2 +1,5 @@
redis
bpython
pyfzf
pyyaml
redis
twilio

View File

@ -1,2 +0,0 @@
from py.scwrypts.getenv import getenv
from py.scwrypts.interactive import interactive

View File

@ -1,3 +0,0 @@
class MissingVariableError(Exception):
def init(self, name):
super().__init__(f'Missing required environment variable "{name}"')

View File

@ -1,23 +0,0 @@
from os import getenv as os_getenv
from pathlib import Path
from subprocess import run
from py.scwrypts.exceptions import MissingVariableError
def getenv(name, required=True):
value = os_getenv(name, None)
if value == None:
ZSH_COMMAND = Path(__file__).parents[2] / 'zsh/scwrypts/environment/stage-variables'
run(
f'{ZSH_COMMAND} {name}',
shell=True,
executable='/bin/zsh',
)
if required:
raise MissingVariableError(name)
return value

View File

@ -1,9 +0,0 @@
from bpython import embed
def interactive(function):
def main(*args, **kwargs):
local_vars = function(*args, **kwargs)
embed(local_vars)
return main

0
py/twilio/__init__.py Normal file
View File

65
py/twilio/send-sms.py Executable file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
from sys import stderr
from py.lib.scwrypts import execute, getenv
from py.lib.twilio import send_sms
from py.lib.scwrypts.exceptions import ImportedExecutableError, MissingFlagAndEnvironmentVariableError
if __name__ != '__main__':
raise ImportedExecutableError()
#####################################################################
def main(args, stream):
if args.body is None:
print(f'reading input from {stream.input.name}', file=stderr)
args.body = ''.join(stream.readlines()).strip()
if len(args.body) == 0:
args.body = 'PING'
if args.from_ is None:
raise MissingFlagAndEnvironmentVariableError(['-f', '--from'], 'TWILIO__DEFAULT_PHONE_FROM')
if args.to is None:
raise MissingFlagAndEnvironmentVariableError(['-t', '--to'], 'TWILIO__DEFAULT_PHONE_TO')
send_sms(
to = args.to,
from_ = args.from_,
body = args.body,
max_char_count = args.max_char_count,
stream = 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,
}),
]
)

322
run Executable file
View File

@ -0,0 +1,322 @@
#!/bin/zsh
export EXECUTION_DIR=$(pwd)
source "${0:a:h}/zsh/lib/import.driver.zsh" || exit 42
#####################################################################
__RUN() {
local USAGE='
usage: scwrypts [OPTIONS ...] SCRIPT -- [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
-m, --name <scwrypt-name> only run the script if there is an exact match
(requires type and group)
-e, --env <env-name> set environment; overwrites SCWRYPTS_ENV
-n, --no-log skip logging and run in quiet mode
--update update scwrypts library to latest version
-v, --version print out scwrypts version and exit
-l, --list print out command list and exit
-h, --help display this message and exit
'
cd "$SCWRYPTS_ROOT"
local ENV_NAME="$SCWRYPTS_ENV"
local SEARCH_PATTERNS=()
local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME
local ERROR=0
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}
;;
-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)
return 0
;;
--update )
cd "$SCWRYPTS__ROOT__scwrypts"
git fetch --quiet origin main
local SYNC_STATUS=$?
git diff --exit-code origin/main -- . >&2
local DIFF_STATUS=$?
[[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && {
SUCCESS 'already up-to-date with origin/main'
} || {
git rebase --autostash origin/main \
&& SUCCESS 'up-to-date with origin/main' \
|| {
git 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'"
shift 1
;;
* )
SEARCH_PATTERNS+=($1)
shift 1
;;
esac
done
[ $SEARCH_NAME ] && {
[ ! $SEARCH_TYPE ] && ERROR '--name requires --type argument'
[ ! $SEARCH_GROUP ] && ERROR '--name requires --group argument'
}
CHECK_ERRORS
##########################################
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=$({
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
echo $SCWRYPTS_AVAILABLE | grep ' [^/]*'$SEARCH_TYPE'[^/]* '
} \
| awk '{$2=""; print $0;}' \
| sed 's/ \+$/'$(printf $__COLOR_RESET)'/; s/ \+/^/g' \
| column -ts '^'
)
}
[ $SEARCH_GROUP ] && {
POTENTIAL_ERROR+="\n GROUP : '$SEARCH_GROUP'"
SCWRYPTS_AVAILABLE=$(
{
echo $SCWRYPTS_AVAILABLE | head -n1
echo $SCWRYPTS_AVAILABLE | grep "$SEARCH_GROUP"'[^/]*$'
} \
| awk '{$NF=""; print $0;}' \
| sed 's/ \+$/'$(printf $__COLOR_RESET)'/; s/ \+/^/g' \
| column -ts '^'
)
}
[[ ${#SEARCH_PATTERNS[@]} -gt 0 ]] && {
POTENTIAL_ERROR+="\n PATTERNS : $SEARCH_PATTERNS"
local P
for P in ${SEARCH_PATTERNS[@]}
do
SCWRYPTS_AVAILABLE=$(
{
echo $SCWRYPTS_AVAILABLE | head -n1
echo $SCWRYPTS_AVAILABLE | grep $P
}
)
done
}
}
[[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -lt 2 ]] && ERROR "$POTENTIAL_ERROR"
CHECK_ERRORS
##########################################
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 ] || exit 2
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
export SCWRYPT_NAME=$NAME
export SCWRYPT_TYPE=$TYPE
export SCWRYPT_GROUP=$GROUP
##########################################
local ENV_REQUIRED=$(__CHECK_ENV_REQUIRED && echo 1 || echo 0)
[[ $ENV_REQUIRED -eq 1 ]] && {
[ ! $ENV_NAME ] && ENV_NAME=$(SCWRYPTS__SELECT_ENV)
local ENV_FILE=$(SCWRYPTS__GET_ENV_FILE "$ENV_NAME")
source "$ENV_FILE" || FAIL 5 "missing or invalid environment '$ENV_NAME'"
export ENV_NAME
}
for f in $(eval 'echo $SCWRYPTS_STATIC_CONFIG__'$SCWRYPT_GROUP)
do
source "$f" || FAIL 5 "invalid static config '$f'"
done
##########################################
[ ! $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() {
STATUS "on '$ENV_NAME'; checking diff against origin/main"
git fetch --quiet origin main
local SYNC_STATUS=$?
git 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
yN 'continue?' || return 1
}
}
__GET_LOGFILE() {
[ $SUBSCWRYPT ] \
|| [[ $SCWRYPT_NAME =~ scwrypts/logs ]] \
|| [[ $SCWRYPT_NAME =~ interactive ]] \
&& return 0
echo 'gets a logfile' >&2
echo "$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log"
}
#####################################################################
__RUN $@

151
scwrypts
View File

@ -1,151 +1,2 @@
#!/bin/zsh
SCWRYPTS_ROOT="${0:a:h}"
source "$SCWRYPTS_ROOT/zsh/common.zsh"
__CHECK_DEPENDENCIES \
fzf \
;
__CHECK_IMPORTS
#####################################################################
__RUN() {
cd "$SCWRYPTS_ROOT"
##########################################
### parse arguments ######################
##########################################
local ENV_NAME
[ $SCWRYPTS_ENV ] && ENV_NAME="$SCWRYPTS_ENV" || {
[ $1 ] && [ -f $(__GET_ENV_FILE $1) ] && {
ENV_NAME="$1"
shift 1
}
}
local SCRIPT
local SCRIPTS=$(__GET_AVAILABLE_SCRIPTS)
[ $1 ] && {
for PATTERN in $*
do
shift 1
[[ $PATTERN =~ ^--$ ]] && break
SCRIPT=$(echo $SCRIPTS | grep $PATTERN)
done
[ ! $SCRIPT ] && __FAIL 2 "no script found by name '$@'"
[[ $(echo $SCRIPT | wc -l) -gt 1 ]] && {
__STATUS "more than one script matched '$@'"
SCRIPT=$(echo $SCRIPT | __FZF 'select a script')
}
true
} || SCRIPT=$(echo $SCRIPTS | __FZF 'select a script')
[ ! $SCRIPT ] && exit 2
##########################################
### check type and min dependencies ######
##########################################
local ENV_REQUIRED=1
local RUN_STRING="./$SCRIPT"
local TYPE=$(echo $SCRIPT | sed 's/\/.*$//')
local VIRTUALENV="$SCWRYPTS_ROOT/$TYPE/.env/bin/activate"
[ -f $VIRTUALENV ] && source $VIRTUALENV
case $TYPE in
py ) __CHECK_DEPENDENCY python || exit 3
python --version | grep -q '3.[91]' || {
__WARNING 'only tested on python>=3.9'
__WARNING 'compatibility may vary'
}
RUN_STRING="python -m $(echo $SCRIPT | sed 's/\//./g; s/\.py$//; s/\.\.//')"
;;
zsh ) __CHECK_DEPENDENCY zsh || exit 3
echo $SCRIPT | grep -q 'scwrypts' && ENV_REQUIRED=0
RUN_STRING="./$SCRIPT"
;;
zx ) __CHECK_DEPENDENCY zx || exit 3
RUN_STRING="FORCE_COLOR=3 ./$SCRIPT.mjs"
;;
* ) __FAIL 4 "unsupported script type '$SCRIPT_TYPE'" ;;
esac
##########################################
### load scwrypts env and virtualenv #####
##########################################
[[ $ENV_REQUIRED -eq 1 ]] && {
[ ! $ENV_NAME ] && ENV_NAME=$(__SELECT_ENV)
[ ! $ENV_NAME ] && __ABORT
local ENV_FILE=$(__GET_ENV_FILE $ENV_NAME)
[ -f "$ENV_FILE" ] \
&& source "$ENV_FILE" \
&& export ENV_NAME \
|| __FAIL 5 "missing or invalid environment '$ENV_NAME'"
[[ $ENV_NAME =~ prod ]] && {
__STATUS "on '$ENV_NAME'; checking diff against origin/main"
git fetch --quiet origin main \
&& git diff --exit-code origin/main -- . >&2 \
&& __SUCCESS 'up-to-date with main!' \
|| {
__WARNING
__WARNING 'your branch differs from origin/main'
__WARNING 'in '$ENV_NAME', being out-of-sync with main may have BAD CONSEQUENCES'
__WARNING
__yN 'continue?' || __ABORT
}
}
}
##########################################
### run the scwrypt ######################
##########################################
local HEADER=$(
echo '====================================================================='
echo "script : $SCRIPT"
echo "run at : $(date)"
echo "config : $ENV_NAME"
echo '------------------------------------------'
)
echo $SCRIPT | grep -q 'interactive' && {
echo $HEADER
eval $RUN_STRING $@ </dev/tty >/dev/tty 2>&1; exit $?
}
local LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $SCRIPT | sed 's/^\.\///; s/\//\%/g').log"
[[ $SCRIPT =~ scwrypts/logs ]] && LOGFILE=/dev/null
{
echo $HEADER
echo '--- BEGIN OUTPUT--------------------------'
eval $RUN_STRING $@; local EXIT_CODE="$?"
echo '--- END OUTPUT ---------------------------'
local C
[[ $EXIT_CODE -eq 0 ]] && C='32m' || C='31m';
echo "terminated with\\033[1;$C code $EXIT_CODE\\033[0m"
} 2>&1 | tee --append "$LOGFILE"
}
#####################################################################
__RUN $@
source "${0:a:h}/run" $@

View File

@ -1,39 +1,42 @@
source ${0:a:h}/zsh/common.zsh
NO_EXPORT_CONFIG=1 source "${0:a:h}/zsh/lib/import.driver.zsh" || return 42
#####################################################################
[ ! $SCWRYPTS_SHORTCUT ] && return 0
__SCWRYPTS() {
local SCRIPT=$(__GET_AVAILABLE_SCRIPTS | __FZF 'select a script')
SCWRYPTS__ZSH_PLUGIN() {
local SCWRYPT_SELECTION=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS | FZF 'select a script' --header-lines 1)
local NAME
local TYPE
local GROUP
zle clear-command-line
[ ! $SCRIPT ] && { zle accept-line; return 0; }
[ ! $SCWRYPT_SELECTION ] && { zle accept-line; return 0; }
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
which scwrypts >/dev/null 2>&1\
&& RBUFFER="scwrypts" || RBUFFER="$SCWRYPTS_ROOT/scwrypts"
RBUFFER+=" $SCRIPT"
RBUFFER+=" --name $NAME --group $GROUP --type $TYPE"
zle accept-line
}
zle -N scwrypts __SCWRYPTS
zle -N scwrypts SCWRYPTS__ZSH_PLUGIN
bindkey $SCWRYPTS_SHORTCUT scwrypts
#####################################################################
[ ! $SCWRYPTS_ENV_SHORTCUT ] && return 0
__SCWRYPTS_ENV() {
SCWRYPTS__ZSH_PLUGIN_ENV() {
local RESET='reset'
local SELECTED=$(\
{ [ $SCWRYPTS_ENV ] && echo $RESET; __GET_ENV_NAMES; } \
| __FZF 'select an environment' \
{ [ $SCWRYPTS_ENV ] && echo $RESET; SCWRYPTS__GET_ENV_NAMES; } \
| FZF 'select an environment' \
)
zle clear-command-line
[ $SELECTED ] && {
[[ $SELECTED =~ ^$RESET$ ]] \
&& RBUFFER='unset SCWRYPTS_ENV' \
|| RBUFFER="export SCWRYPTS_ENV=$SELECTED'"
|| RBUFFER="export SCWRYPTS_ENV=$SELECTED"
}
zle accept-line
}
zle -N scwrypts-setenv __SCWRYPTS_ENV
zle -N scwrypts-setenv SCWRYPTS__ZSH_PLUGIN_ENV
bindkey $SCWRYPTS_ENV_SHORTCUT scwrypts-setenv

View File

@ -4,6 +4,7 @@
[![Generic Badge](https://img.shields.io/badge/junegunn-fzf-informational.svg)](https://github.com/junegunn/fzf)
[![Generic Badge](https://img.shields.io/badge/mikefarah-yq-informational.svg)](https://github.com/mikefarah/yq)
[![Generic Badge](https://img.shields.io/badge/stedolan-jq-informational.svg)](https://github.com/stedolan/jq)
[![Generic Badge](https://img.shields.io/badge/dbcli-pgcli-informational.svg)](https://github.com/dbcli/pgcli)
<br>
Since they emulate direct user interaction, shell scripts are often the straightforward choice for task automation.

View File

@ -1,16 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
aws \
jq \
;
__CHECK_ENV_VARS \
_AWS_ACCOUNT \
_AWS_PROFILE \
_AWS_REGION \
;
#####################################################################
_AWS() { aws --profile $_AWS_PROFILE --region $_AWS_REGION --output json $@; }

View File

@ -1,10 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
docker \
;
__CHECK_ENV_VARS \
;
#####################################################################

View File

@ -1,13 +0,0 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
#####################################################################
__STATUS "performing AWS ECR docker login"
_AWS ecr get-login-password | docker login \
--username AWS \
--password-stdin \
"$_AWS_ACCOUNT.dkr.ecr.$_AWS_REGION.amazonaws.com" \
&& __SUCCESS "logged in to 'AWS:$_AWS_ACCOUNT:$_AWS_REGION'" \
|| __FAIL 1 "unable to login to '$_AWS_ACCOUNT' in '$_AWS_REGION'"

View File

@ -1,10 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
;
__CHECK_ENV_VARS \
AWS__EFS__LOCAL_MOUNT_POINT \
;
#####################################################################

View File

@ -1,64 +0,0 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
#####################################################################
_EFS_CONNECT() {
__GETSUDO || exit 1
[ ! -d $AWS__EFS__LOCAL_MOUNT_POINT ] && {
sudo mkdir $AWS__EFS__LOCAL_MOUNT_POINT \
&& __STATUS "created local mount point '$AWS__EFS__LOCAL_MOUNT_POINT'"
}
local FS_ID=$(\
_AWS efs describe-file-systems \
| jq -r '.[] | .[] | .FileSystemId' \
| __FZF 'select a filesystem to mount' \
)
[ ! $FS_ID ] && __ABORT
local MOUNT_POINT="$AWS__EFS__LOCAL_MOUNT_POINT/$FS_ID"
[ -d "$MOUNT_POINT" ] && {
__STATUS "$FS_ID is already mounted"
exit 0
}
local MOUNT_TARGETS=$(_AWS efs describe-mount-targets --file-system-id $FS_ID)
local ZONE=$(\
echo $MOUNT_TARGETS \
| jq -r '.[] | .[] | .AvailabilityZoneName' \
| sort -u | __FZF 'select availability zone'\
)
[ ! $ZONE ] && __ABORT
local MOUNT_IP=$(\
echo $MOUNT_TARGETS \
| jq -r ".[] | .[] | select (.AvailabilityZoneName == \"$ZONE\") | .IpAddress" \
| head -n1 \
)
__SUCCESS 'ready to mount!'
__REMINDER 'your device must be connected to the appropriate VPN'
__STATUS "file system id : $FS_ID"
__STATUS "availability zone : $ZONE"
__STATUS "file system ip : $MOUNT_IP"
__STATUS "local mount point : $MOUNT_POINT"
__Yn 'proceed?' || __ABORT
sudo mkdir $MOUNT_POINT \
&& sudo mount \
-t nfs4 \
-o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport \
$MOUNT_IP:/ \
"$MOUNT_POINT" \
&& __SUCCESS "mounted at '$MOUNT_POINT'" \
|| {
sudo rmdir $MOUNT_POINT >/dev/null 2>&1
__FAIL 2 "unable to mount '$FS_ID'"
}
}
#####################################################################
_EFS_CONNECT

View File

@ -1,33 +0,0 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
#####################################################################
_EFS_DISCONNECT() {
[ ! -d "$AWS__EFS__LOCAL_MOUNT_POINT" ] && {
__STATUS 'no efs currently mounted'
exit 0
}
local MOUNTED=$(ls "$AWS__EFS__LOCAL_MOUNT_POINT")
[ ! $MOUNTED ] && {
__STATUS 'no efs currently mounted'
exit 0
}
__GETSUDO || exit 1
local SELECTED=$(echo $MOUNTED | __FZF 'select a file system to unmount')
[ ! $SELECTED ] && __ABORT
local EFS="$AWS__EFS__LOCAL_MOUNT_POINT/$SELECTED"
__STATUS "unmounting '$SELECTED'"
sudo umount $EFS >/dev/null 2>&1
sudo rmdir $EFS \
&& __SUCCESS "done" \
|| __FAIL 2 "failed to unmount '$EFS'"
}
#####################################################################
_EFS_DISCONNECT

View File

@ -1,10 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
cli53 \
;
__CHECK_ENV_VARS \
;
#####################################################################

View File

@ -1,9 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
;
__CHECK_ENV_VARS \
;
#####################################################################

View File

@ -1,34 +0,0 @@
source ${0:a:h}/../common.zsh
__CHECK_DEPENDENCIES \
;
__CHECK_ENV_VARS \
AWS__S3__MEDIA_TARGETS \
AWS__S3__MEDIA_BUCKET \
;
AWS__S3__MEDIA_TARGETS=($(echo $AWS__S3__MEDIA_TARGETS | sed 's/,/\n/g'))
#####################################################################
__SYNC_MEDIA() {
local ACTION="$1"
local REMOTE_TARGET="s3://$AWS__S3__MEDIA_BUCKET/$2"
local LOCAL_TARGET="$HOME/$2"
local A B
case $ACTION in
push ) A="$LOCAL_TARGET"; B="$REMOTE_TARGET" ;;
pull ) A="$REMOTE_TARGET"; B="$LOCAL_TARGET" ;;
* ) __ERROR "unknown action '$1'"; return 1 ;;
esac
local FLAGS=(${@:3})
__STATUS "${ACTION}ing $2"
_AWS s3 sync $REMOTE_TARGET $LOCAL_TARGET $FLAGS \
&& __SUCCESS "$2 up-to-date" \
|| { __ERROR "unable to sync $2 (see above)"; return 1; }
}

View File

@ -1,26 +0,0 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
#####################################################################
__PULL_ALL_MEDIA() {
local FLAGS=($@)
local FAILED_COUNT=0
__STATUS 'starting media download from s3'
local TARGET
for TARGET in $AWS__S3__MEDIA_TARGETS
do
__SYNC_MEDIA pull $TARGET $FLAGS || ((FAILED_COUNT+=1))
done
[[ $FAILED_COUNT -eq 0 ]] \
&& __SUCCESS 'local media files now up-to-date' \
|| __FAIL $FAILED_COUNT 'unable to download one or more targets' \
;
}
#####################################################################
__PULL_ALL_MEDIA $@

View File

@ -1,26 +0,0 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
#####################################################################
__PUSH_ALL_MEDIA() {
local FLAGS=($@)
local FAILED_COUNT=0
__STATUS 'starting media upload to s3'
local TARGET
for TARGET in $AWS__S3__MEDIA_TARGETS
do
__SYNC_MEDIA push $TARGET $FLAGS || ((FAILED_COUNT+=1))
done
[[ $FAILED_COUNT -eq 0 ]] \
&& __SUCCESS 's3 media files now up-to-date' \
|| __FAIL $FAILED_COUNT 'unable to upload one or more targets' \
;
}
#####################################################################
__PUSH_ALL_MEDIA $@

10
zsh/cloud/aws/ecr/login Executable file
View File

@ -0,0 +1,10 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/aws/ecr
CHECK_ENVIRONMENT
#####################################################################
ECR_LOGIN $@

69
zsh/cloud/aws/efs/mount Executable file
View File

@ -0,0 +1,69 @@
#!/bin/zsh
DEPENDENCIES+=(jq)
REQUIRED_ENV+=(AWS__EFS__LOCAL_MOUNT_POINT)
use cloud/aws/cli
CHECK_ENVIRONMENT
#####################################################################
EFS_CONNECT() {
GETSUDO || exit 1
[ ! -d $AWS__EFS__LOCAL_MOUNT_POINT ] && {
sudo mkdir $AWS__EFS__LOCAL_MOUNT_POINT \
&& STATUS "created local mount point '$AWS__EFS__LOCAL_MOUNT_POINT'"
}
local FS_ID=$(\
AWS efs describe-file-systems \
| jq -r '.[] | .[] | .FileSystemId' \
| FZF 'select a filesystem to mount' \
)
[ ! $FS_ID ] && ABORT
local MOUNT_POINT="$AWS__EFS__LOCAL_MOUNT_POINT/$FS_ID"
[ -d "$MOUNT_POINT" ] && sudo rmdir "$MOUNT_POINT" >/dev/null 2>&1
[ -d "$MOUNT_POINT" ] && {
STATUS "$FS_ID is already mounted"
exit 0
}
local MOUNT_TARGETS=$(AWS efs describe-mount-targets --file-system-id $FS_ID)
local ZONE=$(\
echo $MOUNT_TARGETS \
| jq -r '.[] | .[] | .AvailabilityZoneName' \
| sort -u | FZF 'select availability zone'\
)
[ ! $ZONE ] && ABORT
local MOUNT_IP=$(\
echo $MOUNT_TARGETS \
| jq -r ".[] | .[] | select (.AvailabilityZoneName == \"$ZONE\") | .IpAddress" \
| head -n1 \
)
SUCCESS 'ready to mount!'
REMINDER 'for private file-systems, you must be connected to the appropriate VPN'
STATUS "file system id : $FS_ID"
STATUS "availability zone : $ZONE"
STATUS "file system ip : $MOUNT_IP"
STATUS "local mount point : $MOUNT_POINT"
Yn 'proceed?' || ABORT
sudo mkdir $MOUNT_POINT \
&& sudo mount \
-t nfs4 \
-o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport \
$MOUNT_IP:/ \
"$MOUNT_POINT" \
&& SUCCESS "mounted at '$MOUNT_POINT'" \
|| {
sudo rmdir $MOUNT_POINT >/dev/null 2>&1
FAIL 2 "unable to mount '$FS_ID'"
}
}
#####################################################################
EFS_CONNECT $@

37
zsh/cloud/aws/efs/unmount Executable file
View File

@ -0,0 +1,37 @@
#!/bin/zsh
DEPENDENCIES+=(jq)
REQUIRED_ENV+=(AWS__EFS__LOCAL_MOUNT_POINT)
use cloud/aws/cli
CHECK_ENVIRONMENT
#####################################################################
EFS_DISCONNECT() {
[ ! -d "$AWS__EFS__LOCAL_MOUNT_POINT" ] && {
STATUS 'no efs currently mounted'
exit 0
}
local MOUNTED=$(ls "$AWS__EFS__LOCAL_MOUNT_POINT")
[ ! $MOUNTED ] && {
STATUS 'no efs currently mounted'
exit 0
}
GETSUDO || exit 1
local SELECTED=$(echo $MOUNTED | FZF 'select a file system to unmount')
[ ! $SELECTED ] && ABORT
local EFS="$AWS__EFS__LOCAL_MOUNT_POINT/$SELECTED"
STATUS "unmounting '$SELECTED'"
sudo umount $EFS >/dev/null 2>&1
sudo rmdir $EFS \
&& SUCCESS "done" \
|| FAIL 2 "failed to unmount '$EFS'"
}
#####################################################################
EFS_DISCONNECT $@

10
zsh/cloud/aws/eks/login Executable file
View File

@ -0,0 +1,10 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/aws/eks
CHECK_ENVIRONMENT
#####################################################################
EKS_CLUSTER_LOGIN $@

25
zsh/cloud/aws/rds/create-backup Executable file
View File

@ -0,0 +1,25 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/aws/rds
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
CREATE_BACKUP() {
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
PG_DUMP \
--host $DB_HOST \
--port $DB_PORT \
--name $DB_NAME \
--user $DB_USER \
--pass $DB_PASS \
;
}
#####################################################################
CREATE_BACKUP $@

View File

@ -0,0 +1,26 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/aws/rds
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
RDS_INTERACTIVE_LOGIN() {
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
POSTGRES__LOGIN_INTERACTIVE \
--host $DB_HOST \
--port $DB_PORT \
--name $DB_NAME \
--user $DB_USER \
--pass $DB_PASS \
;
}
#####################################################################
RDS_INTERACTIVE_LOGIN $@

26
zsh/cloud/aws/rds/load-backup Executable file
View File

@ -0,0 +1,26 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/aws/rds
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
LOAD_BACKUP() {
local DB_HOST DB_PORT DB_NAME DB_USER DB_PASS
RDS__GET_DATABASE_CREDENTIALS $@ || return 1
PG_RESTORE \
--host $DB_HOST \
--port $DB_PORT \
--name $DB_NAME \
--user $DB_USER \
--pass $DB_PASS \
;
}
#####################################################################
LOAD_BACKUP $@

View File

@ -1,20 +1,22 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
DEPENDENCIES+=(cli53)
REQUIRED_ENV+=(AWS_PROFILE)
CHECK_ENVIRONMENT
#####################################################################
_ROUTE53_BACKUP() {
ROUTE53_BACKUP() {
local BACKUP_PATH="$SCWRYPTS_OUTPUT_PATH/$ENV_NAME/aws-dns-backup/$(date '+%Y-%m-%d')"
mkdir -p $BACKUP_PATH >/dev/null 2>&1
local DOMAIN
local JOBS=()
for DOMAIN in $(_ROUTE53_GET_DOMAINS)
for DOMAIN in $(ROUTE53_GET_DOMAINS)
do
( __STATUS "creating '$BACKUP_PATH/$DOMAIN.txt'" \
&& cli53 export --profile $_AWS_PROFILE $DOMAIN > "$BACKUP_PATH/$DOMAIN.txt" \
&& __SUCCESS "backed up '$DOMAIN'" \
|| __ERROR "failed to back up '$DOMAIN'" \
( STATUS "creating '$BACKUP_PATH/$DOMAIN.txt'" \
&& cli53 export --profile $AWS_PROFILE $DOMAIN > "$BACKUP_PATH/$DOMAIN.txt" \
&& SUCCESS "backed up '$DOMAIN'" \
|| ERROR "failed to back up '$DOMAIN'" \
) &
JOBS+=$!
done
@ -23,12 +25,12 @@ _ROUTE53_BACKUP() {
for P in ${JOBS[@]}; do wait $P >/dev/null 2>&1; done
}
_ROUTE53_GET_DOMAINS() {
cli53 list --profile $_AWS_PROFILE \
ROUTE53_GET_DOMAINS() {
cli53 list --profile $AWS_PROFILE \
| awk '{print $2;}' \
| sed '1d; s/\.$//'\
;
}
#####################################################################
_ROUTE53_BACKUP
ROUTE53_BACKUP

10
zsh/cloud/media-sync/pull Executable file
View File

@ -0,0 +1,10 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/media-sync
CHECK_ENVIRONMENT
#####################################################################
MEDIA_SYNC__PULL $@

10
zsh/cloud/media-sync/push Executable file
View File

@ -0,0 +1,10 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use cloud/media-sync
CHECK_ENVIRONMENT
#####################################################################
MEDIA_SYNC__PUSH $@

View File

@ -1,58 +0,0 @@
[ ! $SCWRYPTS_ROOT ] && SCWRYPTS_ROOT="$(dirname ${0:a:h})"
source $SCWRYPTS_ROOT/.config
[ -f $SCWRYPTS_CONFIG_PATH/config ] && source $SCWRYPTS_CONFIG_PATH/config
[ ! -d $SCWRYPTS_CONFIG_PATH ] && mkdir -p $SCWRYPTS_CONFIG_PATH
[ ! -d $SCWRYPTS_ENV_PATH ] && mkdir -p $SCWRYPTS_ENV_PATH
[ ! -d $SCWRYPTS_LOG_PATH ] && mkdir -p $SCWRYPTS_LOG_PATH
__PREFERRED_PYTHON_VERSIONS=(3.10 3.9)
__NODE_VERSION=18.0.0
#####################################################################
__DEPENDENCY_ERROR=0
__ENVIRONMENT_ERROR=0
source ${0:a:h}/utils/io.zsh
source ${0:a:h}/utils/os.zsh
source ${0:a:h}/utils/credits.zsh
source ${0:a:h}/utils/dependencies.zsh
source ${0:a:h}/utils/environment.zsh
#####################################################################
__CHECK_IMPORTS() {
[[ $__DEPENDENCY_ERROR -eq 0 ]] \
&& [[ $__ENVIRONMENT_ERROR -eq 0 ]] \
|| __FAIL 1 'import error (see output above)' \
;
}
#####################################################################
__ENV_TEMPLATE=$SCWRYPTS_ROOT/.template.env
__GET_ENV_FILES() { find $SCWRYPTS_CONFIG_PATH/env -maxdepth 1 -type f; }
[ ! "$(__GET_ENV_FILES)" ] && {
cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/dev"
cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/local"
cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/prod"
}
__GET_ENV_NAMES() { __GET_ENV_FILES | sed 's/.*\///'; }
__GET_ENV_FILE() { echo "$SCWRYPTS_CONFIG_PATH/env/$1"; }
__SELECT_OR_CREATE_ENV() { __GET_ENV_NAMES | __FZF_TAIL 'select/create an environment'; }
__SELECT_ENV() { __GET_ENV_NAMES | __FZF 'select an environment'; }
__GET_AVAILABLE_SCRIPTS() {
cd $SCWRYPTS_ROOT;
find . -mindepth 2 -type f -executable \
| grep -v '\.git' \
| grep -v '\.env' \
| grep -v 'node_modules' \
| sed 's/^\.\///; s/\.[^.]*$//' \
;
}

View File

@ -0,0 +1,9 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
POSTGRES__LOGIN_INTERACTIVE $@

9
zsh/db/postgres/pg_dump Executable file
View File

@ -0,0 +1,9 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
PG_DUMP $@

9
zsh/db/postgres/pg_restore Executable file
View File

@ -0,0 +1,9 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
PG_RESTORE $@

51
zsh/db/postgres/run-sql Executable file
View File

@ -0,0 +1,51 @@
#!/bin/zsh
DEPENDENCIES+=()
REQUIRED_ENV+=()
use db/postgres
CHECK_ENVIRONMENT
#####################################################################
RUN_SQL_POSTGRES() {
local _PASS _ARGS=()
POSTGRES__SET_LOGIN_ARGS $@
local INPUT_FILE="$FILENAME"
local SQL_DIR="$SCWRYPTS_DATA_PATH/sql"
[ ! -d $SQL_DIR ] && mkdir -p $SQL_DIR
cd $SQL_DIR
[[ $(ls "*.sql" 2>&1 | wc -l) -eq 0 ]] && {
ERROR "you haven't made any SQL commands yet"
REMINDER "add '.sql' files here: '$SQL_DIR/'"
return 1
}
[ ! $INPUT_FILE ] && INPUT_FILE=$(FZF 'select a sql file to run')
[ ! $INPUT_FILE ] && ABORT
[ ! -f "$INPUT_FILE" ] && FAIL 2 "no such sql file '$SQL_DIR/$INPUT_FILE'"
STATUS "loading '$INPUT_FILE' preview..."
LESS "$INPUT_FILE"
STATUS "login : $_USER@$_HOST:$_PORT/$_NAME"
STATUS "command : '$INPUT_FILE'"
yN 'run this command?' || ABORT
STATUS "running '$INPUT_FILE'"
PSQL < $INPUT_FILE \
&& 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 $@

19
zsh/docker/cleanup Executable file
View File

@ -0,0 +1,19 @@
#!/bin/zsh
DEPENDENCIES+=(docker)
REQUIRED_ENV+=()
CHECK_ENVIRONMENT
#####################################################################
DOCKER_CLEAN() {
WARNING 'this will prune all docker resources from the current machine'
WARNING 'pruned resources are PERMANENTLY DELETED'
yN 'continue?' || return 1
SUCCESS "CONTAINER : $(docker container prune -f 2>/dev/null | tail -n 1)"
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 $@

View File

@ -1,6 +1,8 @@
#!/bin/zsh
source ${0:a:h}/common.zsh
__CHECK_IMPORTS
DEPENDENCIES+=()
REQUIRED_ENV+=()
CHECK_ENVIRONMENT
#####################################################################
__SUCCESS 'hello world!'
SUCCESS 'hello world!'

View File

@ -0,0 +1,21 @@
#####################################################################
DEPENDENCIES+=(
aws
)
REQUIRED_ENV+=(
AWS_ACCOUNT
AWS_PROFILE
AWS_REGION
)
#####################################################################
AWS() {
aws \
--profile $AWS_PROFILE \
--region $AWS_REGION \
--output json \
$@
}

View File

@ -0,0 +1,28 @@
#####################################################################
DEPENDENCIES+=(
docker
)
REQUIRED_ENV+=(
AWS_ACCOUNT
AWS_REGION
)
use cloud/aws/cli
#####################################################################
ECR_LOGIN() {
STATUS "performing AWS ECR docker login"
AWS ecr get-login-password \
| docker login \
--username AWS \
--password-stdin \
"$AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com" \
&& SUCCESS "authenticated docker for '$AWS_ACCOUNT' in '$AWS_REGION'" \
|| {
ERROR "unable to authenticate docker for '$AWS_ACCOUNT' in '$AWS_REGION'"
return 1
}
}

View File

@ -0,0 +1,60 @@
#####################################################################
DEPENDENCIES+=(
kubectl
)
REQUIRED_ENV+=(
AWS_ACCOUNT
AWS_REGION
)
use cloud/aws/cli
#####################################################################
EKS_CLUSTER_LOGIN() {
local USAGE="
usage: [...options...]
options
-c, --cluster-name <string> (optional) login a specific cluster
Interactively sets the default kubeconfig to match the selected
cluster in EKS. Also creates the kubeconfig entry if it does not
already exist.
"
local CLUSTER_NAME
while [[ $# -gt 0 ]]
do
case $1 in
-c | --cluster-name ) CLUSTER_NAME="$2"; shift 1 ;;
* ) [ ! $APPLICATION ] && APPLICATION="$1" \
|| ERROR "extra positional argument '$1'"
;;
esac
shift 1
done
[ ! $CLUSTER_NAME ] && CLUSTER_NAME=$(\
AWS eks list-clusters \
| jq -r '.[] | .[]' \
| FZF 'select a cluster'
)
[ ! $CLUSTER_NAME ] && ERROR 'must select a valid cluster or use -c flag'
CHECK_ERRORS
##########################################
STATUS 'creating / updating kubeconfig for EKS cluster'
STATUS "updating kubeconfig for '$CLUSTER_NAME'"
AWS eks update-kubeconfig --name $CLUSTER_NAME \
&& SUCCESS "kubeconfig updated with '$CLUSTER_NAME'" \
|| ERROR "failed to update kubeconfig; do you have permissions to access '$CLUSTER_NAME'?"
}

View File

@ -0,0 +1,140 @@
#####################################################################
DEPENDENCIES+=(
docker
)
REQUIRED_ENV+=(
AWS_ACCOUNT
AWS_REGION
)
use cloud/aws/cli
#####################################################################
RDS__SELECT_DATABASE() {
local DATABASES=$(_RDS__GET_AVAILABLE_DATABASES)
[ ! $DATABASES ] && FAIL 1 'no databases available'
local ID=$(\
echo $DATABASES | jq -r '.instance + " @ " + .cluster' \
| FZF 'select a database (instance@cluster)' \
)
[ ! $ID ] && ABORT
local INSTANCE=$(echo $ID | sed 's/ @ .*$//')
local CLUSTER=$(echo $ID | sed 's/^.* @ //')
echo $DATABASES | jq "select (.instance == \"$INSTANCE\" and .cluster == \"$CLUSTER\")"
}
_RDS__GET_AVAILABLE_DATABASES() {
AWS rds describe-db-instances \
| jq -r '.[] | .[] | {
instance: .DBInstanceIdentifier,
cluster: .DBClusterIdentifier,
type: .Engine,
host: .Endpoint.Address,
port: .Endpoint.Port,
user: .MasterUsername,
database: .DBName
}'
}
RDS__GET_DATABASE_CREDENTIALS() {
local PRINT_PASSWORD=0
local ERRORS=0
while [[ $# -gt 0 ]]
do
case $1 in
--print-password ) PRINT_PASSWORD=1 ;;
* )
WARNING "unrecognized argument $1"
ERRORS+=1
;;
esac
shift 1
done
CHECK_ERRORS
##########################################
local DATABASE=$(RDS__SELECT_DATABASE)
[ ! $DATABASE ] && ABORT
DB_HOST="$(echo $DATABASE | jq -r '.host')"
[ ! $DB_HOST ] && { ERROR 'unable to find host'; return 2; }
DB_PORT="$(echo $DATABASE | jq -r '.port')"
[ ! $DB_PORT ] && DB_PORT=5432
[[ $DB_PORT =~ ^null$ ]] && DB_PORT=5432
##########################################
local AUTH_METHOD=$(\
echo "iam\nsecretsmanager\nuser-input" \
| FZF 'select an authentication method' \
)
[ ! $AUTH_METHOD ] && ABORT
case $AUTH_METHOD in
iam ) _RDS_AUTH__iam ;;
secretsmanager ) _RDS_AUTH__secretsmanager ;;
user-input ) _RDS_AUTH__userinput ;;
esac
STATUS
STATUS "host : $DB_HOST"
STATUS "type : $DB_TYPE"
STATUS "port : $DB_PORT"
STATUS "database : $DB_NAME"
STATUS "username : $DB_USER"
[[ $PRINT_PASSWORD -eq 1 ]] && STATUS "password : $DB_PASS"
STATUS
}
_RDS_AUTH__iam() {
DB_PASS=$(\
AWS rds generate-db-auth-token \
--hostname $DB_HOST \
--port $DB_PORT \
--username $DB_USER \
)
}
_RDS_AUTH__secretsmanager() {
local CREDENTIALS=$(_RDS__GET_SECRETSMANAGER_CREDENTIALS)
echo $CREDENTIALS | jq -e '.pass' >/dev/null 2>&1 \
&& DB_PASS="'$(echo $CREDENTIALS | jq -r '.pass' | sed "s/'/'\"'\"'/g")'"
echo $CREDENTIALS | jq -e '.password' >/dev/null 2>&1 \
&& DB_PASS="'$(echo $CREDENTIALS | jq -r '.password' | sed "s/'/'\"'\"'/g")'"
echo $CREDENTIALS | jq -e '.user' >/dev/null 2>&1 \
&& DB_USER=$(echo $CREDENTIALS | jq -r '.user')
echo $CREDENTIALS | jq -e '.username' >/dev/null 2>&1 \
&& DB_USER=$(echo $CREDENTIALS | jq -r '.username')
echo $CREDENTIALS | jq -e '.name' >/dev/null 2>&1 \
&& DB_NAME=$(echo $CREDENTIALS | jq -r '.name')
echo $CREDENTIALS | jq -e '.dbname' >/dev/null 2>&1 \
&& DB_NAME=$(echo $CREDENTIALS | jq -r '.dbname')
}
_RDS__GET_SECRETSMANAGER_CREDENTIALS() {
local ID=$(\
AWS secretsmanager list-secrets \
| jq -r '.[] | .[] | .Name' \
| FZF 'select a secret' \
)
[ ! $ID ] && return 1
AWS secretsmanager get-secret-value --secret-id "$ID" \
| jq -r '.SecretString' | jq
}

View File

@ -0,0 +1,69 @@
#####################################################################
DEPENDENCIES+=()
REQUIRED_ENV+=(
MEDIA_SYNC__TARGETS
MEDIA_SYNC__S3_BUCKET
)
use cloud/aws/cli
#####################################################################
MEDIA_SYNC__PUSH() {
local FLAGS=($@)
local FAILED_COUNT=0
STATUS 'starting media upload to s3'
local TARGET
for TARGET in ${MEDIA_SYNC__TARGETS[@]}
do
_MEDIA_SYNC push $TARGET $FLAGS || ((FAILED_COUNT+=1))
done
[[ $FAILED_COUNT -eq 0 ]] \
&& SUCCESS 's3 media files now up-to-date' \
|| FAIL $FAILED_COUNT 'unable to upload one or more targets' \
;
}
MEDIA_SYNC__PULL() {
local FLAGS=($@)
local FAILED_COUNT=0
STATUS 'starting media download from s3'
local TARGET
for TARGET in ${MEDIA_SYNC__TARGETS[@]}
do
_MEDIA_SYNC pull $TARGET $FLAGS || ((FAILED_COUNT+=1))
done
[[ $FAILED_COUNT -eq 0 ]] \
&& SUCCESS 'local media files now up-to-date' \
|| FAIL $FAILED_COUNT 'unable to download one or more targets' \
;
}
_MEDIA_SYNC() {
local ACTION="$1"
local REMOTE_TARGET="s3://$MEDIA_SYNC__S3_BUCKET/$2"
local LOCAL_TARGET="$HOME/$2"
local A B
case $ACTION in
push ) A="$LOCAL_TARGET"; B="$REMOTE_TARGET" ;;
pull ) A="$REMOTE_TARGET"; B="$LOCAL_TARGET" ;;
* ) ERROR "unknown action '$1'"; return 1 ;;
esac
local FLAGS=(${@:3})
STATUS "${ACTION}ing $2"
AWS s3 sync $A $B $FLAGS \
&& SUCCESS "$2 up-to-date" \
|| { ERROR "unable to sync $2 (see above)"; return 1; }
}

15
zsh/lib/config.group.zsh Normal file
View File

@ -0,0 +1,15 @@
export SCWRYPTS_ROOT__scwrypts="$SCWRYPTS_ROOT"
export SCWRYPTS_LIBRARY_ROOT__scwrypts="$SCWRYPTS_ROOT/zsh/lib"
export SCWRYPTS_COLOR__scwrypts='\033[0;32m'
export SCWRYPTS_ENV_PATH__scwrypts="$SCWRYPTS_CONFIG_PATH/scwrypts/env"
[ ! -d "$SCWRYPTS_ENV_PATH__scwrypts" ] && mkdir -p "$SCWRYPTS_ENV_PATH__scwrypts"
export SCWRYPTS_ENV_TEMPLATE__scwrypts="$SCWRYPTS_ROOT__scwrypts/.env.template"
export SCWRYPTS_ENV_TEMPLATE_DESCRIPTIONS__scwrypts="$SCWRYPTS_ROOT__scwrypts/.env.template.descriptions"
export SCWRYPTS_VIRTUALENV_PATH__scwrypts="$SCWRYPTS_DATA_PATH/virtualenv"
[ ! -d "$SCWRYPTS_VIRTUALENV_PATH__scwrypts" ] && mkdir -p "$SCWRYPTS_VIRTUALENV_PATH__scwrypts"
export SCWRYPTS_PREFERRED_PYTHON_VERSIONS__scwrypts=(3.11 3.10 3.9)
export SCWRYPTS_NODE_VERSION__scwrypts=18.0.0

BIN
zsh/lib/config.user.zsh Normal file

Binary file not shown.

42
zsh/lib/config.zsh Normal file
View File

@ -0,0 +1,42 @@
[[ $__SCWRYPT -eq 1 ]] && return 0
#####################################################################
[ ! $SCWRYPTS_ROOT ] \
&& SCWRYPTS_ROOT="$(cd $(dirname "${0:a:h}"); git rev-parse --show-toplevel)"
#####################################################################
DEFAULT_CONFIG="$SCWRYPTS_ROOT/zsh/lib/config.user.zsh"
source "$DEFAULT_CONFIG"
USER_CONFIG_OVERRIDES="$SCWRYPTS_CONFIG_PATH/config.zsh"
[ ! -f "$USER_CONFIG_OVERRIDES" ] && {
mkdir -p $(dirname "$USER_CONFIG_OVERRIDES")
cp "$DEFAULT_CONFIG" "$USER_CONFIG_OVERRIDES"
}
source "$USER_CONFIG_OVERRIDES"
[ ! -d $SCWRYPTS_CONFIG_PATH ] && mkdir -p $SCWRYPTS_CONFIG_PATH
[ ! -d $SCWRYPTS_DATA_PATH ] && mkdir -p $SCWRYPTS_DATA_PATH
[ ! -d $SCWRYPTS_ENV_PATH ] && mkdir -p $SCWRYPTS_ENV_PATH
[ ! -d $SCWRYPTS_LOG_PATH ] && mkdir -p $SCWRYPTS_LOG_PATH
[ ! -d $SCWRYPTS_OUTPUT_PATH ] && mkdir -p $SCWRYPTS_OUTPUT_PATH
export \
SCWRYPTS_GROUPS \
SCWRYPTS_CONFIG_PATH \
SCWRYPTS_DATA_PATH \
SCWRYPTS_SHORTCUT \
SCWRYPTS_ENV_SHORTCUT \
SCWRYPTS_LOG_PATH \
SCWRYPTS_OUTPUT_PATH \
;
SCWRYPTS_GROUPS+=(scwrypts) # 'scwrypts' group is required!
SCWRYPTS_GROUPS=($(echo $SCWRYPTS_GROUPS | sed 's/\s\+/\n/g' | sort -u))
source "$SCWRYPTS_ROOT/zsh/lib/config.group.zsh" \
|| FAIL 69 'failed to set up scwrypts group; aborting'
#####################################################################
__SCWRYPT=1 # arbitrary; indicates currently inside a scwrypt

Some files were not shown because too many files have changed in this diff Show More