diff --git a/.c8rc.json b/.c8rc.json new file mode 100644 index 0000000..193e3ed --- /dev/null +++ b/.c8rc.json @@ -0,0 +1,28 @@ +{ + "all": true, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/test/**", + "**/*.test.ts", + "**/*.spec.ts", + "**/node_modules/**", + "out/**" + ], + "reporter": [ + "html", + "text", + "json-summary", + "lcov" + ], + "report-dir": "./coverage", + "temp-directory": "./.nyc_output", + "check-coverage": true, + "lines": 70, + "statements": 70, + "functions": 70, + "branches": 60, + "clean": true, + "skip-full": false +} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..748da6e --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, + plugins: [ + '@typescript-eslint' + ], + rules: { + '@typescript-eslint/naming-convention': [ + 'warn', + { + 'selector': 'import', + 'format': ['camelCase', 'PascalCase'] + } + ], + '@typescript-eslint/semi': 'warn', + 'curly': 'warn', + 'eqeqeq': 'warn', + 'no-throw-literal': 'warn', + 'semi': 'off' + }, + ignorePatterns: [ + 'out', + 'dist', + '**/*.d.ts', + 'node_modules', + 'coverage' + ] +}; diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..042ce01 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,89 @@ +## Description + + + +## Type of Change + + + +- [ ] Bug fix (non-breaking change that fixes an issue) +- [ ] New feature (non-breaking change that adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update +- [ ] Performance improvement +- [ ] Refactoring (no functional changes) +- [ ] Dependency update + +## Motivation + + + +## Changes + + + +- +- +- + +## Testing + + + +- [ ] Unit tests added/updated +- [ ] Integration tests pass (`npm run test:integration`) +- [ ] Manually tested locally +- [ ] Tested on MySQL 8.0 and/or MariaDB 10.11 +- [ ] Performance tested (if applicable) + +## License Compliance + + + +- [ ] No new dependencies added +- [ ] New dependencies verified with `npm run license:verify` +- [ ] All dependencies use approved licenses (MIT, Apache-2.0, BSD, ISC, etc.) + +## Checklist + + + +- [ ] Code compiles without errors (`npm run compile`) +- [ ] All tests pass (`npm test`) +- [ ] Linting passes (`npm run lint`) +- [ ] License compliance verified (`npm run license:verify`) +- [ ] Documentation updated (if applicable) +- [ ] CHANGELOG.md updated (if applicable) +- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) +- [ ] Branch is up-to-date with target branch + +## Screenshots (if applicable) + + + +## Breaking Changes + + + +## Additional Notes + + + +## Related Issues + + + +Closes # + +--- + +## Reviewer Checklist + + + +- [ ] Code quality meets standards +- [ ] Tests are adequate and passing +- [ ] License compliance check passed +- [ ] No security vulnerabilities introduced +- [ ] Documentation is clear and complete +- [ ] Breaking changes are documented diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4c33237 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,408 @@ +name: CI + +on: + pull_request: + branches: [main, develop] + push: + branches: [main, develop] + workflow_dispatch: + +# Security: Explicitly define minimal permissions at workflow level +permissions: + actions: none # Prevent workflow manipulation + checks: none # Prevent check manipulation + contents: read # Needed for checkout + deployments: none # Prevent deployment manipulation + issues: write # Needed for creating issues + packages: none # Prevent package manipulation + pull-requests: write # Needed for PR comments + repository-projects: none + security-events: none + statuses: none + +jobs: + build-and-test: + name: Build and Test + runs-on: ${{ matrix.os }} + permissions: + contents: read + + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + node-version: [18.x, 20.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint + continue-on-error: false + + - name: Compile TypeScript + run: npm run compile + + - name: Run Unit Tests + run: npm run test:unit + + - name: Run Integration Tests (Linux) + if: runner.os == 'Linux' && matrix.node-version == '20.x' + run: xvfb-run -a npm test + + - name: Run Integration Tests (non-Linux) + if: runner.os != 'Linux' && matrix.node-version == '20.x' + run: npm test + + - name: Check for compilation errors + run: | + if [ ! -d "out" ]; then + echo "❌ Compilation failed - 'out' directory not found" + exit 1 + fi + if [ ! -f "out/extension.js" ]; then + echo "❌ Compilation failed - 'out/extension.js' not found" + exit 1 + fi + echo "✅ Compilation successful" + shell: bash + + - name: Validate package.json + run: | + node -e " + const pkg = require('./package.json'); + const errors = []; + + if (!pkg.name) errors.push('Missing name'); + if (!pkg.version) errors.push('Missing version'); + if (!pkg.publisher || pkg.publisher === 'YOUR_PUBLISHER_ID') { + console.log('⚠️ Warning: Publisher ID not set (expected for non-release branches)'); + } + if (!pkg.engines || !pkg.engines.vscode) errors.push('Missing vscode engine requirement'); + if (!pkg.main) errors.push('Missing main entry point'); + if (!pkg.contributes) errors.push('Missing contributes section'); + + if (errors.length > 0) { + console.error('❌ package.json validation failed:'); + errors.forEach(e => console.error(' - ' + e)); + process.exit(1); + } + + console.log('✅ package.json is valid'); + " + shell: bash + + - name: Check required files + run: | + files=("README.md" "LICENSE" "CHANGELOG.md" "package.json" "tsconfig.json") + missing=() + + for file in "${files[@]}"; do + if [ ! -f "$file" ]; then + missing+=("$file") + fi + done + + if [ ${#missing[@]} -gt 0 ]; then + echo "❌ Missing required files:" + printf ' - %s\n' "${missing[@]}" + exit 1 + fi + + echo "✅ All required files present" + shell: bash + + - name: Upload compilation artifacts + if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x' + uses: actions/upload-artifact@v4 + with: + name: compiled-extension + path: out/ + retention-days: 7 + + package: + name: Package Extension + runs-on: ubuntu-latest + needs: build-and-test + permissions: + contents: read + # Only package in CI for PRs and non-main branches (main uses publish-release.yml) + if: github.ref != 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install vsce + run: npm install -g @vscode/vsce + + - name: Compile TypeScript + run: npm run compile + + - name: Package extension + run: vsce package + continue-on-error: true + id: package + + - name: Check package result + run: | + if [ -f *.vsix ]; then + VSIX_FILE=$(ls *.vsix) + SIZE=$(du -h "$VSIX_FILE" | cut -f1) + echo "✅ Package created: $VSIX_FILE (${SIZE})" + echo "package_success=true" >> $GITHUB_OUTPUT + else + echo "⚠️ Package creation skipped (may need publisher ID for release)" + echo "package_success=false" >> $GITHUB_OUTPUT + fi + id: check_package + + - name: Upload VSIX + if: steps.check_package.outputs.package_success == 'true' + uses: actions/upload-artifact@v4 + with: + name: vsix-package + path: '*.vsix' + retention-days: 30 + + lint-report: + name: Lint Report + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + continue-on-error: true + + integration-tests-docker: + name: Integration Tests (Docker) + runs-on: ubuntu-latest + permissions: + contents: read + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: test_password + MYSQL_DATABASE: test_db + MYSQL_USER: test_user + MYSQL_PASSWORD: test_password + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping -h localhost -u root -ptest_password" + --health-interval=10s + --health-timeout=5s + --health-retries=10 + volumes: + - ${{ github.workspace }}/test/sql:/docker-entrypoint-initdb.d:ro + + + steps: + - name: Cleanup workspace + run: | + sudo rm -rf "${{ github.workspace }}"/* + sudo rm -rf "${{ github.workspace }}"/.[!.]* + continue-on-error: true + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Compile TypeScript + run: npm run compile + + - name: Start MariaDB with Performance Schema + run: | + docker run -d \ + --name mariadb-test \ + -e MYSQL_ROOT_PASSWORD=test_password \ + -e MYSQL_DATABASE=test_db \ + -e MYSQL_USER=test_user \ + -e MYSQL_PASSWORD=test_password \ + -p 3307:3306 \ + --health-cmd="mysqladmin ping -h localhost -u root -ptest_password" \ + --health-interval=10s \ + --health-timeout=5s \ + --health-retries=10 \ + mariadb:10.11 \ + --performance-schema=ON + echo "MariaDB container started" + + - name: Wait for MySQL to be ready + run: | + timeout 60 bash -c 'until mysqladmin ping -h 127.0.0.1 -P 3306 -u root -ptest_password --silent; do sleep 2; done' + echo "MySQL is ready" + + - name: Wait for MariaDB to be ready + run: | + timeout 60 bash -c 'until mysqladmin ping -h 127.0.0.1 -P 3307 -u root -ptest_password --silent; do sleep 2; done' + echo "MariaDB is ready" + + - name: Enable Performance Schema in MariaDB + run: | + # Note: Performance Schema cannot be enabled at runtime in MariaDB + # We need to restart with the option. Let's verify if it's already enabled + PERF_SCHEMA=$(mysql -h 127.0.0.1 -P 3307 -u root -ptest_password -sNe "SHOW VARIABLES LIKE 'performance_schema'" | awk '{print $2}') + echo "Performance Schema status: $PERF_SCHEMA" + if [ "$PERF_SCHEMA" != "ON" ]; then + echo "⚠️ Warning: Performance Schema is not enabled in MariaDB" + echo "This requires the container to be started with --performance-schema=ON" + fi + + - name: Initialize test data + run: | + # Initialize MySQL + mysql -h 127.0.0.1 -P 3306 -u root -ptest_password test_db < test/sql/sample-data.sql + mysql -h 127.0.0.1 -P 3306 -u root -ptest_password test_db < test/sql/performance-schema-setup.sql + echo "MySQL test data initialized" + + # Initialize MariaDB + mysql -h 127.0.0.1 -P 3307 -u root -ptest_password test_db < test/sql/sample-data.sql + mysql -h 127.0.0.1 -P 3307 -u root -ptest_password test_db < test/sql/performance-schema-setup.sql + echo "MariaDB test data initialized" + + - name: Grant Performance Schema and system database permissions + run: | + # Grant permissions for MySQL + mysql -h 127.0.0.1 -P 3306 -u root -ptest_password -e "GRANT SELECT, UPDATE ON performance_schema.* TO 'test_user'@'%';" + mysql -h 127.0.0.1 -P 3306 -u root -ptest_password -e "GRANT SELECT ON mysql.* TO 'test_user'@'%';" + mysql -h 127.0.0.1 -P 3306 -u root -ptest_password -e "FLUSH PRIVILEGES;" + echo "MySQL permissions granted" + + # Grant permissions for MariaDB + mysql -h 127.0.0.1 -P 3307 -u root -ptest_password -e "GRANT SELECT, UPDATE ON performance_schema.* TO 'test_user'@'%';" + mysql -h 127.0.0.1 -P 3307 -u root -ptest_password -e "GRANT SELECT ON mysql.* TO 'test_user'@'%';" + mysql -h 127.0.0.1 -P 3307 -u root -ptest_password -e "FLUSH PRIVILEGES;" + echo "MariaDB permissions granted" + + - name: Run Integration Tests with XVFB + run: xvfb-run -a npm test + + - name: Run Test Coverage and Generate Report + run: | + xvfb-run -a npm run test:coverage || true + npm run test:coverage:report || echo "⚠️ Coverage thresholds not met (expected for extension integration tests)" + continue-on-error: true + + - name: Upload Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage/ + retention-days: 30 + + - name: Check Coverage Thresholds + run: | + if [ -f coverage/coverage-summary.json ]; then + echo "Coverage report generated successfully" + cat coverage/coverage-summary.json + else + echo "⚠️ Coverage report not found" + fi + + license-compliance: + name: License Compliance Check + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Check licenses + run: | + npx license-checker \ + --failOn "GPL;AGPL;LGPL;SSPL;CC-BY-NC" \ + --summary || { + echo "❌ License compliance check failed!" + echo "Found disallowed licenses. Run 'npm run license:check' locally for details." + exit 1 + } + echo "✅ License compliance check passed!" + + status-check: + name: Status Check + runs-on: ubuntu-latest + needs: [build-and-test, package, lint-report, integration-tests-docker, license-compliance] + permissions: + contents: read + if: always() + + steps: + - name: Check build status + run: | + if [ "${{ needs.build-and-test.result }}" != "success" ]; then + echo "❌ Build and test failed" + exit 1 + fi + # Package job is skipped on main branch (handled by publish-release.yml) + if [ "${{ needs.package.result }}" != "success" ] && [ "${{ needs.package.result }}" != "skipped" ]; then + echo "❌ Package failed" + exit 1 + fi + # Integration tests with Docker + if [ "${{ needs.integration-tests-docker.result }}" != "success" ]; then + echo "❌ Integration tests failed" + exit 1 + fi + # License compliance + if [ "${{ needs.license-compliance.result }}" != "success" ]; then + echo "❌ License compliance check failed" + exit 1 + fi + echo "✅ All checks passed - PR is ready to merge!" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..4bccb14 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,42 @@ +name: CodeQL Security Scan + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + schedule: + # Run at 00:00 UTC every Monday + - cron: '0 0 * * 1' + +jobs: + analyze: + name: Analyze Code + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript'] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # Use default queries (security-and-quality is included by default in v3) + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml new file mode 100644 index 0000000..3511f7d --- /dev/null +++ b/.github/workflows/license-check.yml @@ -0,0 +1,395 @@ +name: License Compliance Check + +on: + pull_request: + branches: [main, develop] + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [main, develop] + schedule: + # Run weekly on Monday at 00:00 UTC to catch new dependency issues + - cron: '0 0 * * 1' + workflow_dispatch: + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + license-check: + name: Check Dependency Licenses + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install license-checker + run: npm install -g license-checker + + - name: Check licenses + id: license_check + run: | + echo "Running license compliance check..." + + # Generate full license report + license-checker --json --out license-report.json || true + license-checker --csv --out license-report.csv || true + + # Check for disallowed licenses + # Fail if we find GPL, AGPL, or other copyleft licenses + license-checker \ + --failOn "GPL;AGPL;LGPL;SSPL;CC-BY-NC" \ + --summary > license-summary.txt 2>&1 || { + echo "❌ License compliance check failed!" + echo "Found disallowed licenses. See summary below:" + cat license-summary.txt + echo "license_failed=true" >> $GITHUB_OUTPUT + exit 1 + } + + # Check for packages without licenses + license-checker \ + --excludePrivatePackages \ + --onlyAllow "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;0BSD;BlueOak-1.0.0;Python-2.0;CC0-1.0;Unlicense" \ + --summary >> license-summary.txt 2>&1 || { + echo "⚠️ Some packages have unverified licenses" + cat license-summary.txt + echo "license_warning=true" >> $GITHUB_OUTPUT + } + + echo "✅ License compliance check passed!" + echo "license_passed=true" >> $GITHUB_OUTPUT + + - name: Generate detailed license report + if: always() + run: | + echo "# License Report" > license-detailed-report.md + echo "" >> license-detailed-report.md + echo "Generated: $(date)" >> license-detailed-report.md + echo "" >> license-detailed-report.md + + # Add summary + echo "## Summary" >> license-detailed-report.md + echo '```' >> license-detailed-report.md + cat license-summary.txt >> license-detailed-report.md + echo '```' >> license-detailed-report.md + echo "" >> license-detailed-report.md + + # Add allowed licenses list + echo "## Allowed Licenses" >> license-detailed-report.md + echo "The following licenses are approved for use:" >> license-detailed-report.md + echo "- MIT" >> license-detailed-report.md + echo "- Apache-2.0" >> license-detailed-report.md + echo "- BSD-2-Clause" >> license-detailed-report.md + echo "- BSD-3-Clause" >> license-detailed-report.md + echo "- ISC" >> license-detailed-report.md + echo "- 0BSD" >> license-detailed-report.md + echo "- CC0-1.0" >> license-detailed-report.md + echo "- Python-2.0" >> license-detailed-report.md + echo "" >> license-detailed-report.md + + # Add disallowed licenses list + echo "## Disallowed Licenses" >> license-detailed-report.md + echo "The following licenses are NOT allowed (copyleft, proprietary, or restrictive):" >> license-detailed-report.md + echo "- GPL (all versions)" >> license-detailed-report.md + echo "- AGPL (all versions)" >> license-detailed-report.md + echo "- LGPL (all versions)" >> license-detailed-report.md + echo "- SSPL (Server Side Public License)" >> license-detailed-report.md + echo "- CC-BY-NC (Non-Commercial Creative Commons)" >> license-detailed-report.md + echo "" >> license-detailed-report.md + + # Count licenses by type + echo "## License Distribution" >> license-detailed-report.md + echo '```' >> license-detailed-report.md + license-checker --summary >> license-detailed-report.md 2>&1 || true + echo '```' >> license-detailed-report.md + + - name: Upload license reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: license-reports + path: | + license-report.json + license-report.csv + license-summary.txt + license-detailed-report.md + retention-days: 90 + + - name: Post PR comment on license violations + if: github.event_name == 'pull_request' && steps.license_check.outputs.license_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const summary = fs.readFileSync('license-summary.txt', 'utf8'); + + // Delete previous bot comments to avoid spam + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + for (const comment of comments) { + if (comment.user.type === 'Bot' && comment.body.includes('License Compliance Check')) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id, + }); + } + } + + // Post new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ❌ License Compliance Check Failed + + This PR introduces dependencies with **disallowed licenses** and cannot be merged. + + ### License Summary + \`\`\` + ${summary} + \`\`\` + + ### Action Required + Please review the dependencies and either: + 1. **Replace packages** with disallowed licenses (recommended) + 2. **Remove the dependency** if not critical + 3. **Request an exception** if the license is necessary (requires legal review) + + ### How to Fix + \`\`\`bash + # Check which packages have issues locally + npm run license:check + + # Verify after making changes + npm run license:verify + \`\`\` + + ### Allowed Licenses ✅ + - MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD, CC0-1.0, Python-2.0 + + ### Disallowed Licenses ❌ + - **GPL, AGPL, LGPL** - Copyleft licenses (require derivative works to be open source) + - **SSPL** - Server Side Public License (restrictive for SaaS) + - **CC-BY-NC** - Non-Commercial (prohibits commercial use) + + ### Additional Resources + - 📖 [License Compliance Policy](https://github.com/${{ github.repository }}/blob/${{ github.head_ref || github.ref_name }}/docs/LICENSE_COMPLIANCE.md) + - 📊 [Detailed License Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + + --- + *This check is required for merging. The PR will remain blocked until resolved.*` + }); + + - name: Post PR comment on license warnings + if: github.event_name == 'pull_request' && steps.license_check.outputs.license_warning == 'true' && steps.license_check.outputs.license_failed != 'true' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const summary = fs.readFileSync('license-summary.txt', 'utf8'); + + // Delete previous bot comments + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + for (const comment of comments) { + if (comment.user.type === 'Bot' && comment.body.includes('License Compliance')) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id, + }); + } + } + + // Post new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ⚠️ License Compliance Warning + + Some dependencies have unverified or non-standard licenses. Please review before merging. + + ### License Summary + \`\`\` + ${summary} + \`\`\` + + ### Recommended Actions + - Review each package's license manually + - Verify licenses are compatible with commercial use + - Check if alternatives with clearer licenses exist + + ### How to Check Locally + \`\`\`bash + npm run license:check + npm run license:report # Generate detailed reports + \`\`\` + + 📊 [View detailed license report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + + --- + *This is a warning only. The PR can still be merged, but please verify licenses are acceptable.*` + }); + + - name: Post success comment on PR + if: github.event_name == 'pull_request' && steps.license_check.outputs.license_passed == 'true' + uses: actions/github-script@v7 + with: + script: | + // Delete previous bot comments + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + for (const comment of comments) { + if (comment.user.type === 'Bot' && comment.body.includes('License Compliance')) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id, + }); + } + } + + // Post success comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ✅ License Compliance Check Passed + + All dependencies use approved licenses. This PR is compliant with the license policy. + + ### Status + - All licenses are compatible with commercial and open-source distribution + - No copyleft or restrictive licenses detected + - Ready to merge from a license perspective + + 📊 [View detailed license report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + + --- + *License compliance verified automatically.*` + }); + + - name: Add PR label on success + if: github.event_name == 'pull_request' && steps.license_check.outputs.license_passed == 'true' + uses: actions/github-script@v7 + with: + script: | + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['license:compliant'] + }); + } catch (error) { + console.log('Label may not exist, skipping...'); + } + + - name: Add PR label on failure + if: github.event_name == 'pull_request' && steps.license_check.outputs.license_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['license:violation', 'blocked'] + }); + } catch (error) { + console.log('Labels may not exist, skipping...'); + } + + - name: Fail on license violations + if: steps.license_check.outputs.license_failed == 'true' + run: | + echo "❌ License compliance check failed. Cannot merge with disallowed licenses." + echo "" + echo "This PR is blocked due to license violations." + echo "See the PR comment for details and remediation steps." + exit 1 + + license-report: + name: Generate License Attribution Report + runs-on: ubuntu-latest + needs: license-check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Generate THIRD_PARTY_LICENSES.md + run: | + npx license-checker --markdown --out THIRD_PARTY_LICENSES.md || true + + # Add header + echo "# Third Party Licenses" > THIRD_PARTY_LICENSES_FINAL.md + echo "" >> THIRD_PARTY_LICENSES_FINAL.md + echo "This document contains the licenses of all third-party dependencies used in MyDBA." >> THIRD_PARTY_LICENSES_FINAL.md + echo "" >> THIRD_PARTY_LICENSES_FINAL.md + echo "Generated: $(date)" >> THIRD_PARTY_LICENSES_FINAL.md + echo "" >> THIRD_PARTY_LICENSES_FINAL.md + echo "---" >> THIRD_PARTY_LICENSES_FINAL.md + echo "" >> THIRD_PARTY_LICENSES_FINAL.md + cat THIRD_PARTY_LICENSES.md >> THIRD_PARTY_LICENSES_FINAL.md + + mv THIRD_PARTY_LICENSES_FINAL.md THIRD_PARTY_LICENSES.md + + - name: Upload Third Party Licenses + uses: actions/upload-artifact@v4 + with: + name: third-party-licenses + path: THIRD_PARTY_LICENSES.md + retention-days: 365 + + status-check: + name: License Check Status + runs-on: ubuntu-latest + needs: [license-check, license-report] + if: always() + + steps: + - name: Check license status + run: | + if [ "${{ needs.license-check.result }}" != "success" ]; then + echo "❌ License check failed" + exit 1 + fi + + echo "✅ All license checks passed" diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 0000000..3ba50bb --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,144 @@ +name: PR Auto-Labeling + +on: + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + auto-label: + name: Auto-label PR + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Add size label + uses: actions/github-script@v7 + with: + script: | + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const additions = files.reduce((sum, file) => sum + file.additions, 0); + const deletions = files.reduce((sum, file) => sum + file.deletions, 0); + const totalChanges = additions + deletions; + + let sizeLabel = ''; + if (totalChanges < 10) { + sizeLabel = 'size:XS'; + } else if (totalChanges < 50) { + sizeLabel = 'size:S'; + } else if (totalChanges < 200) { + sizeLabel = 'size:M'; + } else if (totalChanges < 500) { + sizeLabel = 'size:L'; + } else { + sizeLabel = 'size:XL'; + } + + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: [sizeLabel] + }); + } catch (error) { + console.log('Size labels may not exist, skipping...'); + } + + - name: Add type labels based on files changed + uses: actions/github-script@v7 + with: + script: | + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const labels = new Set(); + + for (const file of files) { + const filename = file.filename; + + if (filename.includes('test/') || filename.includes('.test.')) { + labels.add('type:test'); + } + if (filename.includes('docs/') || filename.endsWith('.md')) { + labels.add('type:documentation'); + } + if (filename.includes('package.json') || filename.includes('package-lock.json')) { + labels.add('type:dependencies'); + } + if (filename.includes('.github/workflows/')) { + labels.add('type:ci'); + } + if (filename.includes('src/adapters/')) { + labels.add('area:database'); + } + if (filename.includes('src/services/ai')) { + labels.add('area:ai'); + } + if (filename.includes('webviews/') || filename.includes('media/')) { + labels.add('area:ui'); + } + if (filename.includes('.sql')) { + labels.add('type:sql'); + } + } + + if (labels.size > 0) { + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: Array.from(labels) + }); + } catch (error) { + console.log('Some labels may not exist, skipping...'); + } + } + + - name: Check for breaking changes + uses: actions/github-script@v7 + with: + script: | + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const body = pr.body || ''; + const title = pr.title || ''; + + const hasBreakingChange = + title.includes('!:') || + title.toUpperCase().includes('BREAKING') || + body.toUpperCase().includes('BREAKING CHANGE'); + + if (hasBreakingChange) { + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['breaking-change'] + }); + } catch (error) { + console.log('Breaking change label may not exist, skipping...'); + } + } diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..f43198b --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,277 @@ +name: Publish Release + +on: + push: + branches: + - main + paths: + - 'package.json' + +concurrency: + group: publish-release + cancel-in-progress: false + +# Security: Explicitly define minimal permissions at workflow level +permissions: + actions: none # Prevent workflow manipulation + checks: none # Prevent check manipulation + contents: write # Needed for creating releases + deployments: none # Prevent deployment manipulation + issues: write # Needed for creating issues on publish failure + packages: none # Prevent package manipulation + pull-requests: read # Needed for waiting on checks + repository-projects: none + security-events: none + statuses: none + +jobs: + wait-for-ci: + name: Wait for CI + runs-on: ubuntu-latest + steps: + - name: Wait for CI checks + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ github.sha }} + check-regexp: '^(Build and Test|Lint Report|Status Check).*$' + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 30 + allowed-conclusions: success + timeout-minutes: 30 + + publish: + name: Publish to Marketplace + runs-on: ubuntu-latest + needs: wait-for-ci + permissions: + contents: write + issues: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 2 # Need previous commit to detect version change + + - name: Check if version changed + id: version_check + run: | + # Get current version from package.json + CURRENT_VERSION=$(node -p "require('./package.json').version") + + # Get previous version from git history + git show HEAD~1:package.json > /tmp/prev-package.json 2>/dev/null || { + echo "First commit or package.json didn't exist before" + PREVIOUS_VERSION="0.0.0" + } + + if [ -f /tmp/prev-package.json ]; then + PREVIOUS_VERSION=$(node -e "try { console.log(require('/tmp/prev-package.json').version); } catch(e) { console.log('0.0.0'); }") + else + PREVIOUS_VERSION="0.0.0" + fi + + echo "Previous version: $PREVIOUS_VERSION" + echo "Current version: $CURRENT_VERSION" + + # Validate semantic version format + if ! npx semver "$CURRENT_VERSION" >/dev/null 2>&1; then + echo "❌ Invalid semantic version format: $CURRENT_VERSION" + echo "changed=false" >> $GITHUB_OUTPUT + exit 1 + fi + + # Check version increment (must be greater than previous) + if [ "$PREVIOUS_VERSION" != "0.0.0" ]; then + if ! npx semver -r ">$PREVIOUS_VERSION" "$CURRENT_VERSION" >/dev/null 2>&1; then + echo "❌ Version $CURRENT_VERSION must be greater than previous version $PREVIOUS_VERSION" + echo "changed=false" >> $GITHUB_OUTPUT + exit 1 + fi + fi + + if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then + echo "✅ Version changed: $PREVIOUS_VERSION → $CURRENT_VERSION" + echo "changed=true" >> $GITHUB_OUTPUT + echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT + else + echo "ℹ️ Version unchanged, skipping release" + echo "changed=false" >> $GITHUB_OUTPUT + fi + + - name: Security scan + if: steps.version_check.outputs.changed == 'true' + uses: github/super-linter@v4 + env: + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VALIDATE_ALL_CODEBASE: false + VALIDATE_YAML: true + VALIDATE_JSON: true + VALIDATE_JAVASCRIPT_ES: true + VALIDATE_TYPESCRIPT_ES: true + + - name: Dependency review + if: steps.version_check.outputs.changed == 'true' + uses: actions/dependency-review-action@v3 + with: + fail-on-severity: moderate + + - name: Setup Node.js + if: steps.version_check.outputs.changed == 'true' + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + if: steps.version_check.outputs.changed == 'true' + run: npm ci + + - name: Install tools + if: steps.version_check.outputs.changed == 'true' + run: | + npm install -g @vscode/vsce + npm install -g semver + + - name: Compile TypeScript + if: steps.version_check.outputs.changed == 'true' + run: npm run compile + + - name: Package extension + if: steps.version_check.outputs.changed == 'true' + run: vsce package + + - name: Verify VSIX package + if: steps.version_check.outputs.changed == 'true' + run: | + # Find the VSIX file + VSIX_FILE=$(ls *.vsix) + echo "Verifying $VSIX_FILE..." + # Basic verification that the file exists and has reasonable size + if [ -f "$VSIX_FILE" ]; then + FILE_SIZE=$(stat -f%z "$VSIX_FILE" 2>/dev/null || stat -c%s "$VSIX_FILE" 2>/dev/null) + echo "✅ VSIX package verified: $VSIX_FILE ($FILE_SIZE bytes)" + else + echo "❌ VSIX package not found" + exit 1 + fi + + - name: Extract version-specific changelog + if: steps.version_check.outputs.changed == 'true' + id: extract_changelog + run: | + VERSION="${{ steps.version_check.outputs.version }}" + + # Extract only this version's section from CHANGELOG.md + if [ -f CHANGELOG.md ]; then + echo "Extracting changelog for version $VERSION..." + + # Look for the version header and extract until the next version header + awk "/^## \[$VERSION\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md > version_changelog.md + + if [ -s version_changelog.md ]; then + echo "✅ Found changelog section for v$VERSION" + echo "has_changelog=true" >> $GITHUB_OUTPUT + else + echo "⚠️ No changelog section found for v$VERSION" + echo "has_changelog=false" >> $GITHUB_OUTPUT + fi + else + echo "⚠️ CHANGELOG.md not found" + echo "has_changelog=false" >> $GITHUB_OUTPUT + fi + + - name: Create GitHub Release + if: steps.version_check.outputs.changed == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.version_check.outputs.version }} + name: Release v${{ steps.version_check.outputs.version }} + body_path: ${{ steps.extract_changelog.outputs.has_changelog == 'true' && 'version_changelog.md' || '' }} + draft: false + prerelease: false + make_latest: true + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check Azure token availability + id: check_token + if: steps.version_check.outputs.changed == 'true' + env: + AZURE_TOKEN: ${{ secrets.AZURE_TOKEN }} + run: | + if [ -n "$AZURE_TOKEN" ]; then + echo "has_token=true" >> $GITHUB_OUTPUT + else + echo "has_token=false" >> $GITHUB_OUTPUT + fi + + - name: Publish to VS Code Marketplace + id: marketplace_publish + if: steps.version_check.outputs.changed == 'true' && steps.check_token.outputs.has_token == 'true' + run: | + # Find the VSIX file + VSIX_FILE=$(ls *.vsix) + echo "Publishing $VSIX_FILE to marketplace..." + vsce publish --packagePath "$VSIX_FILE" --pat $VSCE_PAT + env: + VSCE_PAT: ${{ secrets.AZURE_TOKEN }} + continue-on-error: true + + - name: Report marketplace publish failure + if: steps.version_check.outputs.changed == 'true' && steps.marketplace_publish.outcome == 'failure' + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: '🚨 Failed to publish v${{ steps.version_check.outputs.version }} to VS Code Marketplace', + body: [ + '## Marketplace Publish Failed', + '', + '**Version:** v${{ steps.version_check.outputs.version }}', + '**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}', + '', + 'The automated marketplace publish step failed. This may require manual intervention.', + '', + '### Next Steps:', + '1. Check the [workflow logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details', + '2. Verify AZURE_TOKEN secret is valid and has proper permissions', + '3. If needed, publish manually using: `vsce publish`', + '4. Close this issue once resolved', + '', + '**Note:** The GitHub Release and git tag were created successfully.' + ].join('\n'), + labels: ['release', 'automation', 'bug'] + }); + + - name: Summary + if: steps.version_check.outputs.changed == 'true' + run: | + echo "## 🎉 Release v${{ steps.version_check.outputs.version }} Published!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ Completed" >> $GITHUB_STEP_SUMMARY + echo "- 🔒 Security scan completed" >> $GITHUB_STEP_SUMMARY + echo "- 📋 Dependency review passed" >> $GITHUB_STEP_SUMMARY + echo "- 📦 VSIX package verified" >> $GITHUB_STEP_SUMMARY + echo "- 🏷️ Git tag and GitHub Release created" >> $GITHUB_STEP_SUMMARY + + if [ "${{ steps.check_token.outputs.has_token }}" == "true" ]; then + if [ "${{ steps.marketplace_publish.outcome }}" == "success" ]; then + echo "- ✅ Published to VS Code Marketplace" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ Marketplace publish failed (issue created automatically)" >> $GITHUB_STEP_SUMMARY + fi + else + echo "- ⚠️ Marketplace publish skipped (AZURE_TOKEN not configured)" >> $GITHUB_STEP_SUMMARY + fi + + - name: No release + if: steps.version_check.outputs.changed == 'false' + run: | + echo "## ℹ️ No Release" >> $GITHUB_STEP_SUMMARY + echo "Version in package.json unchanged, skipping release." >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a07c98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +out +dist +node_modules +*.vsix +coverage +.nyc_output +.vscode-test/ +.vscode/ +*.log +.DS_Store +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Temporary files and scripts +docs/tmp/ +temp/ +*.tmp + +# License reports (generated in CI) +license-report.json +license-report.csv +license-summary.txt +license-detailed-report.md diff --git a/.licensecheckrc.json b/.licensecheckrc.json new file mode 100644 index 0000000..0ba2fe4 --- /dev/null +++ b/.licensecheckrc.json @@ -0,0 +1,30 @@ +{ + "excludePrivatePackages": true, + "onlyAllow": [ + "MIT", + "Apache-2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "0BSD", + "CC0-1.0", + "Python-2.0", + "BlueOak-1.0.0" + ], + "failOn": [ + "GPL", + "GPL-2.0", + "GPL-3.0", + "AGPL", + "AGPL-3.0", + "LGPL", + "LGPL-2.1", + "LGPL-3.0", + "SSPL", + "CC-BY-NC", + "CC-BY-NC-SA", + "Unlicense" + ], + "excludePackages": "mydba" +} + diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..7c87c22 --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,44 @@ +.vscode/** +.vscode-test/** +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts +test/** +jest.config.js +jest.integration.config.js +docker-compose.test.yml +test/** +*.test.js +*.test.ts +coverage/** +.nyc_output/** +node_modules/** +*.log +.DS_Store +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* +docs/** +!docs/PRD.md +!docs/PRIVACY.md +!docs/ANONYMIZATION_STRATEGY.md +!docs/ards/** +*.md +LICENSE +CONTRIBUTING.md +SECURITY.md +QUICKSTART.md +# Keep icon +!resources/mydba-icon.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e1156e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,83 @@ +# Changelog + +All notable changes to the MyDBA extension will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0-beta.1] - 2025-10-27 + +### 🎉 Phase 1 Complete - Beta Release + +This beta release marks the completion of Phase 1 monitoring features with comprehensive AI-powered query optimization capabilities. + +### Added + +#### AI-Powered Features +- **Senior DBA AI Persona**: All AI providers now act as a Senior Database Administrator with 15+ years of experience +- **Enhanced Query Profiling**: Comprehensive performance analysis with execution time, efficiency metrics, and stage breakdowns +- **Smart Documentation Integration**: AI uses MySQL/MariaDB documentation internally for grounded recommendations +- **Database-Specific RAG**: Documentation filtering ensures only relevant docs (MySQL OR MariaDB, not both) +- **Performance Context**: AI receives detailed profiling data including rows examined, efficiency percentages, and bottleneck analysis + +#### Monitoring Features +- **Slow Query Detection**: Automatic identification and sorting of slow queries by impact +- **Impact Score Calculation**: Weighted formula prioritizing queries based on total time, frequency, and efficiency +- **Query Profiling**: Deep dive into query execution with Performance Schema integration +- **EXPLAIN Analysis**: Visual execution plan viewer with AI-powered optimization suggestions +- **Queries Without Indexes**: Detection of queries missing proper indexing + +#### Core Infrastructure +- **MySQL 8.0+ Support**: Full compatibility with MySQL 8.0 and above +- **MariaDB 10.6+ Support**: Complete support for MariaDB 10.6 and newer +- **Connection Pooling**: Efficient database connection management +- **Performance Schema Integration**: Advanced query profiling and monitoring +- **Multiple AI Providers**: Support for VSCode LM, OpenAI, Anthropic, and Ollama + +### Changed +- **UI Simplification**: Removed citation/reference sections from UI while maintaining AI documentation usage +- **Query Normalization**: Improved Performance Schema query matching with aggressive normalization +- **EXPLAIN Viewer**: Streamlined interface removing expand/collapse/export buttons +- **Profiling Analysis**: AI now mandatory discusses performance metrics in summaries + +### Fixed +- **Performance Schema Matching**: Resolved "Could not find statement" errors with improved query normalization +- **Connection Threading**: Fixed thread ID vs connection ID mismatch in profiling +- **Double EXPLAIN Prefix**: Eliminated duplicate EXPLAIN keywords in queries +- **MariaDB Documentation**: Ensured MariaDB connections only receive MariaDB-specific documentation + +### Technical Improvements +- **Type System**: Enhanced SchemaContext with performance metrics and flexible table structures +- **Query Matching**: Aggressive normalization (remove spaces, quotes, backticks, lowercase) +- **Connection Handling**: Dedicated connection usage for profiling with proper cleanup +- **Logging**: Comprehensive debug logging for troubleshooting AI and profiling issues + +### Testing +- ✅ 53 tests passing +- ✅ MySQL 8.0 integration tests +- ✅ MariaDB 10.11 integration tests +- ✅ AI service tests +- ✅ Query profiling tests +- ✅ Performance Schema tests + +### Known Limitations +- PostgreSQL, Redis, and Valkey support planned for Phase 2 +- Some Performance Schema queries may timeout on very fast queries +- AI provider configuration required for full AI features + +### Upgrade Notes +- Extension now requires VSCode 1.85.0 or higher +- Performance Schema must be enabled for query profiling features +- AI features require configuration of at least one AI provider + +### Dependencies +- vscode: ^1.85.0 +- mysql2: ^3.11.0 +- @anthropic-ai/sdk: ^0.32.1 +- openai: ^4.73.1 + +--- + +## [0.1.0] - Initial Development + +Initial development version with basic database connection capabilities. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6db27a6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,892 @@ +# Contributing to MyDBA + +Thank you for your interest in contributing to MyDBA! This document provides guidelines and instructions for contributing to the project. + +--- + +## 📋 Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Development Setup](#development-setup) +- [Project Structure](#project-structure) +- [Development Workflow](#development-workflow) +- [Coding Standards](#coding-standards) +- [Testing Guidelines](#testing-guidelines) +- [Submitting Changes](#submitting-changes) +- [Pull Request Process](#pull-request-process) +- [Documentation](#documentation) +- [Community](#community) + +--- + +## Code of Conduct + +This project adheres to a Code of Conduct that all contributors are expected to follow. Please be respectful, inclusive, and considerate in all interactions. + +### Our Standards + +- **Be Respectful**: Treat all contributors with respect and kindness +- **Be Inclusive**: Welcome contributors of all backgrounds and skill levels +- **Be Collaborative**: Work together to improve the project +- **Be Professional**: Keep discussions focused and constructive +- **Be Patient**: Remember that everyone was a beginner once + +### Unacceptable Behavior + +- Harassment, discrimination, or offensive comments +- Personal attacks or trolling +- Spam or self-promotion +- Sharing private information without consent +- Any behavior that would be inappropriate in a professional setting + +### Reporting + +If you experience or witness unacceptable behavior, please report it to the project maintainers at conduct@mydba.dev. + +--- + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js**: v18.x or v20.x LTS ([Download](https://nodejs.org/)) +- **npm**: v9.x or higher (comes with Node.js) +- **VSCode**: v1.85.0 or higher ([Download](https://code.visualstudio.com/)) +- **Git**: v2.30 or higher ([Download](https://git-scm.com/)) +- **Docker**: For running test databases (optional but recommended) + +### Fork and Clone + +1. **Fork the repository** on GitHub (click "Fork" button) + +2. **Clone your fork**: + ```bash + git clone https://github.com/YOUR_USERNAME/mydba.git + cd mydba + ``` + +3. **Add upstream remote**: + ```bash + git remote add upstream https://github.com/original/mydba.git + ``` + +4. **Verify remotes**: + ```bash + git remote -v + # origin https://github.com/YOUR_USERNAME/mydba.git (fetch) + # origin https://github.com/YOUR_USERNAME/mydba.git (push) + # upstream https://github.com/original/mydba.git (fetch) + # upstream https://github.com/original/mydba.git (push) + ``` + +--- + +## Development Setup + +### Install Dependencies + +```bash +npm install +``` + +### Build the Extension + +```bash +npm run compile +``` + +### Watch Mode (Auto-rebuild) + +```bash +npm run watch +``` + +### Run in VSCode + +1. Open the project in VSCode +2. Press `F5` to launch the Extension Development Host +3. A new VSCode window will open with MyDBA installed +4. Test your changes in this window + +### Set Up Test Databases (Optional) + +```bash +# Start MySQL 8.0 and MariaDB 10.11 containers +docker-compose up -d + +# Verify containers are running +docker ps +``` + +Default test database credentials (from `docker-compose.yml`): +- **MySQL 8.0**: `localhost:3306`, user: `root`, password: `test_password` +- **MariaDB 10.11**: `localhost:3307`, user: `root`, password: `test_password` + +For profiling and EXPLAIN features, ensure Performance Schema is enabled for MySQL 8.0+. + +--- + +## Project Structure + +``` +mydba/ +├── src/ # Source code +│ ├── extension.ts # Extension entry point +│ ├── connection/ # Database connection management +│ ├── views/ # Tree view providers +│ ├── webviews/ # Webview panels and content +│ ├── ai/ # AI integration (LM API, RAG, chat) +│ ├── queries/ # SQL query builders +│ ├── metrics/ # Performance metrics collection +│ └── utils/ # Utility functions +├── test/ # Tests +│ ├── unit/ # Unit tests +│ ├── integration/ # Integration tests +│ └── e2e/ # End-to-end tests +├── docs/ # Documentation +│ ├── DATABASE_SETUP.md # Database configuration guide +│ └── QUICK_REFERENCE.md # Quick reference guide +├── resources/ # Icons, images, CSS +├── package.json # Extension manifest and dependencies +├── tsconfig.json # TypeScript configuration +├── .eslintrc.json # ESLint configuration +├── .prettierrc # Prettier configuration +└── docker-compose.yml # Test database setup +``` + +--- + +## Development Workflow + +### 1. Create a Branch + +Always create a new branch for your work: + +```bash +git checkout -b feature/your-feature-name +# or +git checkout -b fix/issue-number-description +``` + +**Branch Naming Conventions**: +- `feature/` - New features +- `fix/` - Bug fixes +- `docs/` - Documentation changes +- `refactor/` - Code refactoring +- `test/` - Test additions/improvements +- `chore/` - Build, CI, or maintenance tasks + +### 2. Make Your Changes + +- Write clean, readable code +- Follow the [Coding Standards](#coding-standards) +- Add tests for new features or bug fixes +- Update documentation if needed + +### 3. Test Your Changes + +```bash +# Run unit tests +npm test + +# Run with coverage +npm run test:coverage + +# Run integration tests +npm run test:integration + +# Run E2E tests (requires VSCode) +npm run test:e2e + +# Lint code +npm run lint + +# Format code +npm run format +``` + +### 4. Commit Your Changes + +We use [Conventional Commits](https://www.conventionalcommits.org/) format: + +```bash +git add . +git commit -m "feat: add support for custom system views" +``` + +**Commit Message Format**: +``` +(): + + + +