delivery arc wired: pixel-derived slice manifests, multi-scene scrub engine, arc page + assembler
This commit is contained in:
@@ -0,0 +1,432 @@
|
||||
<header class="masthead">
|
||||
<p class="eyebrow">The Exploded Cluster · The Delivery Arc</p>
|
||||
<h1>The machine was the easy part.<br><em>Now watch how software reaches it.</em></h1>
|
||||
<p class="lede">Three courses on the toolchain around the cluster: how change travels, how images
|
||||
are named, and how one definition serves a fleet. Scroll, and each machine comes apart.</p>
|
||||
<p class="scrollcue" aria-hidden="true">scroll<span></span></p>
|
||||
</header>
|
||||
|
||||
<!-- ============ COURSE VI ============ -->
|
||||
<section class="course" id="c6" data-scene="gitops" aria-labelledby="c6h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course VI · GitOps</p>
|
||||
<h2 id="c6h" class="thesis">Nobody deploys anything.<br><em>The cluster syncs itself.</em></h2>
|
||||
<p class="lede">The mental model everyone arrives with: someone with credentials pushes manifests
|
||||
at the cluster. In GitOps nothing is pushed. A repository holds the desired state, an agent
|
||||
inside the cluster watches it, and the cluster pulls its own future from git. kubectl is for
|
||||
looking; git is for changing.</p>
|
||||
</header>
|
||||
<div class="pin">
|
||||
<div class="viewport">
|
||||
<figure class="scene">
|
||||
<img class="flat" src="__HERO_GITOPS__"
|
||||
alt="An exploded chain: an etched repository crystal, a twin-ring reconciler engine,
|
||||
a stack of rendered manifests and a cluster slab - with a drift shard falling
|
||||
away and an armoured secret vault floating deliberately apart.">
|
||||
</figure>
|
||||
<ol class="legend"></ol>
|
||||
</div>
|
||||
<div class="bench"></div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>The loop you already know, one level up</h3>
|
||||
<p>Course II taught the reconciliation loop: desired versus actual, fix the difference, repeat.
|
||||
ArgoCD is the same habit applied to <b>delivery</b>. An Application names a repo, a path and a
|
||||
revision - watch this branch of this repository - and the controller renders what it finds
|
||||
there, compares it against the live cluster, and syncs the difference. The deploy button is a
|
||||
git commit; the audit log is git log; code review <i>is</i> change control.</p>
|
||||
<p class="sayit">kubectl apply is for archaeology, not for change.</p>
|
||||
<h3>Pull, not push - the security inversion</h3>
|
||||
<p>The cluster <b>pulls</b>. No CI system, no laptop, no build pipeline holds a credential that
|
||||
can touch it - the agent inside holds a read-only deploy key, and the trust arrow points out.
|
||||
Compromise the build system and you can propose a change, which is visible; you cannot reach
|
||||
into production. Hand-edit a live object and the controller flags it OutOfSync - selfHeal puts
|
||||
it back, and what leaves git leaves the cluster. Rollback is <span class="mono">git revert</span>,
|
||||
which is why commit hygiene is an operational skill.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> Fighting the reconciler over SSH is arm-wrestling a
|
||||
machine that does not get tired - Course II's warning, now with a face. The fix is always
|
||||
upstream, in git.</p>
|
||||
<h3>The one thing git never holds</h3>
|
||||
<p>Git holds everything except secrets - a secret in git is published, forever, to everyone who
|
||||
ever clones. So the pattern splits the reference from the value: git carries an ExternalSecret
|
||||
naming a <b>logical key</b>; a vault - Azure Key Vault in the worked example - holds the value;
|
||||
an operator inside the cluster exchanges one for the other at runtime. Rotation happens in the
|
||||
vault, never as a commit. That is why the vault floats apart in the scene above: it is never
|
||||
absorbed into the pipeline.</p>
|
||||
<p class="sayit">Git holds the shape of the secret. The vault holds the secret.</p>
|
||||
<p class="closeline">Delivery stops being an event and becomes a property: the cluster is always
|
||||
converging on what the repository says. "Who deployed this?" becomes "who merged this?" - and
|
||||
that question always has an answer.</p>
|
||||
<p class="reads"><b>Pre-reads:</b> Course II ·
|
||||
<a href="https://kubernetes.io/docs/concepts/">Kubernetes concepts</a> ·
|
||||
<a href="https://docs.github.com/en/get-started">git + pull requests</a>
|
||||
 <b>Further:</b> <a href="https://argo-cd.readthedocs.io/">Argo CD</a> ·
|
||||
<a href="https://docs.redhat.com/en/documentation/openshift_container_platform">OpenShift GitOps</a> ·
|
||||
<a href="https://external-secrets.io/">External Secrets</a> ·
|
||||
<a href="https://learn.microsoft.com/azure/key-vault/">Azure Key Vault</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ COURSE VII ============ -->
|
||||
<section class="course" id="c7" data-scene="supply" aria-labelledby="c7h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course VII · The image supply chain</p>
|
||||
<h2 id="c7h" class="thesis">A tag is a promise.<br><em>A digest is a fact.</em></h2>
|
||||
<p class="lede"><span class="mono">myapp:latest</span> feels like a name. It is a sticky note - a
|
||||
mutable pointer anyone with push rights can peel off one image and press onto another, and
|
||||
nothing anywhere records that it moved. The digest is the image's actual name: same bytes,
|
||||
same digest, forever.</p>
|
||||
</header>
|
||||
<div class="pin">
|
||||
<div class="viewport">
|
||||
<figure class="scene">
|
||||
<img class="flat" src="__HERO_SUPPLY__"
|
||||
alt="The image journey: a layered image stack, an upstream registry tower, a squat
|
||||
pull-through mirror and a node core - beneath a ghost tag plate and an engraved
|
||||
digest seal floating side by side.">
|
||||
</figure>
|
||||
<ol class="legend"></ol>
|
||||
</div>
|
||||
<div class="bench"></div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>Say the name properly</h3>
|
||||
<p>Three ways to name an image, in rising order of honesty: <span class="mono">:latest</span>
|
||||
(a moving target), <span class="mono">:1.4.2</span> (a promise somebody keeps, until they
|
||||
re-push it), and <span class="mono">name:1.4.2@sha256:...</span> - a fact. The tag stays for
|
||||
human eyes; the digest does the pulling. Pin by digest and "what is running?" has exactly one
|
||||
answer.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> <span class="mono">:latest</span> is how two nodes run
|
||||
different code from one manifest - the second node pulled an hour later, after a re-push.
|
||||
Nobody changed the YAML.</p>
|
||||
<h3>Why a fleet pulls once</h3>
|
||||
<p>Between the build and the node sits the registry chain. Upstream, a managed registry - Azure
|
||||
Container Registry in the worked example - holds what CI built. In front of the cluster sits a
|
||||
<b>mirror</b>: a pull-through cache like zot. The fleet asks the mirror, the mirror asks
|
||||
upstream once, everything after is local. Rate limits, egress cost, disconnected sites,
|
||||
control - one place to gate and audit what enters. OpenShift formalises the re-route with
|
||||
image mirror rules, and carries a sharp edge: digest-mirror rules rewrite <b>digest pulls
|
||||
only</b>, so a by-tag pull silently skips the mirror. The pinning habit is what makes the
|
||||
mirror actually catch the traffic.</p>
|
||||
<h3>Build once, promote by copy</h3>
|
||||
<p>Every rebuild is a different artefact - different digest, untested by the stages before it.
|
||||
So build <b>once</b>, then promote the same digest through environments by copying, registry
|
||||
to registry - dev proves the exact bytes prod will run. Human tags ride along; the digest is
|
||||
the through-line.</p>
|
||||
<p class="sayit">If the digest changed, it is not a promotion - it is a new candidate.</p>
|
||||
<p class="closeline">Names that can move are convenient exactly until they move. Address content
|
||||
by what it is, and the supply chain stops being a chain of trust and becomes a chain of
|
||||
arithmetic.</p>
|
||||
<p class="reads"><b>Pre-reads:</b> Course I ·
|
||||
<a href="https://kubernetes.io/docs/concepts/containers/images/">Kubernetes images</a>
|
||||
 <b>Further:</b> <a href="https://zotregistry.dev/">zot</a> ·
