All checks were successful
continuous-integration/drone/push Build is passing
Add the exact pip/npm registry endpoints and secret names, the scope-scoped (not global) registry requirement given Verdaccio's missing uplink, the host-scoped _auth rule, the per-item Drone shell caveat, and the Verdaccio storage uid/permissions gotcha behind the confusing "404 no such package available" error on publish.
917 lines
27 KiB
HTML
917 lines
27 KiB
HTML
|
||
<!doctype html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="icon" href="../assets/images/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
|
||
|
||
|
||
|
||
<title>Drone Pipelines - Aetos Standards — Reference</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/stylesheets/main.84d31ad4.min.css">
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
|
||
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
|
||
|
||
|
||
|
||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan">
|
||
|
||
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" for="__drawer"></label>
|
||
<div data-md-component="skip">
|
||
|
||
|
||
<a href="#drone-pipelines" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
</div>
|
||
<div data-md-component="announce">
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<header class="md-header md-header--shadow" data-md-component="header">
|
||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
<label class="md-header__button md-icon" for="__drawer">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||
</label>
|
||
<div class="md-header__title" data-md-component="header-title">
|
||
<div class="md-header__ellipsis">
|
||
<div class="md-header__topic">
|
||
<span class="md-ellipsis">
|
||
Aetos Standards — Reference
|
||
</span>
|
||
</div>
|
||
<div class="md-header__topic" data-md-component="header-topic">
|
||
<span class="md-ellipsis">
|
||
|
||
Drone Pipelines
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<form class="md-header__option" data-md-component="palette">
|
||
|
||
|
||
|
||
|
||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan" aria-hidden="true" type="radio" name="__palette" id="__palette_0">
|
||
|
||
|
||
</form>
|
||
|
||
|
||
|
||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-header__button md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
</label>
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||
<label class="md-search__icon md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||
</label>
|
||
<nav class="md-search__options" aria-label="Search">
|
||
|
||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
|
||
</a>
|
||
|
||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="search-result">
|
||
<div class="md-search-result__meta">
|
||
Initializing search
|
||
</div>
|
||
<ol class="md-search-result__list" role="presentation"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
</nav>
|
||
|
||
</header>
|
||
|
||
<div class="md-container" data-md-component="container">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<main class="md-main" data-md-component="main">
|
||
<div class="md-main__inner md-grid">
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||
<label class="md-nav__title" for="__drawer">
|
||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
Aetos Standards — Reference
|
||
</label>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href=".." class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Home
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../01_build_recipe/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Build Recipe
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../02_layering_rules.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Layering Rules
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../03_composition_root.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Composition Root
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../04_spec_first_routing.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Spec-First Routing
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../05_crud_template.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
CRUD Template
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../06_pipeline_scaffold.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Pipeline Scaffold
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../07_worker_and_progress.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Worker & Progress
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../08_human_in_loop.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Human-in-the-Loop
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../09_infra_adaptors.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Infra Adapters
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../10_drone_pipelines.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Drone Pipelines
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../11_versioning_and_changelog.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Versioning & Changelog
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../15_repository_inventory.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Repository Inventory
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../12_genericity_matrix.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Genericity Matrix
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../14_auth_integration.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Auth Integration
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../13_library_registry.md" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Library Registry
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Table of contents
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#summary" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Summary
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#type-a-pypi-package-python-libyaml" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Type A — PyPI package (python-lib.yaml)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#type-b-npm-package-npm-libyaml" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Type B — npm package (npm-lib.yaml)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#type-c-docker-deployment-service-dockeryaml" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Type C — Docker deployment (service-docker.yaml)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#what-not-to-do" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
What not to do
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#related" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Related
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-content" data-md-component="content">
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
|
||
|
||
|
||
<h1 id="drone-pipelines">Drone Pipelines</h1>
|
||
<h2 id="summary">Summary</h2>
|
||
<p>Every Aetos repository ships a Drone CI definition at <code>.drone.yml</code>. There is
|
||
exactly <strong>one standard per repository</strong>, and there are <strong>three types of file</strong>,
|
||
chosen by what the repository produces:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>File</th>
|
||
<th>Produces</th>
|
||
<th>Repos</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>A — PyPI package</strong></td>
|
||
<td><code>templates/drone/python-lib.yaml</code></td>
|
||
<td>A wheel + sdist on the private index</td>
|
||
<td><code>dagpipe</code>, <code>doc-forge</code>, <code>hexa</code>, <code>mail-intake</code>, <code>mongo-ops</code>, <code>omniread</code>, <code>openapi-first</code>, <code>py-jwt</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>B — npm package</strong></td>
|
||
<td><code>templates/drone/npm-lib.yaml</code></td>
|
||
<td>A package published to the private npm registry</td>
|
||
<td><code>doc-weaver</code>, <code>react-auth</code>, <code>react-openapi</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>C — Docker deployment</strong></td>
|
||
<td><code>templates/drone/service-docker.yaml</code></td>
|
||
<td>An image pushed to the registry and run as a container</td>
|
||
<td><code>auth-server</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>All templates run on <code>platform: linux / arm64</code> with the workspace at
|
||
<code>/drone/src</code>. Copy the matching template to <code><repo>/.drone.yml</code>; the Type A and
|
||
Type B files need no edits, the Type C file needs its placeholders filled in
|
||
(below).</p>
|
||
<blockquote>
|
||
<p>Conventions: <code>.drone.yml</code> (not <code>.yaml</code>), one file per repo, secrets injected
|
||
with <code>from_secret</code> — never commit credentials.</p>
|
||
</blockquote>
|
||
<hr />
|
||
<h2 id="type-a-pypi-package-python-libyaml">Type A — PyPI package (<code>python-lib.yaml</code>)</h2>
|
||
<p>For internal Python libraries. The file contains <strong>two pipelines</strong>:</p>
|
||
<ol>
|
||
<li><strong><code>build-and-publish-pypi</code></strong> — triggered on <code>tag</code>:</li>
|
||
<li><code>check-version</code> — <strong>guard</strong>: fail unless <code>$DRONE_TAG</code> equals the version in
|
||
<code>pyproject.toml</code>; then read <code>name</code> / <code>version</code> and query
|
||
<code>$PIP_REPO_URL/simple/<package>/</code>; if that version already exists, exit
|
||
<code>78</code> and skip the whole build (see <a href="../91_versioning_and_changelog/">91 – Versioning & Changelog</a>).</li>
|
||
<li><code>quality-gate</code> — install the package with its <code>[dev]</code> extras from the
|
||
private index, then <code>black --check</code>, <code>ruff check</code>, <code>mypy</code>, <code>pydoclint</code>,
|
||
<code>pytest</code>.</li>
|
||
<li><code>build-package</code> — <code>python -m build</code> into <code>dist/</code>.</li>
|
||
<li><code>upload-to-private-pypi</code> — <code>twine upload</code> to <code>$PIP_REPO_URL</code>.</li>
|
||
<li><strong><code>backfill-pypi-from-tags</code></strong> — triggered on <code>custom</code>:</li>
|
||
<li><code>fetch-tags</code> — <code>git fetch --tags --force</code>.</li>
|
||
<li><code>build-and-upload-missing</code> — walk every tag in version order, skip the ones
|
||
already on the index, <code>git checkout</code> the rest, verify <code>tag == pyproject
|
||
version</code> at that commit, build, and upload. This is the disaster-recovery /
|
||
replay path.</li>
|
||
</ol>
|
||
<p>Secrets: <code>PIP_REPO_URL</code>, <code>PIP_USERNAME</code>, <code>PIP_PASSWORD</code>.</p>
|
||
<ul>
|
||
<li><strong>Ship as-is.</strong> Package name and version come from <code>pyproject.toml</code>, so the
|
||
same file is reused verbatim across every library.</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="type-b-npm-package-npm-libyaml">Type B — npm package (<code>npm-lib.yaml</code>)</h2>
|
||
<p>For internal npm libraries (the React packages). The file contains <strong>two
|
||
pipelines</strong>:</p>
|
||
<ol>
|
||
<li><strong><code>build-and-publish-npm</code></strong> — triggered on <code>tag</code>:</li>
|
||
<li><code>check-version</code> — <strong>guard</strong>: fail unless <code>$DRONE_TAG</code> equals the version in
|
||
<code>package.json</code>; query <code>$NPM_REPO_URL</code> for <code>name@version</code>; if it already
|
||
exists, exit <code>78</code> and skip the whole build.</li>
|
||
<li><code>quality-gate</code> — write <code>.npmrc</code> from the secrets, <code>npm install</code>, then
|
||
<code>npm run quality-gate</code>.</li>
|
||
<li><code>build-package</code> — <code>npm install && npm run build</code> into <code>dist/</code>.</li>
|
||
<li><code>publish-to-private-npm</code> — <code>npm publish --access restricted</code> to
|
||
<code>$NPM_REPO_URL</code>.</li>
|
||
<li><strong><code>backfill-npm-from-tags</code></strong> — triggered on <code>custom</code>:</li>
|
||
<li><code>fetch-tags</code> — <code>git fetch --tags --force</code>.</li>
|
||
<li><code>build-and-publish-missing</code> — walk every tag in version order, skip the
|
||
ones already on the registry, <code>git checkout</code> the rest, verify
|
||
<code>tag == package.json version</code> at that commit, build, and publish. This is
|
||
the disaster-recovery / replay path.</li>
|
||
</ol>
|
||
<p>Secrets: <code>NPM_REPO_URL</code>, <code>NPM_USERNAME</code>, <code>NPM_PASSWORD</code>. Auth is a generated
|
||
<code>.npmrc</code> written from secrets at runtime. It sets a <strong>scope-scoped</strong> registry,
|
||
never a global one — <code>@aetoskia:registry=$NPM_REPO_URL</code> — plus a host-scoped
|
||
auth line <code>//<host>/:_auth=<base64(user:pass)></code>. The scope and host are derived
|
||
from <code>package.json</code> and <code>NPM_REPO_URL</code> respectively. A global <code>registry=</code> would
|
||
push public transitive deps through Verdaccio (which has no uplink) and 404
|
||
<code>npm install</code>; a bare <code>_auth=</code> is rejected by modern npm. See
|
||
<a href="../93_library_registry/">93 – Library Registry</a>.</p>
|
||
<ul>
|
||
<li><strong>Ship as-is.</strong> Name and version come from <code>package.json</code>, so the same file
|
||
is reused verbatim across every npm package.</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p><strong>Registry credentials are shared.</strong> The private npm registry (Verdaccio at
|
||
<code>npm.aetoskia.com</code>) authenticates against the <strong>same <code>htpasswd</code> file</strong> as the
|
||
private PyPI index and the Docker registry (<code>/auth/htpasswd</code>), with
|
||
registration disabled (<code>max_users: -1</code>). <code>NPM_USERNAME</code> / <code>NPM_PASSWORD</code> are
|
||
therefore the <strong>same credentials as <code>PIP_USERNAME</code> / <code>PIP_PASSWORD</code></strong> — no
|
||
separate npm account exists. Verdaccio's htpasswd plugin accepts <strong>bcrypt</strong>
|
||
hashes (<code>$2y$…</code>); non-bcrypt entries that pypiserver tolerates will not
|
||
authenticate against npm. See <a href="../93_library_registry/">93 – Library Registry</a>.</p>
|
||
</blockquote>
|
||
<hr />
|
||
<h2 id="type-c-docker-deployment-service-dockeryaml">Type C — Docker deployment (<code>service-docker.yaml</code>)</h2>
|
||
<p>For a service that ships a container image — a long-running API/worker, e.g.
|
||
<code>auth-server</code>. The file contains <strong>one pipeline</strong>, <code>build-and-deploy-service</code>,
|
||
triggered on <code>tag</code>:</p>
|
||
<ul>
|
||
<li><code>fetch-tags</code> — resolve the latest tag into <code>LATEST_TAG.txt</code>; fail if there
|
||
are no tags.</li>
|
||
<li><code>check-version</code> — <strong>guard</strong>: fail unless <code>$DRONE_TAG</code> equals the repo-root
|
||
<code>VERSION</code> file (see <a href="../91_versioning_and_changelog/">91 – Versioning & Changelog</a>).</li>
|
||
<li><code>check-remote-image</code> — if <code>aetos/<app>:<tag></code> already exists locally, exit
|
||
<code>78</code> and skip the build.</li>
|
||
<li><code>quality-gate</code> — install from <code>requirements.txt</code> and run
|
||
<code>black</code> / <code>ruff</code> / <code>mypy</code> / <code>pytest</code>.</li>
|
||
<li><code>build-image</code> — <code>docker build</code> with the private-index build args, tagged
|
||
<code>aetos/<app>:<tag></code> and <code>:latest</code>.</li>
|
||
<li><code>push-image</code> — login, re-tag with <code>$REGISTRY_HOST</code>, push both tags.</li>
|
||
<li><code>stop-old</code> — <code>docker rm -f <app></code>.</li>
|
||
<li><code>run-container</code> — <code>docker run -d --name <app> -p <host>:<container>
|
||
--restart always</code> with the service's <code>-e</code> environment.</li>
|
||
</ul>
|
||
<p>Secrets: <code>PIP_REPO_URL</code>, <code>PIP_USERNAME</code>, <code>PIP_PASSWORD</code> (service build args),
|
||
<code>REGISTRY_HOST</code>, <code>REGISTRY_USER</code>, <code>REGISTRY_PASS</code>, plus the service runtime env
|
||
(for example <code>MONGO_*</code>, <code>JWT_SECRET</code>).</p>
|
||
<p>Placeholders to fill in per repo:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Placeholder</th>
|
||
<th>Meaning</th>
|
||
<th>Example</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code><app></code></td>
|
||
<td>service image / container name</td>
|
||
<td><code>auth-server</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>aetos/<app></code></td>
|
||
<td>image repository on the registry</td>
|
||
<td><code>aetos/auth-server</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code><host-port></code> / <code><container-port></code></td>
|
||
<td>published : in-image port</td>
|
||
<td><code>9003:8000</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code><ENV_VAR></code></td>
|
||
<td>runtime environment variable</td>
|
||
<td><code>MONGO_HOST</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="what-not-to-do">What not to do</h2>
|
||
<ul>
|
||
<li>Do not hand-roll a fourth shape. A repo is exactly one of Type A, B, or C,
|
||
never combinations.</li>
|
||
<li>Do not inline secrets; always use <code>from_secret</code>.</li>
|
||
<li>Do not drop the <code>check-version</code> / <code>check-remote-image</code> skip — it guards the
|
||
index and the running container against duplicate publishes.</li>
|
||
<li>Do not drop the tag↔version guard. A Python package needs its
|
||
<code>pyproject.toml</code> version to equal the tag; an npm package needs its
|
||
<code>package.json</code> version; a service needs a root <code>VERSION</code> file. See
|
||
<a href="../91_versioning_and_changelog/">91 – Versioning & Changelog</a>.</li>
|
||
<li>Do not rename the pipelines; tooling and runbooks key off
|
||
<code>build-and-publish-pypi</code>, <code>build-and-publish-npm</code>,
|
||
<code>build-and-deploy-service</code>, and their <code>backfill-*-from-tags</code> companions.</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="related">Related</h2>
|
||
<ul>
|
||
<li><a href="../01_build_recipe/">01 – Build Recipe</a> · <a href="../30_infra_adaptors/">30 – Infra Adapters</a> · <a href="../91_versioning_and_changelog/">91 – Versioning & Changelog</a> · <a href="../93_library_registry/">93 – Library Registry</a></li>
|
||
</ul>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</article>
|
||
</div>
|
||
|
||
|
||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||
|
||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||
</div>
|
||
|
||
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
|
||
Back to top
|
||
</button>
|
||
|
||
</main>
|
||
|
||
<footer class="md-footer">
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-copyright">
|
||
|
||
|
||
Made with
|
||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||
Material for MkDocs
|
||
</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
<div class="md-dialog" data-md-component="dialog">
|
||
<div class="md-dialog__inner md-typeset"></div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.sections", "navigation.expand", "navigation.top", "navigation.instant", "navigation.tracking", "navigation.indexes", "content.code.copy", "content.code.annotate", "content.tabs.link", "content.action.edit", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
||
|
||
|
||
<script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
|
||
|
||
|
||
</body>
|
||
</html> |