dhall-zuul
contains Dhall bindings to Zuul,
so you can generate zuul configuration from Dhall expressions.
Using the wrap
function, different objects can be combined in a single list:
-- ./examples/demo.dhall
let Zuul = ../package.dhall
let nodeset-name = "my-nodeset"
in Zuul.Nodeset.wrap
[ Zuul.Nodeset::{
, name = nodeset-name
, nodes = [ { name = "container", label = "my-label" } ]
}
]
# Zuul.Job.wrap
[ Zuul.Job::{
, name = "test"
, nodeset = Some (Zuul.Nodeset.Name nodeset-name)
, vars = Some (Zuul.Vars.mapBool (toMap { debug = True }))
}
, Zuul.Job::{
, name = "test-with-inlined-nodeset"
, nodeset = Zuul.Nodeset.mkSimpleInline "another-label"
}
, Zuul.Job::{ name = "publish" }
]
# dhall-to-yaml --file examples/demo.dhall
- nodeset:
name: my-nodeset
nodes:
- label: my-label
name: container
- job:
name: test
nodeset: my-nodeset
vars:
debug: true
- job:
name: test-with-inlined-nodeset
nodeset:
nodes:
- label: another-label
name: another-label
- job:
name: publish
-- ./examples/pipeline.dhall
let Zuul = ../package.dhall
let mqttReporter =
\(stage : Text) ->
Zuul.Pipeline.Reporter.mqtt
Zuul.Pipeline.Reporter.Mqtt::{
, topic = "zuul/{pipeline}/${stage}/{project}/{branch}"
}
let smtp-config =
{ from = Some "zuul@example.com"
, to = Some "root@localhost"
, subject = Some
"[Zuul] Job failed in periodic pipeline: {change.project}"
}
let periodic =
Zuul.Pipeline::{
, name = "periodic"
, manager = Zuul.Pipeline.independent
, precedence = Some Zuul.Pipeline.low
, post-review = Some True
, description = Some "Jobs in this queue are triggered daily"
, trigger = Some
( toMap
{ timer = Zuul.Pipeline.Trigger.timer [ { time = "0 0 * * *" } ] }
)
, start = Some (toMap { mqtt = mqttReporter "start" })
, success = Some
( toMap
{ sqlreporter = Zuul.Pipeline.Reporter.sql
, mqtt = mqttReporter "result"
}
)
, failure = Some
( toMap
{ sqlreporter = Zuul.Pipeline.Reporter.sql
, mqtt = mqttReporter "result"
, smtp = Zuul.Pipeline.Reporter.smtp smtp-config
}
)
}
let gate =
Zuul.Pipeline::{
, name = "gate"
, manager = Zuul.Pipeline.Manager.dependent
, require = Some
( toMap
{ `opendev.org` =
Zuul.Pipeline.Require.gerrit
Zuul.Pipeline.Require.Gerrit::{
, open = Some True
, approval = Some
[ Zuul.Pipeline.Require.Gerrit.Approval.username "zuul"
, Zuul.Pipeline.Require.Gerrit.Approval.vote "Verified" +1
]
}
}
)
}
in Zuul.Pipeline.wrap [ gate, periodic ]
# dhall-to-yaml --file examples/pipeline.dhall
- pipeline:
manager: dependent
name: gate
require:
opendev.org:
approval:
Verified: 1
username: zuul
open: true
- pipeline:
description: Jobs in this queue are triggered daily
failure:
mqtt:
topic: "zuul/{pipeline}/result/{project}/{branch}"
smtp:
from: "zuul@example.com"
subject: "[Zuul] Job failed in periodic pipeline: {change.project}"
to: "root@localhost"
sqlreporter: []
manager: independent
name: periodic
post-review: true
precedence: low
start:
mqtt:
topic: "zuul/{pipeline}/start/{project}/{branch}"
success:
mqtt:
topic: "zuul/{pipeline}/result/{project}/{branch}"
sqlreporter: []
trigger:
timer:
- time: "0 0 * * *"
-- ./examples/generate-jobs.dhall
let Zuul = ../package.dhall
let Job = Zuul.Job::{ name = "bench-job" }
let Jobs =
Zuul.Job.mapJob
(Zuul.Job.setParent "bench-job")
(Zuul.Job.replicate 3 Job)
in Zuul.Job.wrap ([ Job ] # Jobs)
# Zuul.Project.wrap
[ toMap
{ check =
Zuul.Project.mkSimplePipeline
(Zuul.Job.map Text Zuul.Job.getName Jobs)
}
]
# dhall-to-yaml --file examples/generate-jobs.dhall
- job:
name: bench-job
- job:
name: bench-job-1
parent: bench-job
- job:
name: bench-job-2
parent: bench-job
- job:
name: bench-job-3
parent: bench-job
- project:
check:
jobs:
- bench-job-1
- bench-job-2
- bench-job-3
The project
object admits arbitary attribute for pipeline name, and it is
defined as a Map that can be used like so:
-- ./examples/final.dhall
let Zuul = ../package.dhall
let gateTemplate =
toMap
{ name = Zuul.ProjectTemplate.Name "gate-jobs"
, gate =
Zuul.ProjectTemplate.Pipeline
(Zuul.Project.PipelineConfig.mkSimple [ "test", "publish" ])
}
let project =
toMap
{ name = Zuul.Project.Name "dhall-zuul"
, templates = Zuul.Project.Templates [ "gate-jobs" ]
, check =
Zuul.Project.Pipeline
(Zuul.Project.PipelineConfig.mkSimple [ "test", "publish" ])
}
in Zuul.Job.wrap
[ Zuul.Job::{ name = "test" }
, Zuul.Job::{
, name = "publish"
, dependencies = Some [ Zuul.Job.Dependency.Name "test" ]
}
]
# Zuul.ProjectTemplate.wrap [ gateTemplate ]
# Zuul.Project.wrap [ project ]
# dhall-to-yaml --file examples/final.dhall
- job:
name: test
- job:
dependencies:
- test
name: publish
- project-template:
gate:
jobs:
- test
- publish
name: gate-jobs
- project:
check:
jobs:
- test
- publish
name: dhall-zuul
templates:
- gate-jobs
Function can be used to generate complex job:
-- ./examples/ansible-zuul-jobs.dhall
{-|
[ansible-zuul-jobs](https://github.com/ansible/ansible-zuul-jobs/blob/master/zuul.d/jobs.yaml)
contains a lot of duplication and updates are error prone.
Here is how to use a couple of functions `mkVariant` and `mkNetwork` to
maintain the jobs definitions.
-}
let JSON =
https://prelude.dhall-lang.org/JSON/package.dhall sha256:79dfc281a05bc7b78f927e0da0c274ee5709b1c55c9e5f59499cb28e9d6f3ec0
let Zuul = ../package.dhall
let base =
[ Zuul.Job::{
, name = "base"
, parent = Some "base-minimal"
, abstract = Some True
, ansible-version = Some "2.9"
, description = Some
"The base job for the Ansible installation of Zuul."
, pre-run = Some [ "playbooks/base/pre.yaml" ]
}
, Zuul.Job::{
, name = "github-workflows"
, description = Some
"A job to validate no github workflow directory are found."
, run = Some "playbooks/github-workflows/run.yaml"
, nodeset = Some (Zuul.Nodeset.Inline Zuul.Nodeset.empty)
}
]
let --| A function to create simple zuul-jobs variant with `ansible-` prefix
mkVariant =
\(nodeset : Text) ->
\(parent-name : Text) ->
Zuul.Job::{
, name = "ansible-${parent-name}"
, parent = Some parent-name
, nodeset = Some (Zuul.Nodeset.Name nodeset)
}
let Nodeset =
{ centos8 = "centos-8-1vcpu"
, centos7 = "centos-7-1vcpu"
, fedora = "fedora-latest-1vcpu"
, xenial = "ubuntu-xenial-1vcpu"
, bionic = "ubuntu-bionic-1vcpu"
}
let toxs =
[ mkVariant Nodeset.centos8 "tox-docs"
, mkVariant Nodeset.centos8 "tox-linters"
, mkVariant Nodeset.centos8 "tox-docs"
, mkVariant Nodeset.centos7 "tox-py27"
, mkVariant Nodeset.xenial "tox-py35"
, mkVariant Nodeset.centos8 "tox-py36"
, mkVariant Nodeset.centos8 "tox-py37"
, mkVariant Nodeset.bionic "tox-py38"
]
let network-base =
Zuul.Job::{
, name = "ansible-network-appliance-base"
, pre-run = Some [ "playbooks/ansible-network-appliance-base/pre.yaml" ]
, post-run = Some [ "playbooks/ansible-network-appliance-base/post.yaml" ]
}
let --| A function to create a network job
mkNetwork =
\(name : Text) ->
\(host-var-name : Text) ->
\(nodeset-suffix : Text) ->
let host-vars =
Zuul.Vars.mapText
( toMap
{ ansible_connection = "network_cli"
, ansible_network_os = "asa"
, ansible_python_interpreter = "python"
}
)
in Zuul.Job::{
, name = "ansible-network-${name}-appliance"
, parent = Some (Zuul.Job.getName network-base)
, pre-run = Some
[ "playbooks/ansible/network-${name}-appliance/pre.yaml" ]
, run = Some "playbooks/ansible/network-${name}-appliance/run.yaml"
, host-vars = Some
( Zuul.Vars.object
[ { mapKey = host-var-name, mapValue = host-vars } ]
)
, nodeset = Some
(Zuul.Nodeset.Name "${host-var-name}-${nodeset-suffix}")
, required-projects = Some
[ { name = "github.com/ansible/ansible-zuul-jobs" } ]
}
let networks =
[ network-base
, mkNetwork "asa" "asav9-12-3" "python36"
, mkNetwork "eos" "eos-4.20.10" "python36"
, mkNetwork "ios" "ios-15.6-2T" "python36"
, mkNetwork "iosxr" "iosxr-6.1.3" "python36"
, mkNetwork "vsrx" "vsrx3-18.4R1" "python36"
, mkNetwork "vqfx" "vqfx-18.1R3" "python36"
, mkNetwork "nxos" "nxos-7.0.3" "python36"
, mkNetwork "openvswitch" "openvswitch-2.9.0" "python36"
]
in Zuul.Job.wrap (base # toxs # networks)
# dhall-to-yaml --file examples/ansible-zuul-jobs.dhall
- job:
abstract: true
ansible-version: '2.9'
description: The base job for the Ansible installation of Zuul.
name: base
parent: base-minimal
pre-run:
- playbooks/base/pre.yaml
- job:
description: A job to validate no github workflow directory are found.
name: github-workflows
nodeset:
nodes: []
run: playbooks/github-workflows/run.yaml
- job:
name: ansible-tox-docs
nodeset: centos-8-1vcpu
parent: tox-docs
- job:
name: ansible-tox-linters
nodeset: centos-8-1vcpu
parent: tox-linters
- job:
name: ansible-tox-docs
nodeset: centos-8-1vcpu
parent: tox-docs
- job:
name: ansible-tox-py27
nodeset: centos-7-1vcpu
parent: tox-py27
- job:
name: ansible-tox-py35
nodeset: ubuntu-xenial-1vcpu
parent: tox-py35
- job:
name: ansible-tox-py36
nodeset: centos-8-1vcpu
parent: tox-py36
- job:
name: ansible-tox-py37
nodeset: centos-8-1vcpu
parent: tox-py37
- job:
name: ansible-tox-py38
nodeset: ubuntu-bionic-1vcpu
parent: tox-py38
- job:
name: ansible-network-appliance-base
post-run:
- playbooks/ansible-network-appliance-base/post.yaml
pre-run:
- playbooks/ansible-network-appliance-base/pre.yaml
- job:
host-vars:
asav9-12-3:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-asa-appliance
nodeset: asav9-12-3-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-asa-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-asa-appliance/run.yaml
- job:
host-vars:
eos-4.20.10:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-eos-appliance
nodeset: eos-4.20.10-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-eos-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-eos-appliance/run.yaml
- job:
host-vars:
ios-15.6-2T:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-ios-appliance
nodeset: ios-15.6-2T-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-ios-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-ios-appliance/run.yaml
- job:
host-vars:
iosxr-6.1.3:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-iosxr-appliance
nodeset: iosxr-6.1.3-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-iosxr-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-iosxr-appliance/run.yaml
- job:
host-vars:
vsrx3-18.4R1:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-vsrx-appliance
nodeset: vsrx3-18.4R1-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-vsrx-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-vsrx-appliance/run.yaml
- job:
host-vars:
vqfx-18.1R3:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-vqfx-appliance
nodeset: vqfx-18.1R3-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-vqfx-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-vqfx-appliance/run.yaml
- job:
host-vars:
nxos-7.0.3:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-nxos-appliance
nodeset: nxos-7.0.3-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-nxos-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-nxos-appliance/run.yaml
- job:
host-vars:
openvswitch-2.9.0:
ansible_connection: network_cli
ansible_network_os: asa
ansible_python_interpreter: python
name: ansible-network-openvswitch-appliance
nodeset: openvswitch-2.9.0-python36
parent: ansible-network-appliance-base
pre-run:
- playbooks/ansible/network-openvswitch-appliance/pre.yaml
required-projects:
- name: github.com/ansible/ansible-zuul-jobs
run: playbooks/ansible/network-openvswitch-appliance/run.yaml
-- ./examples/tenant-config.dhall
let Zuul = ../package.dhall
let Project = Zuul.Tenant.Source.Project
let tenant =
Zuul.Tenant::{
, name = "ansible"
, max-nodes-per-job = Some 42
, source = toMap
{ gerrit = Zuul.Tenant.Source::{
, config-projects = Some
[ Project.Name "common-config"
, Project.WithOptions
Project::{ include = Some [ Project.ConfigurationItems.job ] }
"shared-jobs"
]
, untrusted-projects = Some
[ Project.WithOptions
Project::{ shadow = Some [ "comon-config" ] }
"zuul/zuul-jobs"
, Project.Name "project1"
, Project.WithOptions
Project::{ exclude-unprotected-branches = Some True }
"project2"
]
}
}
}
in Zuul.Tenant.wrap [ tenant ]
# dhall-to-yaml --file ./examples/tenant-config.dhall
- tenant:
max-nodes-per-job: 42
name: ansible
source:
gerrit:
config-projects:
- common-config
- shared-jobs:
include:
- job
untrusted-projects:
- zuul/zuul-jobs:
shadow:
- comon-config
- project1
- project2:
exclude-unprotected-branches: true
Frozen package are available in the tag commit.
- Update Job.Secret to support inline name
- Remove post and promote Gerrit Trigger
- Add Semaphore
- Add Job missing attributes
- App Pipeline requires, triggers and reporters
- Changed Job and Nodeset to require a name
- Changed Union type to be capitalized
- Add Ansible modules
- Move SourceProject and TenantOptions to the Tenant package
- Add Pipeline
- Add Semaphore
- Add Job.branches and override-checkout
- Add Secret and Job.secrets
- Initial release