Skip to content

Refactor/monorepo with spec-driven development#321

Draft
wildan3105 wants to merge 73 commits intomasterfrom
refactor/monorepo-with-sdd
Draft

Refactor/monorepo with spec-driven development#321
wildan3105 wants to merge 73 commits intomasterfrom
refactor/monorepo-with-sdd

Conversation

@wildan3105
Copy link
Owner

@wildan3105 wildan3105 commented Feb 14, 2026

Description

Major overhaul

Also:

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Other (security config, docs update, etc.)

Screenshot of change (if there's UI update)

Comment on lines +71 to +89
name: Docker Build
runs-on: ubuntu-latest
needs: test

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

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: ./backend
push: false
tags: gitlingo-backend:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 2 days ago

To fix the problem, add an explicit permissions: block that grants only the minimal rights needed by these jobs. Since the jobs only check out code, set up Node, run tests/lint/build, upload coverage to Codecov, and build a Docker image without pushing, they only need read access to the repository contents.

The most straightforward fix without changing functionality is to add a workflow-level permissions: block right after the name: and before on::

permissions:
  contents: read

This will apply to both test and docker jobs, restricting GITHUB_TOKEN to read-only repository contents, which is sufficient for actions/checkout@v4 and the other used actions. No other scopes (like packages, pull-requests, or id-token) are required based on the provided snippet, and we are not pushing Docker images or writing to GitHub APIs.

Concretely:

  • Edit .github/workflows/backend-ci.yml.
  • Insert the permissions: block near the top of the file (e.g., between line 1 and line 3, after name: Backend CI and the blank line).
  • No imports, methods, or additional definitions are needed, as this is purely a workflow configuration change.
Suggested changeset 1
.github/workflows/backend-ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml
--- a/.github/workflows/backend-ci.yml
+++ b/.github/workflows/backend-ci.yml
@@ -1,5 +1,8 @@
 name: Backend CI
 
+permissions:
+  contents: read
+
 on:
   push:
     branches: [master, main, develop]
EOF
@@ -1,5 +1,8 @@
name: Backend CI

permissions:
contents: read

on:
push:
branches: [master, main, develop]
Copilot is powered by AI and may make mistakes. Always verify output.
@wildan3105
Copy link
Owner Author

@copilot please review

Copy link
Contributor

Copilot AI commented Feb 14, 2026

@wildan3105 I've opened a new pull request, #322, to work on those changes. Once the pull request is ready, I'll request review from you.

@wildan3105 wildan3105 changed the title Refactor/monorepo with sdd Refactor/monorepo with spec-driven development Feb 14, 2026
@wildan3105 wildan3105 requested a review from Copilot February 14, 2026 10:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a large refactor toward a spec-driven monorepo, replacing the legacy JS/Handlebars implementation with a new TypeScript backend under backend/ that follows a DDD + ports/adapters architecture and exposes a JSON API (/health, /api/v1/search) aligned with the new backend specification.

Changes:

  • Remove the legacy Express + Handlebars web app, client JS assets, and Mocha-based utility tests.
  • Introduce a new backend/ TypeScript service (DDD structure, GitHub GraphQL adapter, validation, error handling) plus Jest unit/integration tests.
  • Add/update specs, task docs, Docker/CI setup, and sample API responses to support spec-driven development.

Reviewed changes

Copilot reviewed 86 out of 97 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tests/utils/language-colors-generator.test.js Removed legacy Mocha test for language color generation.
tests/utils/emoji-generator.test.js Removed legacy Mocha test for emoji generator.
src/utils/language-colors-generator.js Removed legacy language color generator utility.
src/utils/emoji-generator.js Removed legacy emoji generator utility.
src/pages/partials/head.handlebars Removed legacy Handlebars head partial (old UI).
src/pages/partials/footer.handlebars Removed legacy Handlebars footer partial (analytics snippet).
src/pages/partials/error.handlebars Removed legacy error partial.
src/pages/partials/copyright.handlebars Removed legacy copyright partial.
src/pages/partials/body.handlebars Removed legacy main UI body (chart + search UI).
src/pages/layouts/main.handlebars Removed legacy Handlebars layout wrapper.
src/config.js Removed legacy config/constants for the old app.
src/api/routes.js Removed legacy Express routes file.
src/api/octokit.js Removed legacy REST-based Octokit wrapper.
src/api/controller.js Removed legacy controller/aggregation logic (REST-based).
src/api/app.js Removed legacy Express app entrypoint.
samples/400-response.json Added sample error response payload for the new API contract.
samples/200-response.json Added sample success response payload for the new API contract.
public/js/spinner.js Removed legacy browser spinner script.
public/js/social.js Removed legacy social share script.
public/js/language-colors.js Removed legacy browser language-colors map.
public/js/chart.js Removed legacy Chart.js integration script.
public/css/themes/_light.scss Removed legacy theme styles.
public/css/themes/_dark.scss Removed legacy theme styles.
public/css/base.scss Removed legacy SCSS entrypoint for UI styling.
package.json Removed legacy root package configuration (pre-monorepo).
gulpfile.js Removed legacy gulp build pipeline (CSS).
eslint.config.mjs Removed legacy root ESLint config.
docs/tasks/7-testing-polish.md Added spec-driven task document for testing/polish steps.
docs/tasks/6-http-layer.md Added spec-driven task document for HTTP layer design.
docs/tasks/5-application-layer.md Added spec-driven task document for application/use-case layer.
docs/tasks/4-github-adapter.md Added spec-driven task document for GitHub GraphQL adapter.
docs/tasks/3-project-structure.md Added spec-driven task document for DDD project structure.
docs/tasks/2-init-deps.md Added spec-driven task document for dependency initialization.
docs/tasks/1-setup-project.md Added spec-driven task document for backend bootstrap.
docs/product-spec.md Added product spec defining goals and refactor plan.
docs/backend-spec.md Added backend architecture + API contract specification.
docker-compose.yaml Removed legacy docker-compose setup.
README.md Replaced root README with a placeholder for the refactor/monorepo docs.
Dockerfile Removed legacy root Dockerfile.
CLAUDE.md Added contributor/agent guidance tying work to specs + iteration practices.
.nvmrc Updated Node version pinned for local development.
.github/workflows/backend-ci.yml Added backend-focused CI workflow (lint/typecheck/test/build + Docker build).
backend/tsconfig.json Added backend TypeScript compiler configuration.
backend/src/shared/types/common.ts Added shared type aliases (provider/env/log level).
backend/src/shared/index.ts Added shared barrel exports.
backend/src/shared/constants/providers.ts Added provider enum + default provider constant.
backend/src/shared/constants/languageColors.ts Added language color map + helpers for consistent coloring.
backend/src/shared/config/env.ts Added dotenv-based config loader/validator.
backend/src/interfaces/validation/searchSchema.ts Added Zod validation + middleware for /api/v1/search.
backend/src/interfaces/routes/searchRoutes.ts Added search routes factory.
backend/src/interfaces/routes/index.ts Added routes root with /health + mounting /api/v1.
backend/src/interfaces/middleware/errorHandler.ts Added global Express error handler middleware.
backend/src/interfaces/index.ts Added interfaces barrel exports.
backend/src/interfaces/controllers/SearchController.ts Added controller mapping HTTP -> SearchService + error code -> status.
backend/src/infrastructure/providers/types/GitHubTypes.ts Added GitHub GraphQL response types (infra-only).
backend/src/infrastructure/providers/GitHubGraphQLAdapter.ts Added GitHub GraphQL provider adapter with pagination + error mapping.
backend/src/infrastructure/index.ts Added infrastructure barrel exports.
backend/src/infrastructure/errors/ProviderError.ts Added ProviderError type for infra failures + helpers.
backend/src/index.ts Added backend entrypoint wiring middleware, DI, routes, and graceful shutdown.
backend/src/domain/ports/ProviderPort.ts Added domain provider port contract returning profile + repositories.
backend/src/domain/models/index.ts Added domain model barrel exports.
backend/src/domain/models/Repository.ts Added provider-agnostic repository domain model.
backend/src/domain/models/Profile.ts Added profile domain model.
backend/src/domain/models/LanguageStatistic.ts Added language statistic domain model for chart-ready output.
backend/src/domain/index.ts Added domain barrel exports.
backend/src/application/types/SearchResult.ts Added success response type matching the spec contract.
backend/src/application/types/SearchError.ts Added error response type matching the spec contract.
backend/src/application/services/SearchService.ts Added use-case orchestration: aggregation, fork handling, color mapping, error transform.
backend/src/application/index.ts Added application barrel exports.
backend/src/tests/unit/errorHandler.test.ts Added unit tests for error handler middleware.
backend/src/tests/unit/SearchService.test.ts Added unit tests for aggregation logic and error transformation.
backend/src/tests/unit/SearchController.test.ts Added unit tests for controller status mapping and provider handling.
backend/src/tests/unit/ProviderError.test.ts Added unit tests for ProviderError behavior.
backend/src/tests/unit/GitHubAdapter.test.ts Added unit tests for GitHub adapter (mocked GraphQL).
backend/src/tests/setup.ts Added Jest setup (env + timeouts).
backend/src/tests/integration/api.test.ts Added integration tests for /health and /api/v1/search.
backend/package.json Added backend package definition, scripts, and dependencies.
backend/jest.config.js Added Jest + ts-jest config and coverage rules.
backend/eslint.config.js Added backend ESLint config (TypeScript + Prettier).
backend/mocks/@octokit/graphql.js Added manual mock for @octokit/graphql intended for network-interceptable tests.
backend/README.md Added backend-specific README with setup, endpoints, and behavior notes.
backend/Dockerfile Added backend production multi-stage Dockerfile + healthcheck.
backend/.prettierrc Added Prettier configuration for backend TS formatting.
backend/.gitignore Added backend-specific gitignore.
backend/.env.example Added backend env example template.
backend/.dockerignore Added backend dockerignore for smaller images.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +60
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests per window
message: {
ok: false,
provider: 'unknown',
error: {
code: 'rate_limited',
message: 'Too many requests, please try again later',
},
meta: {
generatedAt: new Date().toISOString(),
},
},
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

express-rate-limit message is constructed once at startup, so meta.generatedAt will be stale and identical for all rate-limited responses. Consider using a handler (or a message function, if supported) to generate generatedAt per request.

Copilot uses AI. Check for mistakes.
throw error;
}

