Skip to content
Merged
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
32 changes: 32 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Normalize line endings repo-wide to avoid CRLF/LF churn on Windows.
# Default: treat files as text and store them with LF in git.
* text=auto eol=lf

# Windows scripts should remain CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.psm1 text eol=crlf

# Shell scripts should be LF
*.sh text eol=lf

# Common binaries
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.pdf binary
*.zip binary
*.gz binary
*.tgz binary
*.7z binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.mp4 binary
*.webm binary
*.mov binary
97 changes: 97 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: CI

on:
pull_request:
push:
branches:
- "**"

jobs:
commits:
name: Commit Messages (Conventional Commits)
runs-on: ubuntu-latest
steps:
- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install commitlint
run: npm ci --ignore-scripts

- name: Lint commits (PR)
if: ${{ github.event_name == 'pull_request' }}
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.sha }} --verbose

- name: Lint commits (push)
if: ${{ github.event_name == 'push' }}
run: npx commitlint --from ${{ github.event.before }} --to ${{ github.sha }} --verbose

ui:
name: UI Typecheck + Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: apps/ui/package-lock.json

- name: Install UI deps
working-directory: apps/ui
run: npm ci

- name: Typecheck
working-directory: apps/ui
run: npm run typecheck

- name: Build UI
working-directory: apps/ui
run: npm run build

python:
name: Python Compile / Requirements
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install API/Worker requirements
run: |
python -m pip install --upgrade pip
python -m pip install -r apps/api/requirements.txt
python -m pip install -r apps/worker/requirements.txt

- name: Compile Python sources
run: python -m compileall apps/api/src apps/worker/src

docker:
name: Docker Build Verification
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Build API image
run: docker build -t clevis-api -f apps/api/Dockerfile .

- name: Build Worker image
run: docker build -t clevis-worker -f apps/worker/Dockerfile apps/worker

- name: Build UI image
run: docker build -t clevis-ui -f apps/ui/Dockerfile apps/ui

57 changes: 57 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Release (Docker Images)

on:
workflow_dispatch:
push:
tags:
- "v*"

permissions:
contents: read
packages: write

jobs:
publish:
name: Build + Push
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Build + Push API
uses: docker/build-push-action@v6
with:
context: .
file: apps/api/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}-api:latest
ghcr.io/${{ github.repository }}-api:${{ github.ref_name }}

- name: Build + Push Worker
uses: docker/build-push-action@v6
with:
context: apps/worker
file: apps/worker/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}-worker:latest
ghcr.io/${{ github.repository }}-worker:${{ github.ref_name }}

- name: Build + Push UI
uses: docker/build-push-action@v6
with:
context: apps/ui
file: apps/ui/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}-ui:latest
ghcr.io/${{ github.repository }}-ui:${{ github.ref_name }}

5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
# Root-only: unqualified lib/ would ignore apps/ui/lib (Next/shadcn).
/lib/
/lib64/
parts/
sdist/
var/
Expand Down
2 changes: 2 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npx --no -- commitlint --edit "$1"
2 changes: 2 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npm --prefix apps/ui run typecheck
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,14 @@ Basic analytics for GitHub repos and organizations using the GitHub API.

Open the UI (default: `http://localhost:3000`).

## CI / CD
CI runs on **every pull request** and on **pushes to any branch** and verifies:
- UI TypeScript typecheck + production UI build
- Python source compilation
- Docker image build for `api`, `worker`, and `ui`

On version tags (`v*`), Docker images are published to GHCR so others can self-host without rebuilding from source:
- `ghcr.io/<owner>/<repo>-api`
- `ghcr.io/<owner>/<repo>-worker`
- `ghcr.io/<owner>/<repo>-ui`

6 changes: 6 additions & 0 deletions apps/ui/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
1 change: 1 addition & 0 deletions apps/ui/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
3 changes: 3 additions & 0 deletions apps/ui/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
// Avoid Next.js workspace-root inference issues when multiple lockfiles exist.
// This keeps file tracing scoped to the monorepo root.
outputFileTracingRoot: new URL("..", import.meta.url).pathname,
};

export default nextConfig;
2 changes: 2 additions & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"private": true,
"scripts": {
"dev": "next dev -p 3000",
"typecheck": "tsc -p tsconfig.json --noEmit",
"build": "next build",
"check": "npm run typecheck && npm run build",
"start": "next start -p 3000"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions apps/ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
Expand Down
4 changes: 4 additions & 0 deletions commitlint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
extends: ["@commitlint/config-conventional"],
}

Loading
Loading