Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/_tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:
description: What to run under tox
required: true


jobs:
run:
runs-on: "ubuntu-latest"
Expand All @@ -18,5 +17,12 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install Argo
run: |
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v4.0.6/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
sudo mv argo-linux-amd64 /usr/local/bin/argo

- name: Run tox
run: uv run --locked tox -e ${{ inputs.tox }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,6 @@ lockfiles/

# ruff cache
.ruff_cache/

# environment variables file
*.env
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ repos:
types: [python]
require_serial: true

- id: create-workflow-templates
Comment thread
Matt-Carre marked this conversation as resolved.
name: creates workflow templates
language: system
entry: bash scripts/runthefiles.sh
types: [python]
pass_filenames: true
require_serial: true

- id: lint-yaml-files
name: run argo lint on the yaml files
language: system
entry: bash scripts/lintyaml.sh
types: [yaml]
pass_filenames: true
require_serial: true

- id: confirm-diamond-compatability
name: ensure yaml is diamond-compatible
language: system
entry: uv run scripts/checkyamlcompliance.py
types: [yaml]
pass_filenames: true
require_serial: true

- id: ruff-format
name: format with ruff
language: system
Expand Down
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ RUN gunzip "argo-linux-amd64.gz"
RUN chmod +x "argo-linux-amd64"
RUN mv "./argo-linux-amd64" /usr/local/bin/argo

#Install Kubectl
RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
RUN chmod +x ./kubectl
RUN mv ./kubectl /usr/local/bin

# The build stage installs the context into the venv
FROM developer AS build

Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ description = " Python alternative to creating and running argo workflows in the
dependencies = [
"gql",
"aiohttp",
"hera",
"requests",
"PyYAML",
] # Add project dependencies here, e.g. ["click", "numpy"]
dynamic = ["version"]
license.file = "LICENSE"
Expand All @@ -32,6 +35,7 @@ dev = [
"ruff",
"tox-uv",
"types-mock",
"PyYAML",
]

[project.scripts]
Expand Down
81 changes: 81 additions & 0 deletions scripts/checkyamlcompliance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/python
import os
import re

import yaml

yamlpath = "src/python_interface_to_workflows/templates/"
yamllist: list[str] = []
for x in os.listdir(yamlpath):
if x.endswith(".yaml"):
yamllist.append(x)

for file in yamllist:
metadata = api_ver = clst_tmpt = title = repo = group = annotations = labels = False
with open(yamlpath + file) as s:
yamldata = yaml.load(s, Loader=yaml.FullLoader)
if "apiVersion" in yamldata.keys():
match yamldata["apiVersion"]:
case "argoproj.io/v1alpha1":
api_ver = True
case _:
api_ver = False
else:
api_ver = False
if "kind" in yamldata.keys():
match yamldata["kind"]:
case "Workflow":
clst_tmpt = True
case _:
clst_tmpt = False
else:
clst_tmpt = False
if "metadata" in yamldata.keys():
metadata = True
gen_name = "generateName" in yamldata["metadata"].keys()
normal_name = "name" in yamldata["metadata"].keys()
if "annotations" in yamldata["metadata"].keys():
annotations = True
title = (
"workflows.argoproj.io/title"
in yamldata["metadata"]["annotations"].keys()
)
repo = (
"workflows.diamond.ac.uk/repository"
in yamldata["metadata"]["annotations"].keys()
)
else:
annotations = title = repo = False
if "labels" in yamldata["metadata"].keys():
labels = True
if yamldata["metadata"]["labels"]:
group = any(
re.match(r"workflows.diamond.ac.uk/science-group-.+", d)
for d in yamldata["metadata"]["labels"].keys()
)
else:
group = False
else:
group = labels = False
else:
metadata = normal_name = gen_name = False
if all([api_ver, clst_tmpt, title, repo, group, annotations, labels]):
if gen_name != normal_name:
exit(0)
else:
print(
"generated_name and normal_name error, must have one of these not both"
)
exit(1)
else:
print(f"""
within file: {file}...
metadata present?: {metadata}
annotations present?: {annotations}
labels present?: {labels}
api_ver present?: {api_ver}
cluster template present?: {clst_tmpt}
title present?: {title}
repository present?: {repo}
group present?: {group}""")
Comment thread
Matt-Carre marked this conversation as resolved.
exit(1)
9 changes: 9 additions & 0 deletions scripts/lintyaml.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
cd src/python_interface_to_workflows/templates
for file in *
do
argo lint "$file" --offline
SUCCESSFULLINT=$?
[ $SUCCESSFULLINT -ne 0 ] && exit 1
done
exit 0
8 changes: 8 additions & 0 deletions scripts/runthefiles.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
cd src/python_interface_to_workflows/workflow_definitions
for file in *
do
uv run "$file"
done
mv *.yaml ../templates/
git add -u ../templates/
8 changes: 4 additions & 4 deletions src/python_interface_to_workflows/submit_to_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def submit_to_graphql():
proposalNumber: 10000,
number: 3
}
parameters: {
numinput: "19",
numdivisor: "10"
}
parameters: {
numinput: "19",
numdivisor: "10"
}
){
name
}
Expand Down
60 changes: 60 additions & 0 deletions src/python_interface_to_workflows/templates/divisionyaml.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hera-division-
annotations:
workflows.argoproj.io/description: |-
Takes a numerical input and returns the
remainder, output float, and output string to a json file
workflows.argoproj.io/title: Division via hera test
workflows.diamond.ac.uk/repository: https://github.com/DiamondLightSource/python-interface-to-workflows
labels:
workflows.diamond.ac.uk/science-group-examples: 'true'
spec:
entrypoint: divide
templates:
- name: divide
steps:
- - name: first
template: do-division
arguments:
parameters:
- name: a
value: '2'
- name: b
value: '5'
- name: do-division
inputs:
parameters:
- name: a
- name: b
outputs:
artifacts:
- name: json-output
path: /output-dir/output.json
script:
image: None
source: |-
import os
import sys
sys.path.append(os.getcwd())
import json
try: a = json.loads(r'''{{inputs.parameters.a}}''')
except: a = r'''{{inputs.parameters.a}}'''
try: b = json.loads(r'''{{inputs.parameters.b}}''')
except: b = r'''{{inputs.parameters.b}}'''