return response.json();
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This manual mock returns response.json() (which includes { data, errors }), but @octokit/graphql resolves to the unwrapped data payload. As written, tests that rely on this mock will see a different shape than production code. Consider returning const { data, errors } = await response.json() and throwing on errors, otherwise returning data.

Suggested change
return response.json();
const { data, errors } = await response.json();
if (errors && errors.length > 0) {
const graphQLError = new Error(errors[0]?.message || 'GraphQL error');
graphQLError.errors = errors;
throw graphQLError;
}
return data;

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +24

provider: z.enum(Object.values(Providers)).default(DEFAULT_PROVIDER).optional(),
});
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

z.enum(Object.values(Providers)) won’t type-check cleanly because Object.values() is string[] at compile time, but z.enum() expects a non-empty tuple of string literals. Consider defining a const PROVIDERS = ['github','gitlab','bitbucket'] as const (or casting to [string, ...string[]]) and using that for both the enum and the exported types.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I still want to use Providers? What should we do in this file?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot answer the above question

Comment on lines +23 to +24
provider: z.enum(Object.values(Providers)).default(DEFAULT_PROVIDER).optional(),
});
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default(DEFAULT_PROVIDER).optional() likely defeats the defaulting behavior (optional can short-circuit to undefined). If you want a default provider when the param is missing, prefer .optional().default(DEFAULT_PROVIDER) or drop .optional() entirely.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +43
// 2. Aggregate and count repositories by language
const series = this.aggregateLanguageStatistics(repositories);

