mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 17:20:33 +00:00
feat: add integration tests, release runbook, cache fallback
- CI: integration job — build NORA, docker push/pull, npm publish/install, API checks - release: cache-from with ignore-error=true (no dependency on localhost:5000) - RELEASE_RUNBOOK.md: rollback procedure, deploy order, verification steps
This commit is contained in:
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@@ -85,3 +85,74 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
sarif_file: trivy-fs.sarif
|
sarif_file: trivy-fs.sarif
|
||||||
category: trivy-fs
|
category: trivy-fs
|
||||||
|
|
||||||
|
integration:
|
||||||
|
name: Integration
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Cache cargo
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
|
- name: Build NORA
|
||||||
|
run: cargo build --release --package nora-registry
|
||||||
|
|
||||||
|
# -- Start NORA --
|
||||||
|
- name: Start NORA
|
||||||
|
run: |
|
||||||
|
NORA_STORAGE_PATH=/tmp/nora-data ./target/release/nora &
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
curl -sf http://localhost:4000/health && break || sleep 2
|
||||||
|
done
|
||||||
|
curl -sf http://localhost:4000/health | jq .
|
||||||
|
|
||||||
|
# -- Docker push/pull --
|
||||||
|
- name: Configure Docker for insecure registry
|
||||||
|
run: |
|
||||||
|
echo '{"insecure-registries": ["localhost:4000"]}' | sudo tee /etc/docker/daemon.json
|
||||||
|
sudo systemctl restart docker
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
- name: Docker — push and pull image
|
||||||
|
run: |
|
||||||
|
docker pull alpine:3.20
|
||||||
|
docker tag alpine:3.20 localhost:4000/test/alpine:integration
|
||||||
|
docker push localhost:4000/test/alpine:integration
|
||||||
|
docker rmi localhost:4000/test/alpine:integration
|
||||||
|
docker pull localhost:4000/test/alpine:integration
|
||||||
|
echo "Docker push/pull OK"
|
||||||
|
|
||||||
|
- name: Docker — verify catalog and tags
|
||||||
|
run: |
|
||||||
|
curl -sf http://localhost:4000/v2/_catalog | jq .
|
||||||
|
curl -sf http://localhost:4000/v2/test/alpine/tags/list | jq .
|
||||||
|
|
||||||
|
# -- npm publish/install --
|
||||||
|
- name: npm — publish and install package
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/test-pkg && cd /tmp/test-pkg
|
||||||
|
echo '{"name":"nora-integration-test","version":"1.0.0","description":"test"}' > package.json
|
||||||
|
echo "module.exports = true;" > index.js
|
||||||
|
npm publish --registry http://localhost:4000/npm/
|
||||||
|
cd /tmp && mkdir -p install-test && cd install-test
|
||||||
|
npm init -y > /dev/null
|
||||||
|
npm install nora-integration-test --registry http://localhost:4000/npm/
|
||||||
|
echo "npm publish/install OK"
|
||||||
|
|
||||||
|
# -- API checks --
|
||||||
|
- name: API — health, ready, metrics
|
||||||
|
run: |
|
||||||
|
curl -sf http://localhost:4000/health | jq .status
|
||||||
|
curl -sf http://localhost:4000/ready
|
||||||
|
curl -sf http://localhost:4000/metrics | head -5
|
||||||
|
echo "API checks OK"
|
||||||
|
|
||||||
|
- name: Stop NORA
|
||||||
|
if: always()
|
||||||
|
run: pkill nora || true
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta-alpine.outputs.tags }}
|
tags: ${{ steps.meta-alpine.outputs.tags }}
|
||||||
labels: ${{ steps.meta-alpine.outputs.labels }}
|
labels: ${{ steps.meta-alpine.outputs.labels }}
|
||||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine
|
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine,ignore-error=true
|
||||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine,mode=max
|
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:alpine,mode=max
|
||||||
|
|
||||||
# ── RED OS ───────────────────────────────────────────────────────────────
|
# ── RED OS ───────────────────────────────────────────────────────────────
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta-redos.outputs.tags }}
|
tags: ${{ steps.meta-redos.outputs.tags }}
|
||||||
labels: ${{ steps.meta-redos.outputs.labels }}
|
labels: ${{ steps.meta-redos.outputs.labels }}
|
||||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos
|
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos,ignore-error=true
|
||||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos,mode=max
|
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:redos,mode=max
|
||||||
|
|
||||||
# ── Astra Linux SE ───────────────────────────────────────────────────────
|
# ── Astra Linux SE ───────────────────────────────────────────────────────
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta-astra.outputs.tags }}
|
tags: ${{ steps.meta-astra.outputs.tags }}
|
||||||
labels: ${{ steps.meta-astra.outputs.labels }}
|
labels: ${{ steps.meta-astra.outputs.labels }}
|
||||||
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra
|
cache-from: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra,ignore-error=true
|
||||||
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra,mode=max
|
cache-to: type=registry,ref=${{ env.NORA }}/${{ env.IMAGE_NAME }}-cache:astra,mode=max
|
||||||
|
|
||||||
# ── Smoke test ──────────────────────────────────────────────────────────
|
# ── Smoke test ──────────────────────────────────────────────────────────
|
||||||
|
|||||||
80
RELEASE_RUNBOOK.md
Normal file
80
RELEASE_RUNBOOK.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Release Runbook
|
||||||
|
|
||||||
|
## Release process
|
||||||
|
|
||||||
|
1. Update version in `nora-registry/Cargo.toml`
|
||||||
|
2. Update `CHANGELOG.md`
|
||||||
|
3. Commit: `chore: bump version to X.Y.Z`
|
||||||
|
4. Tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
||||||
|
5. CI builds binary + 3 Docker images (alpine, redos, astra)
|
||||||
|
6. CI runs trivy scan on all images
|
||||||
|
7. CI creates GitHub Release with binary, checksums, SBOM
|
||||||
|
|
||||||
|
## Deploy order
|
||||||
|
|
||||||
|
1. **ai-server** (internal) — update first, verify
|
||||||
|
2. **PROD** — update after ai-server is stable
|
||||||
|
3. **GHCR** — public images pushed by CI automatically
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
### Quick rollback (revert to previous version)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On ai-server
|
||||||
|
docker pull ghcr.io/getnora-io/nora:PREVIOUS_VERSION
|
||||||
|
docker stop nora && docker rm nora
|
||||||
|
docker run -d --name nora -p 4000:4000 \
|
||||||
|
-v /srv/nora-data:/data \
|
||||||
|
ghcr.io/getnora-io/nora:PREVIOUS_VERSION
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete a broken release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Delete GitHub Release (keeps tag)
|
||||||
|
gh release delete vX.Y.Z --yes
|
||||||
|
|
||||||
|
# 2. Delete tag
|
||||||
|
git tag -d vX.Y.Z
|
||||||
|
git push origin :refs/tags/vX.Y.Z
|
||||||
|
|
||||||
|
# 3. Delete GHCR images (all variants)
|
||||||
|
for suffix in "" "-redos" "-astra"; do
|
||||||
|
gh api -X DELETE /user/packages/container/nora/versions \
|
||||||
|
--jq ".[] | select(.metadata.container.tags[] | contains(\"X.Y.Z${suffix}\")) | .id" \
|
||||||
|
| xargs -I{} gh api -X DELETE /user/packages/container/nora/versions/{}
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Binary rollback
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -LO https://github.com/getnora-io/nora/releases/download/vPREVIOUS/nora-linux-amd64
|
||||||
|
chmod +x nora-linux-amd64
|
||||||
|
sudo mv nora-linux-amd64 /usr/local/bin/nora
|
||||||
|
sudo systemctl restart nora
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification after deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check
|
||||||
|
curl -sf http://localhost:4000/health | jq .
|
||||||
|
|
||||||
|
# Docker API
|
||||||
|
curl -sf http://localhost:4000/v2/ | jq .
|
||||||
|
|
||||||
|
# Push test image
|
||||||
|
docker pull alpine:3.20
|
||||||
|
docker tag alpine:3.20 localhost:4000/test/alpine:smoke
|
||||||
|
docker push localhost:4000/test/alpine:smoke
|
||||||
|
docker pull localhost:4000/test/alpine:smoke
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
- Self-hosted runner uses localhost:5000 (NORA) for buildx cache.
|
||||||
|
If NORA is down during release, build continues without cache (ignore-error=true).
|
||||||
|
- Trivy image scan runs after push to localhost:5000 but before GitHub Release.
|
||||||
|
A failed scan blocks the release.
|
||||||
Reference in New Issue
Block a user