Merge branch 'main' into troubleshoot-deploy #119
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: ['**'] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| # Quick smoke test to catch import errors early | |
| import-smoke-test: | |
| name: Import Smoke Test | |
| 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' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Test Python package imports | |
| working-directory: packages/flarelette-jwt-py | |
| run: | | |
| echo "Testing Python package can be imported in standard Python environment..." | |
| python -c "import flarelette_jwt; print('✓ Package version:', flarelette_jwt.__version__)" | |
| python -c "from flarelette_jwt import sign, verify, create_token, check_auth, policy, parse; print('✓ All main functions importable')" | |
| - name: Test version extraction (simulates CD pipeline) | |
| run: | | |
| echo "Simulating CD pipeline version check..." | |
| TS_VERSION=$(node -p "require('./packages/flarelette-jwt-ts/package.json').version") | |
| PY_VERSION=$(python -c "import sys; sys.path.insert(0, 'packages/flarelette-jwt-py'); import flarelette_jwt; print(flarelette_jwt.__version__, end='')") | |
| echo "✓ TypeScript version: $TS_VERSION" | |
| echo "✓ Python version: $PY_VERSION" | |
| lint-and-format: | |
| name: Lint and Format Check | |
| runs-on: ubuntu-latest | |
| needs: import-smoke-test | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install black ruff mypy pytest pytest-cov pytest-asyncio | |
| - name: Format check (JavaScript/TypeScript) | |
| run: npm run format:js:check | |
| - name: Format check (Python) | |
| run: npm run format:py:check | |
| - name: Lint (JavaScript/TypeScript) | |
| run: npm run lint:js | |
| - name: Lint (Python) | |
| run: npm run lint:py | |
| typecheck: | |
| name: Type Check | |
| runs-on: ubuntu-latest | |
| needs: import-smoke-test | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install mypy pytest pytest-cov pytest-asyncio | |
| - name: Type check (TypeScript) | |
| run: npm run typecheck:js | |
| - name: Type check (Python) | |
| run: npm run typecheck:py | |
| test: | |
| name: Test (Node ${{ matrix.node }}, Python ${{ matrix.python }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node: [20, 22] | |
| python: ['3.11', '3.12'] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: 'npm' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| cache: 'pip' | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install pytest pytest-cov pytest-asyncio | |
| - name: Build packages | |
| run: npm run build | |
| - name: Run tests with coverage | |
| run: npm run test:coverage | |
| - name: Upload TypeScript coverage to Codecov | |
| if: matrix.node == 20 && matrix.python == '3.11' | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./coverage/coverage-final.json | |
| flags: typescript | |
| name: typescript-coverage | |
| fail_ci_if_error: false | |
| - name: Upload Python coverage to Codecov | |
| if: matrix.node == 20 && matrix.python == '3.11' | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./packages/flarelette-jwt-py/.coverage | |
| flags: python | |
| name: python-coverage | |
| fail_ci_if_error: false | |
| # Additional checks for pull requests | |
| pr-checks: | |
| name: PR-specific Checks | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Fetch all history for commit message validation | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Validate commit messages | |
| run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.sha }} --verbose | |
| - name: Check for breaking changes | |
| run: | | |
| echo "Checking for breaking changes in commit messages..." | |
| git log --format=%B ${{ github.event.pull_request.base.sha }}..${{ github.sha }} | grep -i "BREAKING CHANGE" && echo "⚠️ Breaking changes detected" || echo "✓ No breaking changes" | |
| # Security and dependency checks | |
| security: | |
| name: Security Checks | |
| 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' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run npm audit | |
| run: npm audit --audit-level=moderate | |
| continue-on-error: true | |
| - name: Check for known vulnerabilities | |
| run: npm audit --audit-level=high | |
| continue-on-error: false | |
| # Build verification | |
| build: | |
| name: Build Verification | |
| 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' | |
| cache: 'npm' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Install Python build tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Build TypeScript packages | |
| run: npm run build | |
| - name: Build Python package | |
| working-directory: packages/flarelette-jwt-py | |
| run: python -m build | |
| - name: Check Python package | |
| working-directory: packages/flarelette-jwt-py | |
| run: twine check dist/* | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-artifacts | |
| path: | | |
| packages/*/dist | |
| packages/*/build | |
| retention-days: 7 | |
| # Package installation tests | |
| package-install: | |
| name: Package Installation Tests | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Install Python build tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build | |
| - name: Build packages | |
| run: | | |
| npm run build | |
| cd packages/flarelette-jwt-py && python prepare.py && python -m build | |
| - name: Test TypeScript package installation (from tarball) | |
| run: | | |
| cd packages/flarelette-jwt-ts | |
| npm pack | |
| TARBALL=$(ls *.tgz) | |
| cd /tmp | |
| npm init -y | |
| npm install "$GITHUB_WORKSPACE/packages/flarelette-jwt-ts/$TARBALL" | |
| node -e "const jwt = require('@chrislyons-dev/flarelette-jwt'); console.log('✓ TypeScript package imports successfully');" | |
| - name: Test Python package installation (from wheel) | |
| run: | | |
| cd /tmp | |
| python -m venv test-env | |
| source test-env/bin/activate | |
| pip install $GITHUB_WORKSPACE/packages/flarelette-jwt-py/dist/*.whl | |
| # Test that package imports successfully (lazy-loaded 'js' module only needed at function call time) | |
| python -c "import flarelette_jwt; print('✓ Python package version:', flarelette_jwt.__version__)" | |
| python -c "from flarelette_jwt import sign, verify, create_token, check_auth, policy, parse; print('✓ All functions importable (js module lazy-loaded at runtime)')" | |
| deactivate | |
| - name: Test TypeScript ESM imports | |
| run: | | |
| cd /tmp | |
| mkdir esm-test && cd esm-test | |
| npm init -y | |
| npm pkg set type="module" | |
| npm install "$GITHUB_WORKSPACE/packages/flarelette-jwt-ts"/*.tgz | |
| node -e "import('@chrislyons-dev/flarelette-jwt').then(m => console.log('✓ ESM imports work'));" | |
| # License compliance checks | |
| license-check: | |
| name: License Compliance | |
| 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' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Check TypeScript dependencies licenses | |
| run: npm run licenses:generate | |
| - name: Verify no GPL/AGPL dependencies | |
| run: | | |
| echo "Checking for incompatible licenses..." | |
| if npm run licenses:generate | grep -iE "(GPL|AGPL)" | grep -vE "(LGPL|Unlicense)"; then | |
| echo "❌ Found GPL/AGPL licensed dependencies" | |
| exit 1 | |
| else | |
| echo "✓ No GPL/AGPL dependencies found" | |
| fi | |
| # All checks must pass | |
| all-checks: | |
| name: All Checks Passed | |
| runs-on: ubuntu-latest | |
| needs: | |
| [ | |
| import-smoke-test, | |
| lint-and-format, | |
| typecheck, | |
| test, | |
| security, | |
| build, | |
| package-install, | |
| license-check, | |
| ] | |
| if: always() | |
| steps: | |
| - name: Check all jobs | |
| run: | | |
| if [[ "${{ needs.import-smoke-test.result }}" != "success" ]] || \ | |
| [[ "${{ needs.lint-and-format.result }}" != "success" ]] || \ | |
| [[ "${{ needs.typecheck.result }}" != "success" ]] || \ | |
| [[ "${{ needs.test.result }}" != "success" ]] || \ | |
| [[ "${{ needs.security.result }}" != "success" ]] || \ | |
| [[ "${{ needs.build.result }}" != "success" ]] || \ | |
| [[ "${{ needs.package-install.result }}" != "success" ]] || \ | |
| [[ "${{ needs.license-check.result }}" != "success" ]]; then | |
| echo "❌ One or more checks failed" | |
| exit 1 | |
| else | |
| echo "✅ All checks passed" | |
| fi |