Initial commit
This commit is contained in:
commit
5d4f3c0965
11 changed files with 194 additions and 0 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.forgejo
|
||||||
16
.forgejo/workflows/ci.yaml
Normal file
16
.forgejo/workflows/ci.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Full pipeline: checks -> build+push image -> bump tag in gitops repo.
|
||||||
|
# The heavy lifting lives in nholtware/workflows; this file is all a new
|
||||||
|
# app needs.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checks:
|
||||||
|
uses: nholtware/workflows/.forgejo/workflows/node-checks.yaml@main
|
||||||
|
deploy:
|
||||||
|
needs: checks
|
||||||
|
uses: nholtware/workflows/.forgejo/workflows/build-and-deploy.yaml@main
|
||||||
|
with:
|
||||||
|
app-name: demo-app
|
||||||
|
secrets: inherit
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Build stage: full toolchain, compile TS -> dist/
|
||||||
|
FROM node:22 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Runtime stage: no dev deps (this service has zero runtime deps)
|
||||||
|
FROM node:22-slim
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ARG GIT_SHA=dev
|
||||||
|
ENV GIT_SHA=${GIT_SHA}
|
||||||
|
COPY --from=build /app/dist ./dist
|
||||||
|
COPY package.json ./
|
||||||
|
USER node
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
26
README.md
Normal file
26
README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# demo-app
|
||||||
|
|
||||||
|
Minimal TypeScript microservice proving out the homelab CI/CD pipeline:
|
||||||
|
|
||||||
|
```
|
||||||
|
push to main
|
||||||
|
-> Forgejo Actions (nholtware/workflows reusable workflows)
|
||||||
|
checks: npm ci + lint + test (docker label)
|
||||||
|
build: docker build + push -> 192.168.1.230:3000/nholtware/demo-app:<sha>
|
||||||
|
deploy: kustomize edit set image -> commit to nholtware/gitops
|
||||||
|
-> ArgoCD syncs gitops -> k3s
|
||||||
|
-> served by Traefik at Host: demo-app.k3s.local
|
||||||
|
```
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
- `GET /` — JSON with service name, git sha, pod hostname
|
||||||
|
- `GET /healthz` — liveness/readiness probe target
|
||||||
|
|
||||||
|
## Local dev
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm ci
|
||||||
|
npm test
|
||||||
|
npm run build && npm start # listens on :3000 (PORT to override)
|
||||||
|
```
|
||||||
50
package-lock.json
generated
Normal file
50
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "demo-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "demo-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"typescript": "^5.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
|
||||||
|
"integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
package.json
Normal file
20
package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "demo-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "TypeScript microservice demonstrating the Forgejo Actions -> registry -> ArgoCD -> k3s pipeline",
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"test": "npm run build && node --test dist/index.test.js",
|
||||||
|
"lint": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.6.0",
|
||||||
|
"@types/node": "^22.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/greeting.ts
Normal file
3
src/greeting.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function greeting(): string {
|
||||||
|
return "Hello from the homelab pipeline!";
|
||||||
|
}
|
||||||
7
src/index.test.ts
Normal file
7
src/index.test.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { greeting } from "./greeting.js";
|
||||||
|
|
||||||
|
test("greeting mentions the homelab", () => {
|
||||||
|
assert.match(greeting(), /homelab/);
|
||||||
|
});
|
||||||
32
src/index.ts
Normal file
32
src/index.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { createServer } from "node:http";
|
||||||
|
import { hostname } from "node:os";
|
||||||
|
import { greeting } from "./greeting.js";
|
||||||
|
|
||||||
|
const port = Number(process.env.PORT ?? 3000);
|
||||||
|
const sha = process.env.GIT_SHA ?? "dev";
|
||||||
|
|
||||||
|
const server = createServer((req, res) => {
|
||||||
|
if (req.url === "/healthz") {
|
||||||
|
res.writeHead(200, { "content-type": "text/plain" });
|
||||||
|
res.end("ok");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.writeHead(200, { "content-type": "application/json" });
|
||||||
|
res.end(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
service: "demo-app",
|
||||||
|
message: greeting(),
|
||||||
|
sha,
|
||||||
|
pod: hostname(),
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
) + "\n",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`demo-app ${sha} listening on :${port}`);
|
||||||
|
});
|
||||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue