Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: CI/CD Pipeline

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
lint:
name: Lint Code
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install dependencies
run: npm install

- name: Run linter
run: npm run lint

docker:
name: Build & Push Docker Image
runs-on: ubuntu-latest
needs: lint
permissions:
contents: read
packages: write # needed for GHCR push

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }} ## {{ github.repository }} expands to <org>/<repo> automatically

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
57 changes: 56 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
.DS_Store
.DS_Store
# Node modules
node_modules/

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Dependency directories
jspm_packages/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# dotenv environment variables
.env
.env.test
.env.local
.env.*.local

# Build directories
dist/
build/
out/

# Coverage
coverage/

# IDE/Editor
.vscode/
.idea/
*.swp
*.swo
.DS_Store

# Docker
*.local
docker-compose.override.yml
42 changes: 42 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Stage 1: Build (includes devDependencies like eslint)
FROM node:22-alpine AS build

WORKDIR /usr/src/app

# Copy package.json and lock file first
COPY package*.json ./

# Install all deps (including dev)
RUN npm install

# Copy source code
COPY . .

# Run lint in build stage (optional safety net, CI already does this)
RUN npm run lint


# Stage 2: Production runtime
FROM node:22-alpine AS runtime

WORKDIR /usr/src/app

# Only copy production dependencies
COPY package*.json ./
RUN npm install --production

# Copy only necessary files from build stage
COPY --from=build /usr/src/app ./

# Run as non-root user (security best practice)
RUN addgroup -S app && adduser -S app -G app
USER app

ENV NEW_RELIC_NO_CONFIG_FILE=true
ENV NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=true
ENV NEW_RELIC_LOG=stdout
# Expose app port
EXPOSE 3000

# Start the app
CMD ["node", "index.js"]
7 changes: 7 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import globals from "globals";
import { defineConfig } from "eslint/config";

export default defineConfig([
{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } },
{ files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: globals.browser } },
]);
9 changes: 7 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require('newrelic');
const http = require('http');
const port = process.env.PORT || 3000;

Expand All @@ -7,6 +8,10 @@ const server = http.createServer((req, res) => {
res.end(msg);
});

server.listen(port, () => {
console.log(`Server running on http://localhost:${port}/`);
server.on('request', (req, res) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
});

server.listen(port, '0.0.0.0', () => {
console.log(`Server running on http://0.0.0.0:${port}/`);
});
Loading