Skip to content

Improve dev experience and CI for optimiser#4263

Merged
leslieyip02 merged 25 commits intonusmodifications:masterfrom
thejus03:chore/dev–exp
Feb 21, 2026

Hidden character warning

The head ref may contain hidden characters: "chore/dev\u2013exp"
Merged

Improve dev experience and CI for optimiser#4263
leslieyip02 merged 25 commits intonusmodifications:masterfrom
thejus03:chore/dev–exp

Conversation

@thejus03
Copy link
Contributor

@thejus03 thejus03 commented Dec 2, 2025

Context

Implementation

  • Added a new global variable called OPTIMISER_API_URL
  • Depending on the script that is run it assigns either http://localhost:8020/optimise or api/optimiser/optimise
  • Added a new script called yarn start:local for running the localhost api endpoint on the website
  • Updated the yarn start script to include the real endpoint for production
  • Updated the optimiser README to reflect the above changes

Updated on 21 Dec 2025

  • Add linting and formatting using golangci-lint library.
  • Update README for developers to lint and format themselves locally
  • Update CI to run lint and formatting and run the tests

- Added new global variable to assign the correct optimiser api
@vercel
Copy link

vercel bot commented Dec 2, 2025

@thejus03 is attempting to deploy a commit to the modsbot's projects Team on Vercel.

A member of the Team first needs to authorize it.

@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 56.74%. Comparing base (988c6fd) to head (55f424e).
⚠️ Report is 174 commits behind head on master.

Files with missing lines Patch % Lines
website/src/apis/optimiser.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4263      +/-   ##
==========================================
+ Coverage   54.52%   56.74%   +2.22%     
==========================================
  Files         274      300      +26     
  Lines        6076     6957     +881     
  Branches     1455     1682     +227     
==========================================
+ Hits         3313     3948     +635     
- Misses       2763     3009     +246     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thejus03 thejus03 changed the title Chore/dev–exp New script to start optimiser for developers Dec 3, 2025
@thejus03 thejus03 changed the title New script to start optimiser for developers Add new script to start optimiser for developers Dec 3, 2025
@thejus03 thejus03 changed the title Add new script to start optimiser for developers Improve dev experience and CI for optimiser Dec 21, 2025
Comment on lines 142 to 146
go run ./_test/server/main.go -port 8020 &
SERVER_PID=$!
sleep 5
go test ./_test/. -v
kill $SERVER_PID
Copy link
Member

Choose a reason for hiding this comment

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

  1. I'm not sure if sleep 5 is guaranteed to work. I think we can check if the port is open before running the tests by doing something like this:
for i in {1..10}; do
  nc -z localhost 8020 && break
  sleep 1
done
  1. Instead of kill $SERVER_PID, maybe we can do

go run ./_test/server/main.go -port 8020 &
SERVER_PID=$!

trap "kill $SERVER_PID" EXIT

for i in {1..10}; do
  nc -z localhost 8020 && break
  sleep 1
done

go test ./_test/. -v

This ensures the server is always killed.

// Algorithm:
// 1. Accumulate duration of consecutive classes (no gap between them)
// 2. When a gap is detected, check if accumulated time exceeds limit and penalize if so
// 3. After the loop, check the final consecutive block
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick but step 3 isn't accurate since the final check isn't "after the loop".

t.Fatalf("Failed to marshal request: %v", err)
}

client := &http.Client{Timeout: 60 * time.Second}
Copy link
Member

Choose a reason for hiding this comment

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

Should we reuse the client instead of recreating one for each request?

Comment on lines 228 to 229
respBody, err := io.ReadAll(resp.Body)
resp.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

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

I think it could be better to do

	defer resp.Body.Close()
	respBody, err := io.ReadAll(resp.Body)

so that the body is closed even if io.ReadAll panics.

@leslieyip02
Copy link
Member

@greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 18, 2026

Confidence Score: 2/5

  • This PR has issues that will break the production build and potentially the test suite — should not be merged as-is.
  • The missing OPTIMISER_API_URL in the build script means production deployments will have an undefined API URL, breaking the optimiser feature. The missing leading slash in the start script's URL will cause incorrect API routing on nested pages. Jest and ESLint globals are also missing the new variable.
  • website/package.json (missing env var in build, missing leading slash), website/jest.common.config.js (missing global), website/src/.eslintrc.js (missing global), website/webpack/webpack.config.common.js (no fallback for undefined env var)

Important Files Changed

