Step
def Step(
id:str, label:str, cmd:str, doc:str='', needs:list=<factory>, argv:list=<factory>, meta:dict=<factory>
)->None:One command in a release or deploy, with the keys it needs and the text that describes it.
Before pullup can release a project it has to know what kind of project it is, because each kind is released with different commands. This module reads the marker files in a folder (settings.ini, pyproject.toml, Cargo.toml, setup_app.py) and returns the steps that release it. Nothing here runs a command or touches the network.
One command in a release or deploy, with the keys it needs and the text that describes it.
A step is one command in a release, such as uploading to PyPI. Step holds what a user interface shows about it (label and doc), what to run (cmd), and the environment keys the command needs (needs). Usually cmd is the command itself and the pipeline splits it into arguments. When a command would not split correctly, such as a python -c snippet full of quotes and newlines, put the exact arguments in argv and keep cmd as the text to display. meta carries anything else a caller wants to keep with the step.
dict returns the step as a plain dictionary, with copies of its lists so that changing the result leaves the step alone. A pipeline saves its plan from these dictionaries and reads it back, argv included:
{'id': 'pypi',
'label': 'PyPI release',
'cmd': 'python -m twine upload dist/*',
'doc': 'Upload the built distributions to PyPI.',
'needs': ['TWINE_USERNAME', 'TWINE_PASSWORD'],
'argv': [],
'meta': {}}
Whether the project also builds a desktop app, marked by setup_app.py and tools/build_release.py.
Whether root is a Python package with a pyproject.toml that is not an nbdev project.
Whether root is a Rust crate that maturin builds into a Python wheel.
Whether root is a Rust crate, meaning it has a Cargo.toml.
Whether root is an nbdev project, by its nbdev 2 settings.ini or its pyproject.toml.
Each detector answers one yes-or-no question about a folder:
nbdev_project: an nbdev library, declared by an nbdev 2 settings.ini, a [tool.nbdev] table in pyproject.toml, or an nbdev entry point there.rust_project: a Rust crate, which means there is a Cargo.toml.maturin_project: a Rust crate that maturin also builds into a Python wheel, so its release goes to PyPI.fastship_project: a Python package with a pyproject.toml that is not an nbdev library. fastship is the release tool for such packages.app_project: a project that also builds a desktop app, recognised by setup_app.py together with tools/build_release.py.A pyproject.toml that is missing or is not valid TOML counts as empty instead of raising, so a half-written file never stops detection.
lib = mkproj('a_notebook_lib', {'settings.ini': '[DEFAULT]\nlib_name = demo\nnbs_path = nbs\n'})
crate = mkproj('a_crate', {'Cargo.toml': '[package]\nname = "demo"\n'})
pkg = mkproj('a_package', {'pyproject.toml': '[project]\nname = "demo"\n'})
[(p.name, nbdev_project(p), rust_project(p), fastship_project(p)) for p in (lib, crate, pkg)][('a_notebook_lib', True, False, False),
('a_crate', False, True, False),
('a_package', False, False, True)]
Each kind of project has its own list of steps. NBDEV_STEPS, FASTSHIP_STEPS, PLAIN_STEPS, MATURIN_STEPS and CRATE_STEPS are those lists, and FLOWS maps a flow name to its list. Pass your own mapping to default_steps or Project to change what a flow runs.
APP_STEPS is not a flow of its own. Its two steps build the desktop app and attach it to the GitHub release, and they are added to whichever flow the project uses. A maturin release uploads nothing from your machine: it pushes a tag, and CI builds and publishes a wheel for every platform.
These are the step ids of each flow, then the keys the fastship steps need:
{'nbdev': ['prepare', 'bump', 'gh', 'pypi'],
'fastship': ['test', 'changelog', 'gh', 'pypi', 'bump'],
'plain': ['test', 'build', 'gh', 'pypi'],
'maturin': ['test', 'wheel', 'ci', 'changelog', 'release'],
'crate': ['test', 'clippy', 'package', 'publish']}
{'changelog': ['GITHUB_TOKEN'],
'gh': ['GITHUB_TOKEN'],
'pypi': ['TWINE_USERNAME', 'TWINE_PASSWORD']}
The release steps for root, with the desktop-app steps added when the project builds one.
The name of the release flow that fits the project in root.
release_flow names the flow for a folder. It checks for nbdev first, then for maturin before a plain Rust crate (every maturin project is also a crate), then for fastship. Anything else, including a folder that does not exist, is plain.
default_steps returns that flow’s steps. For a project that builds a desktop app it inserts the app steps straight after the last gh or pypi step, so any step after those still runs last; a flow with neither gets them at the end.
[('a_notebook_lib', 'nbdev'),
('a_crate', 'crate'),
('a_package', 'fastship'),
('no_such_folder', 'plain')]
['test', 'changelog', 'gh', 'pypi', 'app', 'assets', 'bump']
The app steps land after pypi and before bump, because fastship raises the version for the next cycle only once the release is out.
The kind of project one folder holds and the steps that release it.
Project wraps all of this for one folder. It resolves the path once, when you create it, but works out kind and packages_an_app again each time you ask, so it notices files added since. steps returns the release steps and dict returns everything as JSON-ready data for a user interface.