|
||||
<a href="https://learn.microsoft.com/azure/container-registry/">Azure Container Registry</a> ·
|
||||
<a href="https://docs.redhat.com/en/documentation/openshift_container_platform">OpenShift image mirroring</a> ·
|
||||
<a href="https://github.com/containers/skopeo">skopeo</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ COURSE VIII ============ -->
|
||||
<section class="course" id="c8" data-scene="helm" aria-labelledby="c8h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course VIII · Helm</p>
|
||||
<h2 id="c8h" class="thesis">A chart is a function.<br><em>The values file is the cluster speaking.</em></h2>
|
||||
<p class="lede">The default way to run one app on five clusters is five copies of the YAML, and
|
||||
the default result is five slightly different apps. The inversion: stop copying outputs and
|
||||
ship the function. A chart is a template with holes; each cluster supplies one small values
|
||||
file that fills them.</p>
|
||||
</header>
|
||||
<div class="pin">
|
||||
<div class="viewport">
|
||||
<figure class="scene">
|
||||
<img class="flat" src="__HERO_HELM__"
|
||||
alt="The Helm press: an engraved chart plate with empty sockets, four values crystals
|
||||
feeding in, three rendered sheets fanned out in different hues, and a schema gate
|
||||
wedge with a rejected grey sheet stopped behind it.">
|
||||
</figure>
|
||||
<ol class="legend"></ol>
|
||||
</div>
|
||||
<div class="bench"></div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>It's Go under the braces</h3>
|
||||
<p>Helm templates are Go text/template: <span class="mono">{{ .Values.device.address }}</span>
|
||||
is a pipeline walking a values object, <span class="mono">_helpers.tpl</span> holds the named
|
||||
functions every manifest calls. You are not writing YAML - you are writing a program whose
|
||||
output is YAML. So render locally, read the output, and lint what came out, not what went in.</p>
|
||||
<p class="sayit">Review the render, not just the template.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> ArgoCD deploys charts by running
|
||||
<span class="mono">helm template</span> - a pure render. Anything needing a live cluster or an
|
||||
install lifecycle (<span class="mono">lookup</span>, hooks) is silently dead there. Render the
|
||||
way your deployer renders.</p>
|
||||
<h3>Contexts: the cluster's whole voice is one small file</h3>
|
||||
<p>The chart owns everything structural - resources, probes, security, policy. Each cluster owns
|
||||
one values file: names, addresses, sizes, flags. The context is deliberately values-only; the
|
||||
moment it carries its own manifests there are two owners for one object, and they will
|
||||
disagree. One value can feed many rendered artefacts - an address appearing in the app config,
|
||||
the network attachment and two policies renders from one field, so the copies cannot diverge.</p>
|
||||
<p class="sayit">The chart owns the shape. The context owns the numbers.</p>
|
||||
<h3>Make the template refuse</h3>
|
||||
<p>A template that renders whatever it is given just moves the failure downstream. The grown-up
|
||||
chart carries a <span class="mono">values.schema.json</span>: a context missing a required
|
||||
value fails at render time, in the pipeline, with a message naming the field - not months
|
||||
later as enforcement pointed at nothing.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> The failure you want is the render that refuses. It
|
||||
costs a red pipeline. The alternative reports healthy the whole time.</p>
|
||||
<p class="closeline">Fleet consistency is not a discipline problem, it is a construction
|
||||
problem. Divergence stops being something you police and becomes something the tooling cannot
|
||||
express.</p>
|
||||
<p class="reads"><b>Pre-reads:</b> Course II ·
|
||||
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/">Kubernetes objects</a>
|
||||
 <b>Further:</b> <a href="https://helm.sh/docs/">Helm docs</a> ·
