Writing Scripts#

Overview#

Workflow deinitions are text files in YAML format, that are read, compiled, and executed by the Task Runner.

Note

Unless you are a Python programmer, you may have to get used to the fact that whitespace matters in YAML files:
Make sure you indent uniformly. Don’t mix tabs and spaces. We recommend to use an editor that supports the YAML syntax (e.g. VS Code).

A simple confuguration script may look like this:
yabs.yaml:

 1# Yabs Workflow Definition
 2# See https://github.com/mar10/yabs
 3file_version: yabs#1
 4
 5config:
 6  repo: 'mar10/test-release-tool'
 7  version:
 8    - type: __version__
 9      file: src/test_release_tool/__init__.py
10  branches: main  # or master?
11
12
13tasks:
14  # The following tools are available. They are executed in the order
15  # listed here
16
17  # 'check': Assert preconditons and fail otherwise
18  - task: check
19    build: true             # dist/ folder exists
20    can_push: true          # Test if 'git push' would/would not succeed
21    clean: true             # Repo must/must not contain modifications
22    python: ">=3.9"         # SemVer specifier
23    twine: true             # `twine` is available
24    up_to_date: true        # everything pulled from remote
25    venv: true              # running inside a virtual environment
26    version: true           # `setup.py --version` returns the configured version
27    # winget: true            # `wingetcreate` is available
28    yabs: ">=0.5"           # SemVer specifier
29
30  # 'exec': Run arbitrary shell command
31  - task: exec
32    args: ["tox", "-e", "lint"]     # shell command and optional arguments
33    always: true            # `true`: run even in dry-run mode
34
35  # 'bump': Increment project version (requires argument: `yabs run --inc INC`)
36  - task: bump
37    inc: null               # Use value passed as 'yabs run --inc INC'
38
39  # 'commit': Commit modified files
40  - task: commit
41    add_known: true         # Commit with -a flag
42    message: |
43      Bump version to {version}
44
45  # 'tag': Create an annotated tag
46  - task: tag
47    name: v{version}
48    message: |
49      Version {version}
50
51  # 'push': Push changes and tags
52  - task: push
53    tags: true
54
55  # 'pypi_release': Create a release on PyPI using `twine`
56  - task: pypi_release
57    build:
58      - sdist
59      - bdist_wheel
60    upload: true
61
62  # 'github_release': Create a release on GitHub
63  - task: github_release
64    draft: false
65
66  # Bump 'v1.2.3' => 'v1.2.4-a1'
67  - task: bump
68    inc: "postrelease"
69
70  # Commit using '[ci skip]' as part of the message to prevent CI testing
71  - task: commit
72    add_known: true
73    message: |
74      Bump prerelease ({version}) [ci skip]
75
76  # Push to GitHub
77  - task: push

See yabs.yaml for a complete configuration with all available options and defaults.

Note

To get started, run yabs init inside your project’s directory. This will prompt for a few details and create a fresh yabs.yaml file.

Task Types#

See also

See Script Reference for a list of all tasks and options
and yabs.yaml for a complete configuration file with all available tasks.

Template Macros#

Some tasks have string options such as tag names, commit messges, etc. These strings may contain inline macros that will be expanded.

Typical macros are version, tag_name, repo, …
Macro names must be embedded in curly braces, for example:

- task: github_release
  name: 'v{version}'
  message: |
    Released {version}

    [Changelog](https://github.com/{repo}/blob/master/CHANGELOG.md),
    [Commit details](https://github.com/{repo}/compare/{org_tag_name}...{tag_name}).

All attributes of the task context are available as macros:

{inc}

Value of the --inc argument.

{org_tag_name}

The repo’s latest tag name (before ‘bump’).

{org_version}

Latest version (before ‘bump’).

{repo}

GitHub repo name, e.g. ‘USER/PROJECT’.

{tag_name}

The current tag name (after ‘bump’).

{version}

Current version (after ‘bump’).

See TaskContext for a complete list.

Version Locations#

Note

Currently only a small subset is implemented.
Please open an issue if you need another one and are ready to help with testing.

(TODO: verify this section.)

Although there seems to be consent that Python projects should have a version number that is stored at one central location, the community has not agreed upon that location yet.

In order to find and bump this versions, we need to pass a hint in the configuration yabs.yaml like so:

file_version: yabs#1
config:
  ...
  version:
    - type: __version__  # Example!
      file: src/my_project/__init__.py
...

Yabs supports some common approaches.
Following some typical patterns how Python projects store version numbers.

Note

Currently we would recommend this variant (unless Poetry is used):
Store the version in __init__.py of the project’s root folder:

__version__ = "1.2.3"

Then reference this in setup.cfg:

[metadata]
name = my_package
version = attr: my_project.__version__

This would then configured in yabs.yaml like so:

config:
  version:
    - type: __version__
      file: my_project/__init__.py

See below for details about the different use cases.

Poetry#

Todo

Not yet implemented.

Poetry stores the version number in its own section in pyproject.toml (defined in PEP-518):

pyproject.toml:

[project]
...
[tool.poetry]
name = "my_project"
version =  "1.2.3"

yabs.yaml:

config:
  version:
    - type: poetry

flit#

Todo

Not yet implemented.

__init__.py of the project’s root package#

__init__.py:

__version__ = "1.2.3"

yabs.yaml:

config:
  version:
    - type: __version__
      file: src/my_project/__init__.py

Or a variant the mimics Python’s sys.version_info style:

__init__.py:

version_info = (1, 2, 3)
version = ".".join(str(c) for c in version_info)

yabs.yaml:

config:
  version:
    # TODO

Plain Text File#

For example a _version.txt file in the project’s src folder containing:

_version.txt:

1.2.3

yabs.yaml:

config:
  version:
    # TODO

setup.cfg#

See also PEP-396 and setuptools .

setup.cfg in the project’s root folder:

[metadata]
name = my_package
version = 1.2.3

yabs.yaml:

config:
  version:
    # TODO

The follwing two examples for setup.cfg use the special attr: and file: directives that where introduced with setuptools v39.2 ).

Note: This assumes that the version is stored in a separate text- or Python file, which is covered in the examples above.

[metadata]
name = my_package
version = attr: my_project.__version__
[metadata]
name = my_package
version = file: path/to/file

The follwing two examples for setup.cfg use the special version-file and version-from-file options that where proposed for distutils2.

Note: This assumes that the version is stored in a separate text- or Python file, which is covered in the examples above.

[metadata]
# The entire contents of the file contains the version number
version-file = version.txt
[metadata]
# The version number is contained within a larger file, e.g. of Python code,
# such that the file must be parsed to extract the version
version-from-file = elle.py

Debugging#

Use the --verbose (short -v) option to generate more console logging.
Use the --dry-run (short -n) option to run all tasks in a simulation mode:

$ yabs run --inc patch -vn