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
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

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

jobs:
backend:
name: Backend compile check
runs-on: ubuntu-latest

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

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Compile backend modules
run: python -m compileall -q backend

frontend:
name: Frontend lint, typecheck, and build
runs-on: ubuntu-latest

defaults:
run:
working-directory: frontend

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

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Typecheck
run: npx tsc --noEmit

- name: Build
run: npm run build
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,16 @@ python3 app.py
```bash
cd frontend
npm install

# Optional: point the frontend at a non-local backend API.
cp .env.example .env.local

npm run dev
```

The frontend reads `NEXT_PUBLIC_API_BASE_URL` from `frontend/.env.local`.
The default is `http://127.0.0.1:5000/` for local development.

### 4. Access the Application

- Frontend: http://localhost:3000
Expand Down Expand Up @@ -141,6 +148,12 @@ Analyzes a GitHub repository and its description for SDG alignment using Aurora
4. Add tests if applicable
5. Submit a pull request

## Deployment

See [Deployment Readiness Plan](docs/deployment-readiness.md) for the current
hosting recommendation, CI checks, and follow-up work for the DMP 2026 MVP
requirements in issue #8.

## License

See LICENSE file for details.
Expand Down
64 changes: 64 additions & 0 deletions docs/deployment-readiness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Deployment Readiness Plan

This document maps the current application to the hosting and CI/CD part of
[DMP 2026 issue #8](https://github.com/chaoss/UNSDG-classifier-tool/issues/8).
It does not close the full MVP issue; the DPG registry evaluation and accuracy
work remain separate milestones.

## Recommended Hosting Shape

The current architecture is a split application:

- Next.js frontend in `frontend/`
- Flask backend in `backend/`
- External classifier services called from the backend
- GitHub API access for repository metadata

Because GitHub Pages cannot run the Flask backend or Python ML dependencies, a
static-only GitHub Pages deployment is not sufficient for the full tool. A
practical zero-budget MVP path is:

1. Host the Next.js frontend on a static/Node-capable frontend host.
2. Host the Flask backend on a Python-capable host.
3. Configure the frontend with `NEXT_PUBLIC_API_BASE_URL` so staging and
production can point at different backend URLs without code changes.
4. Keep classifier endpoints, tokens, and rate-limit-sensitive settings outside
source code through environment variables.

## Configuration

Frontend configuration:

```bash
cd frontend
cp .env.example .env.local
# Set NEXT_PUBLIC_API_BASE_URL to the hosted Flask API origin.
```

Backend configuration should keep external API endpoints and credentials in the
host environment. If PR #23 is merged first, the backend will have a centralized
`backend/config.py` and `backend/.env.example` for these values.

## CI/CD Baseline

This repository now includes `.github/workflows/ci.yml`, which runs on every
pull request and push to `main`:

- Python backend compile check with Python 3.11
- Frontend dependency install from `package-lock.json`
- ESLint
- TypeScript typecheck
- Next.js production build

That gives maintainers a merge gate before connecting a deployment provider.

## Follow-Up Work For #8

- Add a backend health endpoint for deployment monitoring.
- Add backend tests around API request/response contracts.
- Add deployment provider-specific instructions once maintainers choose the
final no-budget hosting platform.
- Add the DPG registry evaluation spreadsheet/notebook for the 100-project and
85% accuracy acceptance criteria.
- Add API throttling/backoff around Aurora and OSDG calls to preserve the
documented one-request-per-second limit.
4 changes: 4 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copy this file to frontend/.env.local when the backend API is hosted
# somewhere other than the local Flask default.

NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:5000/
1 change: 1 addition & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ yarn-error.log*

# env files (can opt-in for committing if needed)
.env*
!.env.example

# vercel
.vercel
Expand Down
9 changes: 7 additions & 2 deletions frontend/services/api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import axios from "axios";
import {
ResultsData,
SDGClassificationRequest,
SDGClassificationResponse,
} from "@/types/main";

const API_BASE_URL = "http://127.0.0.1:5000/";
const DEFAULT_API_BASE_URL = "http://127.0.0.1:5000/";

const normalizeBaseUrl = (url: string) => (url.endsWith("/") ? url : `${url}/`);

const API_BASE_URL = normalizeBaseUrl(
process.env.NEXT_PUBLIC_API_BASE_URL || DEFAULT_API_BASE_URL,
);

const apiClient = axios.create({
baseURL: API_BASE_URL,
Expand Down
21 changes: 21 additions & 0 deletions frontend/types/global.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
declare module "*.css";

declare module "*.png" {
import type { StaticImageData } from "next/image";

const content: StaticImageData;
export default content;
}

declare module "*.jpg" {
import type { StaticImageData } from "next/image";

const content: StaticImageData;
export default content;
}

declare module "*.jpeg" {
import type { StaticImageData } from "next/image";

const content: StaticImageData;
export default content;
}
2 changes: 1 addition & 1 deletion frontend/types/main.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface SDGClassificationResponse {
projectName?: string;
projectUrl?: string;
repo_url?: string;
[key: string]: any;
[key: string]: unknown;
}

export type SDGCardProps = {
Expand Down