diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..4b64d9b --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,57 @@ +name: changelog + +# Rebuilds CHANGELOG.md with git-cliff (config: cliff.toml) and opens a PR into +# dev with the result. dev and main only change through PRs, so the workflow +# can't push to them. +# +# Runs when a release or hotfix PR is merged into main (by then +# `Nova.ps1 publish` has tagged the release as nova/), or by hand from the +# Actions tab. Uses the CHANGELOG_TOKEN secret if it exists (a fine-grained +# token or GitHub App token with contents + pull-requests write), so the PR's +# CI checks run; without it, the PR is opened with the built-in token and a +# lead merges it without waiting for checks. + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: changelog + cancel-in-progress: false + +jobs: + changelog: + name: changelog + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + ref: dev + fetch-depth: 0 + + - name: Generate CHANGELOG.md + uses: orhun/git-cliff-action@v4 + with: + config: cliff.toml + args: --verbose + env: + OUTPUT: CHANGELOG.md + + - name: Open or update the changelog PR + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.CHANGELOG_TOKEN || github.token }} + base: dev + branch: chore/update-changelog + add-paths: CHANGELOG.md + commit-message: 'docs(changelog): update changelog' + title: 'docs(changelog): update changelog' + body: | + Regenerated by the `changelog` workflow with git-cliff after a release. + Only `CHANGELOG.md` changes. Merge with **Squash and merge**. + delete-branch: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..187fb27 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +What changed in this repo in each Nova release, newest first. + +> [!NOTE] +> This file is **generated** by [git-cliff](https://git-cliff.org) from the PR +> titles merged into `dev`, by the `changelog` GitHub Actions workflow. Don't +> edit it by hand: change `cliff.toml` instead. Each release's test, notes and +> the other repos' versions are on the +> [Nova-Collected releases page](https://github.com/UTATRocketry/Nova-Collected/releases). + diff --git a/README.md b/README.md index 58103e8..674bef7 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,86 @@ -# NovaOps — Frontend +# NovaOps Frontend -Next.js + Chakra UI v3 ground control station. +The operator UI for the Nova ground station: live engine and flight-computer +data, the interactive P&ID, configuration and analysis tools, in any browser. +Built with Next.js, React and Chakra UI v3. It talks only to the +[NovaOps backend](https://github.com/UTATRocketry/novaOps-back), which checks +every command before anything moves. -## Set up +> [!IMPORTANT] +> This is a control surface. "No data" always renders as "—", never a made-up +> `0`, so an operator can tell stale from zero. Keep it that way +> ([style guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/style.md#typescript-and-react-frontend)). + +## Repo structure + +``` +src/ +├── app/ pages: engine, flight, devices, console, config, tools, settings +├── components/ primitives/ (shared), shell/ (app frame), and one folder per page +├── hooks/ data hooks (React Query, store) +└── lib/ no-React logic: api/ (REST), ws/ (WebSocket), store/, theme/, ... +public/ fonts, logos +tools/ backend_API.http: sample requests for the backend +``` + +Where new code goes: [organization guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/organization.md#novaops-front-frontend). + +## Getting started + +### Set up + +Node.js 20+, and a running backend (see the backend's README). ```bash npm install -cp .env.example .env.local # point at your backend -npm run build +cp .env.example .env.local ``` -## Run It +`.env.local` points at the backend. When the UI and backend share a host, the +URLs can be left unset; they're derived from the page's address. + +| Variable | Default in `.env.example` | +|---|---| +| `NEXT_PUBLIC_NOVA_API_BASE_URL` | `http://localhost:8000` | +| `NEXT_PUBLIC_NOVA_WS_BASE_URL` | `ws://localhost:8000` | +| `NEXT_PUBLIC_NOVA_FAKE_SIGNAL` | `0` (dev only: fake signal strength) | + +### Run + +```bash +npm run dev # development, http://localhost:3000 +npm run build && npm run start # production ``` -# for development -npm run dev # http://localhost:3000 -# for production -npm run start +Check a change the way CI does: + +```bash +npm run typecheck +npm run build ``` +> [!NOTE] +> On the ground station the UI runs as a Windows service managed by +> Nova-Collected's `ops/Nova.ps1`. + +### Troubleshoot + +| Symptom | Fix | +|---|---| +| Every value shows "—" | The backend isn't reachable, or nothing is publishing. Check `.env.local` and that the backend and a simulator are running | +| `npm run build` fails fetching fonts | The build downloads Inter and JetBrains Mono from Google Fonts; it needs internet | +| `npm run lint` hangs | There's no ESLint config yet, so it waits for input. Don't run it in scripts | +| Commands do nothing | Your session is a `viewer`. Take a role in Settings | -`npm run typecheck` and `npm run build` both pass. (In a sandbox without -network access to Google Fonts the build can't fetch Inter/JetBrains Mono; -on a normal machine this works.) +More: [Nova SUPPORT.md](https://github.com/UTATRocketry/Nova-Collected/blob/main/SUPPORT.md). -## Notes / decisions baked in +## More information -- Color mode: dark is the default (bunker/night operation). -- Chrome colours (`chrome.*`) are fixed dark and do NOT follow color mode — - this is what keeps the nav rail and top bar dark in light mode. -- All "no data" states render an explicit dash, never a fabricated `0` — - important for a control surface (see the resilience section of the plan). -- Icons use the Material Symbols variable font; weight/fill is centralized in - `Icon.tsx`. +| To... | Read | +|---|---| +| Use the UI as an operator | [Operator guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/user-guides/operator-guide.md) | +| Look up the backend API | [Backend API](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/api/backend-api.md), or `/docs` on a running backend | +| Follow code style | [Style guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/style.md) | +| See what changed in each release | [CHANGELOG.md](CHANGELOG.md) | +| Contribute | [CONTRIBUTING.md](CONTRIBUTING.md) | +| See the whole system | [Nova-Collected](https://github.com/UTATRocketry/Nova-Collected) | diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..6d415a9 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,56 @@ +# git-cliff config: builds CHANGELOG.md from this repo's history. +# Run by .github/workflows/changelog.yml whenever a release reaches main. +# Docs: https://git-cliff.org/docs/configuration +# +# Versions are Nova releases. `Nova.ps1 publish` tags every commit a release +# pins as nova/, e.g. nova/v2026.10.04, and each section lists the +# PRs merged into dev since the previous release. PR titles are squash-merged +# into dev, so they follow Conventional Commits (CONTRIBUTING.md section 6). + +[changelog] +header = """ +# Changelog + +What changed in this repo in each Nova release, newest first. + +> [!NOTE] +> This file is **generated** by [git-cliff](https://git-cliff.org) from the PR +> titles merged into `dev`, by the `changelog` GitHub Actions workflow. Don't +> edit it by hand: change `cliff.toml` instead. Each release's test, notes and +> the other repos' versions are on the +> [Nova-Collected releases page](https://github.com/UTATRocketry/Nova-Collected/releases). +""" +body = """ +{% if version %}\ +{% set release = version | trim_start_matches(pat="nova/") %}\ +## [{{ release }}](https://github.com/UTATRocketry/Nova-Collected/releases/tag/{{ release }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{% for group, commits in commits | group_by(attribute="group") %} +### {{ group | striptags | trim }} +{% for commit in commits %} +- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\ +{% if commit.breaking %}**BREAKING** {% endif %}\ +{{ commit.message | split(pat="\n") | first | trim }}\ +{% endfor %} +{% endfor %} +{% endif %}\ +""" +trim = false + +[git] +conventional_commits = true +filter_unconventional = true +filter_commits = true +split_commits = false +protect_breaking_commits = true +tag_pattern = "^nova/v[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}(\\.[0-9]+)?$" +sort_commits = "oldest" +commit_parsers = [ + # The changelog workflow's own PRs. + { message = "^docs\\(changelog\\)", skip = true }, + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Fixes" }, + { message = "^refactor", group = "Refactoring" }, + { message = "^docs", group = "Documentation" }, + { message = "^test", group = "Tests" }, + { message = "^chore", group = "Chores" }, +]