Files
metapi/scripts/dev/docker.workflow.test.ts

60 lines
3.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
describe('docker and release workflows', () => {
it('uses Node 25 and only publishes amd64 and arm64 images', () => {
const dockerfile = readFileSync(resolve(process.cwd(), 'docker/Dockerfile'), 'utf8');
const ciWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/ci.yml'), 'utf8');
const releaseWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/release.yml'), 'utf8');
const ghcrWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/publish-ghcr.yml'), 'utf8');
expect(dockerfile).toContain('FROM node:25-bookworm-slim AS builder');
expect(dockerfile).toContain('FROM node:25-bookworm-slim');
for (const workflow of [ciWorkflow, releaseWorkflow, ghcrWorkflow]) {
expect(workflow).toContain('arch: amd64');
expect(workflow).toContain('arch: arm64');
expect(workflow).not.toContain('armv7');
expect(workflow).not.toContain('linux/arm/v7');
}
});
it('keeps the runtime volume and stable runner in the Compose image', () => {
const dockerfile = readFileSync(resolve(process.cwd(), 'docker/Dockerfile'), 'utf8');
const compose = readFileSync(resolve(process.cwd(), 'docker/docker-compose.yml'), 'utf8');
expect(dockerfile).toContain('docker-runner.mjs');
expect(dockerfile).toContain('VOLUME ["/app/data", "/app/runtime"]');
expect(dockerfile).toContain('TARGETARCH="${TARGETARCH}" node -e');
expect(dockerfile).toContain('npm rebuild better-sqlite3 --no-audit --no-fund');
expect(dockerfile).toContain("find node_modules -type f -name 'test_extension.node' -delete");
expect(dockerfile).toContain('find /tmp/metapi-bootstrap -type l -delete');
expect(dockerfile).not.toContain('COPY --from=builder /app/node_modules ./node_modules');
expect(dockerfile).not.toContain('COPY --from=builder /app/package.json ./package.json');
expect(dockerfile).not.toMatch(/helm|kubectl|deploy-helper/i);
expect(compose).toContain('./runtime:/app/runtime');
expect(compose).toContain('UPDATE_CENTER_RUNTIME_PERSISTENT: "true"');
});
it('smoke-tests each native server bundle before publishing it', () => {
const releaseWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/release.yml'), 'utf8');
expect(releaseWorkflow).toContain('Smoke-test server bundle and stable runner');
expect(releaseWorkflow).toContain('better-sqlite3');
expect(releaseWorkflow).toContain('docker-runner.mjs');
expect(releaseWorkflow).toContain('state.schemaVersion !== 1');
expect(releaseWorkflow).toContain('server-assets/checksums.txt');
});
it('does not install cluster tooling in the server image', () => {
const dockerfile = readFileSync(resolve(process.cwd(), 'docker/Dockerfile'), 'utf8');
expect(dockerfile).not.toMatch(/KUBECTL_VERSION|HELM_VERSION|dl\.k8s\.io|get\.helm\.sh/i);
expect(dockerfile).toContain('npm prune --omit=dev');
});
it('derives image names from the configured Docker Hub secret', () => {
const ciWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/ci.yml'), 'utf8');
const releaseWorkflow = readFileSync(resolve(process.cwd(), '.github/workflows/release.yml'), 'utf8');
expect(ciWorkflow).toContain('DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/metapi');
expect(releaseWorkflow).toContain('DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/metapi');
});
});