Filename Overview
.circleci/config.yml Adds Go executor and optimiser CI job with linting, formatting, and testing. Test server cleanup could be more robust with a trap.
website/api/optimiser/_solver/solver.go Refactored scoreConsecutiveHoursofStudy with extracted helper function, improved import ordering, expanded comments, and formatting changes. Logic is correct.
website/api/optimiser/_test/api_test.go New integration tests for the optimiser API covering single module, collision detection, and multi-module scenarios with validation helpers.
website/package.json New OPTIMISER_API_URL env var and start:local script. Missing leading slash in start URL, and build script doesn't set the env var, which will break production builds.
website/src/apis/optimiser.ts Changed from hardcoded API path to Webpack-injected global OPTIMISER_API_URL. Will break if the global is undefined in production or test environments.
website/src/types/global.d.ts Added OPTIMISER_API_URL global type declaration as string (not `string
website/webpack/webpack.config.common.js Added OPTIMISER_API_URL to DefinePlugin. When env var is unset, JSON.stringify(undefined) produces undefined, which will break at runtime.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["yarn start / start:local"] -->|"sets OPTIMISER_API_URL env var"| B["Webpack DefinePlugin"]
    B -->|"JSON.stringify(env var)"| C["Global OPTIMISER_API_URL"]
    C --> D["optimiser.ts: const api = OPTIMISER_API_URL"]
    D --> E["axios.post(api, params)"]

    F["yarn build (production)"] -->|"OPTIMISER_API_URL not set"| G["JSON.stringify(undefined)"]
    G -->|"⚠️ undefined injected"| H["optimiser.ts: const api = undefined"]
    H -->|"❌ Runtime error"| I["API call fails"]

    J["yarn start:local"] -->|"starts Go test server"| K["localhost:8020/optimise"]
    E -->|"local dev"| K

    L["CI: optimiser job"] --> M["golangci-lint run"]
    L --> N["golangci-lint fmt --diff"]
    L --> O["go test ./_test/. -v"]
    O -->|"requires"| P["go run _test/server/main.go"]

    style F fill:#fff3cd
    style G fill:#fff3cd
    style H fill:#f8d7da
    style I fill:#f8d7da
Loading

Last reviewed commit: 7a1ae79

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

13 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 18, 2026

Additional Comments (2)

website/jest.common.config.js
Missing OPTIMISER_API_URL in Jest globals

The new global OPTIMISER_API_URL (added in global.d.ts and webpack.config.common.js) is not defined in Jest globals. Any test file that imports from apis/optimiser.ts will fail with a ReferenceError: OPTIMISER_API_URL is not defined because Jest doesn't use Webpack's DefinePlugin.

Other globals like DATA_API_BASE_URL are already defined here, so OPTIMISER_API_URL should be added for consistency:

  globals: {
    NUSMODS_ENV: 'test',
    DATA_API_BASE_URL: '',
    OPTIMISER_API_URL: '',
    VERSION_STR: '',
    DISPLAY_COMMIT_HASH: '',
    DEBUG_SERVICE_WORKER: false,
  },
Prompt To Fix With AI
This is a comment left during a code review.
Path: website/jest.common.config.js
Line: 19-25

Comment:
**Missing `OPTIMISER_API_URL` in Jest globals**

The new global `OPTIMISER_API_URL` (added in `global.d.ts` and `webpack.config.common.js`) is not defined in Jest globals. Any test file that imports from `apis/optimiser.ts` will fail with a `ReferenceError: OPTIMISER_API_URL is not defined` because Jest doesn't use Webpack's `DefinePlugin`.

Other globals like `DATA_API_BASE_URL` are already defined here, so `OPTIMISER_API_URL` should be added for consistency:

```suggestion
  globals: {
    NUSMODS_ENV: 'test',
    DATA_API_BASE_URL: '',
    OPTIMISER_API_URL: '',
    VERSION_STR: '',
    DISPLAY_COMMIT_HASH: '',
    DEBUG_SERVICE_WORKER: false,
  },
```

How can I resolve this? If you propose a fix, please make it concise.

website/src/.eslintrc.js
Missing OPTIMISER_API_URL in ESLint globals

The new OPTIMISER_API_URL global needs to be added to the ESLint globals config, matching how the other Webpack DefinePlugin globals (DATA_API_BASE_URL, VERSION_STR, etc.) are declared. Without this, ESLint may report OPTIMISER_API_URL as an undefined variable in optimiser.ts.

  globals: {
    NUSMODS_ENV: 'readonly',
    DATA_API_BASE_URL: 'readonly',
    VERSION_STR: 'readonly',
    DISPLAY_COMMIT_HASH: 'readonly',
    DEBUG_SERVICE_WORKER: 'readonly',
    OPTIMISER_API_URL: 'readonly',
  },
Prompt To Fix With AI
This is a comment left during a code review.
Path: website/src/.eslintrc.js
Line: 107-113

Comment:
**Missing `OPTIMISER_API_URL` in ESLint globals**

The new `OPTIMISER_API_URL` global needs to be added to the ESLint globals config, matching how the other Webpack `DefinePlugin` globals (`DATA_API_BASE_URL`, `VERSION_STR`, etc.) are declared. Without this, ESLint may report `OPTIMISER_API_URL` as an undefined variable in `optimiser.ts`.

```suggestion
  globals: {
    NUSMODS_ENV: 'readonly',
    DATA_API_BASE_URL: 'readonly',
    VERSION_STR: 'readonly',
    DISPLAY_COMMIT_HASH: 'readonly',
    DEBUG_SERVICE_WORKER: 'readonly',
    OPTIMISER_API_URL: 'readonly',
  },
```

How can I resolve this? If you propose a fix, please make it concise.

@thejus03 thejus03 requested a review from leslieyip02 February 18, 2026 18:02
@vercel
Copy link

vercel bot commented Feb 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nusmods-export Ready Ready Preview, Comment Feb 21, 2026 3:16am
nusmods-website Ready Ready Preview, Comment Feb 21, 2026 3:16am

Request Review

Copy link
Member

@leslieyip02 leslieyip02 left a comment

Choose a reason for hiding this comment

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

LGTM!

@leslieyip02 leslieyip02 merged commit 8981895 into nusmodifications:master Feb 21, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve development experience for optimiser

2 participants