Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
83eedb6
feat(tooling): add fallow + jscpd with regression-gated CI
rlorenzo Apr 23, 2026
4ba38f1
refactor: remove dead code surfaced by fallow (Phase 1)
rlorenzo Apr 24, 2026
9cabae0
refactor(clinical-scheduler): drop orphaned store cluster (Phase 2)
rlorenzo Apr 24, 2026
3747ad5
refactor: remove unused type exports (Phase 3)
rlorenzo Apr 24, 2026
9f0529e
refactor(router): extract createSpaRouter factory
rlorenzo Apr 24, 2026
e323e46
refactor(emergency-contact): extract shared page shell
rlorenzo Apr 24, 2026
6708591
refactor(effort): extract DialogSubmitActions component
rlorenzo Apr 24, 2026
c790f67
refactor(effort): extract PercentAssignmentFormFields
rlorenzo Apr 24, 2026
d787402
refactor: migrate CSS @import to JS-side imports (Phase 4c)
rlorenzo Apr 24, 2026
843b40e
refactor(effort): extract EffortReportPage layout shell
rlorenzo Apr 24, 2026
8cef564
refactor(layouts): extract shared chrome components from ViperLayout
rlorenzo Apr 24, 2026
17867c1
refactor(effort): extract EffortRecordSharedFields from dialogs
rlorenzo Apr 24, 2026
a61ddad
refactor(effort): extract InstructorPageShell for breadcrumbs and states
rlorenzo Apr 24, 2026
a383b74
refactor(effort): extract AsyncOperationDialog shell for preview dialogs
rlorenzo Apr 24, 2026
80c52eb
refactor(effort): extract TermTable for term selection rows
rlorenzo Apr 24, 2026
f920be8
refactor(cts): collapse hardcoded CourseStudents rows into a v-for
rlorenzo Apr 25, 2026
d81baa8
refactor(effort): centralize report context loading in BaseReportService
rlorenzo Apr 25, 2026
eec4927
refactor(scheduler): share schedule filter LINQ via IScheduleEntity
rlorenzo Apr 25, 2026
7fa7d02
refactor(students): consolidate StudentGroupService photo + Ross helpers
rlorenzo Apr 25, 2026
1b3e244
refactor(cms): collapse Codecs UU/XX encoders onto shared map helpers
rlorenzo Apr 25, 2026
d12711e
refactor(effort): extract CalculateWeightedAverage in EvaluationRepor…
rlorenzo Apr 25, 2026
28e2398
feat(tooling): honor LINT_BLOCK_ON_WARNINGS in fallow + jscpd lint st…
rlorenzo Apr 25, 2026
512c655
chore(security): tighten static-analysis tooling
rlorenzo Apr 25, 2026
80a43a4
ci: use fallow dead-code for whole-project regression baseline
rlorenzo Apr 25, 2026
8732695
ci: install VueApp deps + tolerate fallow baseline exit code
rlorenzo Apr 25, 2026
6f808f2
ci: switch fallow regression gate to audit --base mode
rlorenzo Apr 25, 2026
23543b1
fix(effort): restore StatusBanner import in InstructorEdit
rlorenzo Apr 25, 2026
8cdde4a
chore(quality): add C# static analysis + PR-scoped gate
rlorenzo Apr 28, 2026
6dce3cb
chore(quality): optimize using directives across solution
rlorenzo Apr 28, 2026
c9f7dff
chore(quality): shorten reference qualifiers across solution
rlorenzo Apr 28, 2026
561c882
chore(quality): remove dead null-checks flagged by CA1508
rlorenzo Apr 28, 2026
ca2f4bb
chore(quality): clean up stale XML doc comments
rlorenzo Apr 28, 2026
4d3d2da
chore(quality): materialize IEnumerable to avoid multi-enumeration
rlorenzo Apr 28, 2026
c4e4434
chore(quality): remove code redundancies across solution
rlorenzo Apr 29, 2026
99be8c0
feat(tooling): add ReSharper staged-files gate, expand audit umbrella
rlorenzo Apr 30, 2026
50922b9
fix(quality): clear ReSharper PR-gate + CodeQL review findings
rlorenzo Apr 30, 2026
2a2229d
fix(quality): discard unused entry param in cache lambda
rlorenzo Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
"commands": [
"libman"
]
},
"jetbrains.resharper.globaltools": {
"version": "2026.1.1",
"commands": [
"jb"
]
}
}
}
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ dotnet_diagnostic.IDE0005.severity = error
dotnet_diagnostic.IDE0059.severity = warning
# CS8601: Possible null reference assignment.
dotnet_diagnostic.CS8601.severity = silent

