From de602c37c8b83307b115662c20008f39fef87a87 Mon Sep 17 00:00:00 2001 From: Cole Stowell Date: Fri, 31 Oct 2025 10:05:04 -0500 Subject: [PATCH] feat: CI --- .github/workflows/ci.yml | 50 ++++++++++++++++++++++++++ .github/workflows/golangci-lint.yml | 25 ------------- .golangci.yml | 7 ++++ server/web/README.md | 1 + web/.prettierrc.cjs | 13 +++++++ web/README.md | 8 ++--- web/eslint.config.js | 14 ++++---- web/package-lock.json | 21 +++++++++++ web/package.json | 11 ++++-- web/src/App.tsx | 56 +++++++++++++---------------- web/src/main.tsx | 12 +++---- web/tsconfig.json | 5 +-- web/vite.config.ts | 10 +++--- 13 files changed, 148 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .golangci.yml create mode 100644 server/web/README.md create mode 100644 web/.prettierrc.cjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..52965ae --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: Lint +on: + push: + branches: + - master + pull_request: + +permissions: + contents: read + pull-requests: read + +env: + NODE_VERSION: 25 + GOVERSION: 1.25.3 + +jobs: + lint-go: + name: Lint go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: ${{ env.GOVERSION }} + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.6 + - name: Check generated files + run: go generate ./... && git diff --quiet || { git diff; echo "Malformed generated files. Run 'go generate ./...' and commit to fix"; exit 1; } + + lint-web: + name: Lint web + runs-on: ubuntu-latest + steps: + - name: Install NodeJS + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Code Checkout + uses: actions/checkout@v2 + + - name: Install Dependencies + working-directory: ./web + run: npm ci + + - name: Code Linting + working-directory: ./web + run: npm run lint diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml deleted file mode 100644 index c50dadf..0000000 --- a/.github/workflows/golangci-lint.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: golangci-lint -on: - push: - branches: - - master - pull_request: - -permissions: - contents: read - # Optional: allow read access to pull requests. Use with `only-new-issues` option. - pull-requests: read - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-go@v6 - with: - go-version: stable - - name: golangci-lint - uses: golangci/golangci-lint-action@v8 - with: - version: v2.1 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..c6a625a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,7 @@ +version: "2" +linters: + default: standard + +formatters: + enable: + - gofmt diff --git a/server/web/README.md b/server/web/README.md new file mode 100644 index 0000000..5c7f09e --- /dev/null +++ b/server/web/README.md @@ -0,0 +1 @@ +This is a temporary directory so that CI passes since it wants to embed *something*. diff --git a/web/.prettierrc.cjs b/web/.prettierrc.cjs new file mode 100644 index 0000000..ae8e99e --- /dev/null +++ b/web/.prettierrc.cjs @@ -0,0 +1,13 @@ +module.exports = { + printWidth: 120, // max 120 chars in line, code is easy to read + useTabs: false, // use spaces instead of tabs + tabWidth: 2, // "visual width" of of the "tab" + trailingComma: 'es5', // add trailing commas in objects, arrays, etc. + semi: true, // add ; when needed + singleQuote: true, // '' for stings instead of "" + bracketSpacing: true, // import { some } ... instead of import {some} ... + arrowParens: 'always', // braces even for single param in arrow functions (a) => { } + jsxSingleQuote: false, // "" for react props, like in html + bracketSameLine: false, // pretty JSX + endOfLine: 'lf', // 'lf' for linux, 'crlf' for windows, we need to use 'lf' for git +}; diff --git a/web/README.md b/web/README.md index d2e7761..c987b94 100644 --- a/web/README.md +++ b/web/README.md @@ -40,15 +40,15 @@ export default defineConfig([ // other options... }, }, -]) +]); ``` You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: ```js // eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' +import reactX from 'eslint-plugin-react-x'; +import reactDom from 'eslint-plugin-react-dom'; export default defineConfig([ globalIgnores(['dist']), @@ -69,5 +69,5 @@ export default defineConfig([ // other options... }, }, -]) +]); ``` diff --git a/web/eslint.config.js b/web/eslint.config.js index b19330b..ea46802 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -1,9 +1,9 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { defineConfig, globalIgnores } from 'eslint/config' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; +import { defineConfig, globalIgnores } from 'eslint/config'; export default defineConfig([ globalIgnores(['dist']), @@ -20,4 +20,4 @@ export default defineConfig([ globals: globals.browser, }, }, -]) +]); diff --git a/web/package-lock.json b/web/package-lock.json index c9b3465..89b7dca 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -21,9 +21,14 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.22", "globals": "^16.4.0", + "prettier": "3.6.2", "typescript": "~5.9.3", "typescript-eslint": "^8.45.0", "vite": "^7.1.7" + }, + "engines": { + "node": "^25.0.0", + "npm": "^11.0.0" } }, "node_modules/@babel/code-frame": { @@ -2905,6 +2910,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/web/package.json b/web/package.json index 6b852e3..69b6365 100644 --- a/web/package.json +++ b/web/package.json @@ -6,9 +6,13 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "build-dev": "", - "lint": "eslint .", - "preview": "vite preview" + "lint": "eslint . && prettier . --check", + "preview": "vite preview", + "format": "prettier . --write" + }, + "engines": { + "node": "^25.0.0", + "npm": "^11.0.0" }, "dependencies": { "react": "^19.1.1", @@ -24,6 +28,7 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.22", "globals": "^16.4.0", + "prettier": "3.6.2", "typescript": "~5.9.3", "typescript-eslint": "^8.45.0", "vite": "^7.1.7" diff --git a/web/src/App.tsx b/web/src/App.tsx index 9b7237a..a2d52af 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,35 +1,29 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import { useState } from 'react'; +import reactLogo from './assets/react.svg'; +import viteLogo from '/vite.svg'; +import './App.css'; function App() { - const [count, setCount] = useState(0) - fetch("/api/v1/user").then(x => x.json().then(x => console.log(x))) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Skull emoji -

-
-

- Click on the Vite and React logos to learn more -

- - ) + const [count, setCount] = useState(0); + fetch('/api/v1/user').then((x) => x.json().then((x) => console.log(x))); + return ( + <> +
+ + Vite logo + + + React logo + +
+

Vite + React

+
+ +

Skull emoji

+
+

Click on the Vite and React logos to learn more

+ + ); } -export default App +export default App; diff --git a/web/src/main.tsx b/web/src/main.tsx index bef5202..df655ea 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; createRoot(document.getElementById('root')!).render( - , -) + +); diff --git a/web/tsconfig.json b/web/tsconfig.json index 1ffef60..d32ff68 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,7 +1,4 @@ { "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] } diff --git a/web/vite.config.ts b/web/vite.config.ts index 6ee8d74..a047d79 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vite.dev/config/ export default defineConfig({ @@ -7,9 +7,9 @@ export default defineConfig({ proxy: { '/api': { target: 'http://localhost:8080', - changeOrigin: true - } + changeOrigin: true, + }, }, }, plugins: [react()], -}) +});