// 3. Return successful result
return {
ok: true,
provider: this.provider.getProviderName(),
profile,
series,
metadata: {
generatedAt: new Date().toISOString(),
unit: 'repos',
limit: repositories.length,
},
};
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata.limit is currently set to repositories.length, but the API contract/spec uses this as the response limit (e.g., top 20 languages) rather than the number of repos fetched. Also, series is not limited to the top N buckets. Consider introducing a constant (e.g., SERIES_LIMIT = 20), trimming/summing “others” if desired, and setting metadata.limit to that limit.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +11
import { Request, Response, NextFunction } from 'express';
import pino from 'pino';
import { isProduction } from '../../shared/config/env';

const logger = pino();

Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handler creates its own pino() instance with default settings, which can diverge from the app-wide logger config (level, serializers, destinations) and makes logs harder to correlate. Consider passing the configured logger in (DI) or initializing it with config.logLevel so it matches the rest of the service.

Copilot uses AI. Check for mistakes.
@@ -1 +1 @@
18.18.0
24.3.0
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Root .nvmrc is set to Node 24.3.0, but the backend CI matrix runs Node 20.x and backend/package.json requires >=20. Consider aligning .nvmrc to the CI/runtime baseline (or documenting that Node 24 is required for the repo) to avoid local/CI drift.

Suggested change
24.3.0
20

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +37
// Validate query parameters (throws if invalid)
searchQuerySchema.parse(req.query);

// If validation passes, continue to controller
next();
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation middleware parses req.query but discards the parsed/sanitized output, so downstream code still sees the untyped Express query object (e.g., string | string[]). Consider assigning const parsed = searchQuerySchema.parse(req.query) and attaching it to req (e.g., req.query = parsed as any or res.locals.searchQuery = parsed) so the controller can use the validated values.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +45
res.status(400).json({
ok: false,
provider: 'unknown',
error: {
code: 'validation_error',
message: 'Invalid query parameters',
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation error responses always return provider: 'unknown', which diverges from the API contract/samples where provider is still set (typically github). Consider returning the default provider (or the validated provider, if present) to keep responses consistent for clients.

Copilot uses AI. Check for mistakes.
Wildan Nahar added 30 commits February 15, 2026 12:04
…esence and isVerified field value for user and org, respectively
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants