diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3e8f7f6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{Makefile,**.mk}] +indent_style = tab +indent_size = 4 + +[*.{diff,md}] +trim_trailing_whitespace = false + +[docker-compose{,.*}.{yaml,yml}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc.json b/.eslintrc.json index e45642a..fa78e27 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,19 +1,25 @@ { - "extends": ["next", "next/core-web-vitals", "plugin:unicorn/recommended"], + "extends": [ + "next", + "next/core-web-vitals", + "next/typescript", + "plugin:unicorn/recommended" + ], "plugins": ["filenames"], "ignorePatterns": ["next-env.d.ts"], "rules": { "filenames/match-regex": ["error", "^[a-z0-9-]+$", true], - "unicorn/filename-case": [ - "error", - { - "case": "kebabCase" - } - ], + "unicorn/filename-case": ["error", { "case": "kebabCase" }], "unicorn/no-null": 0, "unicorn/no-array-for-each": 0, "unicorn/no-nested-ternary": 0, "unicorn/prefer-spread": 0, - "unicorn/prevent-abbreviations": 0 + "unicorn/prevent-abbreviations": 0, + // TODO: Check these rules later + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-unused-vars": [ + 0, + { "argsIgnorePattern": "^_", "args": "all" } + ] } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..999665b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: Continuous Integration + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + setup: + name: Setup NodeJs, PNPM and Cache + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - name: Install Pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + with: + run_install: false + - name: Install Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version: 22 + cache: "pnpm" + - name: Install dependencies + run: pnpm install --frozen-lockfile + format: + name: FORMAT - Run Biome Check + runs-on: ubuntu-24.04 + needs: [setup] + steps: + - name: Checkout code + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - name: Install Pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + with: + run_install: false + - name: Install Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version: 22 + cache: "pnpm" + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Check lint + run: pnpm run check:ci + lint: + name: LINT - Run Next Lint + runs-on: ubuntu-24.04 + needs: [setup] + steps: + - name: Checkout code + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - name: Install Pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 + with: + run_install: false + - name: Install Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version: 22 + cache: "pnpm" + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Check lint + run: pnpm run lint diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 031d452..9e54bb8 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,7 +4,13 @@ on: push: branches: - main + - develop workflow_dispatch: + inputs: + reason: + description: "Write why you are requesting a manual release" + type: string + required: true concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -19,16 +25,18 @@ jobs: build-and-push: name: Build and Push Docker Image runs-on: ubuntu-24.04 - if: (github.event_name == 'push' && contains(github.event.head_commit.message, '[CI]')) || (github.event_name == 'workflow_dispatch') + # See https://github.com/actions/runner/issues/859#issue-766582646 + if: > + (github.repository == 'basementdevs/scylla-studio') && + ((github.event_name == 'push' && contains(github.event.head_commit.message, '[CD]')) || (github.event_name == 'workflow_dispatch')) permissions: - contents: read - packages: write + contents: read + packages: write steps: - name: Checkout Repository uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - - name: Log in to GitHub Container Registry uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # 3.3.0 with: @@ -38,6 +46,7 @@ jobs: - name: Build Docker image run: | docker buildx build \ + --platform "linux/amd64" \ --label "org.opencontainers.image.source=https://github.com/${{ env.BASE_IMAGE_NAME }}" \ --label "org.opencontainers.image.created=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ --tag "${{ env.IMAGE_NAME }}:latest" \ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e295a29 --- /dev/null +++ b/.npmrc @@ -0,0 +1,6 @@ +# Ensures exact versions are installed and saved in package.json +save-exact=true + +# Disables caching of side effects to avoid issues with post-install scripts +# See https://github.com/evilmartians/lefthook/blob/1c92f5b80de7a210e4cd4c7fe1b29a0e9df8ddd3/docs/install.md?plain=1#L61 +side-effects-cache=false diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..8fdd954 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 463525d..42b925e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,8 @@ { - "editor.formatOnSave": true, - "editor.defaultFormatter": "biomejs.biome" + "editor.codeActionsOnSave": { + "source.fixAll.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true } diff --git a/biome.json b/biome.json index fb3b41c..affee5b 100644 --- a/biome.json +++ b/biome.json @@ -4,7 +4,7 @@ "enabled": true }, "files": { - "ignore": ["node_modules", "build"], + "ignore": ["node_modules", "build", ".next"], "ignoreUnknown": true }, "linter": { @@ -18,7 +18,8 @@ "indentStyle": "space", "indentWidth": 2, "lineWidth": 80, - "lineEnding": "lf" + "lineEnding": "lf", + "useEditorconfig": true }, "javascript": { "formatter": { diff --git a/lefthook.yml b/lefthook.yml index a42df4b..685af1f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -6,14 +6,24 @@ assert_lefthook_installed: true pre-commit: parallel: false commands: - biome: - run: pnpm run check + format: + tags: formatting + run: pnpm run check:hook -- {staged_files} lint: - run: pnpm run lint - include: "*.js,*.jsx,*.ts,*.tsx" + tags: linting + glob: "*.{js,jsx,ts,tsx}" + run: pnpm exec next lint --file {staged_files} pre-push: parallel: false commands: + format: + tags: formatting + run: pnpm run check:hook -- {push_files} lint: - run: pnpm run lint + tags: linting + glob: "*.{js,jsx,ts,tsx}" + run: pnpm exec next lint --file {push_files} + audit: + tags: security + run: pnpm audit --prod diff --git a/package.json b/package.json index 854195c..62d1c50 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "lint": "next lint", "check": "biome check", "check:fix": "biome check --write", - "trigger:ci": "git commit --allow-empty -s -m '[CI]: release docker image'" + "check:ci": "biome ci .", + "check:hook": "biome check --no-errors-on-unmatched --files-ignore-unknown=true", + "trigger:ci": "git commit --allow-empty -s -m '[CD]: release docker image'" + }, + "engines": { + "node": ">=22", + "pnpm": "9.11.0" }, "dependencies": { "@hookform/resolvers": "^3.9.0", @@ -63,7 +69,7 @@ "eslint-config-next": "14.2.9", "eslint-plugin-filenames": "^1.3.2", "eslint-plugin-unicorn": "^56.0.0", - "lefthook": "^1.7.18", + "lefthook": "1.7.18", "monaco-editor": "^0.52.0", "postcss": "^8", "tailwindcss": "^3.4.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e234826..b30f259 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,7 +154,7 @@ importers: specifier: ^56.0.0 version: 56.0.0(eslint@8.57.0) lefthook: - specifier: ^1.7.18 + specifier: 1.7.18 version: 1.7.18 monaco-editor: specifier: ^0.52.0 @@ -4625,7 +4625,7 @@ snapshots: debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 @@ -4638,7 +4638,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): + eslint-module-utils@2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -4668,7 +4668,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 diff --git a/src/actions/execute-query.ts b/src/actions/execute-query.ts index 7987a1f..0a0caa6 100644 --- a/src/actions/execute-query.ts +++ b/src/actions/execute-query.ts @@ -68,7 +68,7 @@ export const getSession = async (inputConnection: Partial) => { let session = connection?.session; // prepare the object for upcomming connections - let connectionObject = { + const connectionObject = { nodes: [`${inputConnection.host}:${inputConnection.port}`], auth: (inputConnection.username && diff --git a/src/actions/query-keyspaces.ts b/src/actions/query-keyspaces.ts index 544889f..e0b22d3 100644 --- a/src/actions/query-keyspaces.ts +++ b/src/actions/query-keyspaces.ts @@ -34,7 +34,7 @@ export const queryKeyspaceAction = actionClient const session = await connection.connect(); const clusterData = await session.getClusterData(); const keyspaces = clusterData.getKeyspaceInfo(); - let betterKeyspaces = await parseKeyspaces(session); + const betterKeyspaces = await parseKeyspaces(session); return { keyspaces, betterKeyspaces }; }); diff --git a/src/app/(main)/connections/actions/connections.ts b/src/app/(main)/connections/actions/connections.ts index 396dfb5..f8fbda9 100644 --- a/src/app/(main)/connections/actions/connections.ts +++ b/src/app/(main)/connections/actions/connections.ts @@ -14,7 +14,7 @@ export async function fetchConnections(): Promise { } async function validateConnectionStatus(newConnection: Connection) { - let connectionObject = { + const connectionObject = { nodes: [`${newConnection.host}:${newConnection.port}`], auth: (newConnection.username && @@ -31,7 +31,7 @@ async function validateConnectionStatus(newConnection: Connection) { await cluster.connect(); newConnection.status = "Connected"; } catch (error: any) { - let message = error.message as string; + const message = error.message as string; console.log(message); newConnection.status = message.includes("Connection reset by peer") || diff --git a/src/app/(main)/query-runner/_components/utils.tsx b/src/app/(main)/query-runner/_components/utils.tsx index 480992c..8a73525 100644 --- a/src/app/(main)/query-runner/_components/utils.tsx +++ b/src/app/(main)/query-runner/_components/utils.tsx @@ -10,7 +10,7 @@ export const getFullQueryAtCursor = ( monaco: Monaco, ) => { const model = editor.getModel(); - let position = editor.getPosition(); + const position = editor.getPosition(); if (!model || !position) return null; diff --git a/src/components/composed/command.tsx b/src/components/composed/command.tsx index 7c635be..31e5fa0 100644 --- a/src/components/composed/command.tsx +++ b/src/components/composed/command.tsx @@ -18,7 +18,7 @@ export function CommandMenu() { const [open, setOpen] = React.useState(false); const { keyspaces } = useLayout(); - let parsedKeyspaces = Object.keys(keyspaces); + const parsedKeyspaces = Object.keys(keyspaces); const router = useRouter(); React.useEffect(() => { @@ -74,8 +74,8 @@ export function CommandMenu() { {parsedKeyspaces.map((keyspace) => { - let currentKeyspace = keyspaces[keyspace]; - let currentTables = Object.keys(currentKeyspace.tables); + const currentKeyspace = keyspaces[keyspace]; + const currentTables = Object.keys(currentKeyspace.tables); return currentTables.map((table) => ( > { - let parsedKeyspaces = new Map(); - let rows = await session.execute("DESC keyspaces"); + const parsedKeyspaces = new Map(); + const rows = await session.execute("DESC keyspaces"); - for (let row of rows) { - let result: DescriptionRow[] = await session.execute( + for (const row of rows) { + const result: DescriptionRow[] = await session.execute( `DESC ${row.keyspace_name}`, ); let parsedKeyspace = {} as KeyspaceDefinition; - for (let row of result) { + for (const row of result) { switch (row.type) { case "keyspace": { parsedKeyspace = parseCreateKeyspace(row); diff --git a/src/lib/cql-parser/table-parser.ts b/src/lib/cql-parser/table-parser.ts index 9d586f0..0304d44 100644 --- a/src/lib/cql-parser/table-parser.ts +++ b/src/lib/cql-parser/table-parser.ts @@ -167,7 +167,7 @@ function parseTableKeys(create_table_cql: string) { const clusteringKeys: string[] = []; if (!keysPart?.includes(",")) { - let partitionKeys = keysPart?.split(")") || []; + const partitionKeys = keysPart?.split(")") || []; return { partitionKeys, clusteringKeys }; }