Skip to content

Commit

Permalink
Provide invoke tasks for development.
Browse files Browse the repository at this point in the history
  • Loading branch information
jenisys committed Oct 15, 2016
1 parent 2d4a696 commit 9f9168d
Show file tree
Hide file tree
Showing 14 changed files with 2,965 additions and 1 deletion.
8 changes: 8 additions & 0 deletions bin/invoke
@@ -0,0 +1,8 @@
#!/bin/sh
#!/bin/bash
# RUN INVOKE: From bundled ZIP file.

HERE=$(dirname $0)
export INVOKE_TASKS_USE_VENDOR_BUNDLES="yes"

python ${HERE}/../tasks/_vendor/invoke.zip $*
5 changes: 4 additions & 1 deletion requirements/develop.txt
Expand Up @@ -4,8 +4,11 @@

# -- BUILD-TOOL:
# PREPARE USAGE: invoke
invoke >= 0.11.1
# ALREADY: six >= 1.10.0
invoke >= 0.13.0
path.py >= 7.2
pathlib; python_version <= '3.4'
pycmd

# -- CONFIGURATION MANAGEMENT (helpers):
bumpversion >= 0.4.0
Expand Down
45 changes: 45 additions & 0 deletions tasks/__behave.py
@@ -0,0 +1,45 @@
# -*- coding: UTF-8 -*-
"""
Invoke build script (python based).
.. seealso:: https://github.com/pyinvoke/invoke
"""

from __future__ import print_function
from invoke import task, Collection
import sys

# USE_PTY = os.isatty(sys.stdout)
USE_PTY = sys.stdout.isatty()

# ---------------------------------------------------------------------------
# TASKS
# ---------------------------------------------------------------------------
@task(help={
"args": "Command line args for behave",
"format": "Formatter to use",
})
def behave_test(ctx, args="", format=""): # XXX , echo=False):
"""Run behave tests."""
format = format or ctx.behave_test.format
options = ctx.behave_test.options or ""
args = args or ctx.behave_test.args
behave = "{python} bin/behave".format(python=sys.executable)
ctx.run("{behave} -f {format} {options} {args}".format(
behave=behave, format=format, options=options, args=args),
pty=USE_PTY)


# ---------------------------------------------------------------------------
# TASK MANAGEMENT / CONFIGURATION
# ---------------------------------------------------------------------------
# namespace.add_task(behave_test, default=True)
namespace = Collection()
namespace.add_task(behave_test, default=True)
namespace.configure({
"behave_test": {
"args": "",
"format": "progress2",
"options": "", # -- NOTE: Overide in configfile "invoke.yaml"
},
})
51 changes: 51 additions & 0 deletions tasks/__init__.py
@@ -0,0 +1,51 @@
# -*- coding: UTF-8 -*-
"""
Invoke build script.
Show all tasks with::
invoke -l
.. seealso::
* http://pyinvoke.org
* https://github.com/pyinvoke/invoke
"""

from __future__ import absolute_import

# -----------------------------------------------------------------------------
# BOOTSTRAP PATH: Use provided vendor bundle if "invoke" is not installed
# -----------------------------------------------------------------------------
INVOKE_MINVERSION = "0.13.0"
from . import _setup
_setup.setup_path()
_setup.require_invoke_minversion(INVOKE_MINVERSION)

# -----------------------------------------------------------------------------
# IMPORTS:
# -----------------------------------------------------------------------------
from invoke import Collection

# -- TASK-LIBRARY:
from . import clean
from . import docs
from . import test

# -----------------------------------------------------------------------------
# TASKS:
# -----------------------------------------------------------------------------
# None


# -----------------------------------------------------------------------------
# TASK CONFIGURATION:
# -----------------------------------------------------------------------------
namespace = Collection()
namespace.add_task(clean.clean)
namespace.add_task(clean.clean_all)
namespace.add_collection(Collection.from_module(docs))
namespace.add_collection(Collection.from_module(test))

# -- INJECT: clean configuration into this namespace
namespace.configure(clean.namespace.configuration())

70 changes: 70 additions & 0 deletions tasks/__main__.py
@@ -0,0 +1,70 @@
# -*- coding: UTF-8 -*-
"""
Provides "invoke" script when invoke is not installed.
Note that this approach uses the "tasks/_vendor/invoke.zip" bundle package.
Usage::
# -- INSTEAD OF: invoke command
# Show invoke version
python -m tasks --version
# List all tasks
python -m tasks -l
.. seealso::
* http://pyinvoke.org
* https://github.com/pyinvoke/invoke
Examples for Invoke Scripts using the Bundle
-------------------------------------------------------------------------------
For UNIX like platforms:
.. code-block:: sh
#!/bin/sh
#!/bin/bash
# RUN INVOKE: From bundled ZIP file (with Bourne shell/bash script).
# FILE: invoke.sh (in directory that contains tasks/ directory)
HERE=$(dirname $0)
export INVOKE_TASKS_USE_VENDOR_BUNDLES="yes"
python ${HERE}/tasks/_vendor/invoke.zip $*
For Windows platform:
.. code-block:: bat
@echo off
REM RUN INVOKE: From bundled ZIP file (with Windows Batchfile).
REM FILE: invoke.cmd (in directory that contains tasks/ directory)
setlocal
set HERE=%~dp0
set INVOKE_TASKS_USE_VENDOR_BUNDLES="yes"
if not defined PYTHON set PYTHON=python
%PYTHON% %HERE%tasks/_vendor/invoke.zip "%*"
"""

from __future__ import absolute_import
import os
import sys

# -----------------------------------------------------------------------------
# BOOTSTRAP PATH: Use provided vendor bundle if "invoke" is not installed
# -----------------------------------------------------------------------------
# NOTE: tasks/__init__.py performs sys.path setup.
os.environ["INVOKE_TASKS_USE_VENDOR_BUNDLES"] = "yes"