# Maintainability rules (off by default in NetAnalyzers; enable explicitly)
# CA1502: Avoid excessive cyclomatic complexity (default threshold 25)
dotnet_diagnostic.CA1502.severity = warning
# CA1505: Avoid unmaintainable code (maintainability index < 10)
dotnet_diagnostic.CA1505.severity = warning
# CA1501: Avoid excessive inheritance (default 5 levels)
dotnet_diagnostic.CA1501.severity = warning
# CA1507: Use nameof in place of string literal
dotnet_diagnostic.CA1507.severity = warning
# CA1508: Avoid dead conditional code (dataflow analysis - on trial; investigate FP rate)
dotnet_diagnostic.CA1508.severity = warning
# CA1510-CA1513: Use throw-helper APIs (.NET 6+ shorthands for argument validation)
dotnet_diagnostic.CA1510.severity = warning
dotnet_diagnostic.CA1511.severity = warning
dotnet_diagnostic.CA1512.severity = warning
dotnet_diagnostic.CA1513.severity = warning
# CA1514: Avoid redundant length argument (Substring/Span slicing)
dotnet_diagnostic.CA1514.severity = warning
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
Expand Down
139 changes: 139 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Code Quality

on:
pull_request:
branches: [main]

# Detects whether a PR *adds* new dead code or duplication compared to main.
# - fallow covers VueApp (dead code, unused exports, complexity, duplication)
# - jscpd covers C# (web/Areas/**/*.cs) since fallow is JS/TS-only
# - resharper-pr-gate covers C# inspections (dead-conditional, NRT-contract, and
# redundancy findings the Roslyn build doesn't catch); PR-scoped so pre-existing
# issues are not blocking — only NEW findings at lines this PR added/modified fail.
# Passes if counts are equal to or lower than main.

jobs:
fallow-regression:
name: Fallow regression (VueApp)
runs-on: ubuntu-latest
steps:
- name: Checkout PR head
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json

- name: Install dependencies (root)
run: npm ci

- name: Install dependencies (VueApp)
# fallow resolves the tsconfig chain (incl. @tsconfig/node24) and needs
# VueApp's node_modules to produce accurate counts. Without this step,
# CI reports inflated unresolved-imports / unused-types numbers.
run: npm ci --prefix VueApp

- name: Audit fallow on changed files
# `fallow audit` is purpose-built for PR-diff regression gating: it
# scopes to files changed since --base and returns a pass/warn/fail
# verdict (exit 0 if no regression).
run: npx fallow audit --root VueApp --base origin/${{ github.base_ref }}

