summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <lwirzenius@wikimedia.org>2019-05-06 17:50:47 +0300
committerLars Wirzenius <lwirzenius@wikimedia.org>2019-05-06 17:50:47 +0300
commitd35d8bbed374e9d836f62737e56bda60ecb6b8e5 (patch)
tree026953f9fab1d63d0ff4fe4613259ac13ac6184e
parent1fb0a569da6cc199e5f34b1e23426d86a74489b2 (diff)
downloadwmf-ci-arch-d35d8bbed374e9d836f62737e56bda60ecb6b8e5.tar.gz
Change: add some figure, edit text
-rw-r--r--Makefile9
-rw-r--r--ci-arch.html34
-rw-r--r--ci-arch.mdwn56
-rw-r--r--ci-arch.pdfbin181344 -> 316475 bytes
-rw-r--r--ecosystem.dot16
-rw-r--r--ecosystem.svg100
-rw-r--r--pipeline.dot18
-rw-r--r--pipeline.svg92
8 files changed, 292 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 2d9cd7f..edaf7fa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,14 @@
-.SUFFIXES: .html .mdwn .pdf
+.SUFFIXES: .html .mdwn .pdf .dot .svg
all: ci-arch.html ci-arch.pdf
+ci-arch.pdf ci-arch.html: ci-arch.mdwn ecosystem.svg pipeline.svg
+
+.dot.svg:
+ dot -Tsvg -o$@ $<
+
.mdwn.html:
pandoc --standalone --toc -o $@ $<
.mdwn.pdf:
- pandoc -Vdocumentclass:report --toc -o $@ $<
+ pandoc -f markdown+implicit_figures -Vdocumentclass:report --toc -o $@ $<
diff --git a/ci-arch.html b/ci-arch.html
index c1b6369..5462577 100644
--- a/ci-arch.html
+++ b/ci-arch.html
@@ -39,8 +39,8 @@
<li><a href="#credentials-management-and-access-control">Credentials management and access control</a></li>
<li><a href="#interdependent-changes-to-multiple-components">Interdependent changes to multiple components</a></li>
</ul></li>
+<li><a href="#architecture-the-wmf-development-ecosystem">Architecture: The WMF development ecosystem</a></li>
<li><a href="#the-default-pipeline">The (default?) pipeline</a></li>
-<li><a href="#architecture-ci-in-an-ecosystem">Architecture: CI in an ecosystem</a></li>
<li><a href="#architecture-internals">Architecture: internals</a></li>
<li><a href="#acceptance-criteria">Acceptance criteria</a></li>
</ul>
@@ -51,6 +51,7 @@
<li><p>We aim to do “continuous deployment”, not only “continous integration” or “continuous delivery”. The goal is to deploy changes to production as often and as quickly as possible, without compromising on the safety and security of the production environment.</p></li>
<li><p>This document goes into more detail of how the new CI system should work, without (yet) discussing which replacement is chosen. A meta-level architecture if you wish.</p></li>
<li><p>It is assumed as of the writing of this document that future CI will build on and deploy to containers orchestrated by Kubernetes.</p></li>
+<li><p>An important change is that we aim to change things so that as much as possible, all software deployments are to containers orchestrated by Kubernetes</p></li>
</ul>
<h1 id="requirements">Requirements</h1>
<ul>
@@ -217,9 +218,27 @@
</ul>
<h2 id="interdependent-changes-to-multiple-components">Interdependent changes to multiple components</h2>
<p>FIXME</p>
+<h1 id="architecture-the-wmf-development-ecosystem">Architecture: The WMF development ecosystem</h1>
+<figure>
+<img src="ecosystem.svg" alt="The WMF development ecosystem, roughly" /><figcaption>The WMF development ecosystem, roughly</figcaption>
+</figure>
+<ul>
+<li>the above figure is simplistic, but gives the general idea
+<ul>
+<li>developer pushes a change to Gerrit</li>
+<li>CI builds and tests change (commit stage)</li>
+<li>CI deploys to a test environment, runs tests against that (acceptance test stage)</li>
+<li>CI can deploy to an environment dedicated for manual testing</li>
+<li>after successful code review, CI merges changes to the master branch, run all automated tests again, and deploys to the production environment</li>
+</ul></li>
+<li><p>the commit and acceptance stages are triggered as soon as developer pushes changes to be reviewed; human reviews won’t be requested until the two stages pass, as there’s no point in spending human attention on things that are not going to be candidates for deployment to production; they may be re-run after code review accepts the changes, to make sure nothing unforeseen has changed while review took place</p></li>
+<li><p>other stages may run in parallel with code review, but if they fail they may nullify candidacy? for example, stages for manual and capacity testing, and security test/review; depending on the change and the component in question, some or all of these may be necessary</p></li>
+</ul>
<h1 id="the-default-pipeline">The (default?) pipeline</h1>
+<figure>
+<img src="pipeline.svg" alt="The default pipeline" /><figcaption>The default pipeline</figcaption>
+</figure>
<ul>
-<li><p>FIXME: this could really do with a graph</p></li>
<li><p>CI will provide a default pipeline for all projects</p>
<ul>
<li><p>divided into several stages</p></li>
@@ -256,18 +275,11 @@
<li><p>this can also be used to demonstrate upcoming features that are not yet enabled in production</p></li>
</ul></li>
</ul>
-<h1 id="architecture-ci-in-an-ecosystem">Architecture: CI in an ecosystem</h1>
-<ul>
-<li><p>code review will be done in Gerrit or otherwise outside the CI pipeline</p></li>
-<li><p>the commit and acceptance stages are triggered as soon as developer pushes changes to be reviewed; human reviews won’t be requested until the two stages pass, as there’s no point in spending human attention on things that are not going to be candidates for deployment to production</p></li>
-<li><p>other stages may run in parallel with code review, but if they fail they may nullify candidacy?</p></li>
-<li><p>deployments go to K8s, everything will run in containers</p></li>
-</ul>
<h1 id="architecture-internals">Architecture: internals</h1>
-<p>FIXME</p>
+<p>FIXME This needs to be written, but it needs a lot of thinking first</p>
<h1 id="acceptance-criteria">Acceptance criteria</h1>
<ul>
-<li>This chapter sketches some automated acceptance tests using a Gherkin/Cucumber-like pseudo code language.</li>
+<li>FIXME: This chapter will sketch some automated acceptance tests using a Gherkin/Cucumber-like pseudo code language. Or in some other way that can be automatically executed.</li>
</ul>
</body>
</html>
diff --git a/ci-arch.mdwn b/ci-arch.mdwn
index 3bea641..631db06 100644
--- a/ci-arch.mdwn
+++ b/ci-arch.mdwn
@@ -46,6 +46,10 @@ abstract: |
* It is assumed as of the writing of this document that future CI will
build on and deploy to containers orchestrated by Kubernetes.
+* An important change is that we aim to change things so that as much
+ as possible, all software deployments are to containers
+ orchestrated by Kubernetes
+
# Requirements
* This chapter lists the requirements we have for the CI system and
@@ -494,9 +498,36 @@ we plan CI to implement them.
FIXME
+# Architecture: The WMF development ecosystem
+
+![The WMF development ecosystem, roughly](ecosystem.svg)
+
+* the above figure is simplistic, but gives the general idea
+ * developer pushes a change to Gerrit
+ * CI builds and tests change (commit stage)
+ * CI deploys to a test environment, runs tests against that
+ (acceptance test stage)
+ * CI can deploy to an environment dedicated for manual testing
+ * after successful code review, CI merges changes to the master
+ branch, run all automated tests again, and deploys to the
+ production environment
+
+* the commit and acceptance stages are triggered as soon as developer
+ pushes changes to be reviewed; human reviews won't be requested
+ until the two stages pass, as there's no point in spending human
+ attention on things that are not going to be candidates for
+ deployment to production; they may be re-run after code review
+ accepts the changes, to make sure nothing unforeseen has changed
+ while review took place
+
+* other stages may run in parallel with code review, but if they fail
+ they may nullify candidacy? for example, stages for manual and
+ capacity testing, and security test/review; depending on the change
+ and the component in question, some or all of these may be necessary
+
# The (default?) pipeline
-* FIXME: this could really do with a graph
+![The default pipeline](pipeline.svg)
* CI will provide a default pipeline for all projects
@@ -578,27 +609,12 @@ FIXME
* this can also be used to demonstrate upcoming features that are
not yet enabled in production
-# Architecture: CI in an ecosystem
-
-* code review will be done in Gerrit or otherwise outside the CI
- pipeline
-
-* the commit and acceptance stages are triggered as soon as developer
- pushes changes to be reviewed; human reviews won't be requested
- until the two stages pass, as there's no point in spending human
- attention on things that are not going to be candidates for
- deployment to production
-
-* other stages may run in parallel with code review, but if they fail
- they may nullify candidacy?
-
-* deployments go to K8s, everything will run in containers
-
# Architecture: internals
-FIXME
+FIXME This needs to be written, but it needs a lot of thinking first
# Acceptance criteria
-* This chapter sketches some automated acceptance tests using a
- Gherkin/Cucumber-like pseudo code language.
+* FIXME: This chapter will sketch some automated acceptance tests
+ using a Gherkin/Cucumber-like pseudo code language. Or in some other
+ way that can be automatically executed.
diff --git a/ci-arch.pdf b/ci-arch.pdf
index 26e4f58..b4b41b9 100644
--- a/ci-arch.pdf
+++ b/ci-arch.pdf
Binary files differ
diff --git a/ecosystem.dot b/ecosystem.dot
new file mode 100644
index 0000000..03f86b0
--- /dev/null
+++ b/ecosystem.dot
@@ -0,0 +1,16 @@
+digraph ecosystem {
+ developer [shape="circle" label="developer"];
+ gerrit [shape="tab" label="Gerrit\ncode review"];
+ CI [shape="tab" label="CI system"];
+ autotestenv [shape="component" label="env for automated tests"];
+ mantestenv [shape="component" label="env for manual testing"];
+ prodenv [shape="component" label="production"];
+
+ developer -> gerrit;
+ gerrit -> developer;
+ gerrit -> CI;
+ CI -> gerrit;
+ CI -> autotestenv;
+ CI -> mantestenv
+ CI -> prodenv;
+}
diff --git a/ecosystem.svg b/ecosystem.svg
new file mode 100644
index 0000000..f47bb66
--- /dev/null
+++ b/ecosystem.svg
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: ecosystem Pages: 1 -->
+<svg width="404pt" height="317pt"
+ viewBox="0.00 0.00 403.50 316.99" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 312.9919)">
+<title>ecosystem</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-312.9919 399.5,-312.9919 399.5,4 -4,4"/>
+<!-- developer -->
+<g id="node1" class="node">
+<title>developer</title>
+<ellipse fill="none" stroke="#000000" cx="232" cy="-263.496" rx="45.4919" ry="45.4919"/>
+<text text-anchor="middle" x="232" y="-259.796" font-family="Times,serif" font-size="14.00" fill="#000000">developer</text>
+</g>
+<!-- gerrit -->
+<g id="node2" class="node">
+<title>gerrit</title>
+<polygon fill="none" stroke="#000000" points="273.5,-182 202.5,-182 202.5,-186 190.5,-186 190.5,-144 273.5,-144 273.5,-182"/>
+<polyline fill="none" stroke="#000000" points="190.5,-182 202.5,-182 "/>
+<text text-anchor="middle" x="232" y="-166.8" font-family="Times,serif" font-size="14.00" fill="#000000">Gerrit</text>
+<text text-anchor="middle" x="232" y="-151.8" font-family="Times,serif" font-size="14.00" fill="#000000">code review</text>
+</g>
+<!-- developer&#45;&gt;gerrit -->
+<g id="edge1" class="edge">
+<title>developer&#45;&gt;gerrit</title>
+<path fill="none" stroke="#000000" d="M225.2798,-218.2458C225.1742,-209.407 225.3495,-200.3599 225.8058,-192.2131"/>
+<polygon fill="#000000" stroke="#000000" points="229.3054,-192.35 226.5675,-182.115 222.3252,-191.8234 229.3054,-192.35"/>
+</g>
+<!-- gerrit&#45;&gt;developer -->
+<g id="edge2" class="edge">
+<title>gerrit&#45;&gt;developer</title>
+<path fill="none" stroke="#000000" d="M237.4325,-182.115C238.1745,-189.6614 238.6031,-198.754 238.7182,-208.0866"/>
+<polygon fill="#000000" stroke="#000000" points="235.2182,-208.2465 238.7202,-218.2458 242.2182,-208.2451 235.2182,-208.2465"/>
+</g>
+<!-- CI -->
+<g id="node3" class="node">
+<title>CI</title>
+<polygon fill="none" stroke="#000000" points="268,-108 208,-108 208,-112 196,-112 196,-72 268,-72 268,-108"/>
+<polyline fill="none" stroke="#000000" points="196,-108 208,-108 "/>
+<text text-anchor="middle" x="232" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">CI system</text>
+</g>
+<!-- gerrit&#45;&gt;CI -->
+<g id="edge3" class="edge">
+<title>gerrit&#45;&gt;CI</title>
+<path fill="none" stroke="#000000" d="M225.9767,-143.8201C225.2688,-136.0117 225.0781,-126.8265 225.4046,-118.2764"/>
+<polygon fill="#000000" stroke="#000000" points="228.8971,-118.5051 226.0595,-108.2973 221.9121,-118.0466 228.8971,-118.5051"/>
+</g>
+<!-- CI&#45;&gt;gerrit -->
+<g id="edge4" class="edge">
+<title>CI&#45;&gt;gerrit</title>
+<path fill="none" stroke="#000000" d="M237.9405,-108.2973C238.6877,-115.9984 238.9176,-125.1526 238.6303,-133.748"/>
+<polygon fill="#000000" stroke="#000000" points="235.1313,-133.6276 238.0233,-143.8201 242.1186,-134.0488 235.1313,-133.6276"/>
+</g>
+<!-- autotestenv -->
+<g id="node4" class="node">
+<title>autotestenv</title>
+<polygon fill="none" stroke="#000000" points="144,-36 0,-36 0,-32 -4,-32 -4,-28 0,-28 0,-8 -4,-8 -4,-4 0,-4 0,0 144,0 144,-36"/>
+<polyline fill="none" stroke="#000000" points="0,-32 4,-32 4,-28 0,-28 "/>
+<polyline fill="none" stroke="#000000" points="0,-8 4,-8 4,-4 0,-4 "/>
+<text text-anchor="middle" x="72" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">env for automated tests</text>
+</g>
+<!-- CI&#45;&gt;autotestenv -->
+<g id="edge5" class="edge">
+<title>CI&#45;&gt;autotestenv</title>
+<path fill="none" stroke="#000000" d="M195.6935,-73.6621C173.7166,-63.7725 145.3938,-51.0272 121.3968,-40.2286"/>
+<polygon fill="#000000" stroke="#000000" points="122.6496,-36.9543 112.0941,-36.0423 119.777,-43.3378 122.6496,-36.9543"/>
+</g>
+<!-- mantestenv -->
+<g id="node5" class="node">
+<title>mantestenv</title>
+<polygon fill="none" stroke="#000000" points="302,-36 162,-36 162,-32 158,-32 158,-28 162,-28 162,-8 158,-8 158,-4 162,-4 162,0 302,0 302,-36"/>
+<polyline fill="none" stroke="#000000" points="162,-32 166,-32 166,-28 162,-28 "/>
+<polyline fill="none" stroke="#000000" points="162,-8 166,-8 166,-4 162,-4 "/>
+<text text-anchor="middle" x="232" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">env for manual testing</text>
+</g>
+<!-- CI&#45;&gt;mantestenv -->
+<g id="edge6" class="edge">
+<title>CI&#45;&gt;mantestenv</title>
+<path fill="none" stroke="#000000" d="M232,-71.8314C232,-64.131 232,-54.9743 232,-46.4166"/>
+<polygon fill="#000000" stroke="#000000" points="235.5001,-46.4132 232,-36.4133 228.5001,-46.4133 235.5001,-46.4132"/>
+</g>
+<!-- prodenv -->
+<g id="node6" class="node">
+<title>prodenv</title>
+<polygon fill="none" stroke="#000000" points="395.5,-36 320.5,-36 320.5,-32 316.5,-32 316.5,-28 320.5,-28 320.5,-8 316.5,-8 316.5,-4 320.5,-4 320.5,0 395.5,0 395.5,-36"/>
+<polyline fill="none" stroke="#000000" points="320.5,-32 324.5,-32 324.5,-28 320.5,-28 "/>
+<polyline fill="none" stroke="#000000" points="320.5,-8 324.5,-8 324.5,-4 320.5,-4 "/>
+<text text-anchor="middle" x="358" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">production</text>
+</g>
+<!-- CI&#45;&gt;prodenv -->
+<g id="edge7" class="edge">
+<title>CI&#45;&gt;prodenv</title>
+<path fill="none" stroke="#000000" d="M263.7951,-71.8314C280.0605,-62.5368 300.0386,-51.1208 317.4051,-41.1971"/>
+<polygon fill="#000000" stroke="#000000" points="319.2691,-44.1631 326.2151,-36.1628 315.7961,-38.0854 319.2691,-44.1631"/>
+</g>
+</g>
+</svg>
diff --git a/pipeline.dot b/pipeline.dot
new file mode 100644
index 0000000..0c56148
--- /dev/null
+++ b/pipeline.dot
@@ -0,0 +1,18 @@
+digraph pipeline {
+ developer [shape="circle" label="developer"];
+ commit [shape="rect" label="Commit stage"];
+ acceptance [shape="rect" label="Acceptance stage"];
+ artifacts [shape="cylinder" label="Artifact store"];
+ testenv [shape="folder" label="A test environment"];
+ production [shape="folder" label="Production environment"];
+
+ developer -> commit;
+ commit -> artifacts;
+
+ commit -> acceptance;
+ acceptance -> testenv;
+ artifacts -> testenv;
+
+ acceptance -> production;
+ artifacts -> production;
+}
diff --git a/pipeline.svg b/pipeline.svg
new file mode 100644
index 0000000..7992246
--- /dev/null
+++ b/pipeline.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: pipeline Pages: 1 -->
+<svg width="298pt" height="315pt"
+ viewBox="0.00 0.00 297.50 314.99" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 310.9919)">
+<title>pipeline</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-310.9919 293.5,-310.9919 293.5,4 -4,4"/>
+<!-- developer -->
+<g id="node1" class="node">
+<title>developer</title>
+<ellipse fill="none" stroke="#000000" cx="138" cy="-261.496" rx="45.4919" ry="45.4919"/>
+<text text-anchor="middle" x="138" y="-257.796" font-family="Times,serif" font-size="14.00" fill="#000000">developer</text>
+</g>
+<!-- commit -->
+<g id="node2" class="node">
+<title>commit</title>
+<polygon fill="none" stroke="#000000" points="185,-180 91,-180 91,-144 185,-144 185,-180"/>
+<text text-anchor="middle" x="138" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">Commit stage</text>
+</g>
+<!-- developer&#45;&gt;commit -->
+<g id="edge1" class="edge">
+<title>developer&#45;&gt;commit</title>
+<path fill="none" stroke="#000000" d="M138,-215.8248C138,-207.1147 138,-198.2368 138,-190.2688"/>
+<polygon fill="#000000" stroke="#000000" points="141.5001,-190.0684 138,-180.0684 134.5001,-190.0685 141.5001,-190.0684"/>
+</g>
+<!-- acceptance -->
+<g id="node3" class="node">
+<title>acceptance</title>
+<polygon fill="none" stroke="#000000" points="134.5,-108 23.5,-108 23.5,-72 134.5,-72 134.5,-108"/>
+<text text-anchor="middle" x="79" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">Acceptance stage</text>
+</g>
+<!-- commit&#45;&gt;acceptance -->
+<g id="edge3" class="edge">
+<title>commit&#45;&gt;acceptance</title>
+<path fill="none" stroke="#000000" d="M123.1118,-143.8314C116.247,-135.454 107.9699,-125.3531 100.4489,-116.1749"/>
+<polygon fill="#000000" stroke="#000000" points="103.1341,-113.9297 94.0886,-108.4133 97.7197,-118.3665 103.1341,-113.9297"/>
+</g>
+<!-- artifacts -->
+<g id="node4" class="node">
+<title>artifacts</title>
+<path fill="none" stroke="#000000" d="M241.5,-104.7273C241.5,-106.5331 221.5544,-108 197,-108 172.4456,-108 152.5,-106.5331 152.5,-104.7273 152.5,-104.7273 152.5,-75.2727 152.5,-75.2727 152.5,-73.4669 172.4456,-72 197,-72 221.5544,-72 241.5,-73.4669 241.5,-75.2727 241.5,-75.2727 241.5,-104.7273 241.5,-104.7273"/>
+<path fill="none" stroke="#000000" d="M241.5,-104.7273C241.5,-102.9214 221.5544,-101.4545 197,-101.4545 172.4456,-101.4545 152.5,-102.9214 152.5,-104.7273"/>
+<text text-anchor="middle" x="197" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">Artifact store</text>
+</g>
+<!-- commit&#45;&gt;artifacts -->
+<g id="edge2" class="edge">
+<title>commit&#45;&gt;artifacts</title>
+<path fill="none" stroke="#000000" d="M152.8882,-143.8314C159.753,-135.454 168.0301,-125.3531 175.5511,-116.1749"/>
+<polygon fill="#000000" stroke="#000000" points="178.2803,-118.3665 181.9114,-108.4133 172.8659,-113.9297 178.2803,-118.3665"/>
+</g>
+<!-- testenv -->
+<g id="node5" class="node">
+<title>testenv</title>
+<polygon fill="none" stroke="#000000" points="122,-36 119,-40 98,-40 95,-36 0,-36 0,0 122,0 122,-36"/>
+<text text-anchor="middle" x="61" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">A test environment</text>
+</g>
+<!-- acceptance&#45;&gt;testenv -->
+<g id="edge4" class="edge">
+<title>acceptance&#45;&gt;testenv</title>
+<path fill="none" stroke="#000000" d="M74.4578,-71.8314C72.5116,-64.0463 70.1932,-54.7729 68.0337,-46.1347"/>
+<polygon fill="#000000" stroke="#000000" points="71.4242,-45.2658 65.6033,-36.4133 64.6332,-46.9636 71.4242,-45.2658"/>
+</g>
+<!-- production -->
+<g id="node6" class="node">
+<title>production</title>
+<polygon fill="none" stroke="#000000" points="289.5,-36 286.5,-40 265.5,-40 262.5,-36 140.5,-36 140.5,0 289.5,0 289.5,-36"/>
+<text text-anchor="middle" x="215" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">Production environment</text>
+</g>
+<!-- acceptance&#45;&gt;production -->
+<g id="edge6" class="edge">
+<title>acceptance&#45;&gt;production</title>
+<path fill="none" stroke="#000000" d="M113.3185,-71.8314C131.0359,-62.4516 152.8345,-50.9112 171.6984,-40.9244"/>
+<polygon fill="#000000" stroke="#000000" points="173.4922,-43.935 180.6924,-36.1628 170.2169,-37.7485 173.4922,-43.935"/>
+</g>
+<!-- artifacts&#45;&gt;testenv -->
+<g id="edge5" class="edge">
+<title>artifacts&#45;&gt;testenv</title>
+<path fill="none" stroke="#000000" d="M164.0782,-72.5708C146.0262,-63.0139 123.4323,-51.0524 104.0019,-40.7657"/>
+<polygon fill="#000000" stroke="#000000" points="105.5144,-37.6063 95.0389,-36.0206 102.2392,-43.7928 105.5144,-37.6063"/>
+</g>
+<!-- artifacts&#45;&gt;production -->
+<g id="edge7" class="edge">
+<title>artifacts&#45;&gt;production</title>
+<path fill="none" stroke="#000000" d="M201.5422,-71.8314C203.4884,-64.0463 205.8068,-54.7729 207.9663,-46.1347"/>
+<polygon fill="#000000" stroke="#000000" points="211.3668,-46.9636 210.3967,-36.4133 204.5758,-45.2658 211.3668,-46.9636"/>
+</g>
+</g>
+</svg>