feat(release): automate versioned releases on main

This commit is contained in:
2026-09-17 18:41:53 +02:00
parent dd37622bb6
commit 92859f3ccf
39 changed files with 7365 additions and 243 deletions

7
backend/.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
node_modules
dist
.git
.env
.env.*
!.env.example
*.log

View File

@@ -1,4 +1,4 @@
FROM node:20-bookworm-slim
FROM node:22-bookworm-slim
WORKDIR /usr/src/app
RUN apt-get update \
@@ -13,15 +13,18 @@ RUN apt-get update \
COPY package*.json ./
# Dev + Prod Dependencies (für TS-Build nötig).
# Sharp benötigt im Linux-Container native optionale Pakete, auch wenn das Lockfile auf macOS erzeugt wurde.
RUN npm install --include=optional \
&& npm install --include=optional --os=linux --cpu=x64 sharp
# Optionale native Linux-Pakete werden aus dem Lockfile installiert.
RUN npm ci --include=optional
# Restlicher Sourcecode
COPY . .
# TypeScript Build
RUN npm run build
ARG FEDEO_VERSION=0.0.0-dev
ARG FEDEO_REVISION=unknown
ENV FEDEO_VERSION=$FEDEO_VERSION
ENV FEDEO_REVISION=$FEDEO_REVISION
RUN npm run build && node --import tsx --test tests/*.test.ts
# Port freigeben
EXPOSE 3100

View File

@@ -5794,6 +5794,44 @@
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@infisical/sdk": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@infisical/sdk/-/sdk-4.0.6.tgz",

View File

@@ -2,6 +2,7 @@ import type { FastifyInstance } from "fastify"
import { centralServicesClient } from "./push-server.client"
import { secrets } from "../utils/secrets"
import { getBuildInfo } from "../utils/buildInfo"
const DEFAULT_HEARTBEAT_INTERVAL_MS = 5 * 60 * 1000
@@ -31,7 +32,7 @@ export function startCentralServicesHeartbeat(server: FastifyInstance) {
const sendHeartbeat = async () => {
try {
const response = await centralServicesClient.heartbeat({
fedeoVersion: secrets.FEDEO_VERSION || "selfhost",
fedeoVersion: getBuildInfo().version,
baseUrl: instanceBaseUrl(),
})
console.log(`✅ Central Services Heartbeat: ${response.instanceId} (${response.status})`)

View File

@@ -1,6 +1,12 @@
import { FastifyInstance } from "fastify";
import { getBuildInfo } from "../utils/buildInfo";
export default async function routes(server: FastifyInstance) {
server.get("/version", async (_request, reply) => {
reply.header("Cache-Control", "no-store");
return getBuildInfo();
});
server.get("/ping", async () => {
// Testquery gegen DB
const result = await server.db.execute("SELECT NOW()");
@@ -10,4 +16,4 @@ export default async function routes(server: FastifyInstance) {
db: JSON.stringify(result.rows[0]),
};
});
}
}

View File

@@ -0,0 +1,7 @@
export function getBuildInfo() {
return {
product: "FEDEO",
version: process.env.FEDEO_VERSION || "0.0.0-dev",
revision: process.env.FEDEO_REVISION || "unknown",
};
}

View File

@@ -0,0 +1,25 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import Fastify from 'fastify';
import healthRoutes from '../src/routes/health';
test('version endpoint reports image metadata without a database or authentication', async () => {
const previousVersion = process.env.FEDEO_VERSION;
const previousRevision = process.env.FEDEO_REVISION;
process.env.FEDEO_VERSION = '1.2.3';
process.env.FEDEO_REVISION = 'a'.repeat(40);
const app = Fastify();
try {
await app.register(healthRoutes);
const response = await app.inject({ method: 'GET', url: '/version' });
assert.equal(response.statusCode, 200);
assert.equal(response.headers['cache-control'], 'no-store');
assert.deepEqual(response.json(), { product: 'FEDEO', version: '1.2.3', revision: 'a'.repeat(40) });
} finally {
await app.close();
if (previousVersion === undefined) delete process.env.FEDEO_VERSION;
else process.env.FEDEO_VERSION = previousVersion;
if (previousRevision === undefined) delete process.env.FEDEO_REVISION;
else process.env.FEDEO_REVISION = previousRevision;
}
});