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