parent
6fec406d04
commit
1376d33e0a
67 changed files with 4093 additions and 987 deletions
@ -1,8 +1,9 @@ |
||||
[run] |
||||
omit = |
||||
docs/* |
||||
scripttease/cli/__init__.py |
||||
scripttease/cli/* |
||||
sandbox |
||||
setup.py |
||||
tests/* |
||||
tmp/* |
||||
tmp.* |
||||
|
@ -1 +1 @@ |
||||
5.8.18-d |
||||
6.0.0-d |
@ -0,0 +1,229 @@ |
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) |
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif |
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help |
||||
help: |
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " epub3 to make an epub3"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
|
||||
.PHONY: clean |
||||
clean: |
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html |
||||
html: |
||||
#./generate_command_signatures.py > source/_command-examples.rst
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: dirhtml |
||||
dirhtml: |
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml |
||||
singlehtml: |
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle |
||||
pickle: |
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json |
||||
json: |
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp |
||||
htmlhelp: |
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp |
||||
qthelp: |
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BasisHR.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BasisHR.qhc"
|
||||
|
||||
.PHONY: applehelp |
||||
applehelp: |
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp |
||||
devhelp: |
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/BasisHR"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BasisHR"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub |
||||
epub: |
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: epub3 |
||||
epub3: |
||||
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
||||
@echo
|
||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||
|
||||
.PHONY: latex |
||||
latex: |
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf |
||||
latexpdf: |
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja |
||||
latexpdfja: |
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text |
||||
text: |
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man |
||||
man: |
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo |
||||
texinfo: |
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info |
||||
info: |
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext |
||||
gettext: |
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes |
||||
changes: |
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck |
||||
linkcheck: |
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest |
||||
doctest: |
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage |
||||
coverage: |
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml |
||||
xml: |
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml |
||||
pseudoxml: |
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
.PHONY: robots |
||||
robots: |
||||
cp source/robots.txt build/html/;
|
||||
|
@ -0,0 +1,65 @@ |
||||
#! /usr/bin/env python |
||||
|
||||
from collections import OrderedDict |
||||
import inspect |
||||
import sys |
||||
|
||||
sys.path.append("../") |
||||
|
||||
from script_tease.mappings import MAPPING |
||||
|
||||
|
||||
# https://stackoverflow.com/a/52003056/241720 |
||||
def get_signature(fn): |
||||
params = inspect.signature(fn).parameters |
||||
args = [] |
||||
kwargs = OrderedDict() |
||||
for p in params.values(): |
||||
if p.default is p.empty: |
||||
args.append(p.name) |
||||
else: |
||||
kwargs[p.name] = p.default |
||||
return args, kwargs |
||||
|
||||
|
||||
keys = list(MAPPING.keys()) |
||||
keys.sort() |
||||
|
||||
for key in keys: |
||||
cls = MAPPING[key] |
||||
|
||||
print(key) |
||||
print("." * len(key)) |
||||
print("") |
||||
|
||||
extra = cls.get_docs() |
||||
if extra is not None: |
||||
print(extra) |
||||
print("") |
||||
|
||||
# if cls.__init__.__doc__: |
||||
# print(cls.__init__.__doc__) |
||||
# print("") |
||||
|
||||
print(".. code-block:: cfg") |
||||
print("") |
||||
|
||||
if cls.__doc__: |
||||
print(" [%s]" % cls.__doc__.strip().replace(".", "").lower()) |
||||
else: |
||||
print(" [run a %s command]" % cls.__name__.lower()) |
||||
|
||||
args, kwargs = get_signature(cls.__init__) |
||||
|
||||
line = list() |
||||
for a in args: |
||||
if a not in ("self", "kwargs"): |
||||
line.append(a) |
||||
|
||||
print(" %s = %s" % (key, " ".join(line))) |
||||
|
||||
for option, value in kwargs.items(): |
||||
print(" %s = %s" % (option, value)) |
||||
|
||||
print("") |
||||
|
@ -0,0 +1,272 @@ |
||||
@ECHO OFF |
||||
|
||||
REM Command file for Sphinx documentation |
||||
|
||||
if "%SPHINXBUILD%" == "" ( |
||||
set SPHINXBUILD=sphinx-build |
||||
) |
||||
set BUILDDIR=build |
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source |
||||
set I18NSPHINXOPTS=%SPHINXOPTS% source |
||||
if NOT "%PAPER%" == "" ( |
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% |
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% |
||||
) |
||||
|
||||
if "%1" == "" goto help |
||||
|
||||
if "%1" == "help" ( |
||||
:help |
||||
echo.Please use `make ^<target^>` where ^<target^> is one of |
||||
echo. html to make standalone HTML files |
||||
echo. dirhtml to make HTML files named index.html in directories |
||||
echo. singlehtml to make a single large HTML file |
||||
echo. pickle to make pickle files |
||||
echo. json to make JSON files |
||||
echo. htmlhelp to make HTML files and a HTML help project |
||||
echo. qthelp to make HTML files and a qthelp project |
||||
echo. devhelp to make HTML files and a Devhelp project |
||||
echo. epub to make an epub |
||||
echo. epub3 to make an epub3 |
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter |
||||
echo. text to make text files |
||||
echo. man to make manual pages |
||||
echo. texinfo to make Texinfo files |
||||
echo. gettext to make PO message catalogs |
||||
echo. changes to make an overview over all changed/added/deprecated items |
||||
echo. xml to make Docutils-native XML files |
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes |
||||
echo. linkcheck to check all external links for integrity |
||||
echo. doctest to run all doctests embedded in the documentation if enabled |
||||
echo. coverage to run coverage check of the documentation if enabled |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "clean" ( |
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i |
||||
del /q /s %BUILDDIR%\* |
||||
goto end |
||||
) |
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any |
||||
%SPHINXBUILD% 1>NUL 2>NUL |
||||
if errorlevel 9009 goto sphinx_python |
||||
goto sphinx_ok |
||||
|
||||
:sphinx_python |
||||
|
||||
set SPHINXBUILD=python -m sphinx.__init__ |
||||
%SPHINXBUILD% 2> nul |
||||
if errorlevel 9009 ( |
||||
echo. |
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx |
||||
echo.installed, then set the SPHINXBUILD environment variable to point |
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you |
||||
echo.may add the Sphinx directory to PATH. |
||||
echo. |
||||
echo.If you don't have Sphinx installed, grab it from |
||||
echo.http://sphinx-doc.org/ |
||||
exit /b 1 |
||||
) |
||||
|
||||
:sphinx_ok |
||||
|
||||
|
||||
if "%1" == "html" ( |
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "dirhtml" ( |
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "singlehtml" ( |
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "pickle" ( |
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished; now you can process the pickle files. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "json" ( |
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished; now you can process the JSON files. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "htmlhelp" ( |
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished; now you can run HTML Help Workshop with the ^ |
||||
.hhp project file in %BUILDDIR%/htmlhelp. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "qthelp" ( |
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^ |
||||
.qhcp project file in %BUILDDIR%/qthelp, like this: |
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\BasisHR.qhcp |
||||
echo.To view the help file: |
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\BasisHR.ghc |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "devhelp" ( |
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "epub" ( |
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "epub3" ( |
||||
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "latex" ( |
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "latexpdf" ( |
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex |
||||
cd %BUILDDIR%/latex |
||||
make all-pdf |
||||
cd %~dp0 |
||||
echo. |
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "latexpdfja" ( |
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex |
||||
cd %BUILDDIR%/latex |
||||
make all-pdf-ja |
||||
cd %~dp0 |
||||
echo. |
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "text" ( |
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The text files are in %BUILDDIR%/text. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "man" ( |
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "texinfo" ( |
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "gettext" ( |
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "changes" ( |
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.The overview file is in %BUILDDIR%/changes. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "linkcheck" ( |
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Link check complete; look for any errors in the above output ^ |
||||
or in %BUILDDIR%/linkcheck/output.txt. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "doctest" ( |
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Testing of doctests in the sources finished, look at the ^ |
||||
results in %BUILDDIR%/doctest/output.txt. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "coverage" ( |
||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Testing of coverage in the sources finished, look at the ^ |
||||
results in %BUILDDIR%/coverage/python.txt. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "xml" ( |
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml. |
||||
goto end |
||||
) |
||||
|
||||
if "%1" == "pseudoxml" ( |
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml |
||||
if errorlevel 1 exit /b 1 |
||||
echo. |
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. |
||||
goto end |
||||
) |
||||
|
||||
:end |
@ -0,0 +1,3 @@ |
||||
sphinx |
||||
sphinx_rtd_theme |
||||
git+https://github.com/develmaycare/sphinx-helpers |
@ -0,0 +1,440 @@ |
||||
apache.disable_module |
||||
..................... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[disable an apache module] |
||||
apache.disable_module = module_name |
||||
|
||||
apache.disable_site |
||||
................... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[disable a virtual host] |
||||
apache.disable_site = domain_name |
||||
|
||||
apache.enable_module |
||||
.................... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[enable an apache module] |
||||
apache.enable_module = module_name |
||||
|
||||
apache.enable_site |
||||
.................. |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[enable a virtual host] |
||||
apache.enable_site = domain_name |
||||
|
||||
apache.test |
||||
........... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[run an apache config test] |
||||
apache: test |
||||
|
||||
append |
||||
...... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[append to a file] |
||||
append = path |
||||
content = None |
||||
|
||||
archive |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create an archive file] |
||||
archive = from_path |
||||
absolute = False |
||||
exclude = None |
||||
file_name = archive.tgz |
||||
strip = 0 |
||||
to_path = . |
||||
view = False |
||||
|
||||
certbot |
||||
....... |
||||
|
||||
Alias: ssl |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[get new ssl certificate from let's encrypt] |
||||
certbot = domain_name |
||||
email = None |
||||
webroot = None |
||||
|
||||
copy |
||||
.... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[copy a file or directory] |
||||
copy = from_path to_path |
||||
overwrite = False |
||||
recursive = False |
||||
|
||||
django |
||||
...... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[run a django management command] |
||||
django = name |
||||
|
||||
django.dumpdata |
||||
............... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[export django fixtures] |
||||
django.dumpdata = app_name |
||||
file_name = initial |
||||
indent = 4 |
||||
natural_foreign = False |
||||
natural_primary = False |
||||
path = None |
||||
|
||||
django.loaddata |
||||
............... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[load django fixtures] |
||||
django.loaddata = app_name |
||||
file_name = initial |
||||
path = None |
||||
|
||||
extract |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[extract an archive] |
||||
extract = from_path |
||||
absolute = False |
||||
exclude = None |
||||
file_name = archive.tgz |
||||
strip = 0 |
||||
to_path = None |
||||
view = False |
||||
|
||||
install |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[install a package using apt-get] |
||||
apt = package |
||||
remove = False |
||||
|
||||
makedir |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a directory] |
||||
makedir = path |
||||
mode = None |
||||
recursive = True |
||||
|
||||
message |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[run a message command] |
||||
message = output |
||||
back_title = Message |
||||
dialog = False |
||||
height = 15 |
||||
width = 100 |
||||
|
||||
mkdir |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a directory] |
||||
mkdir = path |
||||
mode = None |
||||
recursive = True |
||||
|
||||
move |
||||
.... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[move a file or directory] |
||||
move = from_path to_path |
||||
|
||||
perms |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[set permissions on a file or directory] |
||||
perms = path |
||||
group = None |
||||
mode = None |
||||
owner = None |
||||
recursive = False |
||||
|
||||
pg.createdb |
||||
........... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a postgresql database] |
||||
pg.createdb = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
owner = None |
||||
port = 5432 |
||||
template = None |
||||
|
||||
pg.createuser |
||||
............. |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a postgresql user] |
||||
pg.createuser = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
password = None |
||||
port = 5432 |
||||
|
||||
pg.db |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a postgresql database] |
||||
pg.db = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
owner = None |
||||
port = 5432 |
||||
template = None |
||||
|
||||
pg.dropdb |
||||
......... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[remove a postgresql database] |
||||
pg.dropdb = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
port = 5432 |
||||
|
||||
pg.dropuser |
||||
........... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[remove a postgres user] |
||||
pg.dropuser = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
port = 5432 |
||||
|
||||
pg.dump |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[export a postgres database] |
||||
pg.dump = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
file_name = None |
||||
host = localhost |
||||
port = 5432 |
||||
|
||||
pg.exists |
||||
......... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[determine if a postgres database exists] |
||||
pg.exists = name |
||||
admin_pass = None |
||||
admin_user = postgres |
||||
host = localhost |
||||
port = 5432 |
||||
|
||||
pip |
||||
... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[install a python package using pip] |
||||
pip = package |
||||
remove = False |
||||
upgrade = False |
||||
|
||||
psql |
||||
.... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[execute a psql command] |
||||
psql = sql |
||||
database = template1 |
||||
host = localhost |
||||
password = None |
||||
port = 5432 |
||||
user = postgres |
||||
|
||||
reload |
||||
...... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[reload a service] |
||||
reload = service |
||||
|
||||
remove |
||||
...... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[remove a file or directory] |
||||
remove = path |
||||
force = False |
||||
recursive = False |
||||
|
||||
restart |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[restart a service] |
||||
restart = service |
||||
|
||||
rsync |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[synchronize files from a local to remote directory] |
||||
rsync = source target |
||||
delete = False |
||||
guess = False |
||||
host = None |
||||
key_file = None |
||||
links = True |
||||
port = 22 |
||||
recursive = True |
||||
user = None |
||||
|
||||
run |
||||
... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[a command to be executed] |
||||
run = statement |
||||
comment = None |
||||
condition = None |
||||
cd = None |
||||
environments = None |
||||
function = None |
||||
prefix = None |
||||
register = None |
||||
shell = None |
||||
stop = False |
||||
sudo = None |
||||
tags = None |
||||
|
||||
scopy |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[copy a file from the local (machine) to the remote host] |
||||
scp = from_path to_path |
||||
host = None |
||||
key_file = None |
||||
port = 22 |
||||
user = None |
||||
|
||||
sed |
||||
... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[replace text in a file] |
||||
sed = path |
||||
backup = .b |
||||
change = None |
||||
delimiter = / |
||||
find = None |
||||
|
||||
start |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[start a service] |
||||
start = service |
||||
|
||||
stop |
||||
.... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[stop a service] |
||||
stop = service |
||||
|
||||
symlink |
||||
....... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a symlink] |
||||
symlink = source |
||||
force = False |
||||
target = None |
||||
|
||||
touch |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[touch a file or directory] |
||||
touch = path |
||||
|
||||
virtualenv |
||||
.......... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[create a python virtual environment] |
||||
virtualenv = name |
||||
|
||||
write |
||||
..... |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[write to a file] |
||||
write = path |
||||
content = None |
||||
overwrite = False |
unable to load file from base commit
|
@ -0,0 +1,10 @@ |
||||
/* override table width restrictions */ |
||||
.wy-table-responsive table td, .wy-table-responsive table th { |
||||
white-space: normal; |
||||
} |
||||
|
||||
.wy-table-responsive { |
||||
margin-bottom: 24px; |
||||
max-width: 100%; |
||||
overflow: visible; |
||||
} |
After Width: | Height: | Size: 301 KiB |
After Width: | Height: | Size: 393 KiB |
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,243 @@ |
||||
# -*- coding: utf-8 -*- |
||||
# |
||||
# Script Tease documentation build configuration file, created by |
||||
# sphinx-quickstart on Mon Apr 11 17:36:02 2016. |
||||
# |
||||
# This file is execfile()d with the current directory set to its |
||||
# containing dir. |
||||
# |
||||
# Note that not all possible configuration values are present in this |
||||
# autogenerated file. |
||||
# |
||||
# All configuration values have a default; values that are commented out |
||||
# serve to show the default. |
||||
import os |
||||
from sphinx_helpers import get_release, get_version |
||||
import sys |
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory, |
||||
# add these directories to sys.path here. If the directory is relative to the |
||||
# documentation root, use os.path.abspath to make it absolute, like shown here. |
||||
sys.path.insert(0, os.path.abspath("../../")) |
||||
|
||||
# -- General configuration ------------------------------------------------ |
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here. |
||||
#needs_sphinx = '1.0' |
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be |
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
||||
# ones. |
||||
extensions = [ |
||||
'sphinx.ext.autodoc', |
||||
'sphinx.ext.intersphinx', |
||||
'sphinx.ext.todo', |
||||
'sphinx.ext.coverage', |
||||
'sphinx.ext.ifconfig', |
||||
'sphinx.ext.viewcode', |
||||
] |
||||
|
||||
# Add any paths that contain templates here, relative to this directory. |
||||
templates_path = ['_templates'] |
||||
|
||||
# The suffix(es) of source filenames. |
||||
# You can specify multiple suffix as a list of string: |
||||
# source_suffix = ['.rst', '.md'] |
||||
source_suffix = '.rst' |
||||
|
||||
# The encoding of source files. |
||||
#source_encoding = 'utf-8-sig' |
||||
|
||||
# The master toctree document. |
||||
master_doc = 'index' |
||||
|
||||
# General information about the project. |
||||
project = u'Script Tease (Python)' |
||||
copyright = u'Pleasant Tents, LLC' |
||||
author = u'Shawn Davis' |
||||
|
||||
# The version info for the project you're documenting, acts as replacement for |
||||
# |version| and |release|, also used in various other places throughout the |
||||
# built documents. |
||||
# |
||||
# The short X.Y version. |
||||
version = get_version("../../VERSION.txt") |
||||
|
||||
# The full version, including alpha/beta/rc tags. |
||||
release = get_release("../../VERSION.txt") |
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation |
||||
# for a list of supported languages. |
||||
# |
||||
# This is also used if you do content translation via gettext catalogs. |
||||
# Usually you set "language" from the command line for these cases. |
||||
language = None |
||||
|
||||
# There are two options for replacing |today|: either, you set today to some |
||||
# non-false value, then it is used: |
||||
#today = '' |
||||
# Else, today_fmt is used as the format for a strftime call. |
||||
#today_fmt = '%B %d, %Y' |
||||
|
||||
# List of patterns, relative to source directory, that match files and |
||||
# directories to ignore when looking for source files. |
||||
# This patterns also effect to html_static_path and html_extra_path |
||||
exclude_patterns = [] |
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all |
||||
# documents. |
||||
#default_role = None |
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text. |
||||
#add_function_parentheses = True |
||||
|
||||
# If true, the current module name will be prepended to all description |
||||
# unit titles (such as .. function::). |
||||
#add_module_names = True |
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the |
||||
# output. They are ignored by default. |
||||
#show_authors = False |
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use. |
||||
pygments_style = 'sphinx' |
||||
|
||||
# A list of ignored prefixes for module index sorting. |
||||
#modindex_common_prefix = [] |
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents. |
||||
#keep_warnings = False |
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing. |
||||
todo_include_todos = True |
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------- |
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for |
||||
# a list of builtin themes. |
||||
# html_theme = 'alabaster' |
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme |
||||
# further. For a list of options available for each theme, see the |
||||
# documentation. |
||||
#html_theme_options = {} |
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory. |
||||
#html_theme_path = [] |
||||
|
||||
# The name for this set of Sphinx documents. |
||||
# "<project> v<release> documentation" by default. |
||||
#html_title = u'Ninjas v%s' % version |
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title. |
||||
#html_short_title = None |
||||
|
||||
# The name of an image file (relative to this directory) to place at the top |
||||
# of the sidebar. |
||||
#html_logo = None |
||||
|
||||
# The name of an image file (relative to this directory) to use as a favicon of |
||||
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 |
||||
# pixels large. |
||||
#html_favicon = None |
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here, |
||||
# relative to this directory. They are copied after the builtin static files, |
||||
# so a file named "default.css" will overwrite the builtin "default.css". |
||||
html_static_path = ['_static'] |
||||
|
||||
|
||||
def setup(app): |
||||
# Some csv-table tables in the RTD theme were scrolling horizontally. |
||||
# See https://github.com/snide/sphinx_rtd_theme/issues/117 |
||||
# Apparently the placement of this function is significant. It wasn't |
||||
# working at the end of the file. |
||||
# See https://github.com/syncany/syncany-docs/blob/82166518720051e413231d621c3b23b9ac35cbf6/source/conf.py#L135 |
||||
# app.add_javascript("js/custom.js") |
||||
app.add_stylesheet("css/custom.css") |
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or |
||||
# .htaccess) here, relative to this directory. These files are copied |
||||
# directly to the root of the documentation. |
||||
#html_extra_path = [] |
||||
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page |
||||
# bottom, using the given strftime format. |
||||
# The empty string is equivalent to '%b %d, %Y'. |
||||
#html_last_updated_fmt = None |
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to |
||||
# typographically correct entities. |
||||
#html_use_smartypants = True |
||||
|
||||
# Custom sidebar templates, maps document names to template names. |
||||
#html_sidebars = {} |
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to |
||||
# template names. |
||||
#html_additional_pages = {} |
||||
|
||||
# If false, no module index is generated. |
||||
#html_domain_indices = True |
||||
|
||||
# If false, no index is generated. |
||||
#html_use_index = True |
||||
|
||||
# If true, the index is split into individual pages for each letter. |
||||
#html_split_index = False |
||||
|
||||
# If true, links to the reST sources are added to the pages. |
||||
#html_show_sourcelink = True |
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. |
||||
#html_show_sphinx = True |
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. |
||||
#html_show_copyright = True |
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will |
||||
# contain a <link> tag referring to it. The value of this option must be the |
||||
# base URL from which the finished HTML is served. |
||||
#html_use_opensearch = '' |
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml"). |
||||
#html_file_suffix = None |
||||
|
||||
# Language to be used for generating the HTML full-text search index. |
||||
# Sphinx supports the following languages: |
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' |
||||
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' |
||||
#html_search_language = 'en' |
||||
|
||||
# A dictionary with options for the search language support, empty by default. |
||||
# 'ja' uses this config value. |
||||
# 'zh' user can custom change `jieba` dictionary path. |
||||
#html_search_options = {'type': 'default'} |
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that |
||||
# implements a search results scorer. If empty, the default will be used. |
||||
#html_search_scorer = 'scorer.js' |
||||
|
||||
# Output file base name for HTML help builder. |
||||
htmlhelp_basename = 'ScriptTeaseDoc' |
||||
|
||||
# -- Options for manual page output --------------------------------------- |
||||
|
||||
# One entry per manual page. List of tuples |
||||
# (source start file, name, description, authors, manual section). |
||||
man_pages = [ |
||||
(master_doc, 'script_tease', u'Script Tease Documentation', |
||||
[author], 1) |
||||
] |
||||
|
||||
# If true, show URL addresses after external links. |
||||
#man_show_urls = False |
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library. |
||||
intersphinx_mapping = {'https://docs.python.org/': None} |
||||
|
||||
# Read the Docs Theme |
||||
import sphinx_rtd_theme |
||||
html_theme = "sphinx_rtd_theme" |
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] |
@ -0,0 +1,166 @@ |
||||
.. _configuration: |
||||
|
||||
************* |
||||
Configuration |
||||
************* |
||||
|
||||
Generating Commands From a File |
||||
=============================== |
||||
|
||||
The :py:class:`scripttease.parsers.ini.Config` class may instantiate commands by loading a configuration file. |
||||
|
||||
.. note:: |
||||
Additional formats such as JSON or YAML may be supported in the future. |
||||
|
||||
An example file: |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[install apache] |
||||
install: apache2 |
||||
|
||||
[create the web site directory] |
||||
mkdir: /var/www/domains/example_com/www |
||||
recursive: yes |
||||
|
||||
[set permissions on the website directory] |
||||
perms: /var/www/domains/example_com/www |
||||
group: www-data |
||||
mode: 775 |
||||
owner: www-data |
||||
|
||||
Notes regarding this format: |
||||
|
||||
- This is the standard format for Python's ConfigParser. If you prefer, you may use ``=`` instead of ``:``. |
||||
- The first line is the INI section and is used as the default comment. |
||||
- The command name must be the *first* option in the section. |
||||
- The arguments for the command appear as the value of the first option in the section. Arguments are separated by a |
||||
space. |
||||
- Arguments that should be treated as a single value should be enclosed in double quotes. |
||||
- ``yes`` and ``no`` are interpreted as boolean values. |
||||
- List values, where required, are separated by commas. |
||||
|
||||
Common Parameters |
||||
----------------- |
||||
|
||||
All commands support the following common parameters: |
||||
|
||||
- ``comment``: A comment regarding the command. |
||||
- ``condition``: A condition for execution. For example, ``! -f /path/to/some/file.txt`` |
||||
- ``cd``: The path from which a command should be executed. |
||||
- ``environments``: A string or list of strings indicating the operational environments in which the command runs. This |
||||
is *not* used by default, but may be used to programmatically filter commands for a specific environment. For example, |
||||
development versus live. |
||||
- ``prefix``: A statement to be added prior to executing the command. |
||||
- ``register``: A variable name to which the the success or failure (exit code) of the statement is captured. |
||||
- ``shell``: The shell used to run the commands. For example, ``/bin/bash``. This is generally not important, but can |
||||
be a problem when attempting to execute some commands (such as Django management commands). |
||||
- ``stop``: ``True`` indicates no other commands should be executed if the given command fails. |
||||
- ``sudo``: ``True`` indicates the command should be automatically prefixed with ``sudo``. If provided as a string, the |
||||
command is also prefixed with a specific user name. |
||||
- ``tags``: A list of tags used to classify the command. |
||||
|
||||
Defining an "Itemized" Command |
||||
------------------------------ |
||||
|
||||
Certain command definitions may be repeated by defining a list of items. |
||||
|
||||
Example of an "itemized" command: |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[touch a bunch of files] |
||||
touch = /var/www/domains/example_com/www/$item |
||||
items = index.html, assets/index.html, content/index.html |
||||
|
||||
.. note:: |
||||
Command itemization may vary with the command type. |
||||
|
||||
Available Commands |
||||
------------------ |
||||
|
||||
The following commands instantiate command instances. Each example is shown with the defaults. |
||||
|
||||
.. include:: _command-examples.rst |
||||
|
||||
Pre-Parsing Command Files as Templates |
||||
====================================== |
||||
|
||||
Configuration file may be pre-processed as a Jinja2 template by providing a context dictionary: |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[install apache] |
||||
install: apache |
||||
|
||||
[create the website directory] |
||||
mkdir: /var/www/domains/{{ domain_tld }}/www |
||||
recursive: yes |
||||
|
||||
[set permissions on the website directory] |
||||
perms: /var/www/domains/{{ domain_tld }}/www |
||||
group: www-data |
||||
mode: 775 |
||||
owner: www-data |
||||
|
||||
Then with a config instance: |
||||
|
||||
.. code-block:: python |
||||
|
||||
context = { |
||||
'domain_tld': "example_com", |
||||
} |
||||
|
||||
config = Config("commands.ini", context=context) |
||||
config.load() |
||||
|
||||
for command in config.get_commands(): |
||||
print(command.get_statement(cd=True)) |
||||
print("") |
||||
|
||||
Using the Tease Command |
||||
======================= |
||||
|
||||
The ``tease`` command may be used to parse a configuration file, providing additional utilities for working with |
||||
commands. |
||||
|
||||
.. code-block:: text |
||||
|
||||
positional arguments: |
||||
path The path to the configuration file. |
||||
|
||||
optional arguments: |
||||
-h, --help show this help message and exit |
||||
-c, --color Enable code highlighting for terminal output. |
||||
-C= VARIABLES, --context= VARIABLES |
||||
Context variables for use in pre-parsing the config and templates. In the form of: name:value |
||||
-d, --docs Output documentation instead of code. |
||||
-D, --debug Enable debug output. |
||||
-f= FILTERS, --filter= FILTERS |
||||
Filter the commands in the form of: attribute:value |
||||
-O= OPTIONS, --option= OPTIONS |
||||
Common command options in the form of: name:value |
||||
-s, --script Output commands as a script. |
||||
-T= TEMPLATE_LOCATIONS, --template-path= TEMPLATE_LOCATIONS |
||||
The location of template files that may be used with the template command. |
||||
-w= OUTPUT_FILE, --write= OUTPUT_FILE |
||||
Write the output to disk. |
||||
-V= VARIABLES_FILE, --variables-file= VARIABLES_FILE |
||||
Load variables from a file. |
||||
-v Show version number and exit. |
||||
--version Show verbose version information and exit. |
||||
|
||||
The ``path`` argument defaults to ``commands.ini``. |
||||
|
||||
Loading Variables from a File |
||||
----------------------------- |
||||
|
||||
Context variables may be loaded from a file: |
||||
|
||||
.. code-block:: ini |
||||
|
||||
[domain] |
||||
name = example.com |
||||
tld = example_com |
||||
|
||||
The variables above are available as ``section_key``. For example, ``domain_name`` is ``example.com``. |
@ -0,0 +1,110 @@ |
||||
.. _developer-reference: |
||||
|
||||
******************* |
||||
Developer Reference |
||||
******************* |
||||
|
||||
Constants |
||||
========= |
||||
|
||||
.. automodule:: scripttease.constants |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Library |
||||
======= |
||||
|
||||
Commands |
||||
-------- |
||||
|
||||
.. automodule:: scripttease.library.commands.base |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Overlays |
||||
-------- |
||||
|
||||
Common |
||||
...... |
||||
|
||||
.. automodule:: scripttease.library.overlays.common |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Django |
||||
...... |
||||
|
||||
.. automodule:: scripttease.library.overlays.django |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Postgres |
||||
........ |
||||
|
||||
.. automodule:: scripttease.library.overlays.pgsql |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Posix |
||||
..... |
||||
|
||||
.. automodule:: scripttease.library.overlays.posix |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Ubuntu |
||||
...... |
||||
|
||||
.. automodule:: scripttease.library.overlays.ubuntu |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Scripts |
||||
------- |
||||
|
||||
.. automodule:: scripttease.library.scripts |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Factory |
||||
======= |
||||
|
||||
.. automodule:: scripttease.factory |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Parsers |
||||
======= |
||||
|
||||
Base |
||||
---- |
||||
|
||||
.. automodule:: scripttease.parsers.base |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Config (INI) |
||||
------------ |
||||
|
||||
.. automodule:: scripttease.parsers.ini |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
||||
|
||||
Utils |
||||
----- |
||||
|
||||
.. automodule:: scripttease.parsers.utils |
||||
:members: |
||||
:show-inheritance: |
||||
:special-members: __init__ |
@ -0,0 +1,17 @@ |
||||
Python Script Tease |
||||
=================== |
||||
|
||||
.. toctree:: |
||||
:maxdepth: 2 |
||||
|
||||
Introduction <introduction> |
||||
Configuration <configuration> |
||||
Developer Reference <developer> |
||||
Tests <tests> |
||||
|
||||
Indices and tables |
||||
================== |
||||
|
||||
* :ref:`genindex` |
||||
* :ref:`modindex` |
||||
* :ref:`search` |
@ -0,0 +1,27 @@ |
||||
.. _introduction: |
||||
|
||||
************ |
||||
Introduction |
||||
************ |
||||
|
||||
Script Tease is a library and command line tool for generating commands programmatically or using configuration files. |
||||
|
||||
Concepts |
||||
======== |
||||
|
||||
Generating Commands |
||||
------------------- |
||||
|
||||
Script Tease may be used in two (2) ways: |
||||
|
||||
1. Using the library to programmatically define commands and export them as command line statements. See |
||||
:ref:`developer-reference`. |
||||
2. Using the ``tease`` command to generate commands from a configuration file. See :ref:`configuration`. |
||||
|
||||
Overlays |
||||
-------- |
||||
|
||||
An *overlay* is a set of command meta functions that define the capabilities of a specific operating system. |
||||
|
||||
.. note:: |
||||
At present, the only fully defined overlay is for Ubuntu. |
@ -0,0 +1,54 @@ |
||||
***** |
||||
Tests |
||||
***** |
||||
|
||||
Coverage Requirements |
||||
===================== |
||||
|
||||
100% coverage is required for the ``master`` branch. |
||||
|
||||
See `current coverage report <coverage/index.html>`_. |
||||
|
||||
.. csv-table:: Lines of Code |
||||
:file: _data/cloc.csv |
||||
|
||||
Set Up for Testing |
||||
================== |
||||
|
||||
Install requirements: |
||||
|
||||
.. code-block:: bash |
||||
|
||||
pip install tests/requirements.pip |
||||
|
||||
Running Tests |
||||
============= |
||||
|
||||
.. tip:: |
||||
You may use the ``tests`` target of the ``Makefile`` to run tests with coverage: |
||||
|
||||
``make tests;`` |
||||
|
||||
To run unit tests: |
||||
|
||||
.. code-block:: bash |
||||
|
||||
python -m pytest; |
||||
|
||||
Run a specific test: |
||||
|
||||
.. code-block:: bash |
||||
|
||||
python -m pytest tests/units/path/to/test.py |
||||
|
||||
To allow output from print statements within a test method, add the ``-s`` switch: |
||||
|
||||
.. code-block:: bash |
||||
|
||||
python -m pytest -s tests/units/path/to/test.py |
||||
|
||||
Reference |
||||
========= |
||||
|
||||
- `coverage <https://coverage.readthedocs.io/en/v4.5.x/>`_ |
||||
- `pytest <https://pytest.org>`_ |
@ -0,0 +1,12 @@ |
||||
#! /usr/bin/env python |
||||
|
||||
import re |
||||
import sys |
||||
|
||||
sys.path.insert(0, "../") |
||||
|
||||
from scripttease.cli import main_command |
||||
|
||||
if __name__ == '__main__': |
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
sys.exit(main_command()) |
@ -0,0 +1,204 @@ |
||||
# Imports |
||||
|
||||
from argparse import ArgumentParser, RawDescriptionHelpFormatter |
||||
from superpython.logging import LoggingHelper |
||||
from ..constants import LOGGER_NAME |
||||
from . import initialize |
||||
from . import subcommands |
||||
|
||||
DEBUG = 10 |
||||
|
||||
logging = LoggingHelper(colorize=True, name=LOGGER_NAME) |
||||
log = logging.setup() |
||||
|
||||
# Commands |
||||
|
||||
|
||||
def main_command(): |
||||
"""Process script configurations.""" |
||||
|
||||
__author__ = "Shawn Davis <shawn@develmaycare.com>" |
||||
__date__ = "2020-07-21" |
||||
__help__ = """NOTES |
||||
|
||||
This command is used to parse configuration files and output the commands. |
||||
|
||||
""" |
||||
__version__ = "0.10.0-d" |
||||
|
||||
# Main argument parser from which sub-commands are created. |
||||
parser = ArgumentParser(description=__doc__, epilog=__help__, formatter_class=RawDescriptionHelpFormatter) |
||||
|
||||
parser.add_argument( |
||||
"path", |
||||
default="commands.ini", |
||||
nargs="?", |
||||
help="The path to the configuration file." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-c", |
||||
"--color", |
||||
action="store_true", |
||||
dest="color_enabled", |
||||
help="Enable code highlighting for terminal output." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-C=", |
||||
"--context=", |
||||
action="append", |
||||
dest="variables", |
||||
help="Context variables for use in pre-parsing the config and templates. In the form of: name:value" |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-d", |
||||
"--docs", |
||||
action="store_true", |
||||
dest="docs_enabled", |
||||
help="Output documentation instead of code." |
||||
) |
||||
|
||||
# parser.add_argument( |
||||
# "-d=", |
||||
# "--docs=", |
||||
# choices=["html", "markdown", "plain", "rst"], |
||||
# dest="docs_enabled", |
||||
# help="Output documentation instead of code." |
||||
# ) |
||||
|
||||
parser.add_argument( |
||||
"-D", |
||||
"--debug", |
||||
action="store_true", |
||||
dest="debug_enabled", |
||||
help="Enable debug output." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-f=", |
||||
"--filter=", |
||||
action="append", |
||||
dest="filters", |
||||
help="Filter the commands in the form of: attribute:value" |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-O=", |
||||
"--option=", |
||||
action="append", |
||||
dest="options", |
||||
help="Common command options in the form of: name:value" |
||||
) |
||||
|
||||
# parser.add_argument( |
||||
# "-O=", |
||||
# "--output=", |
||||
# # default=os.path.join("prototype", "output"), |
||||
# dest="output_path", |
||||
# help="Output to the given directory. Defaults to ./prototype/output/" |
||||
# ) |
||||
|
||||
parser.add_argument( |
||||
"-s", |
||||
"--script", |
||||
action="store_true", |
||||
dest="script_enabled", |
||||
help="Output commands as a script." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-T=", |
||||
"--template-path=", |
||||
action="append", |
||||
dest="template_locations", |
||||
help="The location of template files that may be used with the template command." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-w=", |
||||
"--write=", |
||||
dest="output_file", |
||||
help="Write the output to disk." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-V=", |
||||
"--variables-file=", |
||||
dest="variables_file", |
||||
help="Load variables from a file." |
||||
) |
||||
|
||||
# Access to the version number requires special consideration, especially |
||||
# when using sub parsers. The Python 3.3 behavior is different. See this |
||||
# answer: http://stackoverflow.com/questions/8521612/argparse-optional-subparser-for-version |
||||
parser.add_argument( |
||||
"-v", |
||||
action="version", |
||||
help="Show version number and exit.", |
||||
version=__version__ |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"--version", |
||||
action="version", |
||||
help="Show verbose version information and exit.", |
||||
version="%(prog)s" + " %s %s by %s" % (__version__, __date__, __author__) |
||||
) |
||||
|
||||
# Parse arguments. |
||||
args = parser.parse_args() |
||||
|
||||
if args.debug_enabled: |
||||
log.setLevel(DEBUG) |
||||
|
||||
log.debug("Namespace: %s" % args) |
||||
|
||||
# Load context. |
||||
context = dict() |
||||
if args.variables: |
||||
context = initialize.context_from_cli(args.variables) |
||||
|
||||
# Handle filters. |
||||
filters = None |
||||
if args.filters: |
||||
filters = initialize.filters_from_cli(args.filters) |
||||
|
||||
# Handle options. |
||||
options = None |
||||
if args.options: |
||||
options = initialize.options_from_cli(args.options) |
||||
|
||||
if args.variables_file: |
||||
variables = initialize.variable_from_file(args.variables_file) |
||||
if variables: |
||||
context.update(variables) |
||||
|
||||
if args.docs_enabled: |
||||
exit_code = subcommands.output_docs( |
||||
args.path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
elif args.script_enabled: |
||||
exit_code = subcommands.output_script( |
||||
args.path, |
||||
color_enabled=args.color_enabled, |
||||
context=context, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
else: |
||||
exit_code = subcommands.output_commands( |
||||
args.path, |
||||
color_enabled=args.color_enabled, |
||||
context=context, |
||||
filters=filters, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
|
||||
exit(exit_code) |
@ -0,0 +1,58 @@ |
||||
# Imports |
||||
|
||||
from configparser import ConfigParser |
||||
import logging |
||||
import os |
||||
from superpython.utils import smart_cast |
||||
from ..constants import LOGGER_NAME |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def context_from_cli(variables): |
||||
context = dict() |
||||
for i in variables: |
||||
key, value = i.split(":") |
||||
context[key] = smart_cast(value) |
||||
|
||||
return context |
||||
|
||||
|
||||
def filters_from_cli(filters): |
||||
_filters = dict() |
||||
for i in filters: |
||||
key, value = i.split(":") |
||||
if key not in filters: |
||||
_filters[key] = list() |
||||
|
||||
_filters[key].append(value) |
||||
|
||||
return _filters |
||||
|
||||
|
||||
def options_from_cli(options): |
||||
_options = dict() |
||||
for i in options: |
||||
key, value = i.split(":") |
||||
_options[key] = smart_cast(value) |
||||
|
||||
return _options |
||||
|
||||
|
||||
def variable_from_file(path): |
||||
if not os.path.exists(path): |
||||
log.warning("Variables file does not exist: %s" % path) |
||||
return None |
||||
|
||||
ini = ConfigParser() |
||||
ini.read(path) |
||||
|
||||
variables = dict() |
||||
for section in ini.sections(): |
||||
for key, value in ini.items(section): |
||||
key = "%s_%s" % (section, key) |
||||
variables[key] = smart_cast(vaue) |
||||
|
||||
return variables |
@ -0,0 +1,76 @@ |
||||
# Imports |
||||
|
||||
from superpython.shell import EXIT |
||||
from superpython.utils import highlight_code |
||||
from ..parsers import load_commands, load_config |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def output_commands(path, color_enabled=False, context=None, filters=None, locations=None, options=None): |
||||
commands = load_commands( |
||||
path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if commands is None: |
||||
return EXIT.ERROR |
||||
|
||||
output = list() |
||||
for command in commands: |
||||
statement = command.get_statement(cd=True) |
||||
if statement is None: |
||||
continue |
||||
|
||||
output.append(statement) |
||||
output.append("") |
||||
|
||||
if color_enabled: |
||||
print(highlight_code("\n".join(output), language="bash")) |
||||
else: |
||||
print("\n".join(output)) |
||||
|
||||
return EXIT.OK |
||||
|
||||
|
||||
def output_docs(path, context=None, filters=None, locations=None, options=None): |
||||
commands = load_commands( |
||||
path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if commands is None: |
||||
return EXIT.ERROR |
||||
|
||||
count = 1 |
||||
output = list() |
||||
for command in commands: |
||||
output.append("%s. %s" % (count, command.comment)) |
||||
count += 1 |
||||
|
||||
print("\n".join(output)) |
||||
|
||||
return EXIT.OK |
||||
|
||||
|
||||
def output_script(path, color_enabled=False, context=None, filters=None, locations=None, options=None): |
||||
config = load_config( |
||||
path, |
||||
context=context, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if config is None: |
||||
return EXIT.ERROR |
||||
|
||||
script = config.as_script() |
||||
if color_enabled: |
||||
print(highlight_code(script.to_string(), language="bash")) |
||||
else: |
||||
print(script) |
||||
|
||||
return EXIT.OK |
@ -1,47 +0,0 @@ |
||||
[apache] |
||||
disable_module = a2dismod {{ module_name }} |
||||
disable_site = a2dissite {{ domain_name }}.conf |
||||
enable_module = a2enmod {{ module_name }} |
||||
enable_site = a2ensite {{ domain_name }}.conf |
||||
reload = service apache2 reload |
||||
restart = service apache2 restart |
||||
start = service apache2 start |
||||
stop = service apache2 stop |
||||
test = apachectl configtest |
||||
|
||||
[package_install] |
||||
system = apt-get install -y {{ package_name }} |
||||
pip = pip3 install{% if upgrade %} --upgrade{% endif %} --quiet {{ package_name }} |
||||
|
||||
[package_remove] |
||||
system = apt-get uninstall -y {{ package_name }} |
||||
pip = pip3 uninstall --quiet {{ package_name }} |
||||
|
||||
[system] |
||||
install = apt-get install -y {{ package_name }} |
||||
reboot = reboot |
||||
remove = apt-get uninstall -y {{ package_name }} |
||||
update = apt-get update -y |
||||
upgrade = apt-get upgrade -y |
||||
|
||||
[python] |
||||
virtualenv = virtualenv {{ name }} |
||||
install = pip3 install{% if upgrade %} --upgrade{% endif %} --quiet {{ package_name }} |
||||
remove = pip3 uninstall --quiet {{ package_name }} |
||||
|
||||
[files] |
||||
append = echo "{{ content }}" >> {{ path }} |
||||
chgrp = chgrp{% if recursive %} -R{% endif %} {{ group }} {{ path }} |
||||
chmod = chmod{% if recursive %} -R{% endif %} {{ owner }} {{ path }} |
||||
chown = chown{% if recursive %} -R{% endif %} {{ mode }} {{ path }} |
||||
copy = cp{% if recursive %} -R{% endif %}{% if overwrite %} -n{% endif %} {{ from_path }} {{ to_path }} |
||||
mkdir = mkdir{% if mode %} -m {{ mode }}{% endif %}{% if recursive %} -p{% endif %} {{ path }} |
||||
move = move {{ from_path }} {{ to_path }} |
||||
rename = move {{ from_path }} {{ to_path }} |
||||
remove = rm{% if force %} -f{% endif %}{% if recursive %} -r{% endif %} {{ path }} |
||||
;rsync = ? |
||||
;scopy = ? |
||||
;sed = ? |
||||
symlink = ln -s{% if force %} -f{% endif %} {{ source }} {{ target }} |
||||
touch = touch {{ path }} |
||||
;write = ? |
@ -1,224 +0,0 @@ |
||||
# Imports |
||||
|
||||
from configparser import RawConfigParser |
||||
import os |
||||
from superpython.utils import parse_jinja_string |
||||
from ..constants import PATH_TO_SCRIPT_TEASE |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"Overlay", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class Overlay(object): |
||||
"""An overlay applies commands specific to a given operating system or platform.""" |
||||
|
||||
def __init__(self, name): |
||||
self.is_loaded = False |
||||
self._name = name |
||||
self._path = os.path.join(PATH_TO_SCRIPT_TEASE, "data", "overlays", "%s.ini" % name) |
||||
self._sections = dict() |
||||
|
||||
def __repr__(self): |
||||
return "<%s %s>" % (self.__class__.__name__, self._name) |
||||
|
||||
@property |
||||
def exists(self): |
||||
"""Indicates whether the overlay file exists. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
return os.path.exists(self._path) |
||||
|
||||
def get(self, section, key, **kwargs): |
||||
"""Get the command statement for the given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
kwargs are used to parse the value of the key within the section. |
||||
|
||||
:rtype: str | None |
||||
|
||||
""" |
||||
if not self.has(section, key): |
||||
return None |
||||
|
||||
template = self._sections[section][key] |
||||
|
||||
return parse_jinja_string(template, kwargs) |
||||
|
||||
def has(self, section, key): |
||||
"""Determine whether the overlay contains a given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if section not in self._sections: |
||||
return False |
||||
|
||||
if key not in self._sections[section]: |
||||
return False |
||||
|
||||
return True |
||||
|
||||
def load(self): |
||||
"""Load the overlay. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if not self.exists: |
||||
return False |
||||
|
||||
ini = RawConfigParser() |
||||
ini.read(self._path) |
||||
|
||||
for section in ini.sections(): |
||||
self._sections[section] = dict() |
||||
for key, value in ini.items(section): |
||||
self._sections[section][key] = value |
||||
|
||||
self.is_loaded = True |
||||
return True |
||||
|
||||
def to_mapping(self): |
||||
"""Export the overlay as a dictionary with command names as values. |
||||
|
||||
:rtype: dict |
||||
|
||||
""" |
||||
d = dict() |
||||
for section in self._sections: |
||||
d[section] = list() |
||||
for command_name, statement in self._sections[section].items(): |
||||
d[section].append(command_name) |
||||
|
||||
|
||||
class Overlay2(object): |
||||
"""An overlay applies commands specific to a given operating system or platform.""" |
||||
|
||||
def __init__(self, name): |
||||
self.is_loaded = False |
||||
self._name = name |
||||
self._path = os.path.join(PATH_TO_SCRIPT_TEASE, "data", "overlays", "%s.ini" % name) |
||||
self._sections = dict() |
||||
|
||||
self.exists = os.path.exists(self._path) |
||||
|
||||
def __repr__(self): |
||||
return "<%s %s>" % (self.__class__.__name__, self._name) |
||||
|
||||
def command_exists(self, name): |
||||
"""Determine whether a given command exists. |
||||
|
||||
:param name: The name of the command to check. |
||||
:type name: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
section = None |
||||
if "." in name: |
||||
section, name = name.split(".") |
||||
|
||||
if section is not None: |
||||
if section in self._sections: |
||||
return name in self._sections[section] |
||||
|
||||
for section in self._sections.keys(): |
||||
if name in self._sections[section]: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
def get_statement(self, name, *args, **kwargs): |
||||
pass |
||||
|
||||
def get(self, section, key, **kwargs): |
||||
"""Get the command statement for the given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
kwargs are used to parse the value of the key within the section. |
||||
|
||||
:rtype: str | None |
||||
|
||||
""" |
||||
if not self.has(section, key): |
||||
return None |
||||
|
||||
template = self._sections[section][key] |
||||
|
||||
return parse_jinja_string(template, kwargs) |
||||
|
||||
def has(self, section, key): |
||||
"""Determine whether the overlay contains a given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if section not in self._sections: |
||||
return False |
||||
|
||||
if key not in self._sections[section]: |
||||
return False |
||||
|
||||
return True |
||||
|
||||
def load(self): |
||||
"""Load the overlay. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if not self.exists: |
||||
return False |
||||
|
||||
ini = RawConfigParser() |
||||
ini.read(self._path) |
||||
|
||||
for section in ini.sections(): |
||||
self._sections[section] = dict() |
||||
for command_name, statement_template in ini.items(section): |
||||
self._sections[section][command_name] = statement_template |
||||
|
||||
self.is_loaded = True |
||||
|
||||
return True |
||||
|
||||
def to_mapping(self): |
||||
"""Export the overlay as a dictionary with command names as values. |
||||
|
||||
:rtype: dict |
||||
|
||||
""" |
||||
d = dict() |
||||
for section in self._sections: |
||||
d[section] = list() |
||||
for command_name, statement in self._sections[section].items(): |
||||
d[section].append(command_name) |
@ -1,2 +1,2 @@ |
||||
from .base import Command, ItemizedCommand |
||||
from .factory import command_factory |
||||
# from .factory import command_factory |
||||
|
@ -1,134 +0,0 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from ...constants import LOGGER_NAME |
||||
from .base import Command |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"MAPPING", |
||||
"ConfigTest", |
||||
"DisableModule", |
||||
"DisableSite", |
||||
"EnableModule", |
||||
"EnableSite", |
||||
"Reload", |
||||
"Restart", |
||||
"Start", |
||||
"Stop", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class ConfigTest(Command): |
||||
"""Run an apache config test.""" |
||||
|
||||
def __init__(self, overlay=None, **kwargs): |
||||
"""There is no argument.""" |
||||
if overlay is not None: |
||||
statement = overlay.get("apache", "test") |
||||
else: |
||||
statement = "apachectl configtest" |
||||
|
||||
kwargs.setdefault('register', "apache_checks_out") |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class DisableModule(Command): |
||||
"""Disable an Apache module.""" |
||||
|
||||
def __init__(self, module_name, overlay=None, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param module_name: The module name. |
||||
:type module_name: str |
||||
|
||||
""" |
||||
if overlay is not None: |
||||
statement = overlay.get("apache", "disable_module", module_name=module_name) |
||||
|
||||
statement = "a2dismod %s" % module_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class DisableSite(Command): |
||||
"""Disable a virtual host.""" |
||||
|
||||
def __init__(self, domain_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param domain_name: The domain name. |
||||
:type domain_name: str |
||||
|
||||
""" |
||||
statement = "a2dissite %s.conf" % domain_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class Enable(Command): |
||||
|
||||
def __init__(self, what, name, **kwargs): |
||||
if what in ("mod", "module"): |
||||
statement = EnableModule(name, **kwargs).statement |
||||
elif what == "site": |
||||
statement = EnableSite(name, **kwargs).statement |
||||
else: |
||||
raise ValueError("Invalid Apache item to be enabled: %s" % what) |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class EnableModule(Command): |
||||
"""Enable an Apache module.""" |
||||
|
||||
def __init__(self, module_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param module_name: The module name. |
||||
:type module_name: str |
||||
|
||||
""" |
||||
statement = "a2enmod %s" % module_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class EnableSite(Command): |
||||
"""Enable a virtual host.""" |
||||
|
||||
def __init__(self, domain_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param domain_name: The domain name. |
||||
:type domain_name: str |
||||
|
||||
""" |
||||
statement = "a2ensite %s.conf" % domain_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
MAPPING = { |
||||
# 'apache': Apache, |
||||
'apache.check': ConfigTest, |
||||
'apache.config': ConfigTest, |
||||
'apache.configtest': ConfigTest, |
||||
'apache.disable': Disable, |
||||
'apache.disable_mod': DisableModule, |
||||
'apache.disable_module': DisableModule, |
||||
'apache.disable_site': DisableSite, |
||||
'apache.enable': Enable, |
||||
'apache.enable_mod': EnableModule, |
||||
'apache.enable_module': EnableModule, |
||||
'apache.enable_site': EnableSite, |
||||
'apache.mod': EnableModule, |
||||
'apache.module': EnableModule, |
||||
'apache.test': ConfigTest, |
||||
} |
@ -1,119 +0,0 @@ |
||||
# Imports |
||||
|
||||
from importlib import import_module |
||||
import logging |
||||
# from ..scripts import Function |
||||
from ...constants import LOGGER_NAME |
||||
# from .base import ItemizedCommand |
||||
# from .mappings import MAPPING |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def command_factory(name, comment, overlay, *args, **kwargs): |
||||
# try: |
||||
# _overlay = import_module("scripttease.library.overlays.%s" % overlay) |
||||
# except ImportError as e: |
||||
# log.error("The %s overlay could not be imported: %s" % (overlay, str(e))) |
||||
# return None |
||||
|
||||
if not overlay.command_exists(name): |
||||
log.warning("Command does not exist in %s overlay: %s" % (overlay.name, name)) |
||||
return None |
||||
|
||||
kwargs['comment'] = comment |
||||
|
||||
callback = overlay.MAPPINGS[name] |
||||
return callback(*args, **kwargs) |
||||
|
||||
''' |
||||
def command_exists(name): |
||||
"""Indicates whether the named command exists. |
||||
|
||||
:param name: The name of the command to be checked. |
||||
:type name: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
return name in MAPPING |
||||
|
||||
|
||||
def command_factory(name, comment, overlay, *args, **kwargs): |
||||
# if name in ("func", "function"): |
||||
# kwargs['comment'] = comment |
||||
# return Function(*args, **kwargs) |
||||
|
||||
if not command_exists(name): |
||||
log.warning("No mapping for command: %s" % name) |
||||
return None |
||||
|
||||
_args = list(args) |
||||
kwargs['comment'] = comment |
||||
kwargs['overlay'] = overlay |
||||
|
||||
log.debug("%s: %s" % (comment, kwargs)) |
||||
|
||||
command_class = MAPPING[name] |
||||
try: |
||||
items = kwargs.pop("items", None) |
||||
if items is not None: |
||||
return ItemizedCommand(command_class, items, *_args, **kwargs) |
||||
|
||||
return command_class(*_args, **kwargs) |
||||
except (KeyError, TypeError, ValueError) as e: |
||||
log.critical("Failed to load %s command: %s" % (name, e)) |
||||
return None |
||||
''' |
||||
|
||||
# |
||||
# |
||||
# |
||||
# MAPPINGS = { |
||||
# 'apache.disable_module': apache_disable_module, |
||||
# 'apache.disable_site': apache_disable_site, |
||||
# 'apache.enable_module': apache_enable_module, |
||||
# 'apache.enable_site': apache_enable_site, |
||||
# 'apache.reload': apache_reload, |
||||
# 'apache.restart': apache_restart, |
||||
# 'apache.start': apache_start, |
||||
# 'apache.stop': apache_stop, |
||||
# 'apache.test': apache_test, |
||||
# 'copy': file_copy, |
||||
# 'pip': python_pip, |
||||
# 'virtualenv': python_virtualenv, |
||||
# # 'python': ("pip", "virtualenv"), |
||||
# # 'apache': ("disable_module", "disable_site", "enable_module", "enable_site", "test"), |
||||
# } |
||||
|
||||
|
||||
def nother_command_exists(name): |
||||
return name in MAPPINGS |
||||
|
||||
|
||||
def other_command_exists(name, section=None): |
||||
if section is not None: |
||||
if section not in MAPPINGS: |
||||
return False |
||||
|
||||
return name in MAPPINGS[section] |
||||
|
||||
for _section, commands in MAPPINGS.items(): |
||||
if name in commands: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
|
||||
|
||||
def other_command_factory(name, comment, overlay, *args, **kwargs): |
||||
if not overlay.command_exists(name): |
||||
log.warning("The %s overlay does not have a mapping for command: %s" % (overlay, name)) |
||||
return None |
||||
|
||||
items = kwargs.pop("items", None) |
||||
if items is not None: |
||||
return ItemizedCommand |
||||
|
@ -1,4 +0,0 @@ |
||||
from .python import MAPPING as PYTHON_MAPPING |
||||
|
||||
MAPPING = dict() |
||||
MAPPING.update(PYTHON_MAPPING) |
@ -1,23 +0,0 @@ |
||||
# Classes |
||||
|
||||
|
||||
class Install(object): |
||||
|
||||
def __init__(self, name, manager="pip", overlay=None, upgrade=False, **kwargs): |
||||
if overlay is not None: |
||||
statement = overlay.get("package_install", manager, package_name=name, upgrade=upgrade) |
||||
else: |
||||
statement = "%s install %s" % (manager, name) |
||||
|
||||
self.statement = statement |
||||
|
||||
|
||||
class Remove(object): |
||||
|
||||
def __init__(self, name, manager="pip", overlay=None): |
||||
if overlay is not None: |
||||
statement = overlay.get("package_remove", manager, package_name=name) |
||||
else: |
||||
statement = "%s uninstall %s" % (manager, name) |
||||
|
||||
|
@ -1,43 +0,0 @@ |
||||
# Imports |
||||
|
||||
from .base import Command |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"Pip", |
||||
"VirtualEnv", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class Pip(Command): |
||||
|
||||
def __init__(self, name, op="install", overlay=None, upgrade=False, venv=None, **kwargs): |
||||
if overlay is not None: |
||||
statement = overlay.get("python", op, package_name=name, upgrade=upgrade) |
||||
else: |
||||
statement = "pip %s -y %s" % (op, name) |
||||
|
||||
if venv is not None: |
||||
kwargs['prefix'] = "source %s/bin/activate" % venv |
||||
|
||||
kwargs.setdefault("comment", "%s %s" % (op, name)) |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class VirtualEnv(Command): |
||||
|
||||
def __init__(self, name="python", overlay=None, **kwargs): |
||||
kwargs.setdefault("comment", "create %s virtual environment" % name) |
||||
|
||||
statement = "virtualenv %s" % name |
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
MAPPING = { |
||||
'pip': Pip, |
||||
'virtualenv': VirtualEnv, |
||||
} |
@ -0,0 +1,164 @@ |
||||
# Imports |
||||
|
||||
import os |
||||
from ..commands import Command |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"DJANGO_MAPPINGS", |
||||
"django", |
||||
"django_check", |
||||
"django_collect_static", |
||||
"django_dumpdata", |
||||
"django_loaddata", |
||||
"django_migrate", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def _django(name, *args, venv=None, **kwargs): |
||||
if venv is not None: |
||||
kwargs['prefix'] = "source %s/bin/activate" % venv |
||||
|
||||
kwargs.setdefault("comment", "run %s django management command" % name) |
||||
|
||||
# Base parameters need to be captured, because all others are assumed to be switches for the management command. |
||||
_kwargs = { |
||||
'comment': kwargs.pop("comment", None), |
||||
'condition': kwargs.pop("condition", None), |
||||
'cd': kwargs.pop("cd", None), |
||||
'environments': kwargs.pop("environments", None), |
||||
'function': kwargs.pop("function", None), |
||||
# 'local': kwargs.pop("local", False), |
||||
'prefix': kwargs.pop("prefix", None), |
||||
'register': kwargs.pop("register", None), |
||||
'shell': kwargs.pop("shell", "/bin/bash"), |
||||
'stop': kwargs.pop("stop", False), |
||||
'sudo': kwargs.pop('sudo', False), |
||||
'tags': kwargs.pop("tags", None), |
||||
} |
||||
|
||||
statement = list() |
||||
statement.append("./manage.py %s" % name) |
||||
|
||||
# Remaining kwargs are assumed to be switches. |
||||
for key, value in kwargs.items(): |
||||
key = key.replace("_", "-") |
||||
if type(value) is bool: |
||||
if value is True: |
||||
statement.append("--%s" % key) |
||||
else: |
||||
statement.append("--%s=%s" % (key, value)) |
||||
|
||||
if len(args) > 0: |
||||
statement.append(" ".join(args)) |
||||
|
||||
return Command(" ".join(statement), **_kwargs) |
||||
|
||||
|
||||
def django(name, *args, venv=None, **kwargs): |
||||
if name == "check": |
||||
return django_check(venv=venv, **kwargs) |
||||
elif name in ("collectstatic", "static"): |
||||
return django_collect_static(venv=venv, **kwargs) |
||||
elif name == "migrate": |
||||
return django_migrate(venv=venv, **kwargs) |
||||
else: |
||||
return _django(name, *args, venv=venv, **kwargs) |
||||
|
||||
|
||||
def django_check(venv=None, **kwargs): |
||||
kwargs.setdefault("comment", "run django checks") |
||||
kwargs.setdefault("register", "django_checks_out") |
||||
|
||||
return _django("check", venv=venv, **kwargs) |
||||
|
||||
|
||||
def django_collect_static(venv=None, **kwargs): |
||||
kwargs.setdefault("comment", "collect static files") |
||||
|
||||
return _django("collectstatic", venv=venv, **kwargs) |
||||
|
||||
|
||||
def django_dumpdata(app_name, base_path="local", file_name="initial", indent=4, natural_foreign=False, |
||||
natural_primary=False, path=None, venv=None, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param app_name: The name (app label) of the app. ``app_label.ModelName`` may also be given. |
||||
:type app_name: str |
||||
|
||||
:param file_name: The file name to which the data will be dumped. |
||||
:type file_name: str |
||||
|
||||
:param indent: Indentation of the exported fixtures. |
||||
:type indent: int |
||||
|
||||
:param natural_foreign: Use the natural foreign parameter. |
||||
:type natural_foreign: bool |
||||
|
||||
:param natural_primary: Use the natural primary parameter. |
||||
:type natural_primary: bool |
||||
|
||||
:param path: The path to the data file. |
||||
:type path: str |
||||
|
||||
""" |
||||
kwargs.setdefault("comment", "export fixtures for %s" % app_name) |
||||
|
||||
output_format = kwargs.pop("format", "json") |
||||
|
||||
_path = path or os.path.join(base_path, app_name, "fixtures", "%s.%s" % (file_name, output_format)) |
||||
|
||||
return _django( |
||||
"dumpdata", |
||||
app_name, |
||||
"> %s" % _path, |
||||
format=output_format, |
||||
indent=indent, |
||||
natural_foreign=natural_foreign, |
||||
natural_primary=natural_primary, |
||||
venv=venv, |
||||
**kwargs |
||||
) |
||||
|
||||
|
||||
def django_loaddata(app_name, base_path="local", file_name="initial", path=None, venv=None, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param app_name: The name (app label) of the app. |
||||
:type app_name: str |
||||
|
||||
:param file_name: The file name to which the data will be dumped. |
||||
:type file_name: str |
||||
|
||||
:param path: The path to the data file. |
||||
:type path: str |
||||
|
||||
""" |
||||
kwargs.setdefault("comment", "load fixtures for %s" % app_name) |
||||
|
||||
output_format = kwargs.pop("format", "json") |
||||
|
||||
_path = path or os.path.join(base_path, app_name, "fixtures", "%s.%s" % (file_name, output_format)) |
||||
|
||||
return _django("loaddata", _path, venv=venv, **kwargs) |
||||
|
||||
|
||||
def django_migrate(venv=None, **kwargs): |
||||
kwargs.setdefault("comment", "run django database migrations") |
||||
|
||||
return _django("migrate", venv=venv, **kwargs) |
||||
|
||||
# Mapping |
||||
|
||||
|
||||
DJANGO_MAPPINGS = { |
||||
'django': django, |
||||
'django.check': django_check, |
||||
'django.collect_static': django_collect_static, |
||||
'django.dumpdata': django_dumpdata, |
||||
'django.loaddata': django_loaddata, |
||||
'django.migrate': django_migrate, |
||||
} |
@ -0,0 +1,292 @@ |
||||
# Imports |
||||
|
||||
from ..commands import Command |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"PGSQL_MAPPINGS", |
||||
"pg_create_database", |
||||
"pg_create_user", |
||||
"pg_database_exists", |
||||
"pg_drop_database", |
||||
"pg_drop_user", |
||||
"pg_dump_database", |
||||
"psql", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def _get_pgsql_command(name, host="localhost", password=None, port=5432, user="postgres"): |
||||
a = list() |
||||
|
||||
if password: |
||||
a.append('export PGPASSWORD="%s" &&' % password) |
||||
|
||||
a.append(name) |
||||
|
||||
a.append("--host=%s" % host) |
||||
a.append("--port=%s" % port) |
||||
a.append("--username=%s" % user) |
||||
|
||||
return a |
||||
|
||||
|
||||
def pg_create_database(name, admin_pass=None, admin_user="postgres", host="localhost", owner=None, port=5432, |
||||
template=None, **kwargs): |
||||
"""Create a PostgreSQL database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param owner: The owner (user/role name) of the new database. |
||||
:type owner: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
:param template: The database template name to use, if any. |
||||
:type template: str |
||||
|
||||
""" |
||||
_owner = owner or admin_user |
||||
|
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("createdb", host=host, password=admin_pass, port=port) |
||||
base.append("--owner=%s" % _owner) |
||||
|
||||
if template is not None: |
||||
base.append("--template=%s" % template) |
||||
|
||||
base.append(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_create_user(name, admin_pass=None, admin_user="postgres", host="localhost", password=None, port=5432, **kwargs): |
||||
"""Create a PostgreSQL user. |
||||
|
||||
:param name: The user name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param password: The password for the new user. |
||||
:type password: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("createuser", host=host, password=admin_pass, port=port) |
||||
base.append("-DRS") |
||||
base.append(name) |
||||
|
||||
if password is not None: |
||||
base.append("&& psql -h %s -U %s" % (host, admin_user)) |
||||
base.append("-c \"ALTER USER %s WITH ENCRYPTED PASSWORD '%s';\"" % (name, password)) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_database_exists(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Determine if a Postgres database exists. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. However, sudo may be required for |
||||
# file writing. |
||||
# kwargs['sudo'] = False |
||||
|
||||
kwargs.setdefault("register", "%s_db_exists" % name) |
||||
|
||||
base = _get_pgsql_command("psql", host=host, password=admin_pass, port=port, user=admin_user) |
||||
base.append(r"-lqt | cut -d \| -f 1 | grep -qw %s" % name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_drop_database(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Remove a PostgreSQL database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("dropdb", host=host, password=admin_pass, port=port, user=admin_user) |
||||
base.append(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_drop_user(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Remove a Postgres user. |
||||
|
||||
:param name: The user name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("dropuser", host=host, password=admin_pass, port=port, user=admin_user) |
||||
base.append(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_dump_database(name, admin_pass=None, admin_user="postgres", file_name=None, host="localhost", port=5432, |
||||
**kwargs): |
||||
"""Export a Postgres database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param file_name: The name (including the path, if desired) of the export file. Defaults to the |
||||
``database_name`` plus ".sql" |
||||
:type file_name: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
_file_name = file_name or "%s.sql" % name |
||||
|
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("pg_dump", host=host, password=admin_pass, port=port, user=admin_user) |
||||
base.append("--column-inserts") |
||||
base.append("--file=%s" % _file_name) |
||||
base.append(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def psql(sql, database="template1", host="localhost", password=None, port=5432, user="postgres", **kwargs): |
||||
"""Execute a psql command. |
||||
|
||||
:param sql: The SQL to be executed. |
||||
:type sql: str |
||||
|
||||
:param database: The database name. |
||||
:type database: str |
||||
|
||||
:param password: The password for the user with sufficient access privileges to execute the command. |
||||
:type password: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
:param user: The name of the user with sufficient access privileges to execute the command. |
||||
:type user: str |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("psql", host=host, password=password, port=port, user=user) |
||||
base.append("--dbname=%s" % database) |
||||
base.append('-c "%s"' % sql) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
PGSQL_MAPPINGS = { |
||||
'pg.client': psql, |
||||
'pg.createdatabase': pg_create_database, |
||||
'pg.createdb': pg_create_database, |
||||
'pg.createuser': pg_create_user, |
||||
'pg.database': pg_create_database, |
||||
'pg.database_exists': pg_database_exists, |
||||
'pg.db': pg_create_database, |
||||
'pg.dropdatabase': pg_drop_database, |
||||
'pg.dropdb': pg_drop_database, |
||||
'pg.dropuser': pg_drop_user, |
||||
'pg.dump': pg_dump_database, |
||||
'pg.dumpdb': pg_dump_database, |
||||
'pg.exists': pg_database_exists, |
||||
'pg.user': pg_create_user, |
||||
'psql': psql, |
||||
} |
@ -1,85 +1,4 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from superpython.utils import any_list_item |
||||
from ..constants import LOGGER_NAME |
||||
from .ini import Config |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"filter_commands", |
||||
"load_commands", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def filter_commands(commands, environments=None, tags=None): |
||||
"""Filter commands based on the given criteria. |
||||
|
||||
:param commands: The commands to be filtered. |
||||
:type commands: list |
||||
|
||||
:param environments: Environment names to be matched. |
||||
:type environments: list[str] |
||||
|
||||
:param tags: Tag names to be matched. |
||||
:type tags |
||||
:return: |
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
if environments is not None: |
||||
if not any_list_item(environments, command.environments): |
||||
continue |
||||
|
||||
if tags is not None: |
||||
if not any_list_item(tags, command.tags): |
||||
continue |
||||
|
||||
filtered.append(command) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def load_commands(path, filters=None, overlay=None, **kwargs): |
||||
"""Load commands from a configuration file. |
||||
|
||||
:param path: The path to the configuration file. |
||||
:type path: str |
||||
|
||||
:param filters: Used to filter commands. |
||||
:type filters: dict |
||||
|
||||
:rtype: list[BaseType[Command] | ItemizedCommand] | None |
||||
|
||||
:returns: A list of command instances or ``None`` if the configuration could not be loaded. |
||||
|
||||
kwargs are passed to the configuration class for instantiation. |
||||
|
||||
""" |
||||
if path.endswith(".ini"): |
||||
_config = Config(path, overlay=overlay, **kwargs) |
||||
# elif path.endswith(".yml"): |
||||
# _config = YAML(path, **kwargs) |
||||
else: |
||||
log.warning("Input file format is not currently supported: %s" % path) |
||||
return None |
||||
|
||||
if _config.load(): |
||||
commands = _config.get_commands() |
||||
|
||||
if filters is not None: |
||||
criteria = dict() |
||||
for attribute, values in filters.items(): |
||||
criteria[attribute] = values |
||||
|
||||
commands = filter_commands(commands, **criteria) |
||||
|
||||
return commands |
||||
|
||||
log.error("Failed to load config file: %s" % path) |
||||
return None |
||||
from .utils import filter_commands, load_commands, load_config |
||||
|
@ -0,0 +1,110 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from superpython.utils import any_list_item |
||||
from ..constants import LOGGER_NAME |
||||
from .ini import Config |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"filter_commands", |
||||
"load_commands", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def filter_commands(commands, environments=None, tags=None): |
||||
"""Filter commands based on the given criteria. |
||||
|
||||
:param commands: The commands to be filtered. |
||||
:type commands: list |
||||
|
||||
:param environments: Environment names to be matched. |
||||
:type environments: list[str] |
||||
|
||||
:param tags: Tag names to be matched. |
||||
:type tags |
||||
:return: |
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
if environments is not None: |
||||
if not any_list_item(environments, command.environments): |
||||
continue |
||||
|
||||
if tags is not None: |
||||
if not any_list_item(tags, command.tags): |
||||
continue |
||||
|
||||
filtered.append(command) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def load_commands(path, filters=None, overlay="ubuntu", **kwargs): |
||||
"""Load commands from a configuration file. |
||||
|
||||
:param path: The path to the configuration file. |
||||
:type path: str |
||||
|
||||
:param filters: Used to filter commands. |
||||
:type filters: dict |
||||
|
||||
:param overlay: The name of the command overlay to apply to generated commands. |
||||
:type overlay: str |
||||
|
||||
:rtype: list[scriptetease.library.commands.base.Command] | scriptetease.library.commands.base.ItemizedCommand] | |
||||
None |
||||
|
||||
:returns: A list of command instances or ``None`` if the configuration could not be loaded. |
||||
|
||||
kwargs are passed to the configuration class for instantiation. |
||||
|
||||
""" |
||||
_config = load_config(path, overlay, **kwargs) |
||||
if _config is None: |
||||
return None |
||||
|
||||
commands = _config.get_commands() |
||||
|
||||
if filters is not None: |
||||
criteria = dict() |
||||
for attribute, values in filters.items(): |
||||
criteria[attribute] = values |
||||
|
||||
commands = filter_commands(commands, **criteria) |
||||
|
||||
return commands |
||||
|
||||
|
||||
def load_config(path, overlay="ubuntu", **kwargs): |
||||
"""Load a command configuration. |
||||
|
||||
:param path: The path to the configuration file. |
||||
:type path: str |
||||
|
||||
:param overlay: The name of the command overlay to apply to generated commands. |
||||
:type overlay: str |
||||
|
||||
:rtype: Config | None |
||||
|
||||
kwargs are passed to the configuration class for instantiation. |
||||
|
||||
""" |
||||
if path.endswith(".ini"): |
||||
_config = Config(path, overlay=overlay, **kwargs) |
||||
# elif path.endswith(".yml"): |
||||
# _config = YAML(path, **kwargs) |
||||
else: |
||||
log.warning("Input file format is not currently supported: %s" % path) |
||||
return None |
||||
|
||||
if not _config.load(): |
||||
log.error("Failed to load config file: %s" % path) |
||||
return None |
||||
|
||||
return _config |
@ -1,159 +0,0 @@ |
||||
# Imports |
||||
|
||||
from pygments import highlight |
||||
from pygments.lexers import get_lexer_by_name |
||||
from pygments.formatters import get_formatter_by_name |
||||
|
||||
BashLexer = get_lexer_by_name("bash") |
||||
JSONLexer = get_lexer_by_name("json") |
||||
PythonLexer = get_lexer_by_name("python") |
||||
TerminalFormatter = get_formatter_by_name("terminal", linenos=True) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"any_list_item", |
||||
"filter_commands", |
||||
"filter_objects", |
||||
"highlight_code", |
||||
"split_csv", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def any_list_item(a, b): |
||||
"""Determine whether any item in ``a`` also exists in ``b``. |
||||
|
||||
:param a: The first list to be compared. |
||||
:type a: list |
||||
|
||||
:param b: The second list to be compared. |
||||
:type b: list |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
for i in a: |
||||
for j in b: |
||||
if i == j: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
|
||||
def filter_commands(commands, values, attribute="tags"): |
||||
"""Filter commands for a given set of values. |
||||
|
||||
:param commands: The commands to be filtered. |
||||
:type commands: list[BaseType[Command]] |
||||
|
||||
:param values: The values to be compared. |
||||
:type values: list |
||||
|
||||
:param attribute: The name of the command attribute to check. This attribute must be a list or tuple of values of |
||||
the same type given in ``values``. |
||||
:type attribute: str |
||||
|
||||
:rtype: bool |
||||
|
||||
.. code-block:: python |
||||
|
||||
commands = [ |
||||
AddUser("bob"), |
||||
Apt("apache2", tags=["apache", "www"]), |
||||
Reload("postgresql", tags=["database", "pgsql"]), |
||||
Touch("/var/www/index.html", tags=["www"]), |
||||
] |
||||
|
||||
values = ["apache", "www"] |
||||
|
||||
# Outputs the Apt and Touch commands above. |
||||
filtered_commands = filter_commands(command, values) |
||||
print(filtered_commands) |
||||
|
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
try: |
||||
list_b = getattr(command, attribute) |
||||
except AttributeError: |
||||
continue |
||||
|
||||
if not any_list_item(values, list_b): |
||||
continue |
||||
|
||||
filtered.append(command) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def filter_objects(objects, environments=None, scope=None, tags=None): |
||||
"""Filter the given objects by the given keys. |
||||
|
||||
:param objects: The objects to be filtered. |
||||
:type objects: list |
||||
|
||||
:param environments: The environments to be included. |
||||
:type environments: list[str] |
||||
|
||||
:param scope: The scope by which to filter; deploy, provision, tenant. |
||||
:type scope: str |
||||
|
||||
:param tags: The tags to be included. |
||||
:type tags: list[str] |
||||
|
||||
:rtype: list |
||||
:returns: Returns the objects that match the given keys. |
||||
|
||||
""" |
||||
filtered = list() |
||||
|
||||
# print("object, object environments, environments, any_list_item") |
||||
|
||||
for o in objects: |
||||
|
||||
# print(o, o.environments, environments, any_list_item(environments, o.environments)) |
||||
|
||||
# Apply environment filter. |
||||
if environments is not None: |
||||
if hasattr(o, "environment"): |
||||
if o.environment is not None and o.environment not in environments: |
||||
continue |
||||
elif hasattr(o, "environments"): |
||||
if type(o.environments) in (list, tuple) and not any_list_item(environments, o.environments): |
||||
continue |
||||
else: |
||||
pass |
||||
|
||||
# # Apply scope filter. |
||||
# if scope is not None: |
||||
# if o.scope not in [None, SCOPE_ALL, scope]: |
||||
# continue |
||||
|
||||
# Apply tag filter. |
||||
if tags is not None: |
||||
if not any_list_item(tags, o.tags): |
||||
continue |
||||
|
||||
# The object has passed the tests above. |
||||
filtered.append(o) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def highlight_code(string, lexer=None): |
||||
"""Highlight (colorize) the given string as Python code. |
||||
|
||||
:param string: The string to be highlighted. |
||||
:type string: str |
||||
|
||||
:rtype: str |
||||
|
||||
""" |
||||
if lexer is None: |
||||
lexer = BashLexer |
||||
|
||||
return highlight(string, lexer, TerminalFormatter) |
||||
|
||||
|
@ -0,0 +1,2 @@ |
||||
[this command will fail to load] |
||||
nonexistent = testing |
@ -0,0 +1,2 @@ |
||||
[this command will fail to load] |
||||
run - testing |
@ -0,0 +1,6 @@ |
||||
[create the site directory] |
||||
mkdir: /var/www/domains/{{ domain_tld }} |
||||
|
||||
[this will cause template parsing to fail] |
||||
touch: /path/to/{% if this.will.break %}testing |
||||
|
@ -0,0 +1,25 @@ |
||||
[function for setting up apache] |
||||
func = apache_setup |
||||
|
||||
[install apache] |
||||
install: apache2 |
||||
func: apache_setup |
||||
|
||||
[install wsgi] |
||||
install: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[enable wsgi] |
||||
apache.enable_module: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[disable the default site] |
||||
apache.disable_site = default |
||||
func: apache_setup |
||||
|
||||
[restart apache] |
||||
apache: restart |
||||
func: apache_setup |
||||
|
||||
[call apache setup] |
||||
run: apache_setup |
@ -0,0 +1,192 @@ |
||||
[update system repos] |
||||
system: update |
||||
|
||||
[upgrade the system] |
||||
system: upgrade |
||||
|
||||
[reboot the system] |
||||
system: reboot |
||||
|
||||
[function for setting up apache] |
||||
func = apache_setup |
||||
|
||||
[install apache] |
||||
install: apache2 |
||||
func: apache_setup |
||||
|
||||
[install wsgi] |
||||
install: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[enable wsgi] |
||||
apache.enable_module: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[disable the default site in function] |
||||
apache.disable_site = default |
||||
func: apache_setup |
||||
|
||||
[restart apache] |
||||
apache: restart |
||||
func: apache_setup |
||||
|
||||
[call apache setup] |
||||
run: apache_setup |
||||
|
||||
[disable the default site] |
||||
apache.disable_site = default |
||||
|
||||
[enable mod SSL] |
||||
apache.enable_module = mod_ssl |
||||
|
||||
[enable more than one apache module at once] |
||||
apache.enable_module = $item |
||||
items = mod_wsgi, mod_rewrite |
||||
|
||||
[make sure apache can be reloaded] |
||||
apache: test |
||||
|
||||
[restart apache outside of function] |
||||
apache: restart |
||||
condition: $apache_checks_out -eq 0 |
||||
|
||||
[install the virtualenv package] |
||||
pip = virtualenv |
||||
|
||||
[install django debug toolbar] |
||||
pip: django-debug-toolbar |
||||
env: development |
||||
tags: python, project |
||||
|
||||
[create a virtual environment] |
||||
virtualenv = python |
||||
cd = /path/to/project |
||||
tags = python, project |
||||
|
||||
[install pillow] |
||||
pip = Pillow |
||||
cd = /path/to/project |
||||
upgrade = yes |
||||
venv = python |
||||
tags = python, project |
||||
|
||||
[apply database migrations] |
||||
django: migrate |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[run a custom django command] |
||||
django = custom_command arg1 arg2 |
||||
cd = /path/to/project |
||||
venv = python |
||||
settings = tenants.example_app.settings |
||||
quiet = yes |
||||
|
||||
[collect the project's static files] |
||||
django: collectstatic |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[load data fixtures] |
||||
django.loaddata: categories |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[dump data fixtures] |
||||
django.dumpdata: projects |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[reload a service] |
||||
reload: postfix |
||||
|
||||
[restart a service] |
||||
restart: postfix |
||||
|
||||
[stop a service] |
||||
stop: postfix |
||||
|
||||
[start a service] |
||||
start: postfix |
||||
|
||||
[install a package] |
||||
install: python3 |
||||
|
||||
[remove a package] |
||||
uninstall: apache-top |
||||
|
||||
[add to a file] |
||||
append: /path/to/file.txt |
||||
content: this is a test |
||||
|
||||
[copy a file] |
||||
copy: /path/to/file.txt /new/path/to/file.txt |
||||
|
||||
[write (overwrite) a file] |
||||
write: /path/to/file.txt |
||||
content: this replaces all text in the file |
||||
|
||||
[create a directory] |
||||
mkdir: /path/to/dir |
||||
mode: 755 |
||||
|
||||
[move a file] |
||||
move: /path/to/file.txt /path/to/file.txt.b |
||||
|
||||
[set permissions on a file] |
||||
perms: /path/to/file.txt |
||||
group: www-data |
||||
mode: 755 |
||||
owner: deploy |
||||
recursive: yes |
||||
|
||||
[remove a file] |
||||
remove: /path/to/file.txt |
||||
|
||||
[sync a directory] |
||||
rsync: /path/to/source /path/to/target |
||||
|
||||
[copy a file to remote server] |
||||
scopy: /path/to/file.txt /path/to/server/file.txt |
||||
host: example.com |
||||
|
||||
[replace text in a file] |
||||
sed: /path/to/file.txt |
||||
find: logging = no |
||||
replace: logging = yes |
||||
|
||||
[create a symlink] |
||||
symlink: /var/www/domains |
||||
|
||||
[touch a file] |
||||
touch: /path/to/file.txt |
||||
|
||||
[create a postgres user/role] |
||||
pg.user: example_app |
||||
|
||||
[create a postgres database] |
||||
pg.db: example_app |
||||
owner: example_app |
||||
|
||||
[determine whether a postgres database exists] |
||||
pg.database_exists: example_app |
||||
|
||||
[export a postgres database] |
||||
pg.dump: testing |
||||
|
||||
[drop a postgres user/role] |
||||
pg.dropuser: testing |
||||
|
||||
[drop a postgres database] |
||||
pg.dropdb: testing |
||||
|
||||
[run an SQL command on a postgres database] |
||||
psql: "SELECT * FROM projects WHERE category = 'testing'" |
||||
database: example_app |
||||
owner: example_app |
||||
|
||||
[create a file archive] |
||||
archive: /var/www/domains/example_com |
||||
|
||||
[extract a file archive] |
||||
extract: /var/www/domains/example_com.tgz |
@ -1,12 +1,15 @@ |
||||
[install the virtualenv package] |
||||
pip = virtualenv |
||||
tags = python-support |
||||
|
||||
[create a virtual environment] |
||||
virtualenv = python |
||||
cd = /path/to/project |
||||
tags = python-support |
||||
|
||||
[install pillow] |
||||
pip = Pillow |
||||
cd = /path/to/project |
||||
upgrade = yes |
||||
venv = python |
||||
tags = depends |
@ -0,0 +1,2 @@ |
||||
[create the site directory] |
||||
mkdir: /var/www/domains/{{ domain_tld }} |
@ -0,0 +1,40 @@ |
||||
from scripttease.library.commands import Command, ItemizedCommand |
||||
from scripttease.factory import Factory |
||||
|
||||
|
||||
class TestFactory(object): |
||||
|
||||
def test_get_command(self): |
||||
f = Factory("ubuntu") |
||||
f.load() |
||||
|
||||
# Non-existent command. |
||||
c = f.get_command("nonexistent") |
||||
assert c is None |
||||
|
||||
# A good command with itemized parameters. |
||||
c = f.get_command( |
||||
"pip", |
||||
"$item", |
||||
items=["Pillow", "psycopg2-binary", "django"] |
||||
) |
||||
assert isinstance(c, ItemizedCommand) |
||||
|
||||
# A good, normal command. |
||||
c = f.get_command("pip", "django") |
||||
assert isinstance(c, Command) |
||||
|
||||
# Command exists, but given bad arguments. |
||||
c = f.get_command("pip") |
||||
assert c is None |
||||
|
||||
def test_load(self): |
||||
f = Factory("nonexistent") |
||||
assert f.load() is False |
||||
|
||||
f = Factory("ubuntu") |
||||
assert f.load() is True |
||||
|
||||
def test_repr(self): |
||||
f = Factory("centos") |
||||
assert repr(f) == "<Factory centos>" |
@ -1,20 +0,0 @@ |
||||
from scripttease.library.commands import command_factory, ItemizedCommand |
||||
from scripttease.library.commands.python import Pip |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
def test_command_factory(): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
|
||||
command = command_factory("nonexistent", "non existent command", overlay) |
||||
assert command is None |
||||
|
||||
command = command_factory("pip", "install pillow", overlay) |
||||
assert command is None |
||||
|
||||
command = command_factory("pip", "install pillow", overlay, "Pillow") |
||||
assert isinstance(command, Pip) |
||||
|
||||
command = command_factory("pip", "install various", overlay, "$item", items=["Pillow", "pyscopg2-binary", "django"]) |
||||
assert isinstance(command, ItemizedCommand) |
@ -1,18 +0,0 @@ |
||||
from scripttease.library.commands.python import * |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
def test_pip(): |
||||
pip = Pip("Pillow") |
||||
assert "pip install -y Pillow" in pip.get_statement() |
||||
|
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
|
||||
pip = Pip("Pillow", op="remove", overlay=overlay, venv="python") |
||||
assert "source python/bin/activate && pip3 uninstall --quiet Pillow" in pip.get_statement() |
||||
|
||||
|
||||
def test_virtualenv(): |
||||
virt = VirtualEnv() |
||||
assert "virtualenv python" in virt.get_statement() |
@ -0,0 +1,17 @@ |
||||
from scripttease.library.overlays.common import * |
||||
|
||||
|
||||
def test_python_pip(): |
||||
c = python_pip("Pillow") |
||||
assert "pip install -y Pillow" in c.get_statement() |
||||
|
||||
c = python_pip("Pillow", upgrade=True) |
||||
assert "--upgrade" in c.get_statement() |
||||
|
||||
c = python_pip("Pillow", venv="python") |
||||
assert "source python/bin/activate" in c.get_statement() |
||||
|
||||
|
||||
def test_python_virtual_env(): |
||||
c = python_virtualenv() |
||||
assert "virtualenv python" in c.get_statement() |
@ -0,0 +1,61 @@ |
||||
from scripttease.library.overlays.django import * |
||||
|
||||
|
||||
def test_django(): |
||||
c = django("check") |
||||
assert "./manage.py check" in c.get_statement() |
||||
|
||||
c = django("collectstatic") |
||||
assert "./manage.py collectstatic" in c.get_statement() |
||||
|
||||
c = django("migrate") |
||||
assert "./manage.py migrate" in c.get_statement() |
||||
|
||||
c = django("custom", "arg1", "arg2", venv="python", settings="tenants.example.settings", quiet=True) |
||||
s = c.get_statement() |
||||
assert "./manage.py custom" in s |
||||
assert "arg1" in s |
||||
assert "arg2" in s |
||||
assert "--settings=" in s |
||||
assert "source python/bin/activate" in s |
||||
assert "--quiet" in s |
||||
|
||||
|
||||
def test_django_check(): |
||||
c = django_check(venv="python") |
||||
s = c.get_statement() |
||||
assert "./manage.py check" in s |
||||
assert "source python/bin/activate" in s |
||||
|
||||
|
||||
def test_django_collect_static(): |
||||
c = django_collect_static(venv="python") |
||||
s = c.get_statement() |
||||
assert "./manage.py collectstatic" in s |
||||
assert "source python/bin/activate" in s |
||||
|
||||
|
||||
def test_django_dumpdata(): |
||||
c = django_dumpdata("projects") |
||||
s = c.get_statement() |
||||
assert "./manage.py dumpdata" in s |
||||
assert "projects >" in s |
||||
assert "--format=json" in s |
||||
assert "--indent=4" in s |
||||
assert "local/projects/fixtures/initial.json" in s |
||||
|
||||
|
||||
def test_django_loaddata(): |
||||
c = django_loaddata("projects") |
||||
s = c.get_statement() |
||||
print(s) |
||||
assert "./manage.py loaddata" in s |
||||
assert "local/projects/fixtures/initial.json" in s |
||||
|
||||
|
||||
def test_django_migrate(): |
||||
c = django_migrate(cd="/path/to/project/", venv="python") |
||||
s = c.get_statement(cd=True) |
||||
assert "./manage.py migrate" in s |
||||
assert "source python/bin/activate" in s |
||||
assert "cd /path/to/project/" in s |
@ -0,0 +1,60 @@ |
||||
from scripttease.library.overlays.pgsql import * |
||||
|
||||
|
||||
def test_pg_create_database(): |
||||
c = pg_create_database("testing", admin_pass="secret", template="mytemplate") |
||||
s = c.get_statement() |
||||
assert "createdb" in s |
||||
assert "export PGPASSWORD=" in s |
||||
assert "--host=" in s |
||||
assert "--port=" in s |
||||
assert "--username=" in s |
||||
assert "--owner=" in s |
||||
assert "--template=mytemplate" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_create_user(): |
||||
c = pg_create_user("testing", password="secret") |
||||
s = c.get_statement() |
||||
assert "createuser" in s |
||||
assert "-DRS" in s |
||||
assert "testing" in s |
||||
assert "ALTER USER testing" in s |
||||
|
||||
|
||||
def test_pg_database_exists(): |
||||
c = pg_database_exists("testing") |
||||
s = c.get_statement() |
||||
assert "psql" in s |
||||
assert "testing_db_exists" in s |
||||
|
||||
|
||||
def test_pg_drop_database(): |
||||
c = pg_drop_database("testing") |
||||
s = c.get_statement() |
||||
assert "dropdb" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_drop_user(): |
||||
c = pg_drop_user("testing") |
||||
s = c.get_statement() |
||||
assert "dropuser" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_dump_database(): |
||||
c = pg_dump_database("testing") |
||||
s = c.get_statement() |
||||
assert "pg_dump" in s |
||||
assert "--column-inserts" in s |
||||
assert "--file=testing.sql" in s |
||||
|
||||
|
||||
def test_psql(): |
||||
c = psql("SELECT * FROM projects", database="testing") |
||||
s = c.get_statement() |
||||
assert "psql" in s |
||||
assert "--dbname=testing" in s |
||||
assert '-c "SELECT * FROM projects"' in s |
@ -0,0 +1,209 @@ |
||||
import pytest |
||||
from scripttease.library.overlays.posix import * |
||||
|
||||
|
||||
def test_archive(): |
||||
c = archive( |
||||
"/path/to/target", |
||||
absolute=True, |
||||
exclude="*.log", |
||||
strip=1, |
||||
view=True |
||||
) |
||||
s = c.get_statement() |
||||
print(s) |
||||
# tar -czPv --exclude *.log --strip-components 1 -f ./archive.tgz /path/to/target |
||||
assert "tar -czPv --exclude *.log --strip-components 1" in s |
||||
assert "-f ./archive.tgz /path/to/target" in s |
||||
|
||||
|
||||
def test_certbot(): |
||||
with pytest.raises(ValueError): |
||||
c = certbot("example.com") |
||||
|
||||
c = certbot("example.com", email="webmaster@example.com") |
||||
s = c.get_statement() |
||||
assert "certbot certonly --agree-tos --email webmaster@example.com -n" in s |
||||
assert "--webroot -w /var/www/domains/example_com/www -d example.com" in s |
||||
|
||||
|
||||
def test_extract(): |
||||
c = extract( |
||||
"/path/to/archive.tgz", |
||||
absolute=True, |
||||
exclude="*.log", |
||||
strip=1, |
||||
view=True |
||||
) |
||||
s = c.get_statement() |
||||
assert "tar -xzPv --exclude *.log --strip-components 1" in s |
||||
assert "-f /path/to/archive.tgz ./" in s |
||||
|
||||
|
||||
def test_file_append(): |
||||
c = file_append("/path/to/file.txt", content="testing = yes") |
||||
assert 'echo "testing = yes" >> /path/to/file.txt' in c.get_statement() |
||||
|
||||
|
||||
def test_file_copy(): |
||||
c = file_copy("/path/to/file.txt", "/path/to/new-file.txt") |
||||
s = c.get_statement() |
||||
assert "cp" in s |
||||
assert "-n" in s |
||||
assert "/path/to/file.txt /path/to/new-file.txt" in s |
||||
|
||||
c = file_copy("/path/to/dir", "/path/to/newdir", recursive=True) |
||||
s = c.get_statement() |
||||
assert "cp" in s |
||||
assert "-R" in s |
||||
assert "/path/to/dir /path/to/newdir" in s |
||||
|
||||
|
||||
def test_file_write(): |
||||
c = file_write("/path/to/file.txt", content="testing 123") |
||||
assert 'echo "testing 123" > /path/to/file.txt' in c.get_statement() |
||||
|
||||
content = [ |
||||
"I am testing", |
||||
"I am testing", |
||||
"I am testing", |
||||
"testing 123", |
||||
] |
||||
c = file_write("/path/to/file.txt", content="\n".join(content)) |
||||
s = c.get_statement() |
||||
assert "cat > /path/to/file.txt << EOF" in s |
||||
assert "I am testing" in s |
||||
assert "testing 123" in s |
||||
|
||||
|
||||
def test_mkdir(): |
||||
c = mkdir("/path/to/dir", mode=755, recursive=True) |
||||
s = c.get_statement() |
||||
assert "mkdir" in s |
||||
assert "-m 755" in s |
||||
assert "-p" in s |
||||
assert "/path/to/dir" in s |
||||
|
||||
|
||||
def test_move(): |
||||
c = move("/path/to/file.txt", "/path/to/file.txt.b") |
||||
assert "mv /path/to/file.txt /path/to/file.txt.b" in c.get_statement() |
||||
|
||||
|
||||
def test_perms(): |
||||
c = perms("/path/to/dir", group="www-data", mode=755, owner="deploy", recursive=True) |
||||
s = c.get_statement() |
||||
assert "chgrp -R www-data /path/to/dir" in s |
||||
assert "chown -R deploy /path/to/dir" in s |
||||
assert "chmod -R 755 /path/to/dir" in s |
||||
|
||||
|
||||
def test_remove(): |
||||
c = remove("/path/to/dir", force=True, recursive=True) |
||||
s = c.get_statement() |
||||
assert "rm" in s |
||||
assert "-f" in s |
||||
assert "-r" in s |
||||
assert "/path/to/dir" in s |
||||
|
||||
|
||||
def test_rsync(): |
||||
c = rsync( |
||||
"/path/to/local/", |
||||
"/path/to/remote", |
||||
links=True, |
||||
delete=True, |
||||
exclude="deploy/exclude.txt", |
||||
recursive=True, |
||||
host="example.com", |
||||
key_file="~/.ssh/deploy", |
||||
user="deploy" |
||||
) |
||||
s = c.get_statement() |
||||
assert "rsync --cvs-exclude --checksum --compress --copy-links --delete" in s |
||||
assert "--exclude-from=deploy/exclude.txt" in s |
||||
assert "-P" in s |
||||
assert "--recursive /path/to/local/" in s |
||||
assert '-e "ssh -i ~/.ssh/deploy -p 22"' in s |
||||
assert "deploy@example.com:/path/to/remote" in s |
||||
|
||||
c = rsync( |
||||
"/path/to/local/", |
||||
"/path/to/remote", |
||||
links=True, |
||||
delete=True, |
||||
exclude="deploy/exclude.txt", |
||||
recursive=True, |
||||
) |
||||
s = c.get_statement() |
||||
assert "rsync --cvs-exclude --checksum --compress --copy-links --delete" in s |
||||
assert "--exclude-from=deploy/exclude.txt" in s |
||||
assert "-P" in s |
||||
assert "--recursive" in s |
||||
assert "/path/to/local/" in s |
||||
assert "/path/to/remote" in s |
||||
|
||||
|
||||
def test_run(): |
||||
c = run("ls -ls") |
||||
assert "ls -ls" in c.get_statement() |
||||
|
||||
|
||||
def test_scopy(): |
||||
with pytest.raises(ValueError): |
||||
c = scopy("/path/to/local/file.txt", "/path/to/remote/file.txt") |
||||
|
||||
c = scopy( |
||||
"/path/to/local/file.txt", |
||||
"/path/to/remote/file.txt", |
||||
key_file="~/.ssh/deploy", |
||||
host="example.com", |
||||
user="deploy" |
||||
) |
||||
s = c.get_statement() |
||||
assert "scp -i ~/.ssh/deploy" in s |
||||
assert "-P 22" in s |
||||
assert "/path/to/local/file.txt" in s |
||||
assert "deploy@example.com:/path/to/remote/file.txt" in s |
||||
|
||||
c = scopy( |
||||
"/path/to/local/file.txt", |
||||
"/path/to/remote/file.txt", |
||||
host="example.com", |
||||
) |
||||
s = c.get_statement() |
||||
assert "scp -P 22" in s |
||||
assert "/path/to/local/file.txt" in s |
||||
assert "example.com:/path/to/remote/file.txt" in s |
||||
|
||||
|
||||
def test_sed(): |
||||
c = sed("/path/to/file.txt", find="testing", replace="123") |
||||
s = c.get_statement() |
||||
assert "sed -i .b" in s |
||||
assert "s/testing/123/g" in s |
||||
assert "/path/to/file.txt" in s |
||||
|
||||
|
||||
def test_symlink(): |
||||
c = symlink("/var/www/domains", force=True) |
||||
s = c.get_statement() |
||||
assert "ln -s" in s |
||||
assert "-f" in s |
||||
assert "/var/www/domains" in s |
||||
|
||||
|
||||
def test_touch(): |
||||
c = touch("/path/to/file.txt") |
||||
assert "touch /path/to/file.txt" in c.get_statement() |
||||
|
||||
|
||||
class TestFunction(object): |
||||
|
||||
def test_to_string(self): |
||||
f = Function("testing", comment="A test function.") |
||||
f.commands.append(touch("/path/to/file.txt")) |
||||
s = f.to_string() |
||||
assert "# A test function." in s |
||||
assert "function testing()" in s |
||||
assert "touch /path/to/file.txt" in s |
@ -0,0 +1,86 @@ |
||||
import pytest |
||||
from scripttease.library.overlays.ubuntu import * |
||||
|
||||
|
||||
def test_apache(): |
||||
c = apache("reload") |
||||
assert "service apache2 reload" in c.get_statement() |
||||
|
||||
c = apache("restart") |
||||
assert "service apache2 restart" in c.get_statement() |
||||
|
||||
c = apache("start") |
||||
assert "service apache2 start" in c.get_statement() |
||||
|
||||
c = apache("stop") |
||||
assert "service apache2 stop" in c.get_statement() |
||||
|
||||
c = apache("test") |
||||
assert "apachectl configtest" in c.get_statement() |
||||
|
||||
with pytest.raises(NameError): |
||||
apache("nonexistent") |
||||
|
||||
|
||||
def test_apache_disable_module(): |
||||
c = apache_disable_module("mod_ssl") |
||||
assert "a2dismod mod_ssl" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_disable_site(): |
||||
c = apache_disable_site("default") |
||||
assert "a2dissite default" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_enable_module(): |
||||
c = apache_enable_module("mod_wsgi") |
||||
assert "a2enmod mod_wsgi" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_enable_site(): |
||||
c = apache_enable_site("example.com") |
||||
assert "a2ensite example.com" in c.get_statement() |
||||
|
||||
|
||||
def test_service_reload(): |
||||
c = service_reload("postfix") |
||||
assert "service postfix reload" in c.get_statement() |
||||
|
||||
|
||||
def test_service_restart(): |
||||
c = service_restart("postfix") |
||||
assert "service postfix restart" in c.get_statement() |
||||
|
||||
|
||||
def test_service_start(): |
||||
c = service_start("postfix") |
||||
assert "service postfix start" in c.get_statement() |
||||
|
||||
|
||||
def test_service_stop(): |
||||
c = service_stop("postfix") |
||||
assert "service postfix stop" in c.get_statement() |
||||
|
||||
|
||||
def test_system(): |
||||
c = system("reboot") |
||||
assert "reboot" in c.get_statement() |
||||
|
||||
c = system("update") |
||||
assert "apt-get update -y" in c.get_statement() |
||||
|
||||
c = system("upgrade") |
||||
assert "apt-get upgrade -y" in c.get_statement() |
||||
|
||||
with pytest.raises(NameError): |
||||
system("nonexistent") |
||||
|
||||
|
||||
def test_system_install(): |
||||
c = system_install("vim") |
||||
assert "apt-get install -y vim" in c.get_statement() |
||||
|
||||
|
||||
def test_system_uninstall(): |
||||
c = system_uninstall("lftp") |
||||
assert "apt-get uninstall -y lftp" in c.get_statement() |
@ -0,0 +1,31 @@ |
||||
from scripttease.library.commands import Command, ItemizedCommand |
||||
from scripttease.library.overlays.posix import Function |
||||
from scripttease.library.scripts import Script |
||||
|
||||
|
||||
class TestScript(object): |
||||
|
||||
def test_append(self): |
||||
s = Script("testing") |
||||
s.append(Command("ls -ls", comment="list some stuff")) |
||||
s.append(Command("touch /path/to/file.txt", comment="touch a file")) |
||||
s.append(Command("ln -s /path/to/file.txt", comment="link to a file")) |
||||
|
||||
assert len(s.commands) == 3 |
||||
|
||||
def test_to_string(self): |
||||
s = Script("testing") |
||||
s.append(Command("ls -ls", comment="list some stuff")) |
||||
s.append(Command("touch /path/to/file.txt", comment="touch a file")) |
||||
s.append(Command("ln -s /path/to/file.txt", comment="link to a file")) |
||||
|
||||
s.functions = list() |
||||
s.functions.append(Function("testing")) |
||||
|
||||
output = s.to_string() |
||||
assert output == str(s) |
||||
|
||||
assert "ls -ls" in output |
||||
assert "touch /path/to/file.txt" in output |
||||
assert "ln -s /path/to/file.txt" in output |
||||
assert "function testing()" in output |
@ -1,19 +0,0 @@ |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
class TestOverlay(object): |
||||
|
||||
def test_get(self): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
assert overlay.get("nonexistent", "nonexistent") is None |
||||
|
||||
def test_has(self): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
assert overlay.has("nonexistent", "nonexistent") is False |
||||
assert overlay.has("python", "nonexistent") is False |
||||
|
||||
def test_load(self): |
||||
overlay = Overlay("nonexistent") |
||||
assert overlay.load() is False |
@ -0,0 +1,22 @@ |
||||
import pytest |
||||
from scripttease.library.scripts import Script |
||||
# from scripttease.parsers import filter_commands, load_commands |
||||
from scripttease.parsers.base import Parser |
||||
|
||||
|
||||
class TestParser(object): |
||||
|
||||
def test_as_script(self): |
||||
p = Parser("/path/to/nonexistent.txt") |
||||
assert isinstance(p.as_script(), Script) |
||||
|
||||
# def test_get_commands(self): |
||||
# pass |
||||
# |
||||
# def test_get_functions(self): |
||||
# pass |
||||
|
||||
def test_load(self): |
||||
p = Parser("/path/to/nonexistent.txt") |
||||
with pytest.raises(NotImplementedError): |
||||
p.load() |
@ -0,0 +1,45 @@ |
||||
import pytest |
||||
from scripttease.parsers.ini import Config |
||||
|
||||
|
||||
class TestConfig(object): |
||||
|
||||
def test_get_commands(self): |
||||
c = Config("tests/examples/kitchen_sink.ini") |
||||
assert c.load() is True |
||||
|
||||
assert len(c.get_commands()) > 0 |
||||
|
||||
def test_get_functions(self): |
||||
c = Config("tests/examples/kitchen_sink.ini") |
||||
assert c.load() is True |
||||
|
||||
assert len(c.get_functions()) > 0 |
||||
|
||||
def test_load(self): |
||||
c = Config("nonexistent.ini") |
||||
assert c.load() is False |
||||
|
||||
c = Config("tests/examples/python_examples.ini", overlay="nonexistent") |
||||
assert c.load() is False |
||||
|
||||
c = Config("tests/examples/bad_examples.ini") |
||||
assert c.load() is False |
||||
|
||||
c = Config("tests/examples/kitchen_sink.ini") |
||||
assert c.load() is True |
||||
|
||||
c = Config("tests/examples/bad_command.ini") |
||||
assert c.load() is False |
||||
|
||||
context = { |
||||
'domain_tld': "example_com", |
||||
} |
||||
c = Config("tests/examples/template_example.ini", context=context) |
||||
assert c.load() is True |
||||
|
||||
context = { |
||||
'domain_tld': "example_com", |
||||
} |
||||
c = Config("tests/examples/bad_template_example.ini", context=context) |
||||
assert c.load() is False |
@ -0,0 +1,42 @@ |
||||
import pytest |
||||
from scripttease.library.commands import Command, ItemizedCommand |
||||
from scripttease.parsers import filter_commands, load_commands |
||||
|
||||
|
||||
def test_filter_commands(): |
||||
commands = [ |
||||
Command("apt-get install apache2 -y", environments=["base"], tags=["web"]), |
||||
Command("apt-get install apache-top -y", environments=["live"], tags=["web"]), |
||||
Command("pip install django-debug-toolbar", environments=["development"], tags=["django"]), |
||||
Command("pip install django", environments=["base"], tags=["django"]), |
||||
] |
||||
f1 = filter_commands(commands, environments=["base", "live"]) |
||||
assert len(f1) == 3 |
||||
|
||||
f2 = filter_commands(commands, tags=["django"]) |
||||
assert len(f2) == 2 |
||||
|
||||
f3 = filter_commands(commands, environments=["base", "development"]) |
||||
assert len(f3) == 3 |
||||
|
||||
f4 = filter_commands(commands, environments=["base"], tags=["web"]) |
||||
assert len(f4) == 1 |
||||
|
||||
|
||||
def test_load_commands(): |
||||
commands = load_commands("nonexistent.xml") |
||||
assert commands is None |
||||
|
||||
commands = load_commands("nonexistent.ini") |
||||
assert commands is None |
||||
|
||||
commands = load_commands("tests/examples/bad_examples.ini") |
||||
assert commands is None |
||||
|
||||
commands = load_commands( |
||||
"tests/examples/python_examples.ini", |
||||
filters={ |
||||
'tags': ["python-support"], |
||||
} |
||||
) |
||||
assert len(commands) == 2 |
Loading…
Reference in new issue