Compare commits
11 Commits
v4.2.4
...
d4ef1c70e0
Author | SHA1 | Date | |
---|---|---|---|
d4ef1c70e0 | |||
c9e107d2fd | |||
b6b4f2e5b8 | |||
432593a0f3 | |||
6629caf459 | |||
8bcc99b898 | |||
05694ed022 | |||
67bd712590 | |||
a90482de8c | |||
261bbee1a4 | |||
fcf492c661 |
@ -1,292 +0,0 @@
|
|||||||
---
|
|
||||||
version: 2.1
|
|
||||||
|
|
||||||
orbs:
|
|
||||||
python: circleci/python@2.1.1
|
|
||||||
|
|
||||||
|
|
||||||
executors:
|
|
||||||
archlinux:
|
|
||||||
docker:
|
|
||||||
- image: archlinux:base-devel
|
|
||||||
resource_class: small
|
|
||||||
working_directory: /
|
|
||||||
|
|
||||||
python:
|
|
||||||
docker:
|
|
||||||
- image: cimg/python:3.11
|
|
||||||
resource_class: small
|
|
||||||
|
|
||||||
nodejs:
|
|
||||||
docker:
|
|
||||||
- image: node:18
|
|
||||||
resource_class: medium
|
|
||||||
|
|
||||||
|
|
||||||
commands:
|
|
||||||
archlinux-run:
|
|
||||||
description: execute command steps in the archlinux container from the CI user
|
|
||||||
parameters:
|
|
||||||
_name:
|
|
||||||
type: string
|
|
||||||
command:
|
|
||||||
type: string
|
|
||||||
working_directory:
|
|
||||||
type: string
|
|
||||||
default: /home/ci
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: << parameters._name >>
|
|
||||||
working_directory: << parameters.working_directory >>
|
|
||||||
command: su ci -c '<< parameters.command >>'
|
|
||||||
|
|
||||||
|
|
||||||
custom:
|
|
||||||
archlinux:
|
|
||||||
prepare:
|
|
||||||
- &archlinux-prepare
|
|
||||||
run:
|
|
||||||
name: prepare archlinux dependencies
|
|
||||||
command: |
|
|
||||||
pacman --noconfirm -Syu git openssh ca-certificates-utils
|
|
||||||
useradd -m ci
|
|
||||||
echo "ci ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
||||||
|
|
||||||
clone-aur:
|
|
||||||
- &archlinux-clone-aur
|
|
||||||
archlinux-run:
|
|
||||||
_name: clone aur/scwrypts
|
|
||||||
command: git clone https://aur.archlinux.org/scwrypts.git aur
|
|
||||||
|
|
||||||
clone-scwrypts:
|
|
||||||
- &archlinux-clone-scwrypts
|
|
||||||
run:
|
|
||||||
name: clone wrynegade/scwrypts
|
|
||||||
working_directory: /home/ci
|
|
||||||
command: |
|
|
||||||
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git clone -b "$(echo $CIRCLE_BRANCH | grep . || echo $CIRCLE_TAG)" "$CIRCLE_REPOSITORY_URL" scwrypts
|
|
||||||
chown -R ci:ci ./scwrypts
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
require-full-semver:
|
|
||||||
executor: python
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: check CIRCLE_TAG for full semantic version
|
|
||||||
command: |
|
|
||||||
: \
|
|
||||||
&& [ $CIRCLE_TAG ] \
|
|
||||||
&& [[ $CIRCLE_TAG =~ ^v[0-9]*.[0-9]*.[0-9]*$ ]] \
|
|
||||||
;
|
|
||||||
|
|
||||||
aur-test:
|
|
||||||
executor: archlinux
|
|
||||||
steps:
|
|
||||||
- *archlinux-prepare
|
|
||||||
- *archlinux-clone-aur
|
|
||||||
- *archlinux-clone-scwrypts
|
|
||||||
- archlinux-run:
|
|
||||||
_name: test aur build on current source
|
|
||||||
working_directory: /home/ci/aur
|
|
||||||
command: >-
|
|
||||||
:
|
|
||||||
&& PKGVER=$(sed -n "s/^pkgver=//p" ./PKGBUILD)
|
|
||||||
&& cp -r ../scwrypts ../scwrypts-$PKGVER
|
|
||||||
&& rm -rf ../scwrypts-$PKGVER/.circleci
|
|
||||||
&& rm -rf ../scwrypts-$PKGVER/.git
|
|
||||||
&& rm -rf ../scwrypts-$PKGVER/.gitattributes
|
|
||||||
&& rm -rf ../scwrypts-$PKGVER/.gitignore
|
|
||||||
&& rm -rf ../scwrypts-$PKGVER/.github
|
|
||||||
&& tar -czf scwrypts.tar.gz ../scwrypts-$PKGVER
|
|
||||||
&& echo "source=(scwrypts.tar.gz)" >> PKGBUILD
|
|
||||||
&& echo "sha256sums=(SKIP)" >> PKGBUILD
|
|
||||||
&& makepkg --noconfirm -si
|
|
||||||
&& scwrypts --version | grep "^scwrypts v$PKGVER$"
|
|
||||||
;
|
|
||||||
|
|
||||||
aur-publish:
|
|
||||||
executor: archlinux
|
|
||||||
steps:
|
|
||||||
- *archlinux-prepare
|
|
||||||
- *archlinux-clone-aur
|
|
||||||
- archlinux-run:
|
|
||||||
_name: update PKGBUILD and .SRCINFO
|
|
||||||
working_directory: /home/ci/aur
|
|
||||||
command: >-
|
|
||||||
:
|
|
||||||
&& NEW_VERSION=$(echo $CIRCLE_TAG | sed 's/^v//')
|
|
||||||
&& NEW_VERSION=4.2.0
|
|
||||||
&& sed "s/pkgver=.*/pkgver=$NEW_VERSION/; s/^pkgrel=.*/pkgrel=1/; /sha256sums/d" PKGBUILD -i
|
|
||||||
&& makepkg -g >> PKGBUILD
|
|
||||||
&& makepkg --printsrcinfo > .SRCINFO
|
|
||||||
;
|
|
||||||
- archlinux-run:
|
|
||||||
_name: sanity check for version build
|
|
||||||
working_directory: /home/ci/aur
|
|
||||||
command: >-
|
|
||||||
:
|
|
||||||
&& makepkg --noconfirm -si
|
|
||||||
&& scwrypts --version
|
|
||||||
&& scwrypts --version | grep -q "^scwrypts $CIRCLE_TAG\$"
|
|
||||||
;
|
|
||||||
- archlinux-run:
|
|
||||||
_name: publish new version
|
|
||||||
working_directory: /home/ci/aur
|
|
||||||
command: >-
|
|
||||||
:
|
|
||||||
&& git add PKGBUILD .SRCINFO
|
|
||||||
&& git -c user.email=yage@yage.io -c user.name=yage commit -am "$CIRCLE_TAG"
|
|
||||||
&& eval $(ssh-agent)
|
|
||||||
&& echo -e $SSH_KEY_PRIVATE__AUR | ssh-add -
|
|
||||||
&& git remote add upstream ssh://aur@aur.archlinux.org/scwrypts.git
|
|
||||||
&& echo 'DO THE GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git push upstream'
|
|
||||||
;
|
|
||||||
|
|
||||||
nodejs-test:
|
|
||||||
executor: nodejs
|
|
||||||
working_directory: ~/scwrypts/zx/lib
|
|
||||||
steps:
|
|
||||||
- checkout:
|
|
||||||
path: ~/scwrypts
|
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
name: restore pnpm cache
|
|
||||||
keys:
|
|
||||||
- pnpm-packages-{{ checksum "pnpm-lock.yaml" }}
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: pnpm install
|
|
||||||
command: |
|
|
||||||
corepack enable
|
|
||||||
corepack prepare pnpm@latest-8 --activate
|
|
||||||
pnpm config set store-dir .pnpm-store
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
- save_cache:
|
|
||||||
name: save pnpm cache
|
|
||||||
key: pnpm-packages-{{ checksum "pnpm-lock.yaml" }}
|
|
||||||
paths:
|
|
||||||
- .pnpm-store
|
|
||||||
|
|
||||||
- run: pnpm test
|
|
||||||
- run: pnpm lint
|
|
||||||
- run: pnpm build
|
|
||||||
|
|
||||||
nodejs-publish:
|
|
||||||
executor: nodejs
|
|
||||||
working_directory: ~/scwrypts/zx/lib
|
|
||||||
steps:
|
|
||||||
- checkout:
|
|
||||||
path: ~/scwrypts
|
|
||||||
|
|
||||||
- restore_cache:
|
|
||||||
name: restore pnpm cache
|
|
||||||
keys:
|
|
||||||
- pnpm-packages-{{ checksum "pnpm-lock.yaml" }}
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: pnpm install
|
|
||||||
command: |
|
|
||||||
corepack enable
|
|
||||||
corepack prepare pnpm@latest-8 --activate
|
|
||||||
pnpm config set store-dir .pnpm-store
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
- save_cache:
|
|
||||||
name: save pnpm cache
|
|
||||||
key: pnpm-packages-{{ checksum "pnpm-lock.yaml" }}
|
|
||||||
paths:
|
|
||||||
- .pnpm-store
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: publish
|
|
||||||
command: |
|
|
||||||
: \
|
|
||||||
&& [ $CIRCLE_TAG ] \
|
|
||||||
&& pnpm build \
|
|
||||||
&& pnpm version $CIRCLE_TAG \
|
|
||||||
&& pnpm set //registry.npmjs.org/:_authToken=$NPM_TOKEN \
|
|
||||||
&& pnpm publish --no-git-checks \
|
|
||||||
;
|
|
||||||
|
|
||||||
python-test:
|
|
||||||
executor: python
|
|
||||||
working_directory: ~/scwrypts/py/lib
|
|
||||||
steps:
|
|
||||||
- checkout:
|
|
||||||
path: ~/scwrypts
|
|
||||||
- run:
|
|
||||||
name: pytest
|
|
||||||
command: |
|
|
||||||
: \
|
|
||||||
&& pip install . .[test] \
|
|
||||||
&& pytest \
|
|
||||||
;
|
|
||||||
- run: pip install build && python -m build
|
|
||||||
|
|
||||||
python-publish:
|
|
||||||
executor: python
|
|
||||||
working_directory: ~/scwrypts/py/lib
|
|
||||||
steps:
|
|
||||||
- checkout:
|
|
||||||
path: ~/scwrypts
|
|
||||||
- run: pip install build && python -m build
|
|
||||||
- run: pip install twine && twine upload dist/*
|
|
||||||
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
test:
|
|
||||||
jobs:
|
|
||||||
- aur-test:
|
|
||||||
&dev-filters
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
ignore: /^main$/
|
|
||||||
|
|
||||||
- python-test: *dev-filters
|
|
||||||
- nodejs-test: *dev-filters
|
|
||||||
|
|
||||||
publish:
|
|
||||||
jobs:
|
|
||||||
- require-full-semver:
|
|
||||||
filters:
|
|
||||||
&only-run-on-full-semver-tag-filters
|
|
||||||
tags:
|
|
||||||
only: /^v\d+\.\d+\.\d+.*$/
|
|
||||||
branches:
|
|
||||||
ignore: /^.*$/
|
|
||||||
|
|
||||||
- aur-test:
|
|
||||||
&only-publish-for-full-semver
|
|
||||||
filters: *only-run-on-full-semver-tag-filters
|
|
||||||
requires:
|
|
||||||
- require-full-semver
|
|
||||||
- aur-publish:
|
|
||||||
#
|
|
||||||
# there's a crazy-low-chance race-condition between this job and the GH Action '../.github/workflows/automatic-release.yaml'
|
|
||||||
# - automatic-release creates the release artifact, but takes no more than 15-30 seconds (current avg:16s max:26s)
|
|
||||||
# - this publish step requires the release artifact, but waits for all language-repository publishes to complete first (a few minutes at least)
|
|
||||||
#
|
|
||||||
# if something goes wrong, this step can be safely rerun after fixing the release artifact :)
|
|
||||||
#
|
|
||||||
filters: *only-run-on-full-semver-tag-filters
|
|
||||||
context: [aur-yage]
|
|
||||||
requires:
|
|
||||||
- aur-test
|
|
||||||
- python-publish
|
|
||||||
- nodejs-publish
|
|
||||||
|
|
||||||
- python-test: *only-publish-for-full-semver
|
|
||||||
- python-publish:
|
|
||||||
filters: *only-run-on-full-semver-tag-filters
|
|
||||||
context: [pypi-yage]
|
|
||||||
requires:
|
|
||||||
- python-test
|
|
||||||
|
|
||||||
- nodejs-test: *only-publish-for-full-semver
|
|
||||||
- nodejs-publish:
|
|
||||||
filters: *only-run-on-full-semver-tag-filters
|
|
||||||
context: [npm-wrynegade]
|
|
||||||
requires:
|
|
||||||
- nodejs-test
|
|
19
.github/workflows/automatic-release.yaml
vendored
19
.github/workflows/automatic-release.yaml
vendored
@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
name: Automatic Tag-release
|
|
||||||
|
|
||||||
on: # yamllint disable-line rule:truthy
|
|
||||||
push:
|
|
||||||
branches-ignore:
|
|
||||||
- '**'
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
automatic-tag-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: marvinpinto/action-automatic-releases@latest
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
prerelease: false
|
|
@ -29,7 +29,6 @@ runs:
|
|||||||
repository: wrynegade/scwrypts
|
repository: wrynegade/scwrypts
|
||||||
path: ./wrynegade/scwrypts
|
path: ./wrynegade/scwrypts
|
||||||
ref: ${{ inputs.version }}
|
ref: ${{ inputs.version }}
|
||||||
fetch-tags: true
|
|
||||||
|
|
||||||
- name: check dependencies
|
- name: check dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -52,7 +51,7 @@ runs:
|
|||||||
} > $HOME/.scwrypts.apt-get.log 2>&1
|
} > $HOME/.scwrypts.apt-get.log 2>&1
|
||||||
|
|
||||||
echo "updating virtual dependencies"
|
echo "updating virtual dependencies"
|
||||||
$GITHUB_WORKSPACE/wrynegade/scwrypts/scwrypts \
|
$GITHUB_WORKSPACE/wrynegade/scwrypts/scwrypts -n \
|
||||||
--name scwrypts/virtualenv/update-all \
|
--name scwrypts/virtualenv/update-all \
|
||||||
--group scwrypts \
|
--group scwrypts \
|
||||||
--type zsh \
|
--type zsh \
|
||||||
|
@ -171,7 +171,7 @@ _SCWRYPTS_KUBECTL_DRIVER() {
|
|||||||
|
|
||||||
[ $NAMESPACE ] && CLI_ARGS+=(--namespace $NAMESPACE)
|
[ $NAMESPACE ] && CLI_ARGS+=(--namespace $NAMESPACE)
|
||||||
[[ $VERBOSE -eq 1 ]] && {
|
[[ $VERBOSE -eq 1 ]] && {
|
||||||
REMINDER "
|
INFO "
|
||||||
context '$CONTEXT'
|
context '$CONTEXT'
|
||||||
namespace '$NAMESPACE'
|
namespace '$NAMESPACE'
|
||||||
environment '$SCWRYPTS_ENV'
|
environment '$SCWRYPTS_ENV'
|
||||||
@ -180,7 +180,7 @@ _SCWRYPTS_KUBECTL_DRIVER() {
|
|||||||
STATUS "running $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}"
|
STATUS "running $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}"
|
||||||
} || {
|
} || {
|
||||||
[[ $(_SCWRYPTS_KUBECTL_SETTINGS get context) =~ ^show$ ]] && {
|
[[ $(_SCWRYPTS_KUBECTL_SETTINGS get context) =~ ^show$ ]] && {
|
||||||
REMINDER "$SCWRYPTS_ENV.$SUBSESSION : $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}"
|
INFO "$SCWRYPTS_ENV.$SUBSESSION : $CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}
|
$CLI ${CLI_ARGS[@]} ${USER_ARGS[@]}
|
||||||
|
@ -112,8 +112,8 @@ KUBECTL__SERVE() {
|
|||||||
SERVICE_PASSWORD="$(KUBECTL__GET_SERVICE_PASSWORD)"
|
SERVICE_PASSWORD="$(KUBECTL__GET_SERVICE_PASSWORD)"
|
||||||
KUBECTL__SERVICE_PARSE
|
KUBECTL__SERVICE_PARSE
|
||||||
|
|
||||||
REMINDER "attempting to serve ${NAMESPACE}/${SERVICE_NAME}:${SERVICE_PORT}"
|
INFO "attempting to serve ${NAMESPACE}/${SERVICE_NAME}:${SERVICE_PORT}"
|
||||||
[ $SERVICE_PASSWORD ] && REMINDER "password : $SERVICE_PASSWORD"
|
[ $SERVICE_PASSWORD ] && INFO "password : $SERVICE_PASSWORD"
|
||||||
|
|
||||||
KUBECTL port-forward service/$SERVICE_NAME $SERVICE_PORT
|
KUBECTL port-forward service/$SERVICE_NAME $SERVICE_PORT
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert csv into json'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert csv into json',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert csv into yaml'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert csv into yaml',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert json into csv'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert json into csv',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert json into yaml'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert json into yaml',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert yaml into csv'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert yaml into csv',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.data import convert
|
|
||||||
|
|
||||||
|
|
||||||
description = 'convert yaml into json'
|
|
||||||
parse_args = []
|
|
||||||
|
|
||||||
def main(_args, stream):
|
def main(_args, stream):
|
||||||
return convert(
|
return convert(
|
||||||
@ -16,5 +18,7 @@ def main(_args, stream):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'convert yaml into json',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1,55 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
|
||||||
#####################################################################
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
|
||||||
from scwrypts.fzf import fzf, fzf_tail
|
from py.lib.fzf import fzf, fzf_tail
|
||||||
from scwrypts.http import directus
|
from py.lib.http import directus
|
||||||
|
from py.lib.scwrypts import execute
|
||||||
|
|
||||||
|
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||||
|
|
||||||
description = 'interactive CLI to get data from directus'
|
if __name__ != '__main__':
|
||||||
|
raise ImportedExecutableError()
|
||||||
|
|
||||||
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):
|
def main(args, stream):
|
||||||
if {None} == { args.collection, args.filters, args.fields }:
|
if {None} == { args.collection, args.filters, args.fields }:
|
||||||
@ -135,6 +96,50 @@ def _get_or_select_fields(args, collection):
|
|||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
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,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
@ -1,40 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
|
||||||
#####################################################################
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from sys import stderr
|
from sys import stderr
|
||||||
|
|
||||||
from scwrypts.http import discord
|
from py.lib.http import discord
|
||||||
|
from py.lib.scwrypts import execute
|
||||||
|
|
||||||
|
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||||
|
|
||||||
description = 'post a message to the indicated discord channel'
|
if __name__ != '__main__':
|
||||||
parse_args = [
|
raise ImportedExecutableError()
|
||||||
( ['-b', '--body'], {
|
|
||||||
'dest' : 'content',
|
#####################################################################
|
||||||
'help' : 'message body',
|
|
||||||
'required' : False,
|
|
||||||
}),
|
|
||||||
( ['-c', '--channel-id'], {
|
|
||||||
'dest' : 'channel_id',
|
|
||||||
'help' : 'override default target channel id',
|
|
||||||
'required' : False,
|
|
||||||
}),
|
|
||||||
( ['-w', '--webhook'], {
|
|
||||||
'dest' : 'webhook',
|
|
||||||
'help' : 'override default target webhook (takes precedence over -c)',
|
|
||||||
'required' : False,
|
|
||||||
}),
|
|
||||||
( ['--avatar-url'], {
|
|
||||||
'dest' : 'avatar_url',
|
|
||||||
'help' : 'override default avatar_url',
|
|
||||||
'required' : False,
|
|
||||||
}),
|
|
||||||
( ['--username'], {
|
|
||||||
'dest' : 'username',
|
|
||||||
'help' : 'override default username',
|
|
||||||
'required' : False,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
def main(args, stream):
|
def main(args, stream):
|
||||||
if args.content is None:
|
if args.content is None:
|
||||||
@ -53,5 +29,33 @@ def main(args, stream):
|
|||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'post a message to the indicated discord channel',
|
||||||
|
parse_args = [
|
||||||
|
( ['-b', '--body'], {
|
||||||
|
'dest' : 'content',
|
||||||
|
'help' : 'message body',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
( ['-c', '--channel-id'], {
|
||||||
|
'dest' : 'channel_id',
|
||||||
|
'help' : 'override default target channel id',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
( ['-w', '--webhook'], {
|
||||||
|
'dest' : 'webhook',
|
||||||
|
'help' : 'override default target webhook (takes precedence over -c)',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
( ['--avatar-url'], {
|
||||||
|
'dest' : 'avatar_url',
|
||||||
|
'help' : 'override default avatar_url',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
( ['--username'], {
|
||||||
|
'dest' : 'username',
|
||||||
|
'help' : 'override default username',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
from py.lib.scwrypts import execute
|
||||||
|
|
||||||
|
from py.lib.scwrypts.exceptions import ImportedExecutableError
|
||||||
|
|
||||||
|
if __name__ != '__main__':
|
||||||
|
raise ImportedExecutableError()
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
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):
|
def main(args, stream):
|
||||||
stream.writeline(args.message)
|
stream.writeline(args.message)
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'a simple "Hello, World!" program',
|
||||||
|
parse_args = [
|
||||||
|
( ['-m', '--message'], {
|
||||||
|
'dest' : 'message',
|
||||||
|
'default' : 'HELLO WORLD',
|
||||||
|
'help' : 'message to print',
|
||||||
|
'required' : False,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
4
py/lib/.gitignore
vendored
4
py/lib/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
dist/
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*.so
|
|
@ -1,3 +0,0 @@
|
|||||||
# Python Scwrypts
|
|
||||||
[](https://python.org)
|
|
||||||
<br>
|
|
6
py/lib/__init__.py
Normal file
6
py/lib/__init__.py
Normal 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
1
py/lib/data/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
import py.lib.data.converter
|
@ -4,6 +4,9 @@ import yaml
|
|||||||
|
|
||||||
|
|
||||||
def convert(input_stream, input_type, output_stream, output_type):
|
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)
|
data = convert_input(input_stream, input_type)
|
||||||
write_output(output_stream, output_type, data)
|
write_output(output_stream, output_type, data)
|
||||||
|
|
1
py/lib/fzf/__init__.py
Normal file
1
py/lib/fzf/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from py.lib.fzf.client import fzf, fzf_tail, fzf_head
|
5
py/lib/http/__init__.py
Normal file
5
py/lib/http/__init__.py
Normal 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
20
py/lib/http/client.py
Normal 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'
|
||||||
|
},
|
||||||
|
)
|
2
py/lib/http/directus/__init__.py
Normal file
2
py/lib/http/directus/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from py.lib.http.directus.client import *
|
||||||
|
from py.lib.http.directus.constant import *
|
56
py/lib/http/directus/client.py
Normal file
56
py/lib/http/directus/client.py
Normal 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]
|
@ -1,23 +1,3 @@
|
|||||||
'''
|
|
||||||
basic scwrypts.http client for directus
|
|
||||||
|
|
||||||
configured by setting DIRECTUS__BASE_URL and DIRECTUS__API_TOKEN in
|
|
||||||
scwrypts environment
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'request',
|
|
||||||
'graphql',
|
|
||||||
'get_collections',
|
|
||||||
'get_fields',
|
|
||||||
'FILTER_OPERATORS',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
from .graphql import graphql
|
|
||||||
from .collections import get_collections
|
|
||||||
from .fields import get_fields
|
|
||||||
|
|
||||||
FILTER_OPERATORS = {
|
FILTER_OPERATORS = {
|
||||||
'_eq',
|
'_eq',
|
||||||
'_neq',
|
'_neq',
|
2
py/lib/http/discord/__init__.py
Normal file
2
py/lib/http/discord/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from py.lib.http.discord.client import *
|
||||||
|
from py.lib.http.discord.send_message import *
|
20
py/lib/http/discord/client.py
Normal file
20
py/lib/http/discord/client.py
Normal 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)
|
@ -1,5 +1,5 @@
|
|||||||
from scwrypts.env import getenv
|
from py.lib.scwrypts import getenv
|
||||||
from .client import request
|
from py.lib.http.discord import request
|
||||||
|
|
||||||
def send_message(content, channel_id=None, webhook=None, username=None, avatar_url=None, **kwargs):
|
def send_message(content, channel_id=None, webhook=None, username=None, avatar_url=None, **kwargs):
|
||||||
if username is None:
|
if username is None:
|
1
py/lib/http/linear/__init__.py
Normal file
1
py/lib/http/linear/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from py.lib.http.linear.client import *
|
20
py/lib/http/linear/client.py
Normal file
20
py/lib/http/linear/client.py
Normal 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,59 +0,0 @@
|
|||||||
[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.vcs]
|
|
||||||
match = ['v[0-9]*.[0-9]*.[0-9]*']
|
|
1
py/lib/redis/__init__.py
Normal file
1
py/lib/redis/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from py.lib.redis.client import get_client
|
@ -1,6 +1,6 @@
|
|||||||
from redis import StrictRedis
|
from redis import StrictRedis
|
||||||
|
|
||||||
from scwrypts.env import getenv
|
from py.lib.scwrypts import getenv
|
||||||
|
|
||||||
CLIENT = None
|
CLIENT = None
|
||||||
|
|
@ -1,13 +1,6 @@
|
|||||||
'''
|
from py.lib.scwrypts.execute import execute
|
||||||
scwrypts
|
from py.lib.scwrypts.getenv import getenv
|
||||||
|
from py.lib.scwrypts.interactive import interactive
|
||||||
|
from py.lib.scwrypts.run import run
|
||||||
|
|
||||||
python library functions and invoker for scwrypts
|
import py.lib.scwrypts.io
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'scwrypts',
|
|
||||||
'execute',
|
|
||||||
'interactive',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .scwrypts import scwrypts, execute, interactive
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from .converter import convert
|
|
@ -1,73 +0,0 @@
|
|||||||
from io import StringIO
|
|
||||||
|
|
||||||
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 = 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)
|
|
16
py/lib/scwrypts/exceptions.py
Normal file
16
py/lib/scwrypts/exceptions.py
Normal 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} }}')
|
@ -1,12 +1,9 @@
|
|||||||
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
||||||
|
|
||||||
from scwrypts.io import get_combined_stream, add_io_arguments
|
from py.lib.scwrypts.io import get_combined_stream, add_io_arguments
|
||||||
|
|
||||||
|
|
||||||
def execute(main, description=None, parse_args=None, allow_input=True, allow_output=True):
|
def execute(main, description=None, parse_args=None, toggle_input=True, toggle_output=True):
|
||||||
'''
|
|
||||||
API to initiate a python-based scwrypt
|
|
||||||
'''
|
|
||||||
if parse_args is None:
|
if parse_args is None:
|
||||||
parse_args = []
|
parse_args = []
|
||||||
|
|
||||||
@ -15,7 +12,7 @@ def execute(main, description=None, parse_args=None, allow_input=True, allow_out
|
|||||||
formatter_class = ArgumentDefaultsHelpFormatter,
|
formatter_class = ArgumentDefaultsHelpFormatter,
|
||||||
)
|
)
|
||||||
|
|
||||||
add_io_arguments(parser, allow_input, allow_output)
|
add_io_arguments(parser, toggle_input, toggle_output)
|
||||||
|
|
||||||
for a in parse_args:
|
for a in parse_args:
|
||||||
parser.add_argument(*a[0], **a[1])
|
parser.add_argument(*a[0], **a[1])
|
@ -1 +0,0 @@
|
|||||||
from .client import fzf, fzf_tail, fzf_head
|
|
@ -1,6 +1,6 @@
|
|||||||
from os import getenv as os_getenv
|
from os import getenv as os_getenv
|
||||||
|
|
||||||
from .scwrypts.exceptions import MissingVariableError
|
from py.lib.scwrypts.exceptions import MissingVariableError
|
||||||
|
|
||||||
|
|
||||||
def getenv(name, required=True):
|
def getenv(name, required=True):
|
@ -1 +0,0 @@
|
|||||||
from .client import get_request_client
|
|
@ -1,25 +0,0 @@
|
|||||||
from requests import request
|
|
||||||
|
|
||||||
|
|
||||||
CLIENTS = {}
|
|
||||||
|
|
||||||
def get_request_client(base_url, headers=None):
|
|
||||||
if CLIENTS.get(base_url, None) is None:
|
|
||||||
if headers is None:
|
|
||||||
headers = {}
|
|
||||||
|
|
||||||
CLIENTS[base_url] = 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'
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return CLIENTS[base_url]
|
|
@ -1,43 +0,0 @@
|
|||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from scwrypts.test import generate
|
|
||||||
from scwrypts.test.character_set import uri
|
|
||||||
|
|
||||||
options = {
|
|
||||||
'str_length_minimum': 8,
|
|
||||||
'str_length_maximum': 128,
|
|
||||||
'uuid_output_type': str,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_request_client_sample_data():
|
|
||||||
return {
|
|
||||||
'base_url' : generate(str, options | {'character_set': uri}),
|
|
||||||
'endpoint' : generate(str, options | {'character_set': uri}),
|
|
||||||
'method' : generate(str, options),
|
|
||||||
'response' : generate('requests_Response', options | {'depth': 4}),
|
|
||||||
'payload' : generate(dict, {
|
|
||||||
**options,
|
|
||||||
'depth': 1,
|
|
||||||
'data_types': { str, 'uuid' },
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
@fixture(name='sample')
|
|
||||||
def fixture_sample():
|
|
||||||
return SimpleNamespace(
|
|
||||||
**get_request_client_sample_data(),
|
|
||||||
|
|
||||||
headers = generate(dict, {
|
|
||||||
**options,
|
|
||||||
'depth': 1,
|
|
||||||
'data_types': { str, 'uuid' },
|
|
||||||
}),
|
|
||||||
|
|
||||||
payload_headers = generate(dict, {
|
|
||||||
**options,
|
|
||||||
'depth': 1,
|
|
||||||
'data_types': { str, 'uuid' },
|
|
||||||
}),
|
|
||||||
)
|
|
@ -1,12 +0,0 @@
|
|||||||
from scwrypts.env import getenv
|
|
||||||
|
|
||||||
from .. import get_request_client
|
|
||||||
|
|
||||||
|
|
||||||
def request(method, endpoint, **kwargs):
|
|
||||||
return get_request_client(
|
|
||||||
base_url = getenv("DIRECTUS__BASE_URL"),
|
|
||||||
headers = {
|
|
||||||
'Authorization': f'bearer {getenv("DIRECTUS__API_TOKEN")}',
|
|
||||||
}
|
|
||||||
)(method, endpoint, **kwargs)
|
|
@ -1,18 +0,0 @@
|
|||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
COLLECTIONS = None
|
|
||||||
|
|
||||||
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
|
|
@ -1,16 +0,0 @@
|
|||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from scwrypts.test import generate
|
|
||||||
from scwrypts.test.character_set import uri
|
|
||||||
from ..conftest import options, get_request_client_sample_data
|
|
||||||
|
|
||||||
|
|
||||||
@fixture(name='sample')
|
|
||||||
def fixture_sample():
|
|
||||||
return SimpleNamespace(
|
|
||||||
**get_request_client_sample_data(),
|
|
||||||
api_token = generate(str, options | {'character_set': uri}),
|
|
||||||
query = generate(str, options),
|
|
||||||
)
|
|
@ -1,16 +0,0 @@
|
|||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
FIELDS = {}
|
|
||||||
|
|
||||||
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]
|
|
@ -1,9 +0,0 @@
|
|||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
def graphql(query, system=False):
|
|
||||||
return request(
|
|
||||||
'POST',
|
|
||||||
'graphql' if system is False else 'graphql/system',
|
|
||||||
json={'query': query},
|
|
||||||
)
|
|
@ -1,43 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
def test_directus_request(sample, _response):
|
|
||||||
assert _response == sample.response
|
|
||||||
|
|
||||||
def test_directus_request_client_setup(sample, _response, mock_get_request_client):
|
|
||||||
mock_get_request_client.assert_called_once_with(
|
|
||||||
base_url = sample.base_url,
|
|
||||||
headers = { 'Authorization': f'bearer {sample.api_token}' },
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response')
|
|
||||||
def fixture_response(sample):
|
|
||||||
return request(
|
|
||||||
method = sample.method,
|
|
||||||
endpoint = sample.endpoint,
|
|
||||||
**sample.payload,
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='mock_getenv', autouse=True)
|
|
||||||
def fixture_mock_getenv(sample):
|
|
||||||
with patch('scwrypts.http.directus.client.getenv',) as mock:
|
|
||||||
mock.side_effect = lambda name: {
|
|
||||||
'DIRECTUS__BASE_URL': sample.base_url,
|
|
||||||
'DIRECTUS__API_TOKEN': sample.api_token,
|
|
||||||
}[name]
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='mock_get_request_client', autouse=True)
|
|
||||||
def fixture_mock_get_request_client(sample):
|
|
||||||
with patch('scwrypts.http.directus.client.get_request_client') as mock:
|
|
||||||
mock.return_value = lambda method, endpoint, **kwargs: sample.response
|
|
||||||
yield mock
|
|
@ -1,45 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .graphql import graphql
|
|
||||||
|
|
||||||
|
|
||||||
def test_directus_graphql(sample, _response, _mock_request):
|
|
||||||
assert _response == sample.response
|
|
||||||
|
|
||||||
def test_directus_graphql_request_payload(sample, _response, _mock_request):
|
|
||||||
_mock_request.assert_called_once_with(
|
|
||||||
'POST',
|
|
||||||
'graphql',
|
|
||||||
json = {'query': sample.query},
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_directus_graphql_system(sample, _response_system):
|
|
||||||
assert _response_system == sample.response
|
|
||||||
|
|
||||||
def test_directus_graphql_system_request_payload(sample, _response_system, _mock_request):
|
|
||||||
_mock_request.assert_called_once_with(
|
|
||||||
'POST',
|
|
||||||
'graphql/system',
|
|
||||||
json = {'query': sample.query},
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response')
|
|
||||||
def fixture_response(sample, _mock_request):
|
|
||||||
return graphql(sample.query)
|
|
||||||
|
|
||||||
|
|
||||||
@fixture(name='_response_system')
|
|
||||||
def fixture_response_system(sample, _mock_request):
|
|
||||||
return graphql(sample.query, system=True)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_mock_request')
|
|
||||||
def fixture_mock_request(sample):
|
|
||||||
with patch('scwrypts.http.directus.graphql.request') as mock:
|
|
||||||
mock.return_value = sample.response
|
|
||||||
yield mock
|
|
@ -1,14 +0,0 @@
|
|||||||
'''
|
|
||||||
basic scwrypts.http client for discord
|
|
||||||
|
|
||||||
configured by setting various DISCORD__* options in the
|
|
||||||
scwrypts environment
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'request',
|
|
||||||
'send_message',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
from .send_message import send_message
|
|
@ -1,15 +0,0 @@
|
|||||||
from scwrypts.env import getenv
|
|
||||||
|
|
||||||
from .. import get_request_client
|
|
||||||
|
|
||||||
|
|
||||||
def request(method, endpoint, **kwargs):
|
|
||||||
headers = {}
|
|
||||||
|
|
||||||
if (token := getenv("DISCORD__BOT_TOKEN", required = False)) is not None:
|
|
||||||
headers['Authorization'] = f'Bot {token}'
|
|
||||||
|
|
||||||
return get_request_client(
|
|
||||||
base_url = 'https://discord.com/api',
|
|
||||||
headers = headers,
|
|
||||||
)(method, endpoint, **kwargs)
|
|
@ -1,25 +0,0 @@
|
|||||||
from string import ascii_letters, digits
|
|
||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from scwrypts.test import generate
|
|
||||||
from scwrypts.test.character_set import uri
|
|
||||||
from ..conftest import options, get_request_client_sample_data
|
|
||||||
|
|
||||||
@fixture(name='sample')
|
|
||||||
def fixture_sample():
|
|
||||||
return SimpleNamespace(
|
|
||||||
**{
|
|
||||||
**get_request_client_sample_data(),
|
|
||||||
'base_url': 'https://discord.com/api',
|
|
||||||
},
|
|
||||||
bot_token = generate(str, options | {'character_set': uri}),
|
|
||||||
username = generate(str, options | {'character_set': ascii_letters + digits}),
|
|
||||||
avatar_url = generate(str, options | {'character_set': uri}),
|
|
||||||
webhook = generate(str, options | {'character_set': uri}),
|
|
||||||
channel_id = generate(str, options | {'character_set': uri}),
|
|
||||||
content_header = generate(str, options),
|
|
||||||
content_footer = generate(str, options),
|
|
||||||
content = generate(str, options),
|
|
||||||
)
|
|
@ -1,54 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
def test_discord_request(sample, _response):
|
|
||||||
assert _response == sample.response
|
|
||||||
|
|
||||||
def test_discord_request_client_setup(sample, mock_get_request_client, _mock_getenv, _response):
|
|
||||||
mock_get_request_client.assert_called_once_with(
|
|
||||||
base_url = sample.base_url,
|
|
||||||
headers = { 'Authorization': f'Bot {sample.bot_token}' },
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_discord_request_client_setup_public(sample, mock_get_request_client, _mock_getenv_optional, _response):
|
|
||||||
mock_get_request_client.assert_called_once_with(
|
|
||||||
base_url = sample.base_url,
|
|
||||||
headers = {},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response')
|
|
||||||
def fixture_response(sample):
|
|
||||||
return request(
|
|
||||||
method = sample.method,
|
|
||||||
endpoint = sample.endpoint,
|
|
||||||
**sample.payload,
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='mock_get_request_client', autouse=True)
|
|
||||||
def fixture_mock_get_request_client(sample):
|
|
||||||
with patch('scwrypts.http.discord.client.get_request_client') as mock:
|
|
||||||
mock.return_value = lambda method, endpoint, **kwargs: sample.response
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='_mock_getenv')
|
|
||||||
def fixture_mock_getenv(sample):
|
|
||||||
with patch('scwrypts.http.discord.client.getenv',) as mock:
|
|
||||||
mock.side_effect = lambda name, **kwargs: {
|
|
||||||
'DISCORD__BOT_TOKEN': sample.bot_token,
|
|
||||||
}[name]
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='_mock_getenv_optional')
|
|
||||||
def fixture_mock_getenv_optional():
|
|
||||||
with patch('scwrypts.http.discord.client.getenv',) as mock:
|
|
||||||
mock.side_effect = lambda name, **kwargs: None
|
|
||||||
yield mock
|
|
@ -1,91 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture, raises
|
|
||||||
|
|
||||||
from .send_message import send_message
|
|
||||||
|
|
||||||
|
|
||||||
def test_discord_send_message(sample, mock_request, _mock_getenv):
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_without_username(sample, mock_request, _mock_getenv):
|
|
||||||
sample.username = None
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
del expected['json']['username']
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_without_avatar_url(sample, mock_request, _mock_getenv):
|
|
||||||
sample.avatar_url = None
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
del expected['json']['avatar_url']
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_to_channel_id(sample, mock_request, _mock_getenv):
|
|
||||||
sample.webhook = None
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
expected['endpoint'] = f'channels/{sample.channel_id}/messages'
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_without_content_header(sample, mock_request, _mock_getenv):
|
|
||||||
sample.content_header = None
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
expected['json']['content'] = f'{sample.content}{sample.content_footer}'
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_without_content_footer(sample, mock_request, _mock_getenv):
|
|
||||||
sample.content_footer = None
|
|
||||||
expected = get_default_called_with(sample)
|
|
||||||
expected['json']['content'] = f'{sample.content_header}{sample.content}'
|
|
||||||
|
|
||||||
assert send_message(sample.content) == sample.response
|
|
||||||
mock_request.assert_called_once_with(**expected)
|
|
||||||
|
|
||||||
def test_discord_send_message_error(sample, mock_request, _mock_getenv):
|
|
||||||
with raises(ValueError):
|
|
||||||
sample.webhook = None
|
|
||||||
sample.channel_id = None
|
|
||||||
|
|
||||||
send_message(sample.content)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
def get_default_called_with(sample):
|
|
||||||
return {
|
|
||||||
'method': 'POST',
|
|
||||||
'endpoint': f'webhooks/{sample.webhook}',
|
|
||||||
'json': {
|
|
||||||
'content': f'{sample.content_header}{sample.content}{sample.content_footer}',
|
|
||||||
'username': sample.username,
|
|
||||||
'avatar_url': sample.avatar_url,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
@fixture(name='mock_request', autouse=True)
|
|
||||||
def fixture_mock_request(sample):
|
|
||||||
with patch('scwrypts.http.discord.send_message.request') as mock:
|
|
||||||
mock.return_value = sample.response
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='_mock_getenv')
|
|
||||||
def fixture_mock_getenv(sample):
|
|
||||||
with patch('scwrypts.http.discord.send_message.getenv',) as mock:
|
|
||||||
mock.side_effect = lambda name, **kwargs: {
|
|
||||||
'DISCORD__DEFAULT_USERNAME': sample.username,
|
|
||||||
'DISCORD__DEFAULT_AVATAR_URL': sample.avatar_url,
|
|
||||||
'DISCORD__DEFAULT_WEBHOOK': sample.webhook,
|
|
||||||
'DISCORD__DEFAULT_CHANNEL_ID': sample.channel_id,
|
|
||||||
'DISCORD__CONTENT_HEADER': sample.content_header,
|
|
||||||
'DISCORD__CONTENT_FOOTER': sample.content_footer,
|
|
||||||
}[name]
|
|
||||||
yield mock
|
|
@ -1,14 +0,0 @@
|
|||||||
'''
|
|
||||||
basic scwrypts.http client for linear
|
|
||||||
|
|
||||||
configured by setting the LINEAR__API_TOKEN option in the
|
|
||||||
scwrypts environment
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'request',
|
|
||||||
'graphql',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
from .graphql import graphql
|
|
@ -1,12 +0,0 @@
|
|||||||
from scwrypts.env import getenv
|
|
||||||
|
|
||||||
from .. import get_request_client
|
|
||||||
|
|
||||||
|
|
||||||
def request(method, endpoint, **kwargs):
|
|
||||||
return get_request_client(
|
|
||||||
base_url = 'https://api.linear.app',
|
|
||||||
headers = {
|
|
||||||
'Authorization': f'bearer {getenv("LINEAR__API_TOKEN")}',
|
|
||||||
},
|
|
||||||
)(method, endpoint, **kwargs)
|
|
@ -1,19 +0,0 @@
|
|||||||
from string import ascii_letters, digits
|
|
||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from scwrypts.test import generate
|
|
||||||
from scwrypts.test.character_set import uri
|
|
||||||
from ..conftest import options, get_request_client_sample_data
|
|
||||||
|
|
||||||
@fixture(name='sample')
|
|
||||||
def fixture_sample():
|
|
||||||
return SimpleNamespace(
|
|
||||||
**{
|
|
||||||
**get_request_client_sample_data(),
|
|
||||||
'base_url': 'https://api.linear.app',
|
|
||||||
},
|
|
||||||
api_token = generate(str, options | {'character_set': uri}),
|
|
||||||
query = generate(str, options),
|
|
||||||
)
|
|
@ -1,5 +0,0 @@
|
|||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
def graphql(query):
|
|
||||||
return request('POST', 'graphql', json={'query': query})
|
|
@ -1,42 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .client import request
|
|
||||||
|
|
||||||
|
|
||||||
def test_discord_request(sample, _response):
|
|
||||||
assert _response == sample.response
|
|
||||||
|
|
||||||
def test_discord_request_client_setup(sample, mock_get_request_client, _response):
|
|
||||||
mock_get_request_client.assert_called_once_with(
|
|
||||||
base_url = sample.base_url,
|
|
||||||
headers = { 'Authorization': f'bearer {sample.api_token}' },
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response')
|
|
||||||
def fixture_response(sample):
|
|
||||||
return request(
|
|
||||||
method = sample.method,
|
|
||||||
endpoint = sample.endpoint,
|
|
||||||
**sample.payload,
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='mock_get_request_client', autouse=True)
|
|
||||||
def fixture_mock_get_request_client(sample):
|
|
||||||
with patch('scwrypts.http.linear.client.get_request_client') as mock:
|
|
||||||
mock.return_value = lambda method, endpoint, **kwargs: sample.response
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='mock_getenv', autouse=True)
|
|
||||||
def fixture_mock_getenv(sample):
|
|
||||||
with patch('scwrypts.http.linear.client.getenv',) as mock:
|
|
||||||
mock.side_effect = lambda name, **kwargs: {
|
|
||||||
'LINEAR__API_TOKEN': sample.api_token,
|
|
||||||
}[name]
|
|
||||||
yield mock
|
|
@ -1,35 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .graphql import graphql
|
|
||||||
|
|
||||||
|
|
||||||
def test_directus_graphql(sample, _response, _mock_request):
|
|
||||||
assert _response == sample.response
|
|
||||||
|
|
||||||
def test_directus_graphql_request_payload(sample, _response, _mock_request):
|
|
||||||
_mock_request.assert_called_once_with(
|
|
||||||
'POST',
|
|
||||||
'graphql',
|
|
||||||
json = {'query': sample.query},
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response')
|
|
||||||
def fixture_response(sample, _mock_request):
|
|
||||||
return graphql(sample.query)
|
|
||||||
|
|
||||||
|
|
||||||
@fixture(name='_response_system')
|
|
||||||
def fixture_response_system(sample, _mock_request):
|
|
||||||
return graphql(sample.query)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_mock_request')
|
|
||||||
def fixture_mock_request(sample):
|
|
||||||
with patch('scwrypts.http.linear.graphql.request') as mock:
|
|
||||||
mock.return_value = sample.response
|
|
||||||
yield mock
|
|
@ -1,55 +0,0 @@
|
|||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from pytest import fixture
|
|
||||||
|
|
||||||
from .client import get_request_client
|
|
||||||
|
|
||||||
|
|
||||||
def test_request_client(sample, _response_basic):
|
|
||||||
assert _response_basic == sample.response
|
|
||||||
|
|
||||||
def test_request_client_forwards_default_headers(sample, mock_request, _response_basic):
|
|
||||||
mock_request.assert_called_once_with(
|
|
||||||
method = sample.method,
|
|
||||||
url = f'{sample.base_url}/{sample.endpoint}',
|
|
||||||
headers = sample.headers,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_request_client_payload(sample, _response_payload):
|
|
||||||
assert _response_payload == sample.response
|
|
||||||
|
|
||||||
def test_request_client_forwards_payload_headers(sample, mock_request, _response_payload):
|
|
||||||
assert mock_request.call_args.kwargs['headers'] == sample.headers | sample.payload_headers
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='mock_request', autouse=True)
|
|
||||||
def fixture_mock_request(sample):
|
|
||||||
with patch('scwrypts.http.client.request') as mock:
|
|
||||||
mock.return_value = sample.response
|
|
||||||
yield mock
|
|
||||||
|
|
||||||
@fixture(name='request_client', autouse=True)
|
|
||||||
def fixture_request_client(sample):
|
|
||||||
return get_request_client(sample.base_url, sample.headers)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@fixture(name='_response_basic')
|
|
||||||
def fixture_response_basic(sample, request_client):
|
|
||||||
return request_client(
|
|
||||||
method = sample.method,
|
|
||||||
endpoint = sample.endpoint,
|
|
||||||
)
|
|
||||||
|
|
||||||
@fixture(name='_response_payload')
|
|
||||||
def fixture_response_payload(sample, request_client):
|
|
||||||
return request_client(
|
|
||||||
method = sample.method,
|
|
||||||
endpoint = sample.endpoint,
|
|
||||||
**{
|
|
||||||
**sample.payload,
|
|
||||||
'headers': sample.payload_headers,
|
|
||||||
},
|
|
||||||
)
|
|
@ -2,9 +2,6 @@ from bpython import embed
|
|||||||
|
|
||||||
|
|
||||||
def interactive(variable_descriptions):
|
def interactive(variable_descriptions):
|
||||||
'''
|
|
||||||
main() decorator to drop to interactive python environment upon completion
|
|
||||||
'''
|
|
||||||
def outer(function):
|
def outer(function):
|
||||||
|
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
@ -2,47 +2,7 @@ from contextlib import contextmanager
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from sys import stdin, stdout, stderr
|
from sys import stdin, stdout, stderr
|
||||||
|
|
||||||
from scwrypts.env import getenv
|
from py.lib.scwrypts.getenv import getenv
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def get_combined_stream(input_file=None, output_file=None):
|
|
||||||
'''
|
|
||||||
context manager to open an "input_file" and "output_file"
|
|
||||||
|
|
||||||
But the "files" can be pipe-streams, stdin/stdout, or even
|
|
||||||
actual files! Helpful when trying to write CLI scwrypts
|
|
||||||
which would like to accept all kinds of input and output
|
|
||||||
configurations.
|
|
||||||
'''
|
|
||||||
with get_stream(input_file, 'r') as input_stream, get_stream(output_file, 'w+') as output_stream:
|
|
||||||
yield CombinedStream(input_stream, output_stream)
|
|
||||||
|
|
||||||
def add_io_arguments(parser, allow_input=True, allow_output=True):
|
|
||||||
'''
|
|
||||||
slap these puppies onto your argparse.ArgumentParser to
|
|
||||||
allow easy use of the get_combined_stream at the command line
|
|
||||||
'''
|
|
||||||
if allow_input:
|
|
||||||
parser.add_argument(
|
|
||||||
'-i', '--input-file',
|
|
||||||
dest = 'input_file',
|
|
||||||
default = None,
|
|
||||||
help = 'path to input file; omit for stdin',
|
|
||||||
required = False,
|
|
||||||
)
|
|
||||||
|
|
||||||
if allow_output:
|
|
||||||
parser.add_argument(
|
|
||||||
'-o', '--output-file',
|
|
||||||
dest = 'output_file',
|
|
||||||
default = None,
|
|
||||||
help = 'path to output file; omit for stdout',
|
|
||||||
required = False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@ -74,6 +34,32 @@ def get_stream(filename=None, mode='r', encoding='utf-8', verbose=False, **kwarg
|
|||||||
stdout.flush()
|
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:
|
class CombinedStream:
|
||||||
def __init__(self, input_stream, output_stream):
|
def __init__(self, input_stream, output_stream):
|
||||||
self.input = input_stream
|
self.input = input_stream
|
@ -1 +0,0 @@
|
|||||||
from .combined_io_stream import get_combined_stream, add_io_arguments
|
|
@ -1 +0,0 @@
|
|||||||
from .client import get_client
|
|
22
py/lib/scwrypts/run.py
Normal file
22
py/lib/scwrypts/run.py
Normal 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')
|
@ -1,23 +0,0 @@
|
|||||||
'''
|
|
||||||
scwrypts meta-configuration
|
|
||||||
|
|
||||||
provides a helpful three ways to run "scwrypts"
|
|
||||||
|
|
||||||
'scwrypts' is an agnostic, top-level executor allowing any scwrypt to be called from python workflows
|
|
||||||
|
|
||||||
'execute' is the default context set-up for python-based scwrypts
|
|
||||||
|
|
||||||
'interactive' is a context set-up for interactive, python-based scwrypts
|
|
||||||
after execution, you are dropped in a bpython shell with all the variables
|
|
||||||
configured during main() execution
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'scwrypts',
|
|
||||||
'execute',
|
|
||||||
'interactive',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .scwrypts import scwrypts
|
|
||||||
from .execute import execute
|
|
||||||
from .interactive import interactive
|
|
@ -1,26 +0,0 @@
|
|||||||
from argparse import ArgumentError
|
|
||||||
|
|
||||||
|
|
||||||
class MissingVariableError(EnvironmentError):
|
|
||||||
def init(self, name):
|
|
||||||
super().__init__(f'Missing required environment variable "{name}"')
|
|
||||||
|
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
|
|
||||||
class BadScwryptsLookupError(ValueError):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__('must provide name/group/type or scwrypt lookup patterns')
|
|
||||||
|
|
||||||
|
|
||||||
class MissingScwryptsGroupOrTypeError(ValueError):
|
|
||||||
def __init__(self, group, _type):
|
|
||||||
super().__init__(f'missing required group or type (group={group} | type={_type}')
|
|
@ -1,48 +0,0 @@
|
|||||||
from os import getenv
|
|
||||||
from shutil import which
|
|
||||||
from subprocess import run
|
|
||||||
|
|
||||||
from .exceptions import MissingScwryptsExecutableError, BadScwryptsLookupError, MissingScwryptsGroupOrTypeError
|
|
||||||
|
|
||||||
|
|
||||||
def scwrypts(*args, patterns=None, name=None, group=None, _type=None, log_level=None):
|
|
||||||
'''
|
|
||||||
top-level scwrypts invoker from python
|
|
||||||
|
|
||||||
- patterns allows for pattern-based scwrypt lookup
|
|
||||||
- name/group/type allos for precise-match lookup
|
|
||||||
|
|
||||||
*args should be a list of strings and is forwarded to the
|
|
||||||
invoked scwrypt
|
|
||||||
|
|
||||||
see 'scwrypts --help' for more information
|
|
||||||
'''
|
|
||||||
executable = which('scwrypts')
|
|
||||||
if executable is None:
|
|
||||||
raise MissingScwryptsExecutableError()
|
|
||||||
|
|
||||||
if patterns is None and name is None:
|
|
||||||
raise BadScwryptsLookupError()
|
|
||||||
|
|
||||||
pre_args = []
|
|
||||||
|
|
||||||
if name is None:
|
|
||||||
pre_args += patterns
|
|
||||||
else:
|
|
||||||
pre_args += ['--name', name, '--group', group, '--type', _type]
|
|
||||||
if group is None or _type is None:
|
|
||||||
raise MissingScwryptsGroupOrTypeError(group, _type)
|
|
||||||
|
|
||||||
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} {" ".join(pre_args)} -- {" ".join(args)}',
|
|
||||||
shell=True,
|
|
||||||
executable='/bin/zsh',
|
|
||||||
check=False,
|
|
||||||
)
|
|
@ -1,10 +0,0 @@
|
|||||||
'''
|
|
||||||
automated testing utilties, but primarily a random data generator
|
|
||||||
'''
|
|
||||||
__all__ = [
|
|
||||||
'generate',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .generate import generate
|
|
||||||
|
|
||||||
from .character_set import *
|
|
@ -1,13 +0,0 @@
|
|||||||
'''
|
|
||||||
string constants typically used for randomly generated data
|
|
||||||
|
|
||||||
the 'string' standard library already contains many character sets,
|
|
||||||
but not these :)
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'uri',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
uri = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&\'()*+,;='
|
|
@ -1,10 +0,0 @@
|
|||||||
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}"')
|
|
@ -1,372 +0,0 @@
|
|||||||
from csv import writer, QUOTE_NONNUMERIC
|
|
||||||
from io import StringIO
|
|
||||||
from json import dumps, loads
|
|
||||||
from random import randint, uniform, choice
|
|
||||||
from re import sub
|
|
||||||
from string import printable
|
|
||||||
from typing import Hashable, Callable
|
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from requests import Response, status_codes
|
|
||||||
from yaml import safe_dump
|
|
||||||
|
|
||||||
from .exceptions import NoDataTypeError, BadGeneratorTypeError
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_OPTIONS = {
|
|
||||||
'data_types': None,
|
|
||||||
'minimum': 0,
|
|
||||||
'maximum': 64,
|
|
||||||
'depth': 1,
|
|
||||||
'character_set': None,
|
|
||||||
'bool_nullable': False,
|
|
||||||
'str_length': None,
|
|
||||||
'str_length_minimum': 0,
|
|
||||||
'str_length_maximum': 64,
|
|
||||||
'uuid_output_type': 'uuid', # str or 'uuid'
|
|
||||||
'list_length': 8,
|
|
||||||
'set_length': 8,
|
|
||||||
'dict_length': 8,
|
|
||||||
'csv_bool_nullable': True,
|
|
||||||
'csv_columns': None,
|
|
||||||
'csv_columns_minimum': 1,
|
|
||||||
'csv_columns_maximum': 16,
|
|
||||||
'csv_rows': None,
|
|
||||||
'csv_rows_minimum': 2,
|
|
||||||
'csv_rows_maximum': 16,
|
|
||||||
'csv_output_type': 'stringio', # str or 'stringio'
|
|
||||||
'json_initial_type': dict, # typically dict or list
|
|
||||||
'json_bool_nullable': True,
|
|
||||||
'json_output_type': 'stringio', # str or 'stringio'
|
|
||||||
'yaml_initial_type': dict, # typically dict or list
|
|
||||||
'yaml_bool_nullable': True,
|
|
||||||
'yaml_use_default_flow_style': False,
|
|
||||||
'yaml_output_type': 'stringio', # str or 'stringio'
|
|
||||||
'requests_response_status_code': status_codes.codes[200],
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = {}
|
|
||||||
|
|
||||||
options = DEFAULT_OPTIONS | options
|
|
||||||
|
|
||||||
if data_type is None:
|
|
||||||
if options['data_types'] is None or len(options['data_types']) == 0:
|
|
||||||
raise NoDataTypeError()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
SUPPORTED_DATA_TYPES = None
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def filter_data_types(cls, options, filters=None):
|
|
||||||
'''
|
|
||||||
returns an options dict with appropriately filtered data_types
|
|
||||||
|
|
||||||
if data_types are not yet defined, starts with all supported data_types
|
|
||||||
'''
|
|
||||||
if options['data_types'] is None:
|
|
||||||
options['data_types'] = Generator.get_supported_data_types()
|
|
||||||
|
|
||||||
if filters is None or len(filters) == 0:
|
|
||||||
return options
|
|
||||||
|
|
||||||
return {
|
|
||||||
**options,
|
|
||||||
'data_types': set(filter(
|
|
||||||
lambda data_type: all(( f(data_type, options) for f in filters )),
|
|
||||||
options['data_types'],
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
|
|
||||||
class Filters:
|
|
||||||
@staticmethod
|
|
||||||
def hashable(data_type, _options):
|
|
||||||
if isinstance(data_type, Callable):
|
|
||||||
return isinstance(data_type(), Hashable)
|
|
||||||
if not isinstance(data_type, str):
|
|
||||||
data_type = data_type.__name__
|
|
||||||
return data_type in { 'bool', 'int', 'float', 'chr', 'str', 'uuid' }
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def filelike(data_type, _options):
|
|
||||||
return data_type in { 'csv', 'json', 'yaml' }
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def complex(data_type, _options):
|
|
||||||
return data_type in { 'requests_Response' }
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def basic(data_type, options):
|
|
||||||
return all([
|
|
||||||
not Generator.Filters.filelike(data_type, options),
|
|
||||||
not Generator.Filters.complex(data_type, options),
|
|
||||||
])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def pythonset(data_type, _options):
|
|
||||||
if not isinstance(data_type, str):
|
|
||||||
data_type = data_type.__name__
|
|
||||||
return data_type == 'set'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def csvsafe(data_type, options):
|
|
||||||
options['depth'] = max(1, options['depth'])
|
|
||||||
return all([
|
|
||||||
Generator.Filters.basic(data_type, options),
|
|
||||||
not Generator.Filters.pythonset(data_type, options),
|
|
||||||
])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def jsonsafe(data_type, options):
|
|
||||||
return all([
|
|
||||||
Generator.Filters.basic(data_type, options),
|
|
||||||
not Generator.Filters.pythonset(data_type, options),
|
|
||||||
])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def yamlsafe(data_type, options):
|
|
||||||
return all([
|
|
||||||
Generator.Filters.basic(data_type, options),
|
|
||||||
not Generator.Filters.pythonset(data_type, options),
|
|
||||||
])
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_option_with_range(cls, options, option_key, data_type=int):
|
|
||||||
'''
|
|
||||||
typically an integer range, allows both:
|
|
||||||
- setting a fixed configuration (e.g. 'str_length')
|
|
||||||
- allowing a configuration range (e.g. 'str_length_minimum' and 'str_length_maximum')
|
|
||||||
'''
|
|
||||||
fixed = options.get(option_key, None)
|
|
||||||
if fixed is not None:
|
|
||||||
return fixed
|
|
||||||
|
|
||||||
return generate(data_type, {
|
|
||||||
'minimum': options[f'{option_key}_minimum'],
|
|
||||||
'maximum': options[f'{option_key}_maximum'],
|
|
||||||
})
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
|
|
||||||
@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):
|
|
||||||
return ''.join((
|
|
||||||
generate(chr, options)
|
|
||||||
for _ in range(Generator.get_option_with_range(options, 'str_length'))
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _uuid(options):
|
|
||||||
'''
|
|
||||||
creates a UUID object or a str containing a uuid (v4)
|
|
||||||
'''
|
|
||||||
uuid = uuid4()
|
|
||||||
return str(uuid) if options['uuid_output_type'] == str else uuid
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _list(options):
|
|
||||||
if options['depth'] <= 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
options['depth'] -= 1
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.basic,
|
|
||||||
])
|
|
||||||
|
|
||||||
return [ generate(None, {**options}) for _ in range(options['list_length']) ]
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _set(options):
|
|
||||||
if options['depth'] <= 0:
|
|
||||||
return set()
|
|
||||||
|
|
||||||
options['depth'] -= 1
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.hashable,
|
|
||||||
])
|
|
||||||
|
|
||||||
return { generate(None, options) for _ in range(options['set_length']) }
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _dict(options):
|
|
||||||
if options['depth'] <= 0:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
options['depth'] -= 1
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.basic,
|
|
||||||
])
|
|
||||||
|
|
||||||
key_options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.hashable,
|
|
||||||
])
|
|
||||||
|
|
||||||
if len(options['data_types']) == 0 or len(key_options['data_types']) == 0:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
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['character_set'] is None:
|
|
||||||
options['character_set'] = printable
|
|
||||||
|
|
||||||
options['bool_nullable'] = options['csv_bool_nullable']
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.csvsafe,
|
|
||||||
])
|
|
||||||
|
|
||||||
columns = Generator.get_option_with_range(options, 'csv_columns')
|
|
||||||
rows = Generator.get_option_with_range(options, 'csv_rows')
|
|
||||||
|
|
||||||
|
|
||||||
csv = StringIO()
|
|
||||||
csv_writer = writer(csv, quoting=QUOTE_NONNUMERIC)
|
|
||||||
|
|
||||||
options['list_length'] = columns
|
|
||||||
|
|
||||||
[ # pylint: disable=expression-not-assigned
|
|
||||||
csv_writer.writerow(generate(list, options))
|
|
||||||
for _ in range(rows)
|
|
||||||
]
|
|
||||||
|
|
||||||
csv.seek(0)
|
|
||||||
return csv.getvalue() if options['csv_output_type'] == str else csv
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _json(options):
|
|
||||||
'''
|
|
||||||
creates a StringIO object or str containing json data
|
|
||||||
'''
|
|
||||||
|
|
||||||
if options['character_set'] is None:
|
|
||||||
options['character_set'] = printable
|
|
||||||
|
|
||||||
options['bool_nullable'] = options['json_bool_nullable']
|
|
||||||
options['uuid_output_type'] = str
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.jsonsafe,
|
|
||||||
])
|
|
||||||
|
|
||||||
json = dumps(generate(
|
|
||||||
options['json_initial_type'],
|
|
||||||
{**options},
|
|
||||||
))
|
|
||||||
|
|
||||||
return json if options['json_output_type'] == str else StringIO(json)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _yaml(options):
|
|
||||||
'''
|
|
||||||
creates a StringIO object or str containing yaml data
|
|
||||||
'''
|
|
||||||
if options['character_set'] is None:
|
|
||||||
options['character_set'] = printable
|
|
||||||
|
|
||||||
options['bool_nullable'] = options['yaml_bool_nullable']
|
|
||||||
options['uuid_output_type'] = str
|
|
||||||
options = Generator.filter_data_types(options, [
|
|
||||||
Generator.Filters.yamlsafe,
|
|
||||||
])
|
|
||||||
|
|
||||||
yaml = StringIO()
|
|
||||||
safe_dump(
|
|
||||||
generate(options['yaml_initial_type'], {**options}),
|
|
||||||
yaml,
|
|
||||||
default_flow_style=options['yaml_use_default_flow_style'],
|
|
||||||
)
|
|
||||||
yaml.seek(0)
|
|
||||||
|
|
||||||
return yaml.getvalue() if options['yaml_output_type'] == str else yaml
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _requests_Response(options):
|
|
||||||
'''
|
|
||||||
creates a requests.Response-like object containing json data
|
|
||||||
'''
|
|
||||||
|
|
||||||
options['json_output_type'] = str
|
|
||||||
|
|
||||||
response = Response()
|
|
||||||
response.status_code = options['requests_response_status_code']
|
|
||||||
json = loads(generate('json', options))
|
|
||||||
response.json = lambda: json
|
|
||||||
|
|
||||||
return response
|
|
@ -1,54 +0,0 @@
|
|||||||
from os import getenv
|
|
||||||
from pprint import pprint
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
from .generate import generate, Generator
|
|
||||||
|
|
||||||
ITERATIONS = int(
|
|
||||||
getenv(
|
|
||||||
'PYTEST_ITERATIONS__scwrypts__test__generator',
|
|
||||||
getenv('PYTEST_ITERATIONS', '99'), # CI should use at least 999
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
FILE_LIKE_DATA_TYPES = { 'csv', 'json', 'yaml' }
|
|
||||||
|
|
||||||
def test_generate(): # generators should be quick and "just work" (no Exceptions)
|
|
||||||
print()
|
|
||||||
for data_type in Generator.get_supported_data_types():
|
|
||||||
print(f'------- {data_type} -------')
|
|
||||||
sample = generate(data_type)
|
|
||||||
pprint(sample.getvalue() if data_type in {'csv', 'json', 'yaml'} else sample)
|
|
||||||
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})
|
|
@ -1,13 +0,0 @@
|
|||||||
'''
|
|
||||||
loads the twilio.rest.Client by referencing TWILIO__API_KEY,
|
|
||||||
TWILIO__API_SECRET, and TWILIO__ACCOUNT_SID in your scwrypts
|
|
||||||
environment
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_client',
|
|
||||||
'send_sms',
|
|
||||||
]
|
|
||||||
|
|
||||||
from .client import get_client
|
|
||||||
from .send_sms import send_sms
|
|
2
py/lib/twilio/__init__.py
Normal file
2
py/lib/twilio/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from py.lib.twilio.client import get_client
|
||||||
|
from py.lib.twilio.send_sms import send_sms
|
@ -1,6 +1,6 @@
|
|||||||
from twilio.rest import Client
|
from twilio.rest import Client
|
||||||
|
|
||||||
from scwrypts.env import getenv
|
from py.lib.scwrypts import getenv
|
||||||
|
|
||||||
CLIENT = None
|
CLIENT = None
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
from json import dumps
|
from json import dumps
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from .client import get_client
|
from py.lib.twilio.client import get_client
|
||||||
|
|
||||||
|
|
||||||
def send_sms(to, from_, body, max_char_count=300, stream=None):
|
def send_sms(to, from_, body, max_char_count=300, stream=None):
|
@ -1,22 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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.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):
|
def get_query(args):
|
||||||
@ -35,6 +26,20 @@ def main(args, stream):
|
|||||||
response = graphql(get_query(args))
|
response = graphql(get_query(args))
|
||||||
stream.writeline(response)
|
stream.writeline(response)
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
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,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
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 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([
|
@interactive([
|
||||||
f'r = StrictRedis(\'{getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}\')',
|
f'r = StrictRedis(\'{getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}\')',
|
||||||
])
|
])
|
||||||
@ -19,5 +20,7 @@ def main(_args, _stream):
|
|||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
description = 'establishes a redis client in an interactive python shell',
|
||||||
|
parse_args = [],
|
||||||
|
)
|
||||||
|
@ -1 +1,5 @@
|
|||||||
./lib
|
bpython
|
||||||
|
pyfzf
|
||||||
|
pyyaml
|
||||||
|
redis
|
||||||
|
twilio
|
||||||
|
@ -1,38 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from scwrypts import execute
|
|
||||||
#####################################################################
|
|
||||||
from sys import stderr
|
from sys import stderr
|
||||||
|
|
||||||
from scwrypts.env import getenv
|
from py.lib.scwrypts import execute, getenv
|
||||||
from scwrypts.twilio import send_sms
|
from py.lib.twilio import send_sms
|
||||||
|
|
||||||
|
from py.lib.scwrypts.exceptions import ImportedExecutableError, MissingFlagAndEnvironmentVariableError
|
||||||
|
|
||||||
description = 'send a simple SMS through twilio'
|
if __name__ != '__main__':
|
||||||
parse_args = [
|
raise ImportedExecutableError()
|
||||||
( ['-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):
|
def main(args, stream):
|
||||||
if args.body is None:
|
if args.body is None:
|
||||||
@ -58,5 +35,31 @@ def main(args, stream):
|
|||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
if __name__ == '__main__':
|
execute(main,
|
||||||
execute(main, description, parse_args)
|
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,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
375
run
Executable file
375
run
Executable file
@ -0,0 +1,375 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
export EXECUTION_DIR=$(pwd)
|
||||||
|
source "${0:a:h}/zsh/lib/import.driver.zsh" || exit 42
|
||||||
|
#####################################################################
|
||||||
|
() {
|
||||||
|
cd "$SCWRYPTS_ROOT__scwrypts"
|
||||||
|
GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; }
|
||||||
|
local ERRORS=0
|
||||||
|
local USAGE='
|
||||||
|
usage: scwrypts [...options...] [...patterns...] -- [...script options...]
|
||||||
|
|
||||||
|
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 shorthand for "--verbosity 0"
|
||||||
|
-v, --verbosity [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 : include warning messages
|
||||||
|
4 : include debug messages
|
||||||
|
|
||||||
|
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)
|
||||||
|
'
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### cli argument parsing and global configuration ###################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
local ENV_NAME="$SCWRYPTS_ENV"
|
||||||
|
local SEARCH_PATTERNS=()
|
||||||
|
|
||||||
|
local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME
|
||||||
|
|
||||||
|
[ ! $SCWRYPTS_LOG_LEVEL ] && {
|
||||||
|
local SCWRYPTS_LOG_LEVEL
|
||||||
|
[ $CI ] && SCWRYPTS_LOG_LEVEL=3 || SCWRYPTS_LOG_LEVEL=2
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]
|
||||||
|
do
|
||||||
|
case $1 in
|
||||||
|
-[a-z][a-z]* )
|
||||||
|
VARSPLIT=$(echo "$1 " | sed 's/^\(-.\)\(.*\) /\1 -\2/')
|
||||||
|
set -- $(echo " $VARSPLIT ") ${@:2}
|
||||||
|
;;
|
||||||
|
|
||||||
|
### alternate commands ###################
|
||||||
|
|
||||||
|
-h | --help )
|
||||||
|
USAGE
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
-l | --list )
|
||||||
|
SCWRYPTS__GET_AVAILABLE_SCWRYPTS
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--list-envs )
|
||||||
|
SCWRYPTS__GET_ENV_NAMES
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--version )
|
||||||
|
echo scwrypts $(GIT_SCWRYPTS describe --tags)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--update )
|
||||||
|
GIT_SCWRYPTS fetch --quiet origin main
|
||||||
|
GIT_SCWRYPTS fetch --quiet origin main --tags
|
||||||
|
local SYNC_STATUS=$?
|
||||||
|
|
||||||
|
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_SCWRYPTS rebase --autostash origin/main \
|
||||||
|
&& SUCCESS 'up-to-date with origin/main' \
|
||||||
|
&& GIT_SCWRYPTS log -n1 \
|
||||||
|
|| {
|
||||||
|
GIT_SCWRYPTS rebase --abort
|
||||||
|
ERROR 'unable to update scwrypts; please try manual upgrade'
|
||||||
|
REMINDER "installation in '$(pwd)'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
### scwrypts filters #####################
|
||||||
|
|
||||||
|
-m | --name )
|
||||||
|
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
||||||
|
SEARCH_NAME=$2
|
||||||
|
shift 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 | --verbosity )
|
||||||
|
[[ $2 =~ ^[0-4]$ ]] || ERROR "invalid setting for verbosity '$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'
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_ERRORS
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### scwrypts selection / filtering ##################################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
local SCWRYPTS_AVAILABLE
|
||||||
|
SCWRYPTS_AVAILABLE=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS)
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
[ $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_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 ] && {
|
||||||
|
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 ]] && {
|
||||||
|
FAIL 1 "$(echo "
|
||||||
|
no such scwrypt exists
|
||||||
|
NAME : '$SEARCH_NAME'
|
||||||
|
TYPE : '$SEARCH_TYPE'
|
||||||
|
GROUP : '$SEARCH_GROUP'
|
||||||
|
PATTERNS : '$SEARCH_PATTERNS'
|
||||||
|
" | sed "1d; \$d; /''$/d")"
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
[[ $(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
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
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 REQUIRED_ENVIRONMENT_REGEX=$(eval echo '$SCWRYPTS_REQUIRED_ENVIRONMENT_REGEX__'$SCWRYPT_GROUP)
|
||||||
|
|
||||||
|
[[ $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")
|
||||||
|
source "$ENV_FILE" || FAIL 5 "missing or invalid environment '$GROUP/$ENV_NAME'"
|
||||||
|
|
||||||
|
for f in $(eval 'echo $SCWRYPTS_STATIC_CONFIG__'$GROUP)
|
||||||
|
do
|
||||||
|
source "$f" || FAIL 5 "invalid static config '$f'"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
export ENV_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
[ $REQUIRED_ENVIRONMENT_REGEX ] && {
|
||||||
|
[[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \
|
||||||
|
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
[ ! $SUBSCWRYPT ] && [[ $ENV_NAME =~ prod ]] && {
|
||||||
|
STATUS "on '$ENV_NAME'; checking diff against origin/main"
|
||||||
|
|
||||||
|
GIT_SCWRYPTS fetch --quiet origin main
|
||||||
|
local SYNC_STATUS=$?
|
||||||
|
|
||||||
|
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'
|
||||||
|
} || {
|
||||||
|
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?' || {
|
||||||
|
REMINDER "you can use 'scwrypts --update' to quickly update scwrypts to latest"
|
||||||
|
ABORT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
local RUN_STRING=$(SCWRYPTS__GET_RUNSTRING $SCWRYPT_NAME $SCWRYPT_TYPE $SCWRYPT_GROUP)
|
||||||
|
[ "$RUN_STRING" ] || return 42
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### 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
|
||||||
|
verbosity : $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 #################################################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
} $@
|
425
scwrypts
425
scwrypts
@ -1,425 +1,2 @@
|
|||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
export EXECUTION_DIR=$(pwd)
|
source "${0:a:h}/run" $@
|
||||||
source "$(dirname $(readlink -f "$0"))/zsh/lib/import.driver.zsh" || exit 42
|
|
||||||
#####################################################################
|
|
||||||
() {
|
|
||||||
cd "$SCWRYPTS_ROOT__scwrypts"
|
|
||||||
GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; }
|
|
||||||
|
|
||||||
local INSTALLATION_TYPE
|
|
||||||
[ ! -d "$SCWRYPTS_ROOT__scwrypts/.git" ] && [ ! -f "$SCWRYPTS_ROOT__scwrypts/.git" ] \
|
|
||||||
&& INSTALLATION_TYPE=$(cat "$SCWRYPTS_ROOT__scwrypts/MANAGED_BY" 2>/dev/null) \
|
|
||||||
|| INSTALLATION_TYPE=manual \
|
|
||||||
;
|
|
||||||
|
|
||||||
local ERRORS=0
|
|
||||||
local USAGE='
|
|
||||||
usage: scwrypts [...options...] [...patterns...] -- [...script options...]
|
|
||||||
|
|
||||||
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 shorthand for "--log-level 0"
|
|
||||||
-v, --log-level [0-4] set incremental scwrypts log level to one of the following:
|
|
||||||
0 : only command output and critical failures; skips logfile
|
|
||||||
1 : include success / failure messages
|
|
||||||
2 : include status update messages
|
|
||||||
3 : (default) include warning messages
|
|
||||||
4 : include debug messages
|
|
||||||
|
|
||||||
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)
|
|
||||||
'
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
### cli argument parsing and global configuration ###################
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
local ENV_NAME="$SCWRYPTS_ENV"
|
|
||||||
local SEARCH_PATTERNS=()
|
|
||||||
|
|
||||||
local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME
|
|
||||||
|
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=3
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]
|
|
||||||
do
|
|
||||||
case $1 in
|
|
||||||
-[a-z][a-z]* )
|
|
||||||
VARSPLIT=$(echo "$1 " | sed 's/^\(-.\)\(.*\) /\1 -\2/')
|
|
||||||
set -- throw-away $(echo " $VARSPLIT ") ${@:2}
|
|
||||||
;;
|
|
||||||
|
|
||||||
### alternate commands ###################
|
|
||||||
|
|
||||||
-h | --help )
|
|
||||||
USAGE
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
-l | --list )
|
|
||||||
SCWRYPTS__GET_AVAILABLE_SCWRYPTS
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
--list-envs )
|
|
||||||
SCWRYPTS__GET_ENV_NAMES
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
--version )
|
|
||||||
case $INSTALLATION_TYPE in
|
|
||||||
manual ) echo "scwrypts $(GIT_SCWRYPTS describe --tags) (via GIT)" ;;
|
|
||||||
* ) echo scwrypts $(cat "$SCWRYPTS_ROOT__scwrypts/VERSION") ;;
|
|
||||||
esac
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
--update )
|
|
||||||
case $INSTALLATION_TYPE in
|
|
||||||
aur )
|
|
||||||
SCWRYPTS_LOG_LEVEL=3 REMINDER "
|
|
||||||
This installation is built from the AUR. Update through 'makepkg' or use
|
|
||||||
your preferred AUR package management tool (e.g. 'yay -Syu scwrypts')
|
|
||||||
"
|
|
||||||
;;
|
|
||||||
|
|
||||||
manual )
|
|
||||||
GIT_SCWRYPTS fetch --quiet origin main
|
|
||||||
GIT_SCWRYPTS fetch --quiet origin main --tags
|
|
||||||
local SYNC_STATUS=$?
|
|
||||||
|
|
||||||
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_SCWRYPTS rebase --autostash origin/main \
|
|
||||||
&& SUCCESS 'up-to-date with origin/main' \
|
|
||||||
&& GIT_SCWRYPTS log -n1 \
|
|
||||||
|| {
|
|
||||||
GIT_SCWRYPTS rebase --abort
|
|
||||||
ERROR 'unable to update scwrypts; please try manual upgrade'
|
|
||||||
REMINDER "installation in '$SCWRYPTS_ROOT__scwrypts'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;;
|
|
||||||
|
|
||||||
* )
|
|
||||||
SCWRYPTS_LOG_LEVEL=3 REMINDER "
|
|
||||||
This is a managed installation of scwrypts. Please update through your
|
|
||||||
system package manager.
|
|
||||||
"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
### scwrypts filters #####################
|
|
||||||
|
|
||||||
-m | --name )
|
|
||||||
[ $2 ] || { ERROR "missing value for argument $1"; break; }
|
|
||||||
SEARCH_NAME=$2
|
|
||||||
shift 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'
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_ERRORS
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
### scwrypts selection / filtering ##################################
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
local SCWRYPTS_AVAILABLE
|
|
||||||
SCWRYPTS_AVAILABLE=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS)
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
[ $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_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 ] && {
|
|
||||||
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 ]] && {
|
|
||||||
FAIL 1 "$(echo "
|
|
||||||
no such scwrypt exists
|
|
||||||
NAME : '$SEARCH_NAME'
|
|
||||||
TYPE : '$SEARCH_TYPE'
|
|
||||||
GROUP : '$SEARCH_GROUP'
|
|
||||||
PATTERNS : '$SEARCH_PATTERNS'
|
|
||||||
" | sed "1d; \$d; /''$/d")"
|
|
||||||
}
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
[[ $(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
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
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 REQUIRED_ENVIRONMENT_REGEX=$(eval echo '$SCWRYPTS_REQUIRED_ENVIRONMENT_REGEX__'$SCWRYPT_GROUP)
|
|
||||||
|
|
||||||
[ $ENV_NAME ] && [ $REQUIRED_ENVIRONMENT_REGEX ] && {
|
|
||||||
[[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \
|
|
||||||
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ $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")
|
|
||||||
source "$ENV_FILE" || FAIL 5 "missing or invalid environment '$GROUP/$ENV_NAME'"
|
|
||||||
|
|
||||||
for f in $(eval 'echo $SCWRYPTS_STATIC_CONFIG__'$GROUP)
|
|
||||||
do
|
|
||||||
source "$f" || FAIL 5 "invalid static config '$f'"
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
export ENV_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
[ $REQUIRED_ENVIRONMENT_REGEX ] && {
|
|
||||||
[[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \
|
|
||||||
|| FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)"
|
|
||||||
}
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
[ ! $SUBSCWRYPT ] && export SUBSCWRYPT=0
|
|
||||||
|
|
||||||
[[ $INSTALLATION_TYPE =~ ^manual$ ]] && {
|
|
||||||
[[ $SUBSCWRYPT -eq 0 ]] && [[ $ENV_NAME =~ prod ]] && [[ $SCWRYPTS_LOG_LEVEL -gt 0 ]] && {
|
|
||||||
STATUS "on '$ENV_NAME'; checking diff against origin/main"
|
|
||||||
|
|
||||||
local WARNING_MESSAGE
|
|
||||||
|
|
||||||
[ ! $WARNING_MESSAGE ] && {
|
|
||||||
GIT_SCWRYPTS fetch --quiet origin main \
|
|
||||||
|| WARNING_MESSAGE='I am unable to verify your scwrypts version'
|
|
||||||
}
|
|
||||||
|
|
||||||
[ ! $WARNING_MESSAGE ] && {
|
|
||||||
GIT_SCWRYPTS diff --exit-code origin/main -- . >/dev/null 2>&1 \
|
|
||||||
|| WARNING_MESSAGE='your scwrypts is currently out-of-date'
|
|
||||||
}
|
|
||||||
|
|
||||||
[ $WARNING_MESSAGE ] && {
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -lt 3 ]] && {
|
|
||||||
REMINDER "you are running in ${__BRIGHT_RED}production${__BRIGHT_MAGENTA} and $WARNING_MESSAGE"
|
|
||||||
} || {
|
|
||||||
GIT_SCWRYPTS diff --exit-code origin/main -- . >&2
|
|
||||||
WARNING "you are trying to run in ${__BRIGHT_RED}production${__YELLOW} but $WARNING_MESSAGE (relevant diffs and errors above)"
|
|
||||||
yN 'continue?' || {
|
|
||||||
REMINDER "you can use 'scwrypts --update' to quickly update scwrypts to latest"
|
|
||||||
ABORT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
local RUN_STRING=$(SCWRYPTS__GET_RUNSTRING $SCWRYPT_NAME $SCWRYPT_TYPE $SCWRYPT_GROUP)
|
|
||||||
[ "$RUN_STRING" ] || return 42
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
### logging and pretty header/footer setup ##########################
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
local LOGFILE \
|
|
||||||
&& [[ $SCWRYPTS_LOG_LEVEL -gt 0 ]] \
|
|
||||||
&& [[ $SUBSCWRYPT -eq 0 ]] \
|
|
||||||
&& [[ ! $SCWRYPT_NAME =~ scwrypts/logs ]] \
|
|
||||||
&& [[ ! $SCWRYPT_NAME =~ interactive ]] \
|
|
||||||
&& LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log" \
|
|
||||||
|| LOGFILE='/dev/null' \
|
|
||||||
;
|
|
||||||
|
|
||||||
local RUN_MODE=normal
|
|
||||||
[[ $LOGFILE =~ ^/dev/null$ ]] && RUN_MODE=no-logfile
|
|
||||||
[[ $SCWRYPT_NAME =~ interactive ]] && RUN_MODE=interactive
|
|
||||||
|
|
||||||
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 -eq 0 ]] || {
|
|
||||||
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 #################################################
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
set -o pipefail
|
|
||||||
{
|
|
||||||
[ $HEADER ] && echo $HEADER
|
|
||||||
case $RUN_MODE in
|
|
||||||
normal )
|
|
||||||
(eval "$RUN_STRING $(printf "%q " "$@")")
|
|
||||||
EXIT_CODE=$?
|
|
||||||
;;
|
|
||||||
no-logfile )
|
|
||||||
eval "$RUN_STRING $(printf "%q " "$@")"
|
|
||||||
EXIT_CODE=$?
|
|
||||||
;;
|
|
||||||
interactive )
|
|
||||||
eval "$RUN_STRING $(printf "%q " "$@")" </dev/tty >/dev/tty 2>&1
|
|
||||||
EXIT_CODE=$?
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
[ $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"
|
|
||||||
} $@
|
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
#####################################################################
|
|
||||||
|
|
||||||
command -v scwrypts &>/dev/null || {
|
|
||||||
echo 'scwrypts is required in your PATH in order to use the zsh plugins; skipping' >&2
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
NO_EXPORT_CONFIG=1 source "${0:a:h}/zsh/lib/import.driver.zsh" || return 42
|
NO_EXPORT_CONFIG=1 source "${0:a:h}/zsh/lib/import.driver.zsh" || return 42
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
SCWRYPTS__ZSH_PLUGIN() {
|
SCWRYPTS__ZSH_PLUGIN() {
|
||||||
local SCWRYPT_SELECTION=$(scwrypts --list | FZF 'select a script' --header-lines 1)
|
local SCWRYPT_SELECTION=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS | FZF 'select a script' --header-lines 1)
|
||||||
local NAME
|
local NAME
|
||||||
local TYPE
|
local TYPE
|
||||||
local GROUP
|
local GROUP
|
||||||
@ -30,10 +22,8 @@ zle -N scwrypts SCWRYPTS__ZSH_PLUGIN
|
|||||||
bindkey $SCWRYPTS_SHORTCUT scwrypts
|
bindkey $SCWRYPTS_SHORTCUT scwrypts
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
SCWRYPTS__ZSH_BUILDER_PLUGIN() {
|
SCWRYPTS__ZSH_BUILDER_PLUGIN() {
|
||||||
local SCWRYPT_SELECTION=$(scwrypts --list | FZF 'select a script' --header-lines 1)
|
local SCWRYPT_SELECTION=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS | FZF 'select a script' --header-lines 1)
|
||||||
echo $SCWRYPT_SELECTION >&2
|
|
||||||
local NAME
|
local NAME
|
||||||
local TYPE
|
local TYPE
|
||||||
local GROUP
|
local GROUP
|
||||||
@ -42,7 +32,7 @@ SCWRYPTS__ZSH_BUILDER_PLUGIN() {
|
|||||||
|
|
||||||
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
|
SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION
|
||||||
|
|
||||||
scwrypts -n --name $NAME --group $GROUP --type $TYPE -- --help >&2 || {
|
scwrypts --name $NAME --group $GROUP --type $TYPE -- --help >&2 || {
|
||||||
zle accept-line
|
zle accept-line
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -59,7 +49,6 @@ zle -N scwrypts-builder SCWRYPTS__ZSH_BUILDER_PLUGIN
|
|||||||
bindkey $SCWRYPTS_BUILDER_SHORTCUT scwrypts-builder
|
bindkey $SCWRYPTS_BUILDER_SHORTCUT scwrypts-builder
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
SCWRYPTS__ZSH_PLUGIN_ENV() {
|
SCWRYPTS__ZSH_PLUGIN_ENV() {
|
||||||
local RESET='reset'
|
local RESET='reset'
|
||||||
local SELECTED=$(\
|
local SELECTED=$(\
|
||||||
|
@ -27,9 +27,6 @@ EKS() {
|
|||||||
|
|
||||||
local CONTEXT="arn:aws:eks:${AWS_REGION}:${AWS_ACCOUNT}:cluster/${CLUSTER_NAME}"
|
local CONTEXT="arn:aws:eks:${AWS_REGION}:${AWS_ACCOUNT}:cluster/${CLUSTER_NAME}"
|
||||||
|
|
||||||
kubectl config get-contexts | grep -q $CONTEXT \
|
|
||||||
|| EKS__CLUSTER_LOGIN -c $CLUSTER_NAME >/dev/null
|
|
||||||
|
|
||||||
local CONTEXT_ARGS=()
|
local CONTEXT_ARGS=()
|
||||||
case $1 in
|
case $1 in
|
||||||
helm ) CONTEXT_ARGS+=(--kube-context $CONTEXT) ;;
|
helm ) CONTEXT_ARGS+=(--kube-context $CONTEXT) ;;
|
||||||
@ -55,7 +52,6 @@ EKS__CLUSTER_LOGIN() {
|
|||||||
"
|
"
|
||||||
REQUIRED_ENV=(AWS_ACCOUNT AWS_REGION) CHECK_ENVIRONMENT || return 1
|
REQUIRED_ENV=(AWS_ACCOUNT AWS_REGION) CHECK_ENVIRONMENT || return 1
|
||||||
|
|
||||||
|
|
||||||
local CLUSTER_NAME
|
local CLUSTER_NAME
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]
|
while [[ $# -gt 0 ]]
|
||||||
|
@ -2,12 +2,7 @@
|
|||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
[ ! $SCWRYPTS_ROOT ] \
|
[ ! $SCWRYPTS_ROOT ] \
|
||||||
&& SCWRYPTS_ROOT="$(cd $(dirname "${0:a:h}"); git rev-parse --show-toplevel 2>/dev/null)"
|
&& SCWRYPTS_ROOT="$(cd $(dirname "${0:a:h}"); git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
[ ! $SCWRYPTS_ROOT ] && [ -d /usr/share/scwrypts ] \
|
|
||||||
&& SCWRYPTS_ROOT=/usr/share/scwrypts
|
|
||||||
|
|
||||||
export SCWRYPTS_ROOT__scwrypts="$SCWRYPTS_ROOT"
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
@ -37,7 +32,8 @@ export \
|
|||||||
SCWRYPTS_OUTPUT_PATH \
|
SCWRYPTS_OUTPUT_PATH \
|
||||||
;
|
;
|
||||||
|
|
||||||
SCWRYPTS_GROUPS=(scwrypts $(echo $SCWRYPTS_GROUPS | sed 's/\s\+/\n/g' | sort -u))
|
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" \
|
source "$SCWRYPTS_ROOT/zsh/lib/config.group.zsh" \
|
||||||
|| FAIL 69 'failed to set up scwrypts group; aborting'
|
|| FAIL 69 'failed to set up scwrypts group; aborting'
|
||||||
|
@ -12,7 +12,7 @@ SCWRYPTS__RUN() { # context wrapper to run scwrypts within scwrypts
|
|||||||
local EXIT_CODE=0
|
local EXIT_CODE=0
|
||||||
((SUBSCWRYPT+=1))
|
((SUBSCWRYPT+=1))
|
||||||
|
|
||||||
SUBSCWRYPT=$SUBSCWRYPT $SCWRYPTS_ROOT/scwrypts $@
|
SUBSCWRYPT=$SUBSCWRYPT $SCWRYPTS_ROOT/run $@
|
||||||
EXIT_CODE=$?
|
EXIT_CODE=$?
|
||||||
|
|
||||||
((SUBSCWRYPT-=1))
|
((SUBSCWRYPT-=1))
|
||||||
|
@ -119,27 +119,6 @@ SCWRYPTS__GET_RUNSTRING__zsh() {
|
|||||||
|| SCWRYPT_FILENAME="$GROUP_PATH/$SCWRYPT_TYPE/$SCWRYPT_NAME" \
|
|| SCWRYPT_FILENAME="$GROUP_PATH/$SCWRYPT_TYPE/$SCWRYPT_NAME" \
|
||||||
;
|
;
|
||||||
|
|
||||||
SCWRYPTS__GET_RUNSTRING__zsh__generic "$SCWRYPT_FILENAME"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
SCWRYPTS__GET_RUNSTRING__zsh__generic() {
|
|
||||||
# boilerplate to allow
|
|
||||||
# - multiflag splitting (e.g. -abc = -a -b -c)
|
|
||||||
# - help flag injection (e.g. -h | --help)
|
|
||||||
# - default USAGE definition (allows USAGE__options style usage definition)
|
|
||||||
# - required MAIN() function wrapping
|
|
||||||
#
|
|
||||||
# this is available automatically in SCWRYPTS_GROUP declaration contexts
|
|
||||||
# (e.g. my-group.scwrypts.zsh)
|
|
||||||
local ZSH_FILENAME="$1"
|
|
||||||
[ $ZSH_FILENAME ] || {
|
|
||||||
ERROR '
|
|
||||||
to use SCWRYPTS__GET_RUNSTRING__zsh__generic, you must provide a
|
|
||||||
ZSH_FILENAME (arg $1) where the MAIN function is defined
|
|
||||||
'
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
printf "
|
printf "
|
||||||
source '$SCWRYPT_FILENAME'
|
source '$SCWRYPT_FILENAME'
|
||||||
CHECK_ENVIRONMENT
|
CHECK_ENVIRONMENT
|
||||||
@ -175,6 +154,22 @@ SCWRYPTS__GET_RUNSTRING__zsh__generic() {
|
|||||||
done
|
done
|
||||||
MAIN \${MAIN_ARGS[@]}
|
MAIN \${MAIN_ARGS[@]}
|
||||||
} "
|
} "
|
||||||
|
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
SCWRYPTS__GET_RUNSTRING__zsh_v3() {
|
||||||
|
WARNING "scwrypts zsh/v3 runstrings are now deprecated; please update to scwrypts v4 format"
|
||||||
|
|
||||||
|
__CHECK_DEPENDENCY zsh || return 1
|
||||||
|
|
||||||
|
[ $(eval echo '$SCWRYPTS_TYPE__'$SCWRYPT_GROUP) ] \
|
||||||
|
&& echo "source $GROUP_PATH/$SCWRYPT_NAME" \
|
||||||
|
|| echo "source $GROUP_PATH/$SCWRYPT_TYPE/$SCWRYPT_NAME" \
|
||||||
|
;
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
SCWRYPTS__GET_RUNSTRING__py() {
|
SCWRYPTS__GET_RUNSTRING__py() {
|
||||||
|
@ -85,6 +85,8 @@ CREATE_VIRTUALENV__scwrypts__py() {
|
|||||||
|
|
||||||
DEPENDENCIES=(virtualenv) CHECK_ENVIRONMENT || return 1
|
DEPENDENCIES=(virtualenv) CHECK_ENVIRONMENT || return 1
|
||||||
|
|
||||||
|
local VIRTUALENV_PATH="$1"
|
||||||
|
|
||||||
STATUS 'creating python virtualenv'
|
STATUS 'creating python virtualenv'
|
||||||
local PY PYTHON
|
local PY PYTHON
|
||||||
for PY in $(echo $SCWRYPTS_PREFERRED_PYTHON_VERSIONS__scwrypts)
|
for PY in $(echo $SCWRYPTS_PREFERRED_PYTHON_VERSIONS__scwrypts)
|
||||||
@ -139,6 +141,8 @@ CREATE_VIRTUALENV__scwrypts__zx() {
|
|||||||
|
|
||||||
DEPENDENCIES=(nodeenv) CHECK_ENVIRONMENT || return 1
|
DEPENDENCIES=(nodeenv) CHECK_ENVIRONMENT || return 1
|
||||||
|
|
||||||
|
local VIRTUALENV_PATH="$1"
|
||||||
|
|
||||||
STATUS 'setting up nodeenv'
|
STATUS 'setting up nodeenv'
|
||||||
nodeenv $VIRTUALENV_PATH --node=$SCWRYPTS_NODE_VERSION__scwrypts \
|
nodeenv $VIRTUALENV_PATH --node=$SCWRYPTS_NODE_VERSION__scwrypts \
|
||||||
&& SUCCESS 'node virtualenv created' \
|
&& SUCCESS 'node virtualenv created' \
|
||||||
|
@ -37,7 +37,7 @@ __CHECK_COREUTILS() {
|
|||||||
do
|
do
|
||||||
__CHECK_DEPENDENCY $UTIL || { ((MISSING_DEPENDENCY_COUNT+=1)); continue; }
|
__CHECK_DEPENDENCY $UTIL || { ((MISSING_DEPENDENCY_COUNT+=1)); continue; }
|
||||||
|
|
||||||
$UTIL --version 2>&1 | grep 'GNU' | grep -qv 'BSD' || {
|
$UTIL --version 2>&1 | grep -q 'GNU' || {
|
||||||
WARNING "non-GNU version of $UTIL detected"
|
WARNING "non-GNU version of $UTIL detected"
|
||||||
((NON_GNU_DEPENDENCY_COUNT+=1))
|
((NON_GNU_DEPENDENCY_COUNT+=1))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ FZF() {
|
|||||||
FZF_ARGS+=(--height=50%)
|
FZF_ARGS+=(--height=50%)
|
||||||
FZF_ARGS+=(--layout=reverse)
|
FZF_ARGS+=(--layout=reverse)
|
||||||
|
|
||||||
local SELECTION=$(fzf ${FZF_ARGS[@]} --prompt "$1 : " ${@:2} 2>/dev/tty)
|
local SELECTION=$(fzf ${FZF_ARGS[@]} --prompt "$1 : " ${@:2})
|
||||||
PROMPT "$1"
|
PROMPT "$1"
|
||||||
|
|
||||||
[ $BE_QUIET ] || {
|
[ $BE_QUIET ] || {
|
||||||
@ -40,3 +40,18 @@ FZF_USER_INPUT() { # allow user to type custom answers; reconfirm if ambiguous w
|
|||||||
echo $FZF_OUTPUT
|
echo $FZF_OUTPUT
|
||||||
[ $FZF_OUTPUT ]
|
[ $FZF_OUTPUT ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
### vvv DEPRECATED vvv ##############################################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
FZF_HEAD() { # prefer user input over selected
|
||||||
|
WARNING 'FZF_HEAD is deprecated and will be unavailable in v4.2; please switch to FZF_USER_INPUT (drop-in fix!)'
|
||||||
|
FZF $@ --print-query | sed '/^$/d' | head -n1;
|
||||||
|
}
|
||||||
|
FZF_TAIL() { # prefer selected over user input
|
||||||
|
WARNING 'FZF_TAIL is deprecated and will be unavailable in v4.2; please switch to FZF_USER_INPUT (drop-in fix!)'
|
||||||
|
FZF $@ --print-query | sed '/^$/d' | tail -n1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
@ -6,7 +6,6 @@ source "${0:a:h}/io.print.zsh"
|
|||||||
[ ! $ERRORS ] && ERRORS=0
|
[ ! $ERRORS ] && ERRORS=0
|
||||||
|
|
||||||
ERROR() { # command encountered an error
|
ERROR() { # command encountered an error
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
||||||
&& PREFIX="ERROR ✖" COLOR=$__RED PRINT "$@"
|
&& PREFIX="ERROR ✖" COLOR=$__RED PRINT "$@"
|
||||||
((ERRORS+=1))
|
((ERRORS+=1))
|
||||||
@ -14,47 +13,35 @@ ERROR() { # command encountered an error
|
|||||||
}
|
}
|
||||||
|
|
||||||
SUCCESS() { # command completed successfully
|
SUCCESS() { # command completed successfully
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
||||||
&& PREFIX="SUCCESS ✔" COLOR=$__GREEN PRINT "$@"
|
&& PREFIX="SUCCESS ✔" COLOR=$__GREEN PRINT "$@"
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
REMINDER() { # include sysadmin reminder or other important notice to users
|
REMINDER() { # include sysadmin reminder or other important notice to users
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
||||||
&& PREFIX="REMINDER " COLOR=$__BRIGHT_MAGENTA PRINT "$@"
|
&& PREFIX="REMINDER " COLOR=$__BRIGHT_MAGENTA PRINT "$@"
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STATUS() { # general status updates (prefer this to generic 'echo')
|
STATUS() { # general status updates (prefer this to generic 'echo')
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] \
|
||||||
&& PREFIX="STATUS " COLOR=$__BLUE PRINT "$@"
|
&& PREFIX="STATUS " COLOR=$__BLUE PRINT "$@"
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WARNING() { # warning-level messages; not errors
|
WARNING() { # warning-level messages; not errors
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 3 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 3 ]] \
|
||||||
&& PREFIX="WARNING " COLOR=$__YELLOW PRINT "$@"
|
&& PREFIX="WARNING " COLOR=$__YELLOW PRINT "$@"
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG() { # helpful during development or (sparingly) to help others' development
|
DEBUG() { # helpful during development or (sparingly) to help others' development
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 4 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 4 ]] \
|
||||||
&& PREFIX="DEBUG ℹ" COLOR=$__WHITE PRINT "$@"
|
&& PREFIX="DEBUG ℹ" COLOR=$__WHITE PRINT "$@"
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PROMPT() { # you probably want to use yN or INPUT from below
|
PROMPT() { # you probably want to use yN or INPUT from below
|
||||||
[ ! $SCWRYPTS_LOG_LEVEL ] && local SCWRYPTS_LOG_LEVEL=4
|
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
[[ $SCWRYPTS_LOG_LEVEL -ge 1 ]] \
|
||||||
&& PREFIX="PROMPT " COLOR=$__CYAN PRINT "$@" \
|
&& PREFIX="PROMPT " COLOR=$__CYAN PRINT "$@" \
|
||||||
&& PREFIX="USER ⌨" COLOR=$__BRIGHT_CYAN PRINT '' --no-line-end \
|
&& PREFIX="USER ⌨" COLOR=$__BRIGHT_CYAN PRINT '' --no-line-end \
|
||||||
;
|
;
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FAIL() { SCWRYPTS_LOG_LEVEL=1 ERROR "${@:2}"; exit $1; }
|
FAIL() { SCWRYPTS_LOG_LEVEL=1 ERROR "${@:2}"; exit $1; }
|
||||||
@ -224,21 +211,15 @@ READ_YN() { # yes/no read is suprisingly tricky
|
|||||||
local yn
|
local yn
|
||||||
PROMPT "${USERPROMPT[@]}"
|
PROMPT "${USERPROMPT[@]}"
|
||||||
|
|
||||||
local PERFORM_FAKE_PROMPT=false
|
|
||||||
case $SKIP_USER_INPUT in
|
case $SKIP_USER_INPUT in
|
||||||
true ) yn=y ;;
|
true ) yn=y ;;
|
||||||
false )
|
false )
|
||||||
[[ $SCWRYPTS_LOG_LEVEL -lt 1 ]] && {
|
[[ $FORCE_USER_INPUT =~ true ]] && [[ $SCWRYPTS_LOG_LEVEL -lt 1 ]] \
|
||||||
[[ $FORCE_USER_INPUT =~ false ]] && [ ! -t 0 ] \
|
|
||||||
|| PERFORM_FAKE_PROMPT=true
|
|
||||||
}
|
|
||||||
|
|
||||||
[[ $PERFORM_FAKE_PROMPT =~ true ]] \
|
|
||||||
&& echo -n "${USERPROMPT[@]} : " >&2
|
&& echo -n "${USERPROMPT[@]} : " >&2
|
||||||
|
|
||||||
READ ${READ_ARGS[@]} -s -k yn
|
READ ${READ_ARGS[@]} -s -k yn
|
||||||
|
|
||||||
[[ $PERFORM_FAKE_PROMPT =~ true ]] \
|
[[ $FORCE_USER_INPUT =~ true ]] && [[ $SCWRYPTS_LOG_LEVEL -lt 1 ]] \
|
||||||
&& echo $yn >&2
|
&& echo $yn >&2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -18,7 +18,7 @@ MAIN() {
|
|||||||
|
|
||||||
REMINDER "
|
REMINDER "
|
||||||
this will permanently remove all artifacts for the scwrypts $ENV_TYPE environment
|
this will permanently remove all artifacts for the scwrypts $ENV_TYPE environment
|
||||||
(safe unless you have put something important in $(_VIRTUALENV__GET_PATH $ENV_GROUP $ENV_TYPE))
|
(safe unless you have put something important in $(GET_VIRTUALENV_PATH $ENV_GROUP $ENV_TYPE))
|
||||||
"
|
"
|
||||||
|
|
||||||
Yn "drop and recreate $ENV_TYPE virtual environment?" || ABORT
|
Yn "drop and recreate $ENV_TYPE virtual environment?" || ABORT
|
||||||
|
2
zx/lib/.gitignore
vendored
2
zx/lib/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
dist/
|
|
||||||
node_modules/
|
|
@ -1,2 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
@ -1,100 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "scwrypts",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"type": "module",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"description": "scwrypts integration for typescript",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rm -rf ./dist && tsc",
|
|
||||||
"test": "jest",
|
|
||||||
"lint": "eslint . && prettier --check src/",
|
|
||||||
"format": "prettier --write src/"
|
|
||||||
},
|
|
||||||
"author": "Wryn (yage) Wagner",
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"devDependencies": {
|
|
||||||
"@jest/globals": "^29.7.0",
|
|
||||||
"@types/jest": "^29.5.12",
|
|
||||||
"@types/node": "^20.11.19",
|
|
||||||
"@types/uuid": "^9.0.8",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
|
||||||
"@typescript-eslint/parser": "^7.0.2",
|
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"prettier": "^3.2.5",
|
|
||||||
"ts-jest": "^29.1.2",
|
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"typescript": "^5.3.3",
|
|
||||||
"uuid": "^9.0.1"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"ignorePatterns": [
|
|
||||||
"dist",
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"es2021": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended"
|
|
||||||
],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
".eslintrc.{js,cjs}"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "script"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest",
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"linebreak-style": [
|
|
||||||
"error",
|
|
||||||
"unix"
|
|
||||||
],
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single"
|
|
||||||
],
|
|
||||||
"semi": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"comma-dangle": [
|
|
||||||
"error",
|
|
||||||
"always-multiline"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prettier": {
|
|
||||||
"printWidth": 120,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all"
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"preset": "ts-jest",
|
|
||||||
"clearMocks": true,
|
|
||||||
"moduleNameMapper": {
|
|
||||||
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"execa": "^8.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
3166
zx/lib/pnpm-lock.yaml
generated
3166
zx/lib/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user