|
||||
<a href="https://helm.sh/docs/chart_template_guide/">chart template guide</a> ·
|
||||
<a href="https://pkg.go.dev/text/template">Go text/template</a> ·
|
||||
<a href="https://docs.redhat.com/en/documentation/openshift_container_platform">Helm on OpenShift</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ APPENDIX ============ -->
|
||||
<section class="course appendix" id="apx" aria-labelledby="apxh">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Appendix · The dependency ledger</p>
|
||||
<h2 id="apxh" class="thesis">Every toolchain stands on<br><em>services it does not run.</em></h2>
|
||||
<p class="lede">The arc reads like a closed machine: repo to reconciler to registry to node. It
|
||||
is not closed. Three load-bearing pieces live outside the cluster - and the honest move is to
|
||||
write down what leans on them, and what actually happens when they are down.</p>
|
||||
</header>
|
||||
<figure class="still">
|
||||
<img src="__HERO_APPENDIX__"
|
||||
alt="Three familiar machines at rest: the etched repository crystal, the armoured secret
|
||||
vault, and the mirror way-station - the supporting cast of the delivery arc.">
|
||||
<figcaption>You have met these three before.</figcaption>
|
||||
</figure>
|
||||
<div class="cards">
|
||||
<article class="card">
|
||||
<h3>GitHub</h3>
|
||||
<p class="what">Where the desired state lives - the system of record the whole loop watches,
|
||||
through a read-only deploy key.</p>
|
||||
<p class="leans"><b>Leans on it:</b> sync, rollback, change review, the "who merged this"
|
||||
answer.</p>
|
||||
<p class="down"><b>When it is down:</b> the cluster does not care - the reconciler enforces
|
||||
the last synced state indefinitely. What stops is <i>change</i>. GitOps degrades to
|
||||
read-only, which is the graceful half of the design.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>Azure Key Vault</h3>
|
||||
<p class="what">Where the secret values live - git carries the reference, the vault carries
|
||||
the value, an operator keeps them synced.</p>
|
||||
<p class="leans"><b>Leans on it:</b> secret sync, rotation, the first deploy of anything that
|
||||
needs a credential.</p>
|
||||
<p class="down"><b>When it is down:</b> already-synced Secrets keep working - values are
|
||||
materialised in-cluster. What stops is <i>rotation</i> and new secrets. Survivable - unless
|
||||
you are inside a rotation window.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<h3>zot</h3>
|
||||
<p class="what">Where the fleet pulls from - a pull-through mirror between the cluster and the
|
||||
internet, and the control point for what enters.</p>
|
||||
<p class="leans"><b>Leans on it:</b> every image pull on every node - boot, reschedule,
|
||||
scale-up, recovery.</p>
|
||||
<p class="down"><b>When it is down:</b> the sharpest edge. Upstream down + mirror up = nobody
|
||||
notices. Mirror down on a mirror-only pull path = nothing new schedules, and a rebooting
|
||||
node may not come back whole.</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<p class="closeline">None of these outages stop what is already running - they stop change,
|
||||
rotation and recovery, in that order of pain. Cache what you pull, split references from
|
||||
values, and let the cluster hold its last known truth without asking anyone's permission.</p>
|
||||
<p class="reads"><b>Further:</b> <a href="https://docs.github.com/">GitHub docs</a> ·
|
||||
<a href="https://learn.microsoft.com/azure/key-vault/">Azure Key Vault</a> ·
|
||||
<a href="https://zotregistry.dev/">zot</a> ·
|
||||
<a href="https://kubernetes.io/docs/">Kubernetes</a> ·
|
||||
<a href="https://helm.sh/docs/">Helm</a> ·
|
||||
<a href="https://docs.redhat.com/en/documentation/openshift_container_platform">Red Hat OpenShift</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="outro">
|
||||
<p>The Exploded Cluster · the delivery arc · every scene is one generated hero,
|
||||
sliced and scrubbed - no diagrams were harmed.</p>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
:root{
|
||||
--void:#070b14; --ink:#c9d7ef; --dim:#7286a8; --line:#1b2740;
|
||||
--cyan:#3fbaf5; --magenta:#e879f9; --amber:#e8b44a;
|
||||
--sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--void);color:var(--ink);font-family:var(--sans);
|
||||
-webkit-font-smoothing:antialiased;overflow-x:hidden}
|
||||
a{color:var(--cyan);text-decoration:none;border-bottom:1px solid var(--line)}
|
||||
a:hover,a:focus-visible{border-bottom-color:var(--cyan);outline:none}
|
||||
b{color:var(--ink)} .mono{font-family:var(--mono);font-size:.9em;color:var(--cyan)}
|
||||
|
||||
.masthead{max-width:64ch;margin:0 auto;padding:16vh 24px 9vh;text-align:center}
|
||||
.eyebrow{font-family:var(--mono);font-size:.72rem;letter-spacing:.22em;text-transform:uppercase;
|
||||
color:var(--cyan);margin:0 0 1.5rem}
|
||||
.masthead h1,.thesis{font-size:clamp(1.9rem,4.8vw,3.2rem);line-height:1.1;margin:0 0 1.3rem;
|
||||
font-weight:600;letter-spacing:-.02em;text-wrap:balance}
|
||||
.masthead h1 em,.thesis em{font-style:normal;color:var(--magenta)}
|
||||
.lede{font-size:clamp(.98rem,1.6vw,1.12rem);line-height:1.65;color:var(--dim);margin:0 auto;max-width:56ch}
|
||||
.scrollcue{font-family:var(--mono);font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;
|
||||
color:var(--dim);margin-top:4.5rem;display:flex;flex-direction:column;align-items:center;gap:.7rem}
|
||||
.scrollcue span{display:block;width:1px;height:46px;background:linear-gradient(var(--cyan),transparent)}
|
||||
|
||||
.course{position:relative;padding-top:10vh}
|
||||
.chead{max-width:64ch;margin:0 auto;padding:4vh 24px 2vh;text-align:center}
|
||||
.chead .thesis{font-size:clamp(1.6rem,4vw,2.6rem)}
|
||||
.pin{min-height:100svh;display:grid;place-items:center;padding:3vh 20px;align-content:center}
|
||||
.viewport{width:min(1220px,100%);display:grid;grid-template-columns:minmax(0,1fr) 290px;
|
||||
gap:26px;align-items:center}
|
||||
.scene{position:relative;margin:0;line-height:0}
|
||||
.scene .flat{display:block;width:100%;height:auto}
|
||||
.slab{position:absolute;inset:0;background-size:100% 100%;background-repeat:no-repeat;will-change:transform}
|
||||
|
||||
.legend{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:13px}
|
||||
.legend li{padding-left:22px;border-left:1px solid var(--line);
|
||||
opacity:.34;transition:opacity .35s ease,border-color .35s ease}
|
||||
.legend li.lit{opacity:1;border-left-color:var(--cyan)}
|
||||
.legend .t{font-size:.92rem;font-weight:600;margin:0 0 .22rem;letter-spacing:-.01em}
|
||||
.legend .b{font-size:.76rem;line-height:1.5;color:var(--dim);margin:0}
|
||||
|
||||
.bench{width:min(720px,100%);margin:16px auto 0;text-align:center}
|
||||
.bench input{width:100%;accent-color:var(--cyan)}
|
||||
.bench .hud{font-family:var(--mono);font-size:.66rem;letter-spacing:.06em;color:var(--dim);margin:.45rem 0 0}
|
||||
|
||||
.prose{max-width:66ch;margin:0 auto;padding:5vh 24px 9vh}
|
||||
.prose h3{font-size:1.18rem;font-weight:600;letter-spacing:-.01em;margin:2.6rem 0 .8rem;color:var(--ink)}
|
||||
.prose p{font-size:.98rem;line-height:1.75;color:var(--dim);margin:0 0 1.1rem}
|
||||
.sayit{font-weight:600;color:var(--cyan) !important;border-left:2px solid var(--cyan);padding-left:18px}
|
||||
.fieldnote{border-left:2px solid var(--amber);padding-left:18px;font-size:.92rem !important}
|
||||
.fieldnote b{color:var(--amber);font-family:var(--mono);font-size:.7rem;letter-spacing:.16em;
|
||||
text-transform:uppercase;display:block;margin-bottom:.35rem}
|
||||
.closeline{color:var(--ink) !important;margin-top:2.2rem !important}
|
||||
.reads{font-size:.8rem !important;line-height:1.9 !important;border-top:1px solid var(--line);
|
||||
padding-top:1.2rem;margin-top:2.4rem !important}
|
||||
.reads b{font-family:var(--mono);font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
|
||||
|
||||
.appendix .still{max-width:900px;margin:2vh auto 0;padding:0 24px}
|
||||
.appendix .still img{width:100%;height:auto;display:block}
|
||||
.appendix .still figcaption{font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;
|
||||
text-transform:uppercase;color:var(--dim);text-align:center;margin-top:.9rem}
|
||||
.cards{max-width:1100px;margin:5vh auto 0;padding:0 24px;display:grid;
|
||||
grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px}
|
||||
.card{border:1px solid var(--line);border-radius:10px;padding:22px 22px 18px;background:#0a1020}
|
||||
.card h3{margin:0 0 .7rem;font-size:1.05rem;letter-spacing:-.01em}
|
||||
.card p{font-size:.85rem;line-height:1.65;color:var(--dim);margin:0 0 .9rem}
|
||||
.card .leans{border-left:2px solid var(--cyan);padding-left:14px}
|
||||
.card .down{border-left:2px solid var(--amber);padding-left:14px;margin-bottom:0}
|
||||
.card .leans b,.card .down b{font-family:var(--mono);font-size:.66rem;letter-spacing:.14em;
|
||||
text-transform:uppercase;display:block;margin-bottom:.3rem}
|
||||
.card .leans b{color:var(--cyan)} .card .down b{color:var(--amber)} .card i{color:var(--ink);font-style:normal;font-weight:600}
|
||||
|
||||
.outro{max-width:64ch;margin:0 auto;padding:6vh 24px 14vh;text-align:center}
|
||||
.outro p{font-family:var(--mono);font-size:.7rem;letter-spacing:.12em;color:var(--dim);line-height:1.9}
|
||||
|
||||
@media (max-width:900px){
|
||||
.viewport{grid-template-columns:1fr;gap:20px}
|
||||
.pin{min-height:0;padding:3vh 18px 5vh}
|
||||
.masthead{padding:9vh 22px 6vh}
|
||||
}
|
||||
body:not(.fx) .slab{display:none}
|
||||
body:not(.fx) .bench{display:none}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
"use strict";
|
||||
const SCENES = { gitops: __MAN_GITOPS__, supply: __MAN_SUPPLY__, helm: __MAN_HELM__ };
|
||||
const RM = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const units = [];
|
||||
|
||||
document.querySelectorAll("section[data-scene]").forEach(sec => {
|
||||
const man = SCENES[sec.dataset.scene];
|
||||
const scene = sec.querySelector(".scene");
|
||||
const flat = scene.querySelector(".flat");
|
||||
const legend = sec.querySelector(".legend");
|
||||
man.parts.forEach(p => {
|
||||
const li = document.createElement("li");
|
||||
li.innerHTML = '<p class="t">' + p.title + '</p><p class="b">' + p.blurb + '</p>';
|
||||
legend.appendChild(li);
|
||||
});
|
||||
units.push({ sec, man, scene, flat, legend });
|
||||
});
|
||||
|
||||
if (!window.gsap || !window.ScrollTrigger) return; // no-JS/no-GSAP: flat heroes + full legends
|
||||
document.body.classList.add("fx");
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
units.forEach(u => {
|
||||
u.slabs = u.man.parts.map(p => {
|
||||
const d = document.createElement("div");
|
||||
d.className = "slab";
|
||||
d.style.backgroundImage = "url(" + u.flat.src + ")";
|
||||
d.style.clipPath = "polygon(" + p.points.map(q => q[0] + "% " + q[1] + "%").join(",") + ")";
|
||||
u.scene.appendChild(d);
|
||||
return d;
|
||||
});
|
||||
u.flat.style.visibility = "hidden";
|
||||
const dists = u.man.parts.map(p => Math.hypot(p.dx, p.dy));
|
||||
const maxD = Math.max(...dists, 1e-6);
|
||||
const lis = [...u.legend.children];
|
||||
u.apply = t => {
|
||||
u.man.parts.forEach((p, i) => {
|
||||
u.slabs[i].style.transform =
|
||||
"translate3d(" + (p.dx * (1 - t)).toFixed(3) + "%," + (p.dy * (1 - t)).toFixed(3) + "%,0)";
|
||||
});
|
||||
u.scene.style.transform = "scale(" + (0.95 + 0.05 * t).toFixed(4) + ")";
|
||||
lis.forEach((li, i) => li.classList.toggle("lit", t >= 0.82 * (dists[i] / maxD)));
|
||||
};
|
||||
// bench: hand scrub + HUD (pilot instrument - strip for the site ship)
|
||||
const bench = u.sec.querySelector(".bench");
|
||||
bench.innerHTML = '<input type="range" min="0" max="1000" value="0" aria-label="scrub the teardown">' +
|
||||
'<p class="hud">boot</p>';
|
||||
u.hud = bench.querySelector(".hud");
|
||||
u.slider = bench.querySelector("input");
|
||||
u.show = v => {
|
||||
u.hud.textContent = u.mode + " · " + innerWidth + "x" + innerHeight +
|
||||
" · scroll " + Math.round(scrollY) + " · t=" + v.toFixed(2);
|
||||
u.slider.value = String(Math.round(v * 1000));
|
||||
};
|
||||
u.drive = v => { u.apply(v); u.show(v); };
|
||||
u.slider.addEventListener("input", () => u.drive(+u.slider.value / 1000));
|
||||
});
|
||||
|
||||
const boot = () => {
|
||||
const scrollable = document.documentElement.scrollHeight - innerHeight > 200;
|
||||
units.forEach(u => {
|
||||
if (RM) { u.mode = "reduced-motion"; u.drive(1); return; }
|
||||
if (scrollable && matchMedia("(min-width: 901px)").matches) {
|
||||
u.mode = "pin+scrub";
|
||||
ScrollTrigger.create({
|
||||
trigger: u.sec.querySelector(".pin"),
|
||||
start: "top top",
|
||||
end: "+=1800",
|
||||
pin: true,
|
||||
scrub: 0.6,
|
||||
onUpdate: self => u.drive(self.progress),
|
||||
onRefreshInit: () => u.apply(0)
|
||||
});
|
||||
u.drive(0);
|
||||
} else {
|
||||
u.mode = "assemble";
|
||||
u.sec.querySelector(".pin").style.minHeight = "0";
|
||||
u.drive(0);
|
||||
const io = new IntersectionObserver(es => {
|
||||
if (!es.some(e => e.isIntersecting)) return;
|
||||
io.disconnect();
|
||||
gsap.to({ v: 0 }, { v: 1, duration: 1.6, ease: "power3.out",
|
||||
onUpdate: function () { u.drive(this.targets()[0].v); } });
|
||||
}, { threshold: 0.2 });
|
||||
io.observe(u.scene);
|
||||
}
|
||||
});
|
||||
if (!RM) ScrollTrigger.refresh();
|
||||
};
|
||||
Promise.all(units.map(u => u.flat.decode ? u.flat.decode().catch(() => {}) : Promise.resolve()))
|
||||
.then(() => requestAnimationFrame(() => requestAnimationFrame(boot)));
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Assemble the delivery-arc page: tpl + inlined GSAP + scene manifests + base64 heroes.
|
||||
|
||||
Heroes are inlined ONCE (in the <img> src); the slice engine reuses img.src for the
|
||||
slab backgrounds, so nothing is duplicated. Manifests come from tools/slice-arc.py.
|
||||
"""
|
||||
import base64, json, pathlib, re
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
|
||||
def b64(webp):
|
||||
return "data:image/webp;base64," + base64.b64encode((ROOT / "assets/dist" / webp).read_bytes()).decode()
|
||||
|
||||
def man(tag):
|
||||
return json.dumps(json.loads((ROOT / "pilot" / f"manifest-{tag}.json").read_text()),
|
||||
separators=(",", ":"))
|
||||
|
||||
out = (ROOT / "pilot/arc.tpl.html").read_text() \
|
||||
.replace("__MAN_GITOPS__", man("gitops")) \
|
||||
.replace("__MAN_SUPPLY__", man("supply")) \
|
||||
.replace("__MAN_HELM__", man("helm")) \
|
||||
.replace("__HERO_GITOPS__", b64("course-VI-gitops.webp")) \
|
||||
.replace("__HERO_SUPPLY__", b64("course-VII-supply-chain.webp")) \
|
||||
.replace("__HERO_HELM__", b64("course-VIII-helm-press.webp")) \
|
||||
.replace("__HERO_APPENDIX__", b64("appendix-dependency-ledger.webp"))
|
||||
|
||||
assert "__MAN_" not in out and "__HERO_" not in out, "unfilled placeholder"
|
||||
|
||||
out = (f"<script>{(ROOT / 'gsap.min.js').read_text()}</script>\n"
|
||||
f"<script>{(ROOT / 'st.min.js').read_text()}</script>\n" + out)
|
||||
|
||||
dest = ROOT / "pilot/arc.html"
|
||||
dest.write_text(out)
|
||||
heroes = len(re.findall(r"data:image/webp", out))
|
||||
print(f"built pilot/arc.html {len(out)/1024:.0f}KB ({heroes} heroes inlined)")
|
||||
@@ -0,0 +1,768 @@
|
||||
{
|
||||
"scene": "gitops",
|
||||
"source": "assets/raw/course-VI-gitops.jpeg",
|
||||
"parts": [
|
||||
{
|
||||
"name": "repo",
|
||||
"title": "The repository",
|
||||
"blurb": "Desired state lives here. The deploy button is a git commit; the audit log is git log.",
|
||||
"points": [
|
||||
[
|
||||
13.7,
|
||||
36.46
|
||||
],
|
||||
[
|
||||
13.26,
|
||||
35.57
|
||||
],
|
||||
[
|
||||
13.19,
|
||||
33.7
|
||||
],
|
||||
[
|
||||
13.19,
|
||||
32.55
|
||||
],
|
||||
[
|
||||
13.26,
|
||||
29.68
|
||||
],
|
||||
[
|
||||
13.84,
|
||||
28.52
|
||||
],
|
||||
[
|
||||
14.35,
|
||||
27.94
|
||||
],
|
||||
[
|
||||
15.66,
|
||||
26.44
|
||||
],
|
||||
[
|
||||
16.46,
|
||||
25.51
|
||||
],
|
||||
[
|
||||
23.36,
|
||||
17.9
|
||||
],
|
||||
[
|
||||
26.79,
|
||||
15.17
|
||||
],
|
||||
[
|
||||
28.04,
|
||||
14.13
|
||||
],
|
||||
[
|
||||
31.24,
|
||||
14.13
|
||||
],
|
||||
[
|
||||
33.21,
|
||||
15.56
|
||||
],
|
||||
[
|
||||
35.14,
|
||||
17.83
|
||||
],
|
||||
[
|
||||
36.52,
|
||||
19.54
|
||||
],
|
||||
[
|
||||
37.46,
|
||||
20.66
|
||||
],
|
||||
[
|
||||
39.06,
|
||||
22.51
|
||||
],
|
||||
[
|
||||
39.57,
|
||||
23.54
|
||||
],
|
||||
[
|
||||
39.64,
|
||||
25.68
|
||||
],
|
||||
[
|
||||
39.64,
|
||||
29.39
|
||||
],
|
||||
[
|
||||
39.06,
|
||||
30.54
|
||||
],
|
||||
[
|
||||
38.7,
|
||||
30.97
|
||||
],
|
||||
[
|
||||
38.19,
|
||||
31.56
|
||||
],
|
||||
[
|
||||
35.94,
|
||||
34.12
|
||||
],
|
||||
[
|
||||
26.02,
|
||||
44.21
|
||||
],
|
||||
[
|
||||
25.17,
|
||||
44.86
|
||||
],
|
||||
[
|
||||
24.92,
|
||||
44.99
|
||||
],
|
||||
[
|
||||
21.96,
|
||||
44.99
|
||||
],
|
||||
[
|
||||
21.35,
|
||||
44.6
|
||||
],
|
||||
[
|
||||
20.45,
|
||||
43.95
|
||||
],
|
||||
[
|
||||
15.15,
|
||||
38.17
|
||||
],
|
||||
[
|
||||
13.92,
|
||||
36.76
|
||||
]
|
||||
],
|
||||
"dx": 9.27,
|
||||
"dy": 8.52
|
||||
},
|
||||
{
|
||||
"name": "engine",
|
||||
"title": "ArgoCD",
|
||||
"blurb": "Watches repo + path + revision, compares desired against actual, and syncs the difference.",
|
||||
"points": [
|
||||
[
|
||||
48.29,
|
||||
31.4
|
||||
],
|
||||
[
|
||||
48.29,
|
||||
43.47
|
||||
],
|
||||
[
|
||||
48.22,
|
||||
43.78
|
||||
],
|
||||
[
|
||||
47.93,
|
||||
44.69
|
||||
],
|
||||
[
|
||||
47.35,
|
||||
45.94
|
||||
],
|
||||
[
|
||||
47.13,
|
||||
46.27
|
||||
],
|
||||
[
|
||||
45.46,
|
||||
48.59
|
||||
],
|
||||
[
|
||||
39.41,
|
||||
54.23
|
||||
],
|
||||
[
|
||||
35.64,
|
||||
57.1
|
||||
],
|
||||
[
|
||||
35.3,
|
||||
57.1
|
||||
],
|
||||
[
|
||||
31.94,
|
||||
54.49
|
||||
],
|
||||
[
|
||||
31.29,
|
||||
53.97
|
||||
],
|
||||
[
|
||||
30.63,
|
||||
52.4
|
||||
],
|
||||
[
|
||||
30.56,
|
||||
52.08
|
||||
],
|
||||
[
|
||||
30.05,
|
||||
45.02
|
||||
],
|
||||
[
|
||||
30.05,
|
||||
43.21
|
||||
],
|
||||
[
|
||||
30.12,
|
||||
42.46
|
||||
],
|
||||
[
|
||||
30.27,
|
||||
41.25
|
||||
],
|
||||
[
|
||||
30.41,
|
||||
40.49
|
||||
],
|
||||
[
|
||||
30.78,
|
||||
38.96
|
||||
],
|
||||
[
|
||||
31.07,
|
||||
38.03
|
||||
],
|
||||
[
|
||||
31.29,
|
||||
37.39
|
||||
],
|
||||
[
|
||||
32.3,
|
||||
35.06
|
||||
],
|
||||
[
|
||||
32.74,
|
||||
34.32
|
||||
],
|
||||
[
|
||||
39.51,
|
||||
27.93
|
||||
],
|
||||
[
|
||||
39.95,
|
||||
27.67
|
||||
],
|
||||
[
|
||||
43.5,
|
||||
25.98
|
||||
],
|
||||
[
|
||||
43.84,
|
||||
25.98
|
||||
],
|
||||
[
|
||||
45.48,
|
||||
27.15
|
||||
],
|
||||
[
|
||||
46.84,
|
||||
28.71
|
||||
],
|
||||
[
|
||||
47.35,
|
||||
29.44
|
||||
],
|
||||
[
|
||||
47.71,
|
||||
29.96
|
||||
],
|
||||
[
|
||||
48.22,
|
||||
31.09
|
||||
]
|
||||
],
|
||||
"dx": 0.0,
|
||||
"dy": 0.0
|
||||
},
|
||||
{
|
||||
"name": "sheets",
|
||||
"title": "Rendered manifests",
|
||||
"blurb": "What the repo's charts and manifests render to - build artefacts nobody hand-edits.",
|
||||
"points": [
|
||||
[
|
||||
39.57,
|
||||
59.81
|
||||
],
|
||||
[
|
||||
39.57,
|
||||
52.61
|
||||
],
|
||||
[
|
||||
40.08,
|
||||
51.59
|
||||
],
|
||||
[
|
||||
40.59,
|
||||
51.01
|
||||
],
|
||||
[
|
||||
42.04,
|
||||
49.36
|
||||
],
|
||||
[
|
||||
47.79,
|
||||
42.25
|
||||
],
|
||||
[
|
||||
51.84,
|
||||
39.13
|
||||
],
|
||||
[
|
||||
54.79,
|
||||
36.65
|
||||
],
|
||||
[
|
||||
54.96,
|
||||
36.52
|
||||
],
|
||||
[
|
||||
55.04,
|
||||
36.52
|
||||
],
|
||||
[
|
||||
56.61,
|
||||
37.69
|
||||
],
|
||||
[
|
||||
61.59,
|
||||
42.77
|
||||
],
|
||||
[
|
||||
64.35,
|
||||
45.97
|
||||
],
|
||||
[
|
||||
64.57,
|
||||
46.27
|
||||
],
|
||||
[
|
||||
65.01,
|
||||
47.15
|
||||
],
|
||||
[
|
||||
65.01,
|
||||
54.33
|
||||
],
|
||||
[
|
||||
64.57,
|
||||
55.2
|
||||
],
|
||||
[
|
||||
64.35,
|
||||
55.49
|
||||
],
|
||||
[
|
||||
51.02,
|
||||
69.08
|
||||
],
|
||||
[
|
||||
49.95,
|
||||
69.99
|
||||
],
|
||||
[
|
||||
49.79,
|
||||
70.12
|
||||
],
|
||||
[
|
||||
49.54,
|
||||
70.12
|
||||
],
|
||||
[
|
||||
48.69,
|
||||
69.47
|
||||
],
|
||||
[
|
||||
41.24,
|
||||
62.12
|
||||
],
|
||||
[
|
||||
40.01,
|
||||
60.7
|
||||
]
|
||||
],
|
||||
"dx": -9.34,
|
||||
"dy": -8.61
|
||||
},
|
||||
{
|
||||
"name": "cluster",
|
||||
"title": "The cluster",
|
||||
"blurb": "Actual state. It pulls its own future - no CI system holds a credential that can touch it.",
|
||||
"points": [
|
||||
[
|
||||
52.8,
|
||||
72.93
|
||||
],
|
||||
[
|
||||
52.29,
|
||||
71.93
|
||||
],
|
||||
[
|
||||
52.22,
|
||||
70.38
|
||||
],
|
||||
[
|
||||
52.22,
|
||||
68.69
|
||||
],
|
||||
[
|
||||
52.29,
|
||||
68.13
|
||||
],
|
||||
[
|
||||
52.94,
|
||||
66.85
|
||||
],
|
||||
[
|
||||
53.16,
|
||||
66.57
|
||||
],
|
||||
[
|
||||
54.61,
|
||||
64.7
|
||||
],
|
||||
[
|
||||
61.55,
|
||||
55.92
|
||||
],
|
||||
[
|
||||
64.87,
|
||||
53.32
|
||||
],
|
||||
[
|
||||
67.36,
|
||||
51.37
|
||||
],
|
||||
[
|
||||
69.43,
|
||||
49.93
|
||||
],
|
||||
[
|
||||
70.76,
|
||||
49.93
|
||||
],
|
||||
[
|
||||
74.11,
|
||||
52.28
|
||||
],
|
||||
[
|
||||
75.36,
|
||||
53.19
|
||||
],
|
||||
[
|
||||
78.6,
|
||||
56.99
|
||||
],
|
||||
[
|
||||
82.09,
|
||||
61.01
|
||||
],
|
||||
[
|
||||
82.45,
|
||||
61.46
|
||||
],
|
||||
[
|
||||
83.76,
|
||||
63.05
|
||||
],
|
||||
[
|
||||
83.98,
|
||||
63.34
|
||||
],
|
||||
[
|
||||
84.56,
|
||||
64.48
|
||||
],
|
||||
[
|
||||
84.56,
|
||||
68.27
|
||||
],
|
||||
[
|
||||
83.98,
|
||||
69.39
|
||||
],
|
||||
[
|
||||
83.76,
|
||||
69.68
|
||||
],
|
||||
[
|
||||
81.8,
|
||||
71.84
|
||||
],
|
||||
[
|
||||
74.1,
|
||||
81.06
|
||||
],
|
||||
[
|
||||
69.78,
|
||||
84.57
|
||||
],
|
||||
[
|
||||
67.95,
|
||||
86.13
|
||||
],
|
||||
[
|
||||
67.62,
|
||||
86.39
|
||||
],
|
||||
[
|
||||
66.3,
|
||||
86.39
|
||||
],
|
||||
[
|
||||
64.8,
|
||||
85.22
|
||||
],
|
||||
[
|
||||
58.54,
|
||||
79.49
|
||||
],
|
||||
[
|
||||
53.81,
|
||||
74.11
|
||||
],
|
||||
[
|
||||
53.02,
|
||||
73.22
|
||||
]
|
||||
],
|
||||
"dx": -20.96,
|
||||
"dy": -19.23
|
||||
},
|
||||
{
|
||||
"name": "shard",
|
||||
"title": "Drift",
|
||||
"blurb": "A hand-edited live object. selfHeal puts it back; what leaves git leaves the cluster.",
|
||||
"points": [
|
||||
[
|
||||
80.04,
|
||||
94.73
|
||||
],
|
||||
[
|
||||
77.39,
|
||||
94.21
|
||||
],
|
||||
[
|
||||
76.89,
|
||||
94.08
|
||||
],
|
||||
[
|
||||
76.58,
|
||||
93.95
|
||||
],
|
||||
[
|
||||
76.37,
|
||||
93.82
|
||||
],
|
||||
[
|
||||
75.51,
|
||||
92.91
|
||||
],
|
||||
[
|
||||
74.82,
|
||||
91.82
|
||||
],
|
||||
[
|
||||
72.35,
|
||||
82.43
|
||||
],
|
||||
[
|
||||
72.35,
|
||||
82.26
|
||||
],
|
||||
[
|
||||
72.42,
|
||||
81.76
|
||||
],
|
||||
[
|
||||
72.93,
|
||||
80.47
|
||||
],
|
||||
[
|
||||
73.15,
|
||||
80.05
|
||||
],
|
||||
[
|
||||
73.51,
|
||||
79.38
|
||||
],
|
||||
[
|
||||
75.28,
|
||||
77.67
|
||||
],
|
||||
[
|
||||
75.49,
|
||||
77.54
|
||||
],
|
||||
[
|
||||
75.58,
|
||||
77.54
|
||||
],
|
||||
[
|
||||
79.69,
|
||||
78.32
|
||||
],
|
||||
[
|
||||
80.2,
|
||||
78.45
|
||||
],
|
||||
[
|
||||
81.18,
|
||||
79.49
|
||||
],
|
||||
[
|
||||
81.63,
|
||||
80.01
|
||||
],
|
||||
[
|
||||
82.01,
|
||||
81.62
|
||||
],
|
||||
[
|
||||
83.9,
|
||||
88.5
|
||||
],
|
||||
[
|
||||
84.05,
|
||||
88.98
|
||||
],
|
||||
[
|
||||
84.05,
|
||||
89.31
|
||||
],
|
||||
[
|
||||
83.98,
|
||||
89.65
|
||||
],
|
||||
[
|
||||
83.39,
|
||||
91.11
|
||||
],
|
||||
[
|
||||
83.18,
|
||||
91.52
|
||||
],
|
||||
[
|
||||
80.34,
|
||||
94.6
|
||||
],
|
||||
[
|
||||
80.14,
|
||||
94.73
|
||||
]
|
||||
],
|
||||
"dx": -27.99,
|
||||
"dy": -32.13
|
||||
},
|
||||
{
|
||||
"name": "vault",
|
||||
"title": "The vault",
|
||||
"blurb": "Azure Key Vault in the worked example. Git carries the reference; the vault carries the value. Never absorbed into the pipeline.",
|
||||
"points": [
|
||||
[
|
||||
56.07,
|
||||
10.57
|
||||
],
|
||||
[
|
||||
56.07,
|
||||
23.29
|
||||
],
|
||||
[
|
||||
55.63,
|
||||
24.38
|
||||
],
|
||||
[
|
||||
55.42,
|
||||
24.79
|
||||
],
|
||||
[
|
||||
55.05,
|
||||
25.44
|
||||
],
|
||||
[
|
||||
51.4,
|
||||
28.32
|
||||
],
|
||||
[
|
||||
50.13,
|
||||
29.23
|
||||
],
|
||||
[
|
||||
49.87,
|
||||
29.23
|
||||
],
|
||||
[
|
||||
49.33,
|
||||
28.84
|
||||
],
|
||||
[
|
||||
45.24,
|
||||
25.98
|
||||
],
|
||||
[
|
||||
44.51,
|
||||
24.92
|
||||
],
|
||||
[
|
||||
44.29,
|
||||
24.5
|
||||
],
|
||||
[
|
||||
43.86,
|
||||
23.37
|
||||
],
|
||||
[
|
||||
43.86,
|
||||
10.48
|
||||
],
|
||||
[
|
||||
44.29,
|
||||
9.35
|
||||
],
|
||||
[
|
||||
44.51,
|
||||
8.92
|
||||
],
|
||||
[
|
||||
44.9,
|
||||
8.27
|
||||
],
|
||||
[
|
||||
48.89,
|
||||
5.4
|
||||
],
|
||||
[
|
||||
49.78,
|
||||
4.75
|
||||
],
|
||||
[
|
||||
50.3,
|
||||
4.75
|
||||
],
|
||||
[
|
||||
50.66,
|
||||
5.01
|
||||
],
|
||||
[
|
||||
55.05,
|
||||
8.39
|
||||
],
|
||||
[
|
||||
55.56,
|
||||
9.28
|
||||
]
|
||||
],
|
||||
"dx": -2.34,
|
||||
"dy": 5.37
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
{
|
||||
"scene": "helm",
|
||||
"source": "assets/raw/course-VIII-helm-press.jpeg",
|
||||
"parts": [
|
||||
{
|
||||
"name": "press",
|
||||
"title": "The chart",
|
||||
"blurb": "A function with holes - the template owns every structural decision, and renders whatever fills it.",
|
||||
"points": [
|
||||
[
|
||||
28.09,
|
||||
44.03
|
||||
],
|
||||
[
|
||||
28.23,
|
||||
43.34
|
||||
],
|
||||
[
|
||||
28.81,
|
||||
42.23
|
||||
],
|
||||
[
|
||||
41.58,
|
||||
28.45
|
||||
],
|
||||
[
|
||||
47.7,
|
||||
22.85
|
||||
],
|
||||
[
|
||||
49.1,
|
||||
21.55
|
||||
],
|
||||
[
|
||||
49.41,
|
||||
21.29
|
||||
],
|
||||
[
|
||||
49.88,
|
||||
21.29
|
||||
],
|
||||
[
|
||||
50.11,
|
||||
21.42
|
||||
],
|
||||
[
|
||||
60.78,
|
||||
31.05
|
||||
],
|
||||
[
|
||||
69.66,
|
||||
40.45
|
||||
],
|
||||
[
|
||||
70.02,
|
||||
40.89
|
||||
],
|
||||
[
|
||||
70.6,
|
||||
42.04
|
||||
],
|
||||
[
|
||||
70.68,
|
||||
42.6
|
||||
],
|
||||
[
|
||||
71.4,
|
||||
68.03
|
||||
],
|
||||
[
|
||||
71.4,
|
||||
69.14
|
||||
],
|
||||
[
|
||||
71.33,
|
||||
69.42
|
||||
],
|
||||
[
|
||||
70.89,
|
||||
70.27
|
||||
],
|
||||
[
|
||||
70.02,
|
||||
71.43
|
||||
],
|
||||
[
|
||||
65.08,
|
||||
78.29
|
||||
],
|
||||
[
|
||||
60.99,
|
||||
82.75
|
||||
],
|
||||
[
|
||||
58.16,
|
||||
85.74
|
||||
],
|
||||
[
|
||||
57.76,
|
||||
86.13
|
||||
],
|
||||
[
|
||||
57.37,
|
||||
86.26
|
||||
],
|
||||
[
|
||||
56.27,
|
||||
86.26
|
||||
],
|
||||
[
|
||||
55.5,
|
||||
85.61
|
||||
],
|
||||
[
|
||||
53.39,
|
||||
83.66
|
||||
],
|
||||
[
|
||||
44.95,
|
||||
76.11
|
||||
],
|
||||
[
|
||||
42.79,
|
||||
74.29
|
||||
],
|
||||
[
|
||||
41.68,
|
||||
73.03
|
||||
],
|
||||
[
|
||||
41.46,
|
||||
72.69
|
||||
],
|
||||
[
|
||||
28.74,
|
||||
53.35
|
||||
],
|
||||
[
|
||||
28.31,
|
||||
52.53
|
||||
],
|
||||
[
|
||||
28.23,
|
||||
52.26
|
||||
],
|
||||
[
|
||||
28.09,
|
||||
44.72
|
||||
]
|
||||
],
|
||||
"dx": 0.0,
|
||||
"dy": 0.0
|
||||
},
|
||||
{
|
||||
"name": "contexts",
|
||||
"title": "Values contexts",
|
||||
"blurb": "One small file per cluster - names, addresses, sizes. The cluster's whole voice, values only.",
|
||||
"points": [
|
||||
[
|
||||
24.11,
|
||||
21.94
|
||||
],
|
||||
[
|
||||
34.92,
|
||||
19.22
|
||||
],
|
||||
[
|
||||
35.07,
|
||||
19.25
|
||||
],
|
||||
[
|
||||
36.67,
|
||||
21.02
|
||||
],
|
||||
[
|
||||
36.81,
|
||||
21.19
|
||||
],
|
||||
[
|
||||
37.39,
|
||||
22.28
|
||||
],
|
||||
[
|
||||
38.12,
|
||||
23.8
|
||||
],
|
||||
[
|
||||
38.19,
|
||||
24.23
|
||||
],
|
||||
[
|
||||
38.48,
|
||||
28.14
|
||||
],
|
||||
[
|
||||
38.48,
|
||||
28.43
|
||||
],
|
||||
[
|
||||
38.34,
|
||||
30.15
|
||||
],
|
||||
[
|
||||
38.26,
|
||||
30.44
|
||||
],
|
||||
[
|
||||
37.68,
|
||||
31.58
|
||||
],
|
||||
[
|
||||
37.32,
|
||||
32.01
|
||||
],
|
||||
[
|
||||
15.52,
|
||||
51.59
|
||||
],
|
||||
[
|
||||
14.64,
|
||||
52.14
|
||||
],
|
||||
[
|
||||
14.35,
|
||||
52.08
|
||||
],
|
||||
[
|
||||
14.06,
|
||||
51.74
|
||||
],
|
||||
[
|
||||
13.41,
|
||||
50.91
|
||||
],
|
||||
[
|
||||
13.19,
|
||||
50.59
|
||||
],
|
||||
[
|
||||
12.68,
|
||||
49.51
|
||||
],
|
||||
[
|
||||
12.68,
|
||||
46.65
|
||||
],
|
||||
[
|
||||
14.5,
|
||||
35.5
|
||||
],
|
||||
[
|
||||
14.57,
|
||||
35.06
|
||||
],
|
||||
[
|
||||
14.72,
|
||||
34.63
|
||||
],
|
||||
[
|
||||
15.3,
|
||||
33.46
|
||||
],
|
||||
[
|
||||
22.06,
|
||||
22.98
|
||||
],
|
||||
[
|
||||
22.35,
|
||||
22.72
|
||||
],
|
||||
[
|
||||
22.82,
|
||||
22.46
|
||||
]
|
||||
],
|
||||
"dx": 17.79,
|
||||
"dy": 14.42
|
||||
},
|
||||
{
|
||||
"name": "renders",
|
||||
"title": "The renders",
|
||||
"blurb": "Same framework, different numbers - one function, many clusters. Review the render, not just the template.",
|
||||
"points": [
|
||||
[
|
||||
54.4,
|
||||
28.31
|
||||
],
|
||||
[
|
||||
53.81,
|
||||
26.93
|
||||
],
|
||||
[
|
||||
53.67,
|
||||
25.14
|
||||
],
|
||||
[
|
||||
54.76,
|
||||
22.23
|
||||
],
|
||||
[
|
||||
60.5,
|
||||
7.22
|
||||
],
|
||||
[
|
||||
61.23,
|
||||
5.85
|
||||
],
|
||||
[
|
||||
62.48,
|
||||
5.4
|
||||
],
|
||||
[
|
||||
66.73,
|
||||
8.79
|
||||
],
|
||||
[
|
||||
71.55,
|
||||
12.69
|
||||
],
|
||||
[
|
||||
77.03,
|
||||
17.51
|
||||
],
|
||||
[
|
||||
81.94,
|
||||
22.56
|
||||
],
|
||||
[
|
||||
84.63,
|
||||
26.52
|
||||
],
|
||||
[
|
||||
88.34,
|
||||
32.13
|
||||
],
|
||||
[
|
||||
91.03,
|
||||
36.33
|
||||
],
|
||||
[
|
||||
94.3,
|
||||
41.38
|
||||
],
|
||||
[
|
||||
94.88,
|
||||
42.61
|
||||
],
|
||||
[
|
||||
94.73,
|
||||
44.56
|
||||
],
|
||||
[
|
||||
92.92,
|
||||
47.61
|
||||
],
|
||||
[
|
||||
86.33,
|
||||
55.8
|
||||
],
|
||||
[
|
||||
82.05,
|
||||
60.22
|
||||
],
|
||||
[
|
||||
80.26,
|
||||
59.96
|
||||
]
|
||||
],
|
||||
"dx": -15.11,
|
||||
"dy": 12.16
|
||||
},
|
||||
{
|
||||
"name": "gate",
|
||||
"title": "The schema gate",
|
||||
"blurb": "values.schema.json + required: a context missing a value fails at render time, in the pipeline.",
|
||||
"points": [
|
||||
[
|
||||
52.58,
|
||||
82.79
|
||||
],
|
||||
[
|
||||
52.58,
|
||||
84.22
|
||||
],
|
||||
[
|
||||
49.17,
|
||||
92.01
|
||||
],
|
||||
[
|
||||
48.73,
|
||||
92.99
|
||||
],
|
||||
[
|
||||
48.44,
|
||||
93.51
|
||||
],
|
||||
[
|
||||
48.04,
|
||||
93.56
|
||||
],
|
||||
[
|
||||
47.89,
|
||||
93.43
|
||||
],
|
||||
[
|
||||
29.76,
|
||||
74.97
|
||||
],
|
||||
[
|
||||
28.74,
|
||||
73.82
|
||||
],
|
||||
[
|
||||
28.31,
|
||||
72.96
|
||||
],
|
||||
[
|
||||
28.23,
|
||||
72.68
|
||||
],
|
||||
[
|
||||
28.23,
|
||||
68.29
|
||||
],
|
||||
[
|
||||
28.67,
|
||||
67.36
|
||||
],
|
||||
[
|
||||
29.03,
|
||||
66.72
|
||||
],
|
||||
[
|
||||
29.69,
|
||||
66.48
|
||||
],
|
||||
[
|
||||
31.58,
|
||||
65.77
|
||||
],
|
||||
[
|
||||
35.11,
|
||||
65.04
|
||||
],
|
||||
[
|
||||
36.17,
|
||||
65.04
|
||||
],
|
||||
[
|
||||
36.51,
|
||||
65.3
|
||||
],
|
||||
[
|
||||
51.56,
|
||||
81.1
|
||||
],
|
||||
[
|
||||
52.0,
|
||||
81.66
|
||||
]
|
||||
],
|
||||
"dx": 4.13,
|
||||
"dy": -10.4
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
{
|
||||
"scene": "supply",
|
||||
"source": "assets/raw/course-VII-supply-chain.jpeg",
|
||||
"parts": [
|
||||
{
|
||||
"name": "image",
|
||||
"title": "The image",
|
||||
"blurb": "Course I's layers, shipped as one unit - the thing whose name we must say precisely.",
|
||||
"points": [
|
||||
[
|
||||
8.03,
|
||||
70.47
|
||||
],
|
||||
[
|
||||
8.03,
|
||||
51.54
|
||||
],
|
||||
[
|
||||
8.47,
|
||||
50.6
|
||||
],
|
||||
[
|
||||
8.68,
|
||||
50.26
|
||||
],
|
||||
[
|
||||
13.09,
|
||||
45.51
|
||||
],
|
||||
[
|
||||
17.11,
|
||||
42.12
|
||||
],
|
||||
[
|
||||
17.36,
|
||||
41.99
|
||||
],
|
||||
[
|
||||
17.61,
|
||||
42.12
|
||||
],
|
||||
[
|
||||
17.77,
|
||||
42.25
|
||||
],
|
||||
[
|
||||
23.01,
|
||||
46.68
|
||||
],
|
||||
[
|
||||
25.91,
|
||||
49.65
|
||||
],
|
||||
[
|
||||
26.27,
|
||||
50.2
|
||||
],
|
||||
[
|
||||
26.85,
|
||||
51.52
|
||||
],
|
||||
[
|
||||
26.85,
|
||||
70.67
|
||||
],
|
||||
[
|
||||
26.42,
|
||||
71.67
|
||||
],
|
||||
[
|
||||
26.2,
|
||||
72.03
|
||||
],
|
||||
[
|
||||
25.69,
|
||||
72.78
|
||||
],
|
||||
[
|
||||
18.76,
|
||||
78.71
|
||||
],
|
||||
[
|
||||
17.85,
|
||||
79.49
|
||||
],
|
||||
[
|
||||
17.36,
|
||||
79.88
|
||||
],
|
||||
[
|
||||
16.54,
|
||||
79.23
|
||||
],
|
||||
[
|
||||
14.7,
|
||||
77.67
|
||||
],
|
||||
[
|
||||
8.61,
|
||||
71.75
|
||||
]
|
||||
],
|
||||
"dx": 26.55,
|
||||
"dy": -0.01
|
||||
},
|
||||
{
|
||||
"name": "registry",
|
||||
"title": "Upstream registry",
|
||||
"blurb": "The managed registry CI pushes to - Azure Container Registry in the worked example.",
|
||||
"points": [
|
||||
[
|
||||
31.21,
|
||||
82.1
|
||||
],
|
||||
[
|
||||
31.11,
|
||||
38.48
|
||||
],
|
||||
[
|
||||
31.63,
|
||||
37.69
|
||||
],
|
||||
[
|
||||
31.88,
|
||||
37.43
|
||||
],
|
||||
[
|
||||
34.11,
|
||||
35.48
|
||||
],
|
||||
[
|
||||
37.16,
|
||||
32.75
|
||||
],
|
||||
[
|
||||
37.8,
|
||||
32.23
|
||||
],
|
||||
[
|
||||
38.43,
|
||||
32.23
|
||||
],
|
||||
[
|
||||
39.3,
|
||||
33.01
|
||||
],
|
||||
[
|
||||
43.66,
|
||||
36.91
|
||||
],
|
||||
[
|
||||
44.24,
|
||||
37.43
|
||||
],
|
||||
[
|
||||
44.49,
|
||||
37.69
|
||||
],
|
||||
[
|
||||
45.0,
|
||||
38.48
|
||||
],
|
||||
[
|
||||
45.48,
|
||||
45.25
|
||||
],
|
||||
[
|
||||
45.6,
|
||||
63.08
|
||||
],
|
||||
[
|
||||
44.92,
|
||||
81.97
|
||||
],
|
||||
[
|
||||
44.25,
|
||||
83.01
|
||||
],
|
||||
[
|
||||
43.12,
|
||||
84.05
|
||||
],
|
||||
[
|
||||
41.67,
|
||||
85.35
|
||||
],
|
||||
[
|
||||
38.51,
|
||||
88.22
|
||||
],
|
||||
[
|
||||
38.35,
|
||||
88.35
|
||||
],
|
||||
[
|
||||
37.8,
|
||||
88.48
|
||||
],
|
||||
[
|
||||
33.1,
|
||||
84.18
|
||||
],
|
||||
[
|
||||
32.54,
|
||||
83.66
|
||||
],
|
||||
[
|
||||
31.72,
|
||||
82.88
|
||||
]
|
||||
],
|
||||
"dx": 14.12,
|
||||
"dy": 0.34
|
||||
},
|
||||
{
|
||||
"name": "mirror",
|
||||
"title": "The mirror",
|
||||
"blurb": "zot, a pull-through cache: the fleet asks it, it asks upstream once, everything after is local.",
|
||||
"points": [
|
||||
[
|
||||
48.51,
|
||||
64.72
|
||||
],
|
||||
[
|
||||
48.87,
|
||||
52.91
|
||||
],
|
||||
[
|
||||
51.05,
|
||||
49.54
|
||||
],
|
||||
[
|
||||
60.12,
|
||||
40.69
|
||||
],
|
||||
[
|
||||
61.98,
|
||||
39.78
|
||||
],
|
||||
[
|
||||
64.72,
|
||||
42.12
|
||||
],
|
||||
[
|
||||
73.22,
|
||||
50.34
|
||||
],
|
||||
[
|
||||
74.38,
|
||||
52.54
|
||||
],
|
||||
[
|
||||
74.75,
|
||||
56.78
|
||||
],
|
||||
[
|
||||
74.46,
|
||||
68.54
|
||||
],
|
||||
[
|
||||
73.87,
|
||||
69.89
|
||||
],
|
||||
[
|
||||
73.29,
|
||||
70.67
|
||||
],
|
||||
[
|
||||
71.48,
|
||||
73.01
|
||||
],
|
||||
[
|
||||
65.04,
|
||||
79.23
|
||||
],
|
||||
[
|
||||
62.2,
|
||||
81.58
|
||||
],
|
||||
[
|
||||
61.46,
|
||||
81.71
|
||||
],
|
||||
[
|
||||
59.38,
|
||||
80.67
|
||||
],
|
||||
[
|
||||
51.27,
|
||||
72.31
|
||||
],
|
||||
[
|
||||
49.53,
|
||||
69.74
|
||||
],
|
||||
[
|
||||
48.8,
|
||||
67.85
|
||||
]
|
||||
],
|
||||
"dx": 0.0,
|
||||
"dy": 0.0
|
||||
},
|
||||
{
|
||||
"name": "core",
|
||||
"title": "The node",
|
||||
"blurb": "Pulls local. Boot, reschedule and recovery all lean on the mirror being there.",
|
||||
"points": [
|
||||
[
|
||||
75.83,
|
||||
58.68
|
||||
],
|
||||
[
|
||||
75.91,
|
||||
50.73
|
||||
],
|
||||
[
|
||||
76.63,
|
||||
49.02
|
||||
],
|
||||
[
|
||||
76.93,
|
||||
48.46
|
||||
],
|
||||
[
|
||||
78.33,
|
||||
47.07
|
||||
],
|
||||
[
|
||||
80.6,
|
||||
45.25
|
||||
],
|
||||
[
|
||||
82.93,
|
||||
43.29
|
||||
],
|
||||
[
|
||||
84.1,
|
||||
42.38
|
||||
],
|
||||
[
|
||||
84.43,
|
||||
42.38
|
||||
],
|
||||
[
|
||||
85.43,
|
||||
43.16
|
||||
],
|
||||
[
|
||||
87.93,
|
||||
45.25
|
||||
],
|
||||
[
|
||||
89.38,
|
||||
46.42
|
||||
],
|
||||
[
|
||||
91.75,
|
||||
48.97
|
||||
],
|
||||
[
|
||||
92.55,
|
||||
50.81
|
||||
],
|
||||
[
|
||||
92.62,
|
||||
52.04
|
||||
],
|
||||
[
|
||||
92.62,
|
||||
68.68
|
||||
],
|
||||
[
|
||||
92.48,
|
||||
69.61
|
||||
],
|
||||
[
|
||||
92.26,
|
||||
70.26
|
||||
],
|
||||
[
|
||||
91.46,
|
||||
72.16
|
||||
],
|
||||
[
|
||||
90.05,
|
||||
73.5
|
||||
],
|
||||
[
|
||||
84.75,
|
||||
77.93
|
||||
],
|
||||
[
|
||||
84.51,
|
||||
78.06
|
||||
],
|
||||
[
|
||||
84.1,
|
||||
78.06
|
||||
],
|
||||
[
|
||||
83.68,
|
||||
77.8
|
||||
],
|
||||
[
|
||||
79.31,
|
||||
74.16
|
||||
],
|
||||
[
|
||||
76.93,
|
||||
72.13
|
||||
],
|
||||
[
|
||||
76.05,
|
||||
70.03
|
||||
],
|
||||
[
|
||||
75.98,
|
||||
69.7
|
||||
],
|
||||
[
|
||||
75.83,
|
||||
68.6
|
||||
]
|
||||
],
|
||||
"dx": -13.37,
|
||||
"dy": 0.57
|
||||
},
|
||||
{
|
||||
"name": "tag",
|
||||
"title": "The tag",
|
||||
"blurb": "A promise - a mutable pointer anyone with push rights can move, and nothing records that it moved.",
|
||||
"points": [
|
||||
[
|
||||
26.2,
|
||||
20.46
|
||||
],
|
||||
[
|
||||
26.63,
|
||||
19.59
|
||||
],
|
||||
[
|
||||
27.29,
|
||||
18.86
|
||||
],
|
||||
[
|
||||
32.81,
|
||||
12.02
|
||||
],
|
||||
[
|
||||
37.75,
|
||||
7.75
|
||||
],
|
||||
[
|
||||
37.93,
|
||||
7.62
|
||||
],
|
||||
[
|
||||
38.02,
|
||||
7.62
|
||||
],
|
||||
[
|
||||
39.43,
|
||||
8.66
|
||||
],
|
||||
[
|
||||
40.81,
|
||||
9.7
|
||||
],
|
||||
[
|
||||
43.64,
|
||||
11.66
|
||||
],
|
||||
[
|
||||
44.59,
|
||||
13.25
|
||||
],
|
||||
[
|
||||
49.17,
|
||||
18.99
|
||||
],
|
||||
[
|
||||
49.67,
|
||||
19.58
|
||||
],
|
||||
[
|
||||
50.11,
|
||||
20.46
|
||||
],
|
||||
[
|
||||
50.11,
|
||||
22.5
|
||||
],
|
||||
[
|
||||
49.67,
|
||||
23.39
|
||||
],
|
||||
[
|
||||
49.46,
|
||||
23.69
|
||||
],
|
||||
[
|
||||
47.06,
|
||||
26.57
|
||||
],
|
||||
[
|
||||
38.47,
|
||||
34.96
|
||||
],
|
||||
[
|
||||
38.3,
|
||||
35.09
|
||||
],
|
||||
[
|
||||
38.04,
|
||||
35.22
|
||||
],
|
||||
[
|
||||
37.36,
|
||||
34.7
|
||||
],
|
||||
[
|
||||
27.0,
|
||||
23.79
|
||||
],
|
||||
[
|
||||
26.63,
|
||||
23.35
|
||||
],
|
||||
[
|
||||
26.2,
|
||||
22.47
|
||||
]
|
||||
],
|
||||
"dx": 8.22,
|
||||
"dy": 13.81
|
||||
},
|
||||
{
|
||||
"name": "digest",
|
||||
"title": "The digest",
|
||||
"blurb": "A fact - sha256 of the content. Same bytes, same digest, forever. The digest does the pulling.",
|
||||
"points": [
|
||||
[
|
||||
49.82,
|
||||
20.3
|
||||
],
|
||||
[
|
||||
50.25,
|
||||
19.42
|
||||
],
|
||||
[
|
||||
51.05,
|
||||
18.51
|
||||
],
|
||||
[
|
||||
53.74,
|
||||
15.22
|
||||
],
|
||||
[
|
||||
56.92,
|
||||
11.39
|
||||
],
|
||||
[
|
||||
61.05,
|
||||
8.27
|
||||
],
|
||||
[
|
||||
61.91,
|
||||
7.62
|
||||
],
|
||||
[
|
||||
65.17,
|
||||
10.09
|
||||
],
|
||||
[
|
||||
67.62,
|
||||
12.55
|
||||
],
|
||||
[
|
||||
72.35,
|
||||
18.41
|
||||
],
|
||||
[
|
||||
73.15,
|
||||
19.3
|
||||
],
|
||||
[
|
||||
73.66,
|
||||
20.32
|
||||
],
|
||||
[
|
||||
73.66,
|
||||
22.33
|
||||
],
|
||||
[
|
||||
73.15,
|
||||
23.35
|
||||
],
|
||||
[
|
||||
69.95,
|
||||
27.03
|
||||
],
|
||||
[
|
||||
63.07,
|
||||
34.31
|
||||
],
|
||||
[
|
||||
62.01,
|
||||
35.09
|
||||
],
|
||||
[
|
||||
61.83,
|
||||
35.09
|
||||
],
|
||||
[
|
||||
60.62,
|
||||
34.18
|
||||
],
|
||||
[
|
||||
58.43,
|
||||
32.49
|
||||
],
|
||||
[
|
||||
53.6,
|
||||
27.21
|
||||
],
|
||||
[
|
||||
50.4,
|
||||
23.38
|
||||
],
|
||||
[
|
||||
49.89,
|
||||
22.34
|
||||
],
|
||||
[
|
||||
49.82,
|
||||
20.59
|
||||
]
|
||||
],
|
||||
"dx": -0.06,
|
||||
"dy": 13.82
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user