Skip to content

Commit 5c80f10

Browse files
committed
build: add basic nodejs app
1 parent 38817de commit 5c80f10

13 files changed

Lines changed: 298 additions & 0 deletions

.eslintignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
build/
3+
dist/
4+
coverage/
5+
*.config.ts
6+
*.config.js

.eslintrc.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"parserOptions": {
5+
"ecmaVersion": 2022,
6+
"sourceType": "module",
7+
"project": "./tsconfig.json"
8+
},
9+
"plugins": ["@typescript-eslint"],
10+
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
11+
"env": {
12+
"node": true,
13+
"es2022": true
14+
},
15+
"rules": {
16+
"@typescript-eslint/no-unused-vars": [
17+
"error",
18+
{ "argsIgnorePattern": "^_" }
19+
],
20+
"@typescript-eslint/explicit-function-return-type": "off",
21+
"@typescript-eslint/no-explicit-any": "warn"
22+
}
23+
}

.github/workflows/pull-request.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: PR
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
packages: write
14+
15+
jobs:
16+
ci:
17+
name: CI
18+
uses: getdevopspro/github-actions/.github/workflows/pull-request.yml@v1.0.1
19+
with:
20+
version-package: package.json
21+
version-package-lock: package-lock.json
22+
pre-lint-command: npm ci --ignore-scripts; npm run lint
23+
pre-test-unit-command: npm ci --ignore-scripts; npm run test

.github/workflows/release.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
tags:
9+
- 'v*'
10+
paths-ignore:
11+
- '**.md'
12+
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
permissions:
18+
contents: write
19+
packages: write
20+
checks: write
21+
22+
jobs:
23+
release:
24+
uses: getdevopspro/github-actions/.github/workflows/promote.yml@v1.0.1
25+
# secrets:
26+
# checkout-token: ${{ secrets.CHECKOUT_TOKEN }}
27+
with:
28+
version-package: package.json
29+
git-add-files: package.json
30+
git-user-name: getdevopspro-cibot
31+
git-user-email: 203600057+getdevopspro-cibot@users.noreply.github.com

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules/
2+
build/
3+
dist/
4+
.npm/
5+
*.log
6+
.env
7+
.env.local
8+
.DS_Store
9+
coverage/

Dockerfile

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Set default base image
2+
ARG ARG_IMAGE_FROM=docker.io/node:22
3+
4+
# -----------------------------------------------------------------------------
5+
# Stage 1: Nodeapp build
6+
# -----------------------------------------------------------------------------
7+
FROM ${ARG_IMAGE_FROM} AS nodeapp-base
8+
9+
WORKDIR /home/node/app
10+
11+
# -----------------------------------------------------------------------------
12+
# Stage 2: Nodeapp build
13+
# -----------------------------------------------------------------------------
14+
FROM nodeapp-base AS nodeapp-build
15+
16+
COPY static /home/node/app/static
17+
COPY *.json *.ts *.js *.cjs /home/node/app/
18+
COPY src /home/node/app/src
19+
20+
RUN --mount=type=cache,target=/home/node/.npm,sharing=locked \
21+
npm ci --ignore-scripts && \
22+
npm run build && \
23+
npm prune --omit=dev
24+
25+
# -----------------------------------------------------------------------------
26+
# TARGET 1: Nodeapp dev image
27+
# -----------------------------------------------------------------------------
28+
FROM nodeapp-base AS nodeapp-dev
29+
30+
ARG NODE_ENV=development
31+
ARG NODE_UID=1000
32+
ENV NODE_UID=$NODE_UID
33+
ENV NODE_ENV=$NODE_ENV
34+
ENV NPM_CONFIG_PREFIX=/home/node/app/.npm
35+
ENV NPM_CONFIG_CACHE=/home/node/app/.npm
36+
37+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
38+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
39+
set -ex && \
40+
apt-get update && \
41+
apt-get install --no-install-recommends -y \
42+
# Alphabetical order per best practice
43+
# (To make finding packages in lists easier and to help avoid duplicates)
44+
git \
45+
curl \
46+
make \
47+
tar \
48+
unzip \
49+
zip
50+
51+
RUN usermod -u $NODE_UID node && \
52+
groupmod -g $NODE_UID node && \
53+
usermod -d /home/node -m node && \
54+
chown -R $NODE_UID:$NODE_UID /home/node
55+
56+
USER $NODE_UID
57+
58+
CMD ["/bin/bash"]
59+
60+
# -----------------------------------------------------------------------------
61+
# TARGET 2: Nodeapp image
62+
# -----------------------------------------------------------------------------
63+
FROM nodeapp-base AS nodeapp
64+
65+
ARG ENV=production
66+
ARG PORT=3000
67+
ENV NODE_ENV=$ENV
68+
69+
COPY --from=nodeapp-build --chown=node:root /home/node/app/package.json .
70+
COPY --from=nodeapp-build --chown=node:root /home/node/app/node_modules node_modules/
71+
COPY --from=nodeapp-build --chown=node:root /home/node/app/build build/
72+
73+
EXPOSE $PORT
74+
75+
CMD [ "node", "-r", "dotenv/config", "build" ]
76+
77+
# -----------------------------------------------------------------------------
78+
# TARGET 3: Final image
79+
# -----------------------------------------------------------------------------
80+
FROM nodeapp AS final

docker-bake.hcl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// docker-bake.hcl
2+
target "docker-metadata-action" {}
3+
4+
target "build" {
5+
inherits = ["docker-metadata-action"]
6+
context = "./"
7+
dockerfile = "Dockerfile"
8+
platforms = [
9+
"linux/amd64",
10+
"linux/arm64",
11+
]
12+
}

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "github-actions-test",
3+
"version": "0.0.15",
4+
"type": "module",
5+
"scripts": {
6+
"build": "tsc",
7+
"start": "node build/index.js",
8+
"dev": "tsx watch src/index.ts",
9+
"lint": "eslint src/**/*.ts",
10+
"lint:fix": "eslint src/**/*.ts --fix",
11+
"test": "vitest run",
12+
"test:watch": "vitest"
13+
},
14+
"dependencies": {
15+
"express": "^4.18.2",
16+
"dotenv": "^16.3.1"
17+
},
18+
"devDependencies": {
19+
"@types/express": "^4.17.21",
20+
"@types/node": "^20.10.0",
21+
"@typescript-eslint/eslint-plugin": "^6.15.0",
22+
"@typescript-eslint/parser": "^6.15.0",
23+
"eslint": "^8.56.0",
24+
"typescript": "^5.3.3",
25+
"tsx": "^4.7.0",
26+
"vitest": "^1.1.0"
27+
}
28+
}

src/index.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, it, expect } from "vitest";
2+
3+
describe("Basic Tests", () => {
4+
it("should pass a simple test", () => {
5+
expect(true).toBe(true);
6+
});
7+
8+
it("should perform basic math", () => {
9+
expect(2 + 2).toBe(4);
10+
});
11+
12+
it("should handle strings", () => {
13+
const message = "Hello from GitHub Actions Test!";
14+
expect(message).toContain("GitHub Actions");
15+
});
16+
17+
it("should check environment", () => {
18+
expect(process.env.NODE_ENV).toBeDefined();
19+
});
20+
});

src/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import express from "express";
2+
3+
const app = express();
4+
const PORT = process.env.PORT || 3000;
5+
6+
app.get("/", (req, res) => {
7+
res.json({
8+
message: "Hello from GitHub Actions Test!",
9+
version: process.env.npm_package_version || "0.0.15",
10+
timestamp: new Date().toISOString(),
11+
platform: process.platform,
12+
arch: process.arch,
13+
});
14+
});
15+
16+
app.get("/health", (req, res) => {
17+
res.json({ status: "healthy", uptime: process.uptime() });
18+
});
19+
20+
app.listen(PORT, () => {
21+
console.log(`Server is running on port ${PORT}`);
22+
console.log(`Environment: ${process.env.NODE_ENV || "development"}`);
23+
});

0 commit comments

Comments
 (0)