Add npm backfill pipeline, shared htpasswd registry auth docs
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- drone npm template gains backfill-npm-from-tags (mirrors PyPI) - document the shared /auth/htpasswd credential model (npm + PyPI + docker) and Verdaccio's bcrypt requirement - point doc-weaver source/path at ../react/doc-weaver
This commit is contained in:
@@ -52,17 +52,24 @@ Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD`.
|
||||
|
||||
## Type B — npm package (`npm-lib.yaml`)
|
||||
|
||||
For internal npm libraries (the React packages). One pipeline,
|
||||
**`build-and-publish-npm`**, triggered on `tag`:
|
||||
For internal npm libraries (the React packages). The file contains **two
|
||||
pipelines**:
|
||||
|
||||
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the version in
|
||||
`package.json`; query `$NPM_REPO_URL` for `name@version`; if it already
|
||||
exists, exit `78` and skip the whole build.
|
||||
- `quality-gate` — write `.npmrc` from the secrets, `npm install`, then
|
||||
`npm run quality-gate`.
|
||||
- `build-package` — `npm install && npm run build` into `dist/`.
|
||||
- `publish-to-private-npm` — `npm publish --access restricted` to
|
||||
`$NPM_REPO_URL`.
|
||||
1. **`build-and-publish-npm`** — triggered on `tag`:
|
||||
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the version in
|
||||
`package.json`; query `$NPM_REPO_URL` for `name@version`; if it already
|
||||
exists, exit `78` and skip the whole build.
|
||||
- `quality-gate` — write `.npmrc` from the secrets, `npm install`, then
|
||||
`npm run quality-gate`.
|
||||
- `build-package` — `npm install && npm run build` into `dist/`.
|
||||
- `publish-to-private-npm` — `npm publish --access restricted` to
|
||||
`$NPM_REPO_URL`.
|
||||
2. **`backfill-npm-from-tags`** — triggered on `custom`:
|
||||
- `fetch-tags` — `git fetch --tags --force`.
|
||||
- `build-and-publish-missing` — walk every tag in version order, skip the
|
||||
ones already on the registry, `git checkout` the rest, verify
|
||||
`tag == package.json version` at that commit, build, and publish. This is
|
||||
the disaster-recovery / replay path.
|
||||
|
||||
Secrets: `NPM_REPO_URL`, `NPM_USERNAME`, `NPM_PASSWORD`. Auth is a generated
|
||||
`.npmrc` (`registry=` + `_auth=` base64 of `user:pass`) written from secrets at
|
||||
@@ -71,6 +78,15 @@ runtime — the committed `.npmrc` in a repo only points at the registry URL.
|
||||
- **Ship as-is.** Name and version come from `package.json`, so the same file
|
||||
is reused verbatim across every npm package.
|
||||
|
||||
> **Registry credentials are shared.** The private npm registry (Verdaccio at
|
||||
> `npm.aetoskia.com`) authenticates against the **same `htpasswd` file** as the
|
||||
> private PyPI index and the Docker registry (`/auth/htpasswd`), with
|
||||
> registration disabled (`max_users: -1`). `NPM_USERNAME` / `NPM_PASSWORD` are
|
||||
> therefore the **same credentials as `PIP_USERNAME` / `PIP_PASSWORD`** — no
|
||||
> separate npm account exists. Verdaccio's htpasswd plugin accepts **bcrypt**
|
||||
> hashes (`$2y$…`); non-bcrypt entries that pypiserver tolerates will not
|
||||
> authenticate against npm. See [93 – Library Registry](93_library_registry.md).
|
||||
|
||||
---
|
||||
|
||||
## Type C — Docker deployment (`service-docker.yaml`)
|
||||
@@ -121,8 +137,8 @@ Placeholders to fill in per repo:
|
||||
`package.json` version; a service needs a root `VERSION` file. See
|
||||
[91 – Versioning & Changelog](91_versioning_and_changelog.md).
|
||||
- Do not rename the pipelines; tooling and runbooks key off
|
||||
`build-and-publish-pypi`, `build-and-publish-npm`, and
|
||||
`build-and-deploy-service`.
|
||||
`build-and-publish-pypi`, `build-and-publish-npm`,
|
||||
`build-and-deploy-service`, and their `backfill-*-from-tags` companions.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -53,6 +53,17 @@ endpoint (or, for the auth-server service, a spec pointer). It also serves the
|
||||
pinned auth-server OpenAPI contract at `docs://spec`. Query it before writing
|
||||
any infrastructure.
|
||||
|
||||
## Publishing & registry auth
|
||||
|
||||
Python packages publish to the private index (`pip.aetoskia.com`, `pypiserver`)
|
||||
and npm packages to the private registry (`npm.aetoskia.com`, Verdaccio). Both
|
||||
authenticate against the **same `htpasswd` file** (`/auth/htpasswd`) used by the
|
||||
Docker registry and the Traefik basic-auth middleware; registration is disabled,
|
||||
so the CI credentials (`PIP_USERNAME` / `PIP_PASSWORD`, `NPM_USERNAME` /
|
||||
`NPM_PASSWORD`) are one and the same account — there is no separate npm user.
|
||||
Verdaccio requires **bcrypt** htpasswd entries; see
|
||||
[90 – Drone Pipelines](90_drone_pipelines.md) for the CI wiring.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Hexa** is a full library with its own MCP server on `:8008`: read
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
{
|
||||
"name": "doc-weaver",
|
||||
"type": "package",
|
||||
"path": "doc-weaver",
|
||||
"path": "react/doc-weaver",
|
||||
"version_source": "package.json",
|
||||
"drone": "npm-lib.yaml",
|
||||
"trigger": "tag",
|
||||
|
||||
@@ -87,3 +87,70 @@ steps:
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: backfill-npm-from-tags
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
workspace:
|
||||
path: /drone/src
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: build-and-publish-missing
|
||||
image: node:22-alpine
|
||||
environment:
|
||||
NPM_REPO_URL:
|
||||
from_secret: NPM_REPO_URL
|
||||
NPM_USERNAME:
|
||||
from_secret: NPM_USERNAME
|
||||
NPM_PASSWORD:
|
||||
from_secret: NPM_PASSWORD
|
||||
commands:
|
||||
- apk add --no-cache git
|
||||
- echo "registry=$NPM_REPO_URL" > /tmp/.npmrc
|
||||
- echo "_auth=$(echo -n "$NPM_USERNAME:$NPM_PASSWORD" | base64 | tr -d '\n')" >> /tmp/.npmrc
|
||||
- |
|
||||
set -e
|
||||
|
||||
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
||||
echo "Package: $PACKAGE_NAME"
|
||||
|
||||
for TAG in $(git tag --sort=version:refname); do
|
||||
VERSION="$TAG"
|
||||
echo "Version: $VERSION"
|
||||
|
||||
if npm view "$PACKAGE_NAME@$VERSION" version --userconfig /tmp/.npmrc >/dev/null 2>&1; then
|
||||
echo "Exists, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
git checkout --force "$TAG"
|
||||
|
||||
TAG_VERSION=$(node -p "require('./package.json').version")
|
||||
if [ "$TAG" != "$TAG_VERSION" ]; then
|
||||
echo "Tag $TAG != package.json version $TAG_VERSION at that commit; skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Building $VERSION"
|
||||
rm -rf dist
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
echo "Publishing $VERSION"
|
||||
npm publish --access restricted --userconfig /tmp/.npmrc
|
||||
done
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- custom
|
||||
|
||||
@@ -104,9 +104,10 @@ commit, and move the tag **only** if it was never pushed.
|
||||
If an older tag never published (a past version-file mismatch, an index
|
||||
outage):
|
||||
|
||||
- **Package:** run the `backfill-pypi-from-tags` pipeline (custom event). It
|
||||
checks out every tag, verifies `tag == pyproject version` at that commit,
|
||||
and uploads the ones missing from the index. Already-fixed history is never
|
||||
- **Package:** run the matching `backfill-*-from-tags` pipeline (custom event) —
|
||||
`backfill-pypi-from-tags` for Python, `backfill-npm-from-tags` for npm. It
|
||||
checks out every tag, verifies `tag ==` the version file at that commit, and
|
||||
uploads the ones missing from the index. Already-fixed history is never
|
||||
rewritten.
|
||||
- **Service:** rebuild the image for the missing tag with a fresh commit and a
|
||||
new patch version; do not move a published tag.
|
||||
|
||||
Reference in New Issue
Block a user