Skip to content

Merge pull request #44 from OpenLake/ci/add-automated-cicd #2

Merge pull request #44 from OpenLake/ci/add-automated-cicd

Merge pull request #44 from OpenLake/ci/add-automated-cicd #2

Workflow file for this run

# OpenLake Standard CI/CD Workflow

Check failure on line 1 in .github/workflows/openlake-ci.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/openlake-ci.yml

Invalid workflow file

(Line: 233, Col: 9): Unrecognized function: 'hashFiles'. Located at position 1 within expression: hashFiles('Dockerfile') != '' || hashFiles('docker-compose.yml') != ''
# NEVER pushes directly to main - only runs on PRs
# Use this as a template for all OpenLake repos
name: OpenLake CI
on:
pull_request:
branches: [main, master, develop]
types: [opened, synchronize, reopened]
push:
branches: [main, master]
# Only run on main for status checks, never auto-merge without PR review
# Prevent multiple concurrent workflows on same PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ==============================
# SECURITY SCANNING
# ==============================
security:
name: 🔒 Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan for hardcoded secrets
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dependency vulnerability scan
run: |
# Python
if [ -f "requirements.txt" ]; then
pip install safety
safety check -r requirements.txt --json || true
fi
# Node.js
if [ -f "package.json" ]; then
npm audit --production || true
fi
# Go
if [ -f "go.mod" ]; then
go list -m -json all | govulncheck || true
fi
continue-on-error: true
# ==============================
# CODE QUALITY
# ==============================
code-quality:
name: 🧹 Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check formatting
run: |
# Python
if [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
pip install black isort flake8
black --check . || echo "Python formatting issues found"
isort --check-only . || echo "Python import sorting issues found"
fi
# Node.js
if [ -f "package.json" ]; then
npm ci || npm install
npm run lint || echo "ESLint issues found (non-blocking)"
fi
# Go
if [ -f "go.mod" ]; then
go fmt ./...
if [ -n "$(git status --porcelain)" ]; then
echo "Go formatting issues found - run 'go fmt'"
fi
fi
# Rust
if [ -f "Cargo.toml" ]; then
rustup component add rustfmt
cargo fmt -- --check || echo "Rust formatting issues found"
fi
continue-on-error: true
- name: Check for TODOs and FIXMEs
run: |
echo "=== TODOs in codebase ==="
grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . || true
echo "=== Count ==="
grep -rc "TODO\|FIXME\|HACK\|XXX" --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . || true
continue-on-error: true
# ==============================
# BUILD & TEST
# ==============================
build-test:
name: 🔨 Build & Test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [detected]
steps:
- name: Checkout code
uses: actions/checkout@v4
# ==========================
# Python
# ==========================
- name: Setup Python
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Python dependencies
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
run: |
pip install -r requirements.txt || true
pip install pytest pytest-cov || true
- name: Run Python tests
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
run: |
pytest --cov=. --cov-report=xml || echo "Tests failed or none found"
continue-on-error: true
# ==========================
# Node.js
# ==========================
- name: Setup Node.js
if: hashFiles('package.json') != ''
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Node dependencies
if: hashFiles('package.json') != ''
run: npm ci || npm install
- name: Run Node.js tests
if: hashFiles('package.json') != ''
run: npm test || echo "Tests failed or none found"
continue-on-error: true
- name: Build check
if: hashFiles('package.json') != ''
run: npm run build || echo "Build failed or no build script"
continue-on-error: true
# ==========================
# Go
# ==========================
- name: Setup Go
if: hashFiles('go.mod') != ''
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true
- name: Download Go dependencies
if: hashFiles('go.mod') != ''
run: go mod download
- name: Run Go tests
if: hashFiles('go.mod') != ''
run: |
go test -v -coverprofile=coverage.out ./... || echo "Go tests failed or none found"
continue-on-error: true
- name: Build Go binary
if: hashFiles('go.mod') != ''
run: go build -v ./... || echo "Go build failed"
continue-on-error: true
# ==========================
# Rust
# ==========================
- name: Setup Rust
if: hashFiles('Cargo.toml') != ''
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Run Rust tests
if: hashFiles('Cargo.toml') != ''
run: cargo test || echo "Rust tests failed or none found"
continue-on-error: true
- name: Build Rust binary
if: hashFiles('Cargo.toml') != ''
run: cargo build || echo "Rust build failed"
continue-on-error: true
# ==========================
# Flutter
# ==========================
- name: Setup Flutter
if: hashFiles('pubspec.yaml') != ''
uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
channel: 'stable'
cache: true
- name: Install Flutter dependencies
if: hashFiles('pubspec.yaml') != ''
run: flutter pub get
- name: Run Flutter tests
if: hashFiles('pubspec.yaml') != ''
run: flutter test || echo "Flutter tests failed or none found"
continue-on-error: true
- name: Analyze Flutter code
if: hashFiles('pubspec.yaml') != ''
run: flutter analyze || echo "Flutter analysis issues found"
continue-on-error: true
# ==============================
# DOCKER BUILD (if Dockerfile exists)
# ==============================
docker:
name: 🐳 Docker Build
runs-on: ubuntu-latest
if: hashFiles('Dockerfile') != '' || hashFiles('docker-compose.yml') != ''
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: openlake/${{ github.event.repository.name }}:pr-${{ github.event.pull_request.number }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ==============================
# PR COMMENT
# ==============================
pr-comment:
name: 💬 PR Status Comment
runs-on: ubuntu-latest
needs: [security, code-quality, build-test, docker]
if: always()
steps:
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const needs = ${{ toJSON(needs) }};
let status = "✅";
let summary = "All checks passed!";
for (const [job, result] of Object.entries(needs)) {
if (result.result === 'failure') {
status = "❌";
summary = `Job '${job}' failed.`;
break;
} else if (result.result === 'cancelled') {
status = "⚠️";
summary = `Job '${job}' was cancelled.`;
}
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `${status} **OpenLake CI Status**: ${summary}\n\n` +
`Run #${context.runNumber} | [View Logs](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`
});