jscpd-regression-csharp:
name: JSCPD regression (C#)
runs-on: ubuntu-latest
steps:
- name: Checkout PR head
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json

- name: Install dependencies (root)
run: npm ci

- name: Baseline jscpd on main
run: |
git checkout origin/${{ github.base_ref }} -- web
node scripts/audit-jscpd-regression.js --save .jscpd-baseline-cs.json web/Areas --format csharp --pattern '**/*.cs' --min-lines 15
git checkout HEAD -- web

- name: Check jscpd regression on PR
run: node scripts/audit-jscpd-regression.js --check .jscpd-baseline-cs.json web/Areas --format csharp --pattern '**/*.cs' --min-lines 15

jscpd-regression-vue:
name: JSCPD regression (Vue/TS)
runs-on: ubuntu-latest
steps:
- name: Checkout PR head
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json

- name: Install dependencies (root)
run: npm ci

- name: Baseline jscpd on main
run: |
git checkout origin/${{ github.base_ref }} -- VueApp/src
node scripts/audit-jscpd-regression.js --save .jscpd-baseline-vue.json VueApp/src
git checkout HEAD -- VueApp/src

- name: Check jscpd regression on PR
run: node scripts/audit-jscpd-regression.js --check .jscpd-baseline-vue.json VueApp/src

resharper-pr-gate:
name: ReSharper PR-scoped gate (C#)
runs-on: ubuntu-latest
steps:
- name: Checkout PR head
uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json

- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json

- name: Install dependencies (root)
run: npm ci

- name: Restore dotnet local tools (jb / dotnet-ef / libman)
run: dotnet tool restore

- name: Restore NuGet
run: dotnet restore Viper.sln

- name: Run inspectcode + PR-diff gate
# The script runs the full inspectcode scan, parses the SARIF, and only
# fails on findings located at lines this PR added/modified vs base ref.
run: node scripts/audit-resharper-regression.js --base origin/${{ github.base_ref }}

- name: Upload SARIF report
if: always()
uses: actions/upload-artifact@v4
with:
name: resharper-sarif
path: inspect-report/inspect.sarif
retention-days: 3
7 changes: 1 addition & 6 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,7 @@ jobs:
uses: github/codeql-action/init@v4.35.2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
queries: security-and-quality

# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,15 @@ awscredentials.xml
# Precommit build artifacts (isolated from dev server)
.artifacts-precommit/
.artifacts-lint/
.artifacts-resharper/

# Effort migration script outputs
web/Areas/Effort/Scripts/AnalysisOutput/
web/Areas/Effort/Scripts/RemediationOutput/
web/Areas/Effort/Scripts/Effort_Database_Schema_And_Data_LEGACY.txt

# Code-quality tool outputs (fallow cache + jscpd + ReSharper reports)
.fallow/
VueApp/.fallow/
jscpd-report/
inspect-report/
13 changes: 13 additions & 0 deletions .jscpd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"ignore": [
"**/__tests__/**",
"**/node_modules/**",
"**/dist/**",
"**/coverage/**",
"**/bin/**",
"**/obj/**"
],
"ignorePattern": [
"^\\s*import\\s+.*from\\s+['\"][^'\"]+['\"]\\s*;?\\s*$"
]
}
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,24 @@ The project includes VS Code launch configurations for debugging both frontend a
- `npm run lint:staged` - Lint only git-staged files
- `npm run precommit` - Run full pre-commit checks manually (lint, test, build verify)

### Auditing (regression detection)

Heavier whole-project scanners that surface dead code, duplication, and C# inspection findings that the per-file linter doesn't catch. Outputs land in gitignored report directories (`.fallow/`, `jscpd-report/`, `inspect-report/`).

**Whole-project scans** — run locally for human review:

- `npm run audit` - Run all whole-project audits (fallow + jscpd + ReSharper); takes several minutes due to the ReSharper scan
- `npm run audit:fallow` - Vue/TS dead code, unused exports, complexity, duplication (`VueApp/`)
- `npm run audit:dupes` - jscpd duplicate-code report across `VueApp/src/` and `web/Areas/`
- `npm run audit:resharper` - ReSharper `inspectcode` whole-solution scan (writes SARIF + HTML to `inspect-report/`); takes several minutes

**Diff-scoped gates** — fail only on *new* findings at touched lines, so pre-existing issues never block:

- `npm run audit:resharper:pr` - C# inspection gate vs `origin/main`. Same gate the `Code Quality` GitHub Actions workflow runs on PRs.
- `npm run audit:resharper-staged` - C# inspection gate vs the git index (`git diff --cached`). Useful as a manual pre-commit check. For fast iteration after one full scan, append `-- --skip-scan` to reuse the existing SARIF: `npm run audit:resharper-staged -- --skip-scan`.

**CI integration** — `.github/workflows/code-quality.yml` runs four gates on every PR to `main`: `fallow-regression` (Vue), `jscpd-regression-csharp`, `jscpd-regression-vue`, and `resharper-pr-gate` (C# inspections). All four are diff-scoped so existing technical debt doesn't block merges.

### Build Cache

The linter, test, and build verification scripts use caching to avoid redundant rebuilds. If you encounter stale cache issues (e.g., linter showing warnings for already-fixed code), clear the cache:
Expand Down
5 changes: 5 additions & 0 deletions Viper.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=OptimizeUsings/@EntryIndexedValue">&lt;Profile name="OptimizeUsings"&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=ShortenReferences/@EntryIndexedValue">&lt;Profile name="ShortenReferences"&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=RemoveRedundancies/@EntryIndexedValue">&lt;Profile name="RemoveRedundancies"&gt;&lt;CSRemoveCodeRedundancies&gt;True&lt;/CSRemoveCodeRedundancies&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSRemoveRedundantArgumentDefaultValues&gt;True&lt;/CSRemoveRedundantArgumentDefaultValues&gt;&lt;CSRemoveRedundantInitializers&gt;True&lt;/CSRemoveRedundantInitializers&gt;&lt;/Profile&gt;</s:String>
</wpf:ResourceDictionary>
39 changes: 39 additions & 0 deletions VueApp/.fallowrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/fallow-schema.json",
"entry": [
"src/main.ts",
"src/CAHFS/cahfs.ts",
"src/CMS/cms.ts",
"src/CTS/cts.ts",
"src/ClinicalScheduler/clinicalscheduler.ts",
"src/Computing/computing.ts",
"src/Effort/effort.ts",
"src/Students/students.ts",
"index.html",
"src/CAHFS/index.html",
"src/CMS/index.html",
"src/CTS/index.html",
"src/ClinicalScheduler/index.html",
"src/Computing/index.html",
"src/Effort/index.html",
"src/Students/index.html"
],
"ignorePatterns": [
"node_modules/**",
"dist/**",
"coverage/**",
"obj/**",
"bin/**",
"public/**"
],
"ignoreDependencies": [
"@tsconfig/node24",
"vue-eslint-parser"
],
"duplicates": {
"ignoreImports": true,
"ignore": [
"**/__tests__/**"
]
}
}
25 changes: 0 additions & 25 deletions VueApp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions VueApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"test:run": "vitest run"
},
"dependencies": {
"@quasar/extras": "^1.18.0",
"@vueuse/core": "^14.0.0",
"chart.js": "^4.4.3",
"inflection": "^3.0.2",
Expand All @@ -39,7 +38,6 @@
"devDependencies": {
"@codecov/vite-plugin": "^2.0.1",
"@eslint/js": "^10.0.0",
"@pinia/testing": "^1.0.2",
"@quasar/app-vite": "^2.6.0",
"@quasar/vite-plugin": "^1.9.0",
"@tsconfig/node24": "^24.0.4",
Expand Down
Empty file removed VueApp/src/CAHFS/assets/cahfs.css
Empty file.
12 changes: 2 additions & 10 deletions VueApp/src/CAHFS/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { createRouter, createWebHistory } from "vue-router"
import { createSpaRouter } from "@/shared/createSpaRouter"
import { routes } from "./routes"
import { useRequireLogin } from "@/composables/RequireLogin"
import { checkHasOnePermission } from "@/composables/CheckPagePermission"
import { useRouteFocus } from "@/composables/use-route-focus"

const baseUrl = import.meta.env.VITE_VIPER_HOME
const router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
history: createWebHistory(baseUrl),
routes,
})
const router = createSpaRouter(routes)

router.beforeEach(async (to) => {
const { requireLogin } = useRequireLogin(to)
Expand All @@ -25,6 +19,4 @@ router.beforeEach(async (to) => {
}
})

useRouteFocus(router)

export { router as CAHFSRouter }
Empty file removed VueApp/src/CMS/assets/cms.css
Empty file.
Loading
Loading