diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..49aeaf71 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,46 @@ +# Configuration for PR auto-labeling +# Labels are applied based on file path patterns + +'๐Ÿ“ฆ dependencies': + - changed-files: + - any-glob-to-any-file: ['package.json', 'pnpm-lock.yaml', '**/package.json'] + +'๐Ÿ“š documentation': + - changed-files: + - any-glob-to-any-file: ['docs/**/*', '**/*.md', 'README.md'] + +'๐Ÿ”ง tooling': + - changed-files: + - any-glob-to-any-file: ['packages/tools/**/*', '.github/**/*', 'scripts/**/*'] + +'๐Ÿ—๏ธ foundation': + - changed-files: + - any-glob-to-any-file: ['packages/foundation/**/*'] + +'๐Ÿ”Œ drivers': + - changed-files: + - any-glob-to-any-file: ['packages/drivers/**/*'] + +'๐Ÿš€ runtime': + - changed-files: + - any-glob-to-any-file: ['packages/runtime/**/*'] + +'๐Ÿ“ examples': + - changed-files: + - any-glob-to-any-file: ['examples/**/*'] + +'โšก starters': + - changed-files: + - any-glob-to-any-file: ['packages/starters/**/*'] + +'๐Ÿงช tests': + - changed-files: + - any-glob-to-any-file: ['**/*.test.ts', '**/*.spec.ts', '**/jest.config.js'] + +'๐Ÿ”’ security': + - changed-files: + - any-glob-to-any-file: ['**/*.permission.yml', '**/security/**/*'] + +'โš™๏ธ configuration': + - changed-files: + - any-glob-to-any-file: ['tsconfig*.json', '.gitignore', '.npmrc', 'pnpm-workspace.yaml'] diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..c10bdb49 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,45 @@ +name: "CodeQL Security Scan" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + # Run every Monday at 00:00 UTC + - cron: '0 0 * * 1' + +jobs: + analyze: + name: Analyze Code + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you want to specify custom queries, add them here + # queries: security-extended,security-and-quality + + - 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/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..50c5a1ba --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +name: "Dependency Review" + +on: + pull_request: + branches: [ "main" ] + +permissions: + contents: read + pull-requests: write + +jobs: + dependency-review: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + # Fail the action if vulnerabilities with severity >= moderate are detected + fail-on-severity: moderate + # Warn about deprecated packages + warn-on-deprecated: true + # Comment on the PR with the review results + comment-summary-in-pr: on-failure diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..d581832b --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,23 @@ +name: "Pull Request Labeler" + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + labeler: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Run Labeler + uses: actions/labeler@v5 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + configuration-path: .github/labeler.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..adf9c32a --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,54 @@ +name: "Stale Issue Management" + +on: + schedule: + # Run every day at 00:00 UTC + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Mark/Close Stale Issues and PRs + uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + # Issues + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed in 14 days if no further activity occurs. + Thank you for your contributions. + close-issue-message: > + This issue was automatically closed because it has not had activity for 74 days + (marked stale after 60 days, then closed after 14 more days of inactivity). + Please feel free to reopen if you believe this issue is still relevant. + days-before-issue-stale: 60 + days-before-issue-close: 14 + stale-issue-label: 'stale' + exempt-issue-labels: 'pinned,security,roadmap,help wanted' + + # Pull Requests + stale-pr-message: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed in 7 days if no further activity occurs. + Please address any review comments or conflicts. + close-pr-message: > + This pull request was automatically closed because it has not had activity for 37 days + (marked stale after 30 days, then closed after 7 more days of inactivity). + Please feel free to reopen and address the review comments if you wish to continue. + days-before-pr-stale: 30 + days-before-pr-close: 7 + stale-pr-label: 'stale' + exempt-pr-labels: 'pinned,security,in progress' + + # General settings + operations-per-run: 100 + remove-stale-when-updated: true + ascending: false diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 00000000..1ec30737 --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,36 @@ +name: Type Check + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + typecheck: + name: TypeScript Type Check + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v3 + with: + version: 10 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + timeout-minutes: 5 + + - name: Run TypeScript type check + run: | + echo "Running TypeScript compiler in build mode for type checking..." + pnpm tsc -b + timeout-minutes: 5 diff --git a/.github/workflows/validate-metadata.yml b/.github/workflows/validate-metadata.yml new file mode 100644 index 00000000..b31fa0b3 --- /dev/null +++ b/.github/workflows/validate-metadata.yml @@ -0,0 +1,52 @@ +name: Validate Metadata Files + +on: + push: + branches: [ "main" ] + paths: + - '**/*.object.yml' + - '**/*.validation.yml' + - '**/*.permission.yml' + - '**/*.app.yml' + - '**/*.page.yml' + - '**/*.menu.yml' + - 'scripts/validate-yaml.js' + pull_request: + branches: [ "main" ] + paths: + - '**/*.object.yml' + - '**/*.validation.yml' + - '**/*.permission.yml' + - '**/*.app.yml' + - '**/*.page.yml' + - '**/*.menu.yml' + - 'scripts/validate-yaml.js' + +jobs: + validate: + name: Validate YAML Metadata + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v3 + with: + version: 10 + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + timeout-minutes: 5 + + - name: Validate YAML syntax + run: | + echo "Checking YAML syntax for metadata files..." + pnpm exec node scripts/validate-yaml.js + timeout-minutes: 5 diff --git a/scripts/validate-yaml.js b/scripts/validate-yaml.js new file mode 100755 index 00000000..f666ef84 --- /dev/null +++ b/scripts/validate-yaml.js @@ -0,0 +1,121 @@ +#!/usr/bin/env node +/** + * Validate YAML syntax for ObjectQL metadata files + * + * This script validates all metadata files in the repository: + * - *.object.yml + * - *.validation.yml + * - *.permission.yml + * - *.app.yml + * - *.page.yml + * - *.menu.yml + * + * Dependencies: + * - Requires 'js-yaml' from devDependencies + * - Run 'pnpm install' before executing this script + */ + +const yaml = require('js-yaml'); +const fs = require('fs'); +const path = require('path'); + +// Define metadata file extensions +const extensions = [ + '.object.yml', + '.validation.yml', + '.permission.yml', + '.app.yml', + '.page.yml', + '.menu.yml' +]; + +// Directories to exclude +const excludeDirs = ['node_modules', 'dist', '.git']; + +/** + * Recursively find all files matching the specified extensions + */ +function findFiles(dir, fileList = []) { + const files = fs.readdirSync(dir); + + files.forEach(file => { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + + if (stat.isDirectory()) { + // Skip excluded directories + if (!excludeDirs.includes(file)) { + findFiles(filePath, fileList); + } + } else { + // Check if file matches any of our extensions + if (extensions.some(ext => file.endsWith(ext))) { + fileList.push(filePath); + } + } + }); + + return fileList; +} + +let hasErrors = false; +let validCount = 0; +let errorCount = 0; + +console.log('๐Ÿ” Validating ObjectQL metadata YAML files...\n'); + +// Find all metadata files +const files = findFiles(process.cwd()); + +if (files.length === 0) { + console.log('โ„น๏ธ No metadata files found to validate.'); + process.exit(0); +} + +// Validate all files concurrently +async function validateFiles() { + const results = await Promise.allSettled( + files.map(async (file) => { + const content = await fs.promises.readFile(file, 'utf8'); + yaml.load(content, { schema: yaml.DEFAULT_SAFE_SCHEMA }); + return file; + }) + ); + + results.forEach((result, index) => { + const file = files[index]; + const relativePath = path.relative(process.cwd(), file); + + if (result.status === 'fulfilled') { + console.log(`โœ“ ${relativePath}`); + validCount++; + } else { + const error = result.reason; + console.error(`โœ— ${relativePath}`); + console.error(` Error: ${error.message}`); + if (error.mark) { + console.error(` Line ${error.mark.line + 1}, Column ${error.mark.column + 1}`); + } + console.error(''); + hasErrors = true; + errorCount++; + } + }); + + // Print summary + console.log('\n' + '='.repeat(60)); + if (hasErrors) { + console.error(`โŒ Validation failed: ${errorCount} error(s) found`); + console.log(`โœ“ Valid files: ${validCount}`); + console.error(`โœ— Invalid files: ${errorCount}`); + process.exit(1); + } else { + console.log(`โœ… All ${validCount} YAML metadata file(s) are valid!`); + process.exit(0); + } +} + +validateFiles().catch((error) => { + console.error('Unexpected error during validation:', error); + process.exit(1); +}); \ No newline at end of file