div = a / b
intdiv = a // b
remain = a % b
dictionary_of_results = {'divide': div, 'quotient': intdiv, 'remainder': remain}
with open('/output-dir/output.json', 'w') as otpt:
json.dump(dictionary_of_results, otpt)
command:
- python
volumeMounts:
- name: output-dir
mountPath: /output-dir/
volumes:
- name: output-dir
emptyDir: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import json
import os

from hera.shared import global_config
from hera.workflows import (
Artifact,
EmptyDirVolume,
Steps,
Workflow,
script, # pyright: ignore[reportUnknownVariableType]
)
from hera.workflows import models as m

global_config.host = os.environ.get("HOST")
global_config.image = str(os.environ.get("IMAGE"))
global_config.token = os.environ.get("TOKEN")


@script(
volume_mounts=[m.VolumeMount(name="output-dir", mount_path="/output-dir/")],
Comment thread
Matt-Carre marked this conversation as resolved.
outputs=Artifact(name="json-output", path="/output-dir/output.json"),
)
def do_division(a: int, b: int):
div = a / b
intdiv = a // b
remain = a % b
dictionary_of_results = {
"divide": div,
"quotient": intdiv,
"remainder": remain,
}
with open("/output-dir/output.json", "w") as otpt:
json.dump(dictionary_of_results, otpt)


with Workflow(
generate_name="hera-division-", # when running on graphql this should be name
entrypoint="divide",
namespace=os.environ.get("NAMESPACE"),
api_version="argoproj.io/v1alpha1",
kind="Workflow", # ClusterWorkflowTemplate", when on graphql
labels={"workflows.diamond.ac.uk/science-group-examples": "true"},
annotations={
"workflows.argoproj.io/title": "Division via hera test",
"workflows.argoproj.io/description": """Takes a numerical input and returns the
remainder, output float, and output string to a json file""",
"workflows.diamond.ac.uk/repository": "https://github.com/DiamondLightSource/python-interface-to-workflows",
},
volumes=EmptyDirVolume(name="output-dir", mount_path="/output-dir"),
) as w:
with Steps(name="divide"):
do_division(name="first", arguments={"a": 2, "b": 5})


with open("divisionyaml.yaml", "w") as div:
div.write(w.to_yaml()) # pyright: ignore[reportUnknownMemberType]


# w.create()
Loading
Loading