wt-compiler 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wt_compiler-0.3.0/CHANGELOG.md +26 -0
- {wt_compiler-0.2.0/src/wt_compiler.egg-info → wt_compiler-0.3.0}/PKG-INFO +2 -2
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/pyproject.toml +3 -3
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/_version.py +3 -3
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/compiler.py +3 -8
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/requirements.py +1 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/abstract.py +1 -0
- wt_compiler-0.3.0/src/wt_compiler/wizard/templates/.github/workflows/ci.yml.jinja2 +97 -0
- wt_compiler-0.3.0/src/wt_compiler/wizard/templates/.github/workflows/tag.yml.jinja2 +97 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/.gitignore.jinja2 +3 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0/src/wt_compiler.egg-info}/PKG-INFO +2 -2
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler.egg-info/SOURCES.txt +2 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler.egg-info/requires.txt +1 -1
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_wizard_default.py +58 -2
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_wizard_extensibility.py +15 -8
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/uv.lock +4 -4
- wt_compiler-0.2.0/CHANGELOG.md +0 -16
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/README.md +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/setup.cfg +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/__init__.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/__main__.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/_models.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/artifacts.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/cli.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/discovery.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/exceptions.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/formatting.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/jsonschema.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/progress.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/py.typed +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/pypi_source.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/spec.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/Dockerfile.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/README.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/dockerignore.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pixi.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/cli.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dags/_macros.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dags/init.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dags/run_async.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dags/run_sequential.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dispatch.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/metadata.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/response.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/tests/conftest.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/tests/test_metadata.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/tests/test_results.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/util.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/README.md +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/__init__.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/default.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/.gitattributes.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/LICENSE.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/README.md.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/licenses/Apache-2.0.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/licenses/BSD-3-Clause.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/licenses/MIT.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/spec.yaml.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/test-cases.yaml.jinja2 +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler.egg-info/dependency_links.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler.egg-info/entry_points.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler.egg-info/top_level.txt +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/__init__.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/conftest.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/fixtures/simple_spec.yaml +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_artifacts.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_cli.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_compiler.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_discovery_integration.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_exceptions.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_jsonschema.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_progress.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_pypi_discovery.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_pypi_source.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_requirements.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_response_model.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_spec.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_wizard_abstract.py +0 -0
- {wt_compiler-0.2.0 → wt_compiler-0.3.0}/tests/test_wizard_cli_compat.py +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.3.0 — 2026-03-19
|
|
4
|
+
|
|
5
|
+
- Add CI and Tag GitHub Actions workflow templates to the wizard scaffold ([#83](https://github.com/wildlife-dynamics/wt/pull/83))
|
|
6
|
+
- Add `.gitignore` template to the wizard scaffold ([#83](https://github.com/wildlife-dynamics/wt/pull/83))
|
|
7
|
+
- Add `linux-aarch64` platform support ([#83](https://github.com/wildlife-dynamics/wt/pull/83))
|
|
8
|
+
- Fix wizard template rendering for nested output paths ([#83](https://github.com/wildlife-dynamics/wt/pull/83))
|
|
9
|
+
- Pin compiled output version bounds for wt-task (`>=0.1.2`) and wt-runner (`>=0.1.4`) ([#96](https://github.com/wildlife-dynamics/wt/pull/96))
|
|
10
|
+
- Remove errant injection of wt-task into wt-runner conda environment ([#96](https://github.com/wildlife-dynamics/wt/pull/96))
|
|
11
|
+
- Pin `datamodel-code-generator` to `==0.42.1` ([#96](https://github.com/wildlife-dynamics/wt/pull/96))
|
|
12
|
+
|
|
13
|
+
## v0.2.0 — 2026-03-13
|
|
14
|
+
|
|
15
|
+
- Support PyPI dependencies (`git`/`path`/`url` sources) in workflow specs, emitted as `[pypi-dependencies]` in compiled `pixi.toml` ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
16
|
+
- Generalize compiled `ResponseModel.result` from `DashboardJson | OutputFiles` to `Any`, removing hard dependency on `ecoscope_workflows_core` types ([#61](https://github.com/wildlife-dynamics/wt/pull/61))
|
|
17
|
+
- Make Graphviz graph generation best-effort — warns instead of failing when `dot` binary is unavailable ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
18
|
+
- Add `wt_registry` entry-point auto-discovery to task discovery pipeline ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
19
|
+
|
|
20
|
+
## v0.1.1 — 2026-03-05
|
|
21
|
+
|
|
22
|
+
- Fix incorrect license metadata
|
|
23
|
+
|
|
24
|
+
## v0.1.0 — 2026-03-05
|
|
25
|
+
|
|
26
|
+
- Initial release
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wt-compiler
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Workflow compiler for generating DAG artifacts from workflow specifications
|
|
5
5
|
Author: Ecoscope Team
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -16,7 +16,7 @@ Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
|
16
16
|
Requires-Dist: jinja2>=3.0.0
|
|
17
17
|
Requires-Dist: ruamel.yaml>=0.17.0
|
|
18
18
|
Requires-Dist: py-rattler<0.23.0,>=0.22.0
|
|
19
|
-
Requires-Dist: datamodel-code-generator
|
|
19
|
+
Requires-Dist: datamodel-code-generator==0.42.1
|
|
20
20
|
Requires-Dist: pydot>=1.4.0
|
|
21
21
|
Requires-Dist: ruff>=0.1.0
|
|
22
22
|
Requires-Dist: tomli-w>=1.0.0
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"jinja2>=3.0.0",
|
|
26
26
|
"ruamel.yaml>=0.17.0",
|
|
27
27
|
"py-rattler>=0.22.0,<0.23.0",
|
|
28
|
-
"datamodel-code-generator
|
|
28
|
+
"datamodel-code-generator==0.42.1",
|
|
29
29
|
"pydot>=1.4.0",
|
|
30
30
|
"ruff>=0.1.0",
|
|
31
31
|
"tomli-w>=1.0.0",
|
|
@@ -111,7 +111,7 @@ ignore = []
|
|
|
111
111
|
|
|
112
112
|
[tool.pixi.package]
|
|
113
113
|
name = "wt-compiler"
|
|
114
|
-
version = "0.
|
|
114
|
+
version = "0.3.0"
|
|
115
115
|
|
|
116
116
|
[tool.pixi.package.build]
|
|
117
117
|
backend = { name = "pixi-build-python", version = "*", channels = ["conda-forge", "https://prefix.dev/pixi-build-backends"] }
|
|
@@ -131,7 +131,7 @@ pydantic = ">=2.0.0,<3.0.0"
|
|
|
131
131
|
jinja2 = ">=3.0.0"
|
|
132
132
|
"ruamel.yaml" = ">=0.17.0"
|
|
133
133
|
py-rattler = ">=0.22.0,<0.23.0"
|
|
134
|
-
datamodel-code-generator = "
|
|
134
|
+
datamodel-code-generator = "==0.42.1"
|
|
135
135
|
pydot = ">=1.4.0"
|
|
136
136
|
ruff = ">=0.1.0"
|
|
137
137
|
tomli-w = ">=1.0.0"
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.3.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g7b40845'
|
|
@@ -471,7 +471,7 @@ class DagCompiler(BaseModel):
|
|
|
471
471
|
# Only add wt-task to conda deps when not using PyPI mode
|
|
472
472
|
if self.wt_pypi_deps is None:
|
|
473
473
|
cli_runtime_deps[task_pkg] = NamelessMatchSpec.from_match_spec(
|
|
474
|
-
MatchSpec(f"{runner_channel}::{task_pkg}
|
|
474
|
+
MatchSpec(f"{runner_channel}::{task_pkg} >=0.1.2,<1.0.0")
|
|
475
475
|
)
|
|
476
476
|
dependencies.update(cli_runtime_deps)
|
|
477
477
|
|
|
@@ -480,15 +480,10 @@ class DagCompiler(BaseModel):
|
|
|
480
480
|
runner_pypi_deps: dict[str, str | dict[str, Any]] = {}
|
|
481
481
|
|
|
482
482
|
if self.wt_pypi_deps is None:
|
|
483
|
-
# Conda mode: wt-runner
|
|
483
|
+
# Conda mode: wt-runner comes from conda channel
|
|
484
484
|
runner_conda_deps[runner_pkg] = NamelessMatchSpec.from_match_spec(
|
|
485
|
-
MatchSpec(f"{runner_channel}::{runner_pkg}
|
|
485
|
+
MatchSpec(f"{runner_channel}::{runner_pkg} >=0.1.4,<1.0.0")
|
|
486
486
|
)
|
|
487
|
-
if self.variant:
|
|
488
|
-
task_pkg = f"wt-task-{self.variant}"
|
|
489
|
-
runner_conda_deps[task_pkg] = NamelessMatchSpec.from_match_spec(
|
|
490
|
-
MatchSpec(f"{runner_channel}::{task_pkg} *")
|
|
491
|
-
)
|
|
492
487
|
else:
|
|
493
488
|
# PyPI mode: wt-runner goes in runner feature pypi-deps,
|
|
494
489
|
# wt-task goes in top-level pypi-deps
|
|
@@ -384,4 +384,5 @@ class AbstractWizardProvider(ABC):
|
|
|
384
384
|
template = env.get_template(tname)
|
|
385
385
|
output_name = tname.removesuffix(".jinja2")
|
|
386
386
|
output_path = workdir / output_name
|
|
387
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
387
388
|
output_path.write_text(template.render(context))
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{% raw %}
|
|
2
|
+
name: CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: ["main"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: ["**"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
parse-test-cases:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
outputs:
|
|
14
|
+
cases: ${{ steps.parse.outputs.cases }}
|
|
15
|
+
first_case: ${{ steps.parse.outputs.first_case }}
|
|
16
|
+
release_dir: ${{ steps.parse.outputs.release_dir }}
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
- name: Parse test cases
|
|
21
|
+
id: parse
|
|
22
|
+
run: |
|
|
23
|
+
# Discover release directory
|
|
24
|
+
shopt -s nullglob
|
|
25
|
+
dirs=(*-workflow/)
|
|
26
|
+
if [ ${#dirs[@]} -eq 0 ]; then
|
|
27
|
+
echo "Error: no *-workflow directory found" >&2; exit 1
|
|
28
|
+
elif [ ${#dirs[@]} -gt 1 ]; then
|
|
29
|
+
echo "Error: multiple *-workflow directories found: ${dirs[*]}" >&2; exit 1
|
|
30
|
+
fi
|
|
31
|
+
echo "release_dir=${dirs[0]%/}" >> $GITHUB_OUTPUT
|
|
32
|
+
|
|
33
|
+
# Fail if no test cases defined
|
|
34
|
+
keys=$(yq -o=json -I=0 'keys' test-cases.yaml)
|
|
35
|
+
if [ "$keys" = "[]" ] || [ "$keys" = "null" ]; then
|
|
36
|
+
echo "::error::test-cases.yaml has no test cases defined" && exit 1
|
|
37
|
+
fi
|
|
38
|
+
# Fail if any case is missing .params
|
|
39
|
+
for case in $(yq 'keys | .[]' test-cases.yaml); do
|
|
40
|
+
params=$(yq ".[\"$case\"].params" test-cases.yaml)
|
|
41
|
+
if [ "$params" = "null" ]; then
|
|
42
|
+
echo "::error::test case '$case' is missing required 'params' field" && exit 1
|
|
43
|
+
fi
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
echo "cases=$keys" >> $GITHUB_OUTPUT
|
|
47
|
+
echo "first_case=$(yq 'keys | .[0]' test-cases.yaml)" >> $GITHUB_OUTPUT
|
|
48
|
+
|
|
49
|
+
test:
|
|
50
|
+
needs: parse-test-cases
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
case: ${{ fromJSON(needs.parse-test-cases.outputs.cases) }}
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v6
|
|
59
|
+
- name: Install pixi
|
|
60
|
+
run: curl -fsSL https://pixi.sh/install.sh | bash && echo "$HOME/.pixi/bin" >> $GITHUB_PATH
|
|
61
|
+
- name: Set CI variables
|
|
62
|
+
run: |
|
|
63
|
+
echo "RELEASE_DIR=${{ needs.parse-test-cases.outputs.release_dir }}" >> $GITHUB_ENV
|
|
64
|
+
- name: Test
|
|
65
|
+
run: >-
|
|
66
|
+
pixi run --manifest-path $RELEASE_DIR/pixi.toml
|
|
67
|
+
--locked -e test
|
|
68
|
+
test-app-sequential-mock-io
|
|
69
|
+
--case ${{ matrix.case }}
|
|
70
|
+
|
|
71
|
+
docker:
|
|
72
|
+
needs: parse-test-cases
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v6
|
|
76
|
+
- name: Install pixi
|
|
77
|
+
run: curl -fsSL https://pixi.sh/install.sh | bash && echo "$HOME/.pixi/bin" >> $GITHUB_PATH
|
|
78
|
+
- name: Set CI variables
|
|
79
|
+
run: |
|
|
80
|
+
echo "RELEASE_DIR=${{ needs.parse-test-cases.outputs.release_dir }}" >> $GITHUB_ENV
|
|
81
|
+
echo "CI_TEST_CASE=${{ needs.parse-test-cases.outputs.first_case }}" >> $GITHUB_ENV
|
|
82
|
+
- name: Docker build + run
|
|
83
|
+
run: |
|
|
84
|
+
pixi run --manifest-path $RELEASE_DIR/pixi.toml --locked -e default docker-build
|
|
85
|
+
docker run --name $RELEASE_DIR -d \
|
|
86
|
+
-v $(pwd)/.wt-tmp/$RELEASE_DIR-results:/workflow/results \
|
|
87
|
+
-e PORT=4000 -p 4000:4000 $RELEASE_DIR
|
|
88
|
+
sleep 5
|
|
89
|
+
docker ps
|
|
90
|
+
- name: Invoke container with curl
|
|
91
|
+
run: |
|
|
92
|
+
params=$(yq -o=json -I=0 ".[\"$CI_TEST_CASE\"].params" test-cases.yaml)
|
|
93
|
+
curl --fail-with-body \
|
|
94
|
+
-X POST "http://localhost:4000/?execution_mode=sequential&mock_io=true&results_url=/workflow/results" \
|
|
95
|
+
-H "Content-Type: application/json" \
|
|
96
|
+
-d '{"params": '"${params}"'}'
|
|
97
|
+
{% endraw %}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{% raw %}
|
|
2
|
+
name: Tag
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, reopened, synchronize, closed]
|
|
7
|
+
branches:
|
|
8
|
+
- 'main'
|
|
9
|
+
- 'patch-*'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
generate-tag:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
outputs:
|
|
15
|
+
tag: ${{ steps.create_tag.outputs.result }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- name: List existing tags
|
|
21
|
+
run: git tag -l
|
|
22
|
+
- name: Find release directory
|
|
23
|
+
id: find_release_dir
|
|
24
|
+
run: |
|
|
25
|
+
shopt -s nullglob
|
|
26
|
+
dirs=(*-workflow/)
|
|
27
|
+
if [ ${#dirs[@]} -eq 0 ]; then
|
|
28
|
+
echo "Error: no *-workflow directory found" >&2; exit 1
|
|
29
|
+
elif [ ${#dirs[@]} -gt 1 ]; then
|
|
30
|
+
echo "Error: multiple *-workflow directories found: ${dirs[*]}" >&2; exit 1
|
|
31
|
+
fi
|
|
32
|
+
echo "name=${dirs[0]%/}" >> $GITHUB_OUTPUT
|
|
33
|
+
- name: Create tag from VERSION.yaml
|
|
34
|
+
id: create_tag
|
|
35
|
+
uses: mikefarah/yq@v4
|
|
36
|
+
with:
|
|
37
|
+
cmd: yq -r '"v" + (.MAJ | tostring) + "." + (.MIN | tostring) + "." + (.PATCH | tostring)' ${{ steps.find_release_dir.outputs.name }}/VERSION.yaml
|
|
38
|
+
- name: Validate tag doesn't already exist
|
|
39
|
+
run: |
|
|
40
|
+
if git tag -l | grep -q "${{ steps.create_tag.outputs.result }}"; then
|
|
41
|
+
echo "Tag ${{ steps.create_tag.outputs.result }} already exists. Exiting."
|
|
42
|
+
exit 1
|
|
43
|
+
else
|
|
44
|
+
echo "Tag ${{ steps.create_tag.outputs.result }} does not exist. Proceeding."
|
|
45
|
+
fi
|
|
46
|
+
- name: Validate tag contains all three semantic elements
|
|
47
|
+
run: |
|
|
48
|
+
if [[ "${{ steps.create_tag.outputs.result }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
49
|
+
echo "Tag ${{ steps.create_tag.outputs.result }} is valid, because it adheres to vMAJ.MIN.PATCH format."
|
|
50
|
+
else
|
|
51
|
+
echo "Tag ${{ steps.create_tag.outputs.result }} is not valid. It must adhere to vMAJ.MIN.PATCH format."
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
- name: If this is a PR to a patch branch, ensure the patch version is not 0
|
|
55
|
+
if: startsWith(github.base_ref, 'patch-')
|
|
56
|
+
run: |
|
|
57
|
+
patch_version=$(echo "${{ steps.create_tag.outputs.result }}" | cut -d '.' -f 3)
|
|
58
|
+
if [ "$patch_version" -ne 0 ]; then
|
|
59
|
+
echo "Patch version is not 0. This is an acceptable PR to a patch branch."
|
|
60
|
+
else
|
|
61
|
+
echo "Patch version is 0. Please ensure that the patch version is incremented before merging this PR."
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
cut-release:
|
|
66
|
+
needs: generate-tag
|
|
67
|
+
if: github.event.pull_request.merged == true
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
permissions:
|
|
70
|
+
contents: write
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v6
|
|
73
|
+
with:
|
|
74
|
+
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
|
75
|
+
fetch-depth: 0
|
|
76
|
+
- name: List existing tags
|
|
77
|
+
run: git tag -l
|
|
78
|
+
- name: Tag
|
|
79
|
+
run: |
|
|
80
|
+
echo "Tagging version ${{ needs.generate-tag.outputs.tag }}"
|
|
81
|
+
echo "Tagging from:\n$(git log -1)"
|
|
82
|
+
git tag ${{ needs.generate-tag.outputs.tag }}
|
|
83
|
+
git tag -l
|
|
84
|
+
- name: Push tag
|
|
85
|
+
run: |
|
|
86
|
+
git config user.name "github-actions[bot]"
|
|
87
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
88
|
+
git push origin --tags
|
|
89
|
+
- name: Create GitHub Release
|
|
90
|
+
env:
|
|
91
|
+
GH_TOKEN: ${{ github.token }}
|
|
92
|
+
run: >-
|
|
93
|
+
gh release create
|
|
94
|
+
'${{ needs.generate-tag.outputs.tag }}'
|
|
95
|
+
--repo '${{ github.repository }}'
|
|
96
|
+
--generate-notes
|
|
97
|
+
{% endraw %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wt-compiler
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Workflow compiler for generating DAG artifacts from workflow specifications
|
|
5
5
|
Author: Ecoscope Team
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -16,7 +16,7 @@ Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
|
16
16
|
Requires-Dist: jinja2>=3.0.0
|
|
17
17
|
Requires-Dist: ruamel.yaml>=0.17.0
|
|
18
18
|
Requires-Dist: py-rattler<0.23.0,>=0.22.0
|
|
19
|
-
Requires-Dist: datamodel-code-generator
|
|
19
|
+
Requires-Dist: datamodel-code-generator==0.42.1
|
|
20
20
|
Requires-Dist: pydot>=1.4.0
|
|
21
21
|
Requires-Dist: ruff>=0.1.0
|
|
22
22
|
Requires-Dist: tomli-w>=1.0.0
|
|
@@ -50,6 +50,8 @@ src/wt_compiler/wizard/templates/LICENSE.jinja2
|
|
|
50
50
|
src/wt_compiler/wizard/templates/README.md.jinja2
|
|
51
51
|
src/wt_compiler/wizard/templates/spec.yaml.jinja2
|
|
52
52
|
src/wt_compiler/wizard/templates/test-cases.yaml.jinja2
|
|
53
|
+
src/wt_compiler/wizard/templates/.github/workflows/ci.yml.jinja2
|
|
54
|
+
src/wt_compiler/wizard/templates/.github/workflows/tag.yml.jinja2
|
|
53
55
|
src/wt_compiler/wizard/templates/licenses/Apache-2.0.txt
|
|
54
56
|
src/wt_compiler/wizard/templates/licenses/BSD-3-Clause.txt
|
|
55
57
|
src/wt_compiler/wizard/templates/licenses/MIT.txt
|
|
@@ -231,7 +231,7 @@ class TestDump:
|
|
|
231
231
|
return provider
|
|
232
232
|
|
|
233
233
|
def test_dump_creates_all_files(self, tmp_path: Path) -> None:
|
|
234
|
-
"""All
|
|
234
|
+
"""All expected files exist after dump, including nested paths."""
|
|
235
235
|
provider = self._make_provider_with_answers()
|
|
236
236
|
provider.dump(tmp_path)
|
|
237
237
|
expected_files = [
|
|
@@ -241,6 +241,8 @@ class TestDump:
|
|
|
241
241
|
"LICENSE",
|
|
242
242
|
".gitignore",
|
|
243
243
|
".gitattributes",
|
|
244
|
+
".github/workflows/ci.yml",
|
|
245
|
+
".github/workflows/tag.yml",
|
|
244
246
|
]
|
|
245
247
|
for fname in expected_files:
|
|
246
248
|
assert (tmp_path / fname).exists(), f"{fname} not found"
|
|
@@ -279,12 +281,13 @@ class TestDump:
|
|
|
279
281
|
assert "MIT" in text
|
|
280
282
|
|
|
281
283
|
def test_dump_gitignore_contents(self, tmp_path: Path) -> None:
|
|
282
|
-
"""Has __pycache__/, .pixi/."""
|
|
284
|
+
"""Has __pycache__/, .pixi/, and .wt-tmp/."""
|
|
283
285
|
provider = self._make_provider_with_answers()
|
|
284
286
|
provider.dump(tmp_path)
|
|
285
287
|
text = (tmp_path / ".gitignore").read_text()
|
|
286
288
|
assert "__pycache__/" in text
|
|
287
289
|
assert ".pixi/" in text
|
|
290
|
+
assert ".wt-tmp/" in text
|
|
288
291
|
|
|
289
292
|
def test_dump_gitattributes_contents(self, tmp_path: Path) -> None:
|
|
290
293
|
"""Has linguist-generated=true."""
|
|
@@ -293,6 +296,59 @@ class TestDump:
|
|
|
293
296
|
text = (tmp_path / ".gitattributes").read_text()
|
|
294
297
|
assert "linguist-generated=true" in text
|
|
295
298
|
|
|
299
|
+
def test_dump_ci_yml_parse_test_cases_job(self, tmp_path: Path) -> None:
|
|
300
|
+
"""CI workflow contains parse-test-cases job with expected outputs."""
|
|
301
|
+
provider = self._make_provider_with_answers()
|
|
302
|
+
provider.dump(tmp_path)
|
|
303
|
+
text = (tmp_path / ".github/workflows/ci.yml").read_text()
|
|
304
|
+
assert "parse-test-cases:" in text
|
|
305
|
+
assert "steps.parse.outputs.cases" in text
|
|
306
|
+
assert "steps.parse.outputs.first_case" in text
|
|
307
|
+
assert "steps.parse.outputs.release_dir" in text
|
|
308
|
+
|
|
309
|
+
def test_dump_ci_yml_validation_errors(self, tmp_path: Path) -> None:
|
|
310
|
+
"""CI workflow contains validation error messages for empty test-cases and missing params."""
|
|
311
|
+
provider = self._make_provider_with_answers()
|
|
312
|
+
provider.dump(tmp_path)
|
|
313
|
+
text = (tmp_path / ".github/workflows/ci.yml").read_text()
|
|
314
|
+
assert "test-cases.yaml has no test cases defined" in text
|
|
315
|
+
assert "is missing required 'params' field" in text
|
|
316
|
+
|
|
317
|
+
def test_dump_ci_yml_test_job_matrix(self, tmp_path: Path) -> None:
|
|
318
|
+
"""CI test job uses matrix.case strategy."""
|
|
319
|
+
provider = self._make_provider_with_answers()
|
|
320
|
+
provider.dump(tmp_path)
|
|
321
|
+
text = (tmp_path / ".github/workflows/ci.yml").read_text()
|
|
322
|
+
assert "needs: parse-test-cases" in text
|
|
323
|
+
assert "matrix:" in text
|
|
324
|
+
assert "fromJSON(needs.parse-test-cases.outputs.cases)" in text
|
|
325
|
+
assert "matrix.case" in text
|
|
326
|
+
|
|
327
|
+
def test_dump_ci_yml_docker_job_uses_first_case(self, tmp_path: Path) -> None:
|
|
328
|
+
"""CI docker job uses first_case output from parse-test-cases."""
|
|
329
|
+
provider = self._make_provider_with_answers()
|
|
330
|
+
provider.dump(tmp_path)
|
|
331
|
+
text = (tmp_path / ".github/workflows/ci.yml").read_text()
|
|
332
|
+
assert "needs.parse-test-cases.outputs.first_case" in text
|
|
333
|
+
assert ".wt-tmp/" in text
|
|
334
|
+
assert "$RELEASE_DIR" in text
|
|
335
|
+
|
|
336
|
+
def test_dump_tag_yml_preserves_github_expressions(self, tmp_path: Path) -> None:
|
|
337
|
+
"""Tag workflow preserves ${{ }} GitHub Actions expressions literally."""
|
|
338
|
+
provider = self._make_provider_with_answers()
|
|
339
|
+
provider.dump(tmp_path)
|
|
340
|
+
text = (tmp_path / ".github/workflows/tag.yml").read_text()
|
|
341
|
+
assert "${{ steps.create_tag.outputs.result }}" in text
|
|
342
|
+
assert "${{ steps.find_release_dir.outputs.name }}" in text
|
|
343
|
+
assert "${{ github.event.pull_request.merge_commit_sha }}" in text
|
|
344
|
+
assert "${{ needs.generate-tag.outputs.tag }}" in text
|
|
345
|
+
|
|
346
|
+
def test_dump_creates_nested_directories(self, tmp_path: Path) -> None:
|
|
347
|
+
"""dump() creates .github/workflows/ directory automatically."""
|
|
348
|
+
provider = self._make_provider_with_answers()
|
|
349
|
+
provider.dump(tmp_path)
|
|
350
|
+
assert (tmp_path / ".github" / "workflows").is_dir()
|
|
351
|
+
|
|
296
352
|
def test_dump_raises_on_incomplete_answers(self, tmp_path: Path) -> None:
|
|
297
353
|
"""Incomplete provider raises UndefinedError when rendering."""
|
|
298
354
|
import jinja2
|
|
@@ -451,14 +451,18 @@ class TestCustomTemplates:
|
|
|
451
451
|
assert (out_dir / "extra.yaml").exists()
|
|
452
452
|
extra_content = (out_dir / "extra.yaml").read_text()
|
|
453
453
|
assert "my_workflow" in extra_content
|
|
454
|
-
# Default files also exist
|
|
455
|
-
|
|
456
|
-
|
|
454
|
+
# Default files also exist
|
|
455
|
+
top_level_files = {f.name for f in out_dir.iterdir() if f.is_file()}
|
|
456
|
+
default_top_files = {
|
|
457
457
|
"spec.yaml", "test-cases.yaml", "README.md",
|
|
458
458
|
"LICENSE", ".gitignore", ".gitattributes",
|
|
459
459
|
}
|
|
460
|
-
assert
|
|
461
|
-
|
|
460
|
+
assert default_top_files.issubset(top_level_files)
|
|
461
|
+
# Extra + 6 defaults at top level = 7 files
|
|
462
|
+
assert len(top_level_files) == 7
|
|
463
|
+
# Also verify nested CI files exist
|
|
464
|
+
assert (out_dir / ".github" / "workflows" / "ci.yml").exists()
|
|
465
|
+
assert (out_dir / ".github" / "workflows" / "tag.yml").exists()
|
|
462
466
|
|
|
463
467
|
def test_default_templates_inherited(self, tmp_path: Path) -> None:
|
|
464
468
|
"""Subclass with no custom templates inherits all defaults."""
|
|
@@ -474,6 +478,9 @@ class TestCustomTemplates:
|
|
|
474
478
|
drive_wizard(provider, answers)
|
|
475
479
|
provider.dump(tmp_path)
|
|
476
480
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
assert
|
|
481
|
+
expected_top = {"spec.yaml", "test-cases.yaml", "README.md", "LICENSE", ".gitignore", ".gitattributes"}
|
|
482
|
+
actual_top = {f.name for f in tmp_path.iterdir() if f.is_file()}
|
|
483
|
+
assert expected_top == actual_top
|
|
484
|
+
# Nested CI files
|
|
485
|
+
assert (tmp_path / ".github" / "workflows" / "ci.yml").exists()
|
|
486
|
+
assert (tmp_path / ".github" / "workflows" / "tag.yml").exists()
|
|
@@ -149,7 +149,7 @@ wheels = [
|
|
|
149
149
|
|
|
150
150
|
[[package]]
|
|
151
151
|
name = "datamodel-code-generator"
|
|
152
|
-
version = "0.
|
|
152
|
+
version = "0.42.1"
|
|
153
153
|
source = { registry = "https://pypi.org/simple" }
|
|
154
154
|
dependencies = [
|
|
155
155
|
{ name = "argcomplete" },
|
|
@@ -162,9 +162,9 @@ dependencies = [
|
|
|
162
162
|
{ name = "pydantic" },
|
|
163
163
|
{ name = "pyyaml" },
|
|
164
164
|
]
|
|
165
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
165
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4f/0b/03bc35aa726bfc8693c2e6d973df0822b7fe357f466b78e494b11ba5aef2/datamodel_code_generator-0.42.1.tar.gz", hash = "sha256:a7513cf51f6a04cb8e3a390143bd97774a29894568c2ddab28fe49b2b3358e4a", size = 502513, upload-time = "2025-12-08T14:55:03.556Z" }
|
|
166
166
|
wheels = [
|
|
167
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/51/d7/35796dc80be514f8f05d3e6a8d462bc384f9c52b03e4d3ca9562f0de2095/datamodel_code_generator-0.42.1-py3-none-any.whl", hash = "sha256:60771ef27bf2ed8c705a5fd7719493b5f87e0372ff1fb91a754680c0d71ec9cd", size = 169360, upload-time = "2025-12-08T14:55:01.655Z" },
|
|
168
168
|
]
|
|
169
169
|
|
|
170
170
|
[[package]]
|
|
@@ -810,7 +810,7 @@ dev = [
|
|
|
810
810
|
|
|
811
811
|
[package.metadata]
|
|
812
812
|
requires-dist = [
|
|
813
|
-
{ name = "datamodel-code-generator", specifier = "
|
|
813
|
+
{ name = "datamodel-code-generator", specifier = "==0.42.1" },
|
|
814
814
|
{ name = "jinja2", specifier = ">=3.0.0" },
|
|
815
815
|
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" },
|
|
816
816
|
{ name = "py-rattler", specifier = ">=0.22.0,<0.23.0" },
|
wt_compiler-0.2.0/CHANGELOG.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## v0.2.0 — 2026-03-13
|
|
4
|
-
|
|
5
|
-
- Support PyPI dependencies (`git`/`path`/`url` sources) in workflow specs, emitted as `[pypi-dependencies]` in compiled `pixi.toml` ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
6
|
-
- Generalize compiled `ResponseModel.result` from `DashboardJson | OutputFiles` to `Any`, removing hard dependency on `ecoscope_workflows_core` types ([#61](https://github.com/wildlife-dynamics/wt/pull/61))
|
|
7
|
-
- Make Graphviz graph generation best-effort — warns instead of failing when `dot` binary is unavailable ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
8
|
-
- Add `wt_registry` entry-point auto-discovery to task discovery pipeline ([#60](https://github.com/wildlife-dynamics/wt/pull/60))
|
|
9
|
-
|
|
10
|
-
## v0.1.1 — 2026-03-05
|
|
11
|
-
|
|
12
|
-
- Fix incorrect license metadata
|
|
13
|
-
|
|
14
|
-
## v0.1.0 — 2026-03-05
|
|
15
|
-
|
|
16
|
-
- Initial release
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/pkg/dags/run_sequential.jinja2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/templates/tests/test_metadata.jinja2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/.gitattributes.jinja2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/licenses/Apache-2.0.txt
RENAMED
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/licenses/BSD-3-Clause.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wt_compiler-0.2.0 → wt_compiler-0.3.0}/src/wt_compiler/wizard/templates/test-cases.yaml.jinja2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|