# -----------------------------------------------------------------------------
# AUTO-MAIN:
# -----------------------------------------------------------------------------
if __name__ == "__main__":
from invoke.main import program
sys.exit(program.run())
135 changes: 135 additions & 0 deletions tasks/_setup.py
@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""
Decides if vendor bundles are used or not.
Setup python path accordingly.
"""

from __future__ import absolute_import, print_function
import os.path
import sys

# -----------------------------------------------------------------------------
# DEFINES:
# -----------------------------------------------------------------------------
HERE = os.path.dirname(__file__)
TASKS_VENDOR_DIR = os.path.join(HERE, "_vendor")
INVOKE_BUNDLE = os.path.join(TASKS_VENDOR_DIR, "invoke.zip")
INVOKE_BUNDLE_VERSION = "0.13.0"

DEBUG_SYSPATH = False


# -----------------------------------------------------------------------------
# EXCEPTIONS:
# -----------------------------------------------------------------------------
class VersionRequirementError(SystemExit): pass

# -----------------------------------------------------------------------------
# FUNCTIONS:
# -----------------------------------------------------------------------------
def setup_path(invoke_minversion=None):
"""Setup python search and add ``TASKS_VENDOR_DIR`` (if available)."""
# print("INVOKE.tasks: setup_path")
if not os.path.isdir(TASKS_VENDOR_DIR):
print("SKIP: TASKS_VENDOR_DIR=%s is missing" % TASKS_VENDOR_DIR)
return
elif os.path.abspath(TASKS_VENDOR_DIR) in sys.path:
# -- SETUP ALREADY DONE:
# return
pass

use_vendor_bundles = os.environ.get("INVOKE_TASKS_USE_VENDOR_BUNDLES", "no")
if need_vendor_bundles(invoke_minversion):
use_vendor_bundles = "yes"

if use_vendor_bundles == "yes":
syspath_insert(0, os.path.abspath(TASKS_VENDOR_DIR))
if setup_path_for_bundle(INVOKE_BUNDLE, pos=1):
import invoke
bundle_path = os.path.relpath(INVOKE_BUNDLE, os.getcwd())
print("USING: %s (version: %s)" % (bundle_path, invoke.__version__))
else:
# -- BEST-EFFORT: May rescue something
syspath_append(os.path.abspath(TASKS_VENDOR_DIR))
setup_path_for_bundle(INVOKE_BUNDLE, pos=len(sys.path))

if DEBUG_SYSPATH:
for index, p in enumerate(sys.path):
print(" %d. %s" % (index, p))


def require_invoke_minversion(min_version, verbose=False):
"""Ensures that :mod:`invoke` has at the least the :param:`min_version`.
Otherwise,
:param min_version: Minimal acceptable invoke version (as string).
:param verbose: Indicates if invoke.version should be shown.
:raises: VersionRequirementError=SystemExit if requirement fails.
"""
# -- REQUIRES: sys.path is setup and contains invoke
try:
import invoke
invoke_version = invoke.__version__
except ImportError:
invoke_version = "__NOT_INSTALLED"

if invoke_version < min_version:
message = "REQUIRE: invoke.version >= %s (but was: %s)" % \
(min_version, invoke_version)
message += "\nUSE: pip install invoke>=%s" % min_version
raise VersionRequirementError(message)

INVOKE_VERSION = os.environ.get("INVOKE_VERSION", None)
if verbose and not INVOKE_VERSION:
os.environ["INVOKE_VERSION"] = invoke_version
print("USING: invoke.version=%s" % invoke_version)

def need_vendor_bundles(invoke_minversion=None):
invoke_minversion = invoke_minversion or "0.0.0"
need_vendor_answers = []
need_vendor_answers.append(need_vendor_bundle_invoke(invoke_minversion))
# -- REQUIRE: path.py
try:
import path
need_bundle = False
except ImportError:
need_bundle = True
need_vendor_answers.append(need_bundle)

# -- DIAG: print("INVOKE: need_bundle=%s" % need_bundle1)
# return need_bundle1 or need_bundle2
return any(need_vendor_answers)

def need_vendor_bundle_invoke(invoke_minversion="0.0.0"):
# -- REQUIRE: invoke
try:
import invoke
need_bundle = invoke.__version__ < invoke_minversion
if need_bundle:
del sys.modules["invoke"]
del invoke
except ImportError:
need_bundle = True
except Exception:
need_bundle = True
return need_bundle

# -----------------------------------------------------------------------------
# UTILITY FUNCTIONS:
# -----------------------------------------------------------------------------
def setup_path_for_bundle(bundle_path, pos=0):
if os.path.exists(bundle_path):
syspath_insert(pos, os.path.abspath(bundle_path))
return True
return False

def syspath_insert(pos, path):
if path in sys.path:
sys.path.remove(path)
sys.path.insert(pos, path)

def syspath_append(path):
if path in sys.path:
sys.path.remove(path)
sys.path.append(path)

35 changes: 35 additions & 0 deletions tasks/_vendor/README.txt
@@ -0,0 +1,35 @@
tasks/_vendor: Bundled vendor parts -- needed by tasks
===============================================================================

This directory contains bundled archives that may be needed to run the tasks.
Especially, it contains an executable "invoke.zip" archive.
This archive can be used when invoke is not installed.

To execute invoke from the bundled ZIP archive::


python -m tasks/_vendor/invoke.zip --help
python -m tasks/_vendor/invoke.zip --version


Example for a local "bin/invoke" script in a UNIX like platform environment::

#!/bin/bash
# RUN INVOKE: From bundled ZIP file.

HERE=$(dirname $0)

python ${HERE}/../tasks/_vendor/invoke.zip $*

Example for a local "bin/invoke.cmd" script in a Windows environment::

@echo off
REM ==========================================================================
REM RUN INVOKE: From bundled ZIP file.
REM ==========================================================================

setlocal
set HERE=%~dp0
if not defined PYTHON set PYTHON=python

%PYTHON% %HERE%../tasks/_vendor/invoke.zip "%*"
Binary file added tasks/_vendor/invoke.zip
Binary file not shown.

0 comments on commit 9f9168d

Please sign in to comment.