Skip to content

Commit 01b69fe

Browse files
committed
ci: add automated OpenLake CI/CD workflow
- Security scanning (secrets, dependencies) - Code quality (formatting, linting) - Build and test (Node.js) - PR status comments Auto-generated by OpenLake automation.
1 parent c6f45b6 commit 01b69fe

1 file changed

Lines changed: 284 additions & 0 deletions

File tree

.github/workflows/openlake-ci.yml

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# OpenLake Standard CI/CD Workflow
2+
# NEVER pushes directly to main - only runs on PRs
3+
# Use this as a template for all OpenLake repos
4+
5+
name: OpenLake CI
6+
7+
on:
8+
pull_request:
9+
branches: [main, master, develop]
10+
types: [opened, synchronize, reopened]
11+
push:
12+
branches: [main, master]
13+
# Only run on main for status checks, never auto-merge without PR review
14+
15+
# Prevent multiple concurrent workflows on same PR
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
# ==============================
22+
# SECURITY SCANNING
23+
# ==============================
24+
security:
25+
name: 🔒 Security Scan
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 0
32+
33+
- name: Scan for hardcoded secrets
34+
uses: gitleaks/gitleaks-action@v2
35+
env:
36+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37+
38+
- name: Dependency vulnerability scan
39+
run: |
40+
# Python
41+
if [ -f "requirements.txt" ]; then
42+
pip install safety
43+
safety check -r requirements.txt --json || true
44+
fi
45+
# Node.js
46+
if [ -f "package.json" ]; then
47+
npm audit --production || true
48+
fi
49+
# Go
50+
if [ -f "go.mod" ]; then
51+
go list -m -json all | govulncheck || true
52+
fi
53+
continue-on-error: true
54+
55+
# ==============================
56+
# CODE QUALITY
57+
# ==============================
58+
code-quality:
59+
name: 🧹 Code Quality
60+
runs-on: ubuntu-latest
61+
steps:
62+
- name: Checkout code
63+
uses: actions/checkout@v4
64+
65+
- name: Check formatting
66+
run: |
67+
# Python
68+
if [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
69+
pip install black isort flake8
70+
black --check . || echo "Python formatting issues found"
71+
isort --check-only . || echo "Python import sorting issues found"
72+
fi
73+
# Node.js
74+
if [ -f "package.json" ]; then
75+
npm ci || npm install
76+
npm run lint || echo "ESLint issues found (non-blocking)"
77+
fi
78+
# Go
79+
if [ -f "go.mod" ]; then
80+
go fmt ./...
81+
if [ -n "$(git status --porcelain)" ]; then
82+
echo "Go formatting issues found - run 'go fmt'"
83+
fi
84+
fi
85+
# Rust
86+
if [ -f "Cargo.toml" ]; then
87+
rustup component add rustfmt
88+
cargo fmt -- --check || echo "Rust formatting issues found"
89+
fi
90+
continue-on-error: true
91+
92+
- name: Check for TODOs and FIXMEs
93+
run: |
94+
echo "=== TODOs in codebase ==="
95+
grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . || true
96+
echo "=== Count ==="
97+
grep -rc "TODO\|FIXME\|HACK\|XXX" --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . || true
98+
continue-on-error: true
99+
100+
# ==============================
101+
# BUILD & TEST
102+
# ==============================
103+
build-test:
104+
name: 🔨 Build & Test
105+
runs-on: ubuntu-latest
106+
strategy:
107+
fail-fast: false
108+
matrix:
109+
language: [detected]
110+
steps:
111+
- name: Checkout code
112+
uses: actions/checkout@v4
113+
114+
# ==========================
115+
# Python
116+
# ==========================
117+
- name: Setup Python
118+
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
119+
uses: actions/setup-python@v5
120+
with:
121+
python-version: '3.11'
122+
cache: 'pip'
123+
124+
- name: Install Python dependencies
125+
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
126+
run: |
127+
pip install -r requirements.txt || true
128+
pip install pytest pytest-cov || true
129+
130+
- name: Run Python tests
131+
if: hashFiles('requirements.txt') != '' || hashFiles('setup.py') != '' || hashFiles('pyproject.toml') != ''
132+
run: |
133+
pytest --cov=. --cov-report=xml || echo "Tests failed or none found"
134+
continue-on-error: true
135+
136+
# ==========================
137+
# Node.js
138+
# ==========================
139+
- name: Setup Node.js
140+
if: hashFiles('package.json') != ''
141+
uses: actions/setup-node@v4
142+
with:
143+
node-version: '20'
144+
cache: 'npm'
145+
146+
- name: Install Node dependencies
147+
if: hashFiles('package.json') != ''
148+
run: npm ci || npm install
149+
150+
- name: Run Node.js tests
151+
if: hashFiles('package.json') != ''
152+
run: npm test || echo "Tests failed or none found"
153+
continue-on-error: true
154+
155+
- name: Build check
156+
if: hashFiles('package.json') != ''
157+
run: npm run build || echo "Build failed or no build script"
158+
continue-on-error: true
159+
160+
# ==========================
161+
# Go
162+
# ==========================
163+
- name: Setup Go
164+
if: hashFiles('go.mod') != ''
165+
uses: actions/setup-go@v5
166+
with:
167+
go-version: '1.21'
168+
cache: true
169+
170+
- name: Download Go dependencies
171+
if: hashFiles('go.mod') != ''
172+
run: go mod download
173+
174+
- name: Run Go tests
175+
if: hashFiles('go.mod') != ''
176+
run: |
177+
go test -v -coverprofile=coverage.out ./... || echo "Go tests failed or none found"
178+
continue-on-error: true
179+
180+
- name: Build Go binary
181+
if: hashFiles('go.mod') != ''
182+
run: go build -v ./... || echo "Go build failed"
183+
continue-on-error: true
184+
185+
# ==========================
186+
# Rust
187+
# ==========================
188+
- name: Setup Rust
189+
if: hashFiles('Cargo.toml') != ''
190+
uses: actions-rust-lang/setup-rust-toolchain@v1
191+
192+
- name: Run Rust tests
193+
if: hashFiles('Cargo.toml') != ''
194+
run: cargo test || echo "Rust tests failed or none found"
195+
continue-on-error: true
196+
197+
- name: Build Rust binary
198+
if: hashFiles('Cargo.toml') != ''
199+
run: cargo build || echo "Rust build failed"
200+
continue-on-error: true
201+
202+
# ==========================
203+
# Flutter
204+
# ==========================
205+
- name: Setup Flutter
206+
if: hashFiles('pubspec.yaml') != ''
207+
uses: subosito/flutter-action@v2
208+
with:
209+
flutter-version: '3.x'
210+
channel: 'stable'
211+
cache: true
212+
213+
- name: Install Flutter dependencies
214+
if: hashFiles('pubspec.yaml') != ''
215+
run: flutter pub get
216+
217+
- name: Run Flutter tests
218+
if: hashFiles('pubspec.yaml') != ''
219+
run: flutter test || echo "Flutter tests failed or none found"
220+
continue-on-error: true
221+
222+
- name: Analyze Flutter code
223+
if: hashFiles('pubspec.yaml') != ''
224+
run: flutter analyze || echo "Flutter analysis issues found"
225+
continue-on-error: true
226+
227+
# ==============================
228+
# DOCKER BUILD (if Dockerfile exists)
229+
# ==============================
230+
docker:
231+
name: 🐳 Docker Build
232+
runs-on: ubuntu-latest
233+
if: hashFiles('Dockerfile') != '' || hashFiles('docker-compose.yml') != ''
234+
steps:
235+
- name: Checkout code
236+
uses: actions/checkout@v4
237+
238+
- name: Set up Docker Buildx
239+
uses: docker/setup-buildx-action@v3
240+
241+
- name: Build Docker image
242+
uses: docker/build-push-action@v5
243+
with:
244+
context: .
245+
push: false
246+
tags: openlake/${{ github.event.repository.name }}:pr-${{ github.event.pull_request.number }}
247+
cache-from: type=gha
248+
cache-to: type=gha,mode=max
249+
250+
# ==============================
251+
# PR COMMENT
252+
# ==============================
253+
pr-comment:
254+
name: 💬 PR Status Comment
255+
runs-on: ubuntu-latest
256+
needs: [security, code-quality, build-test, docker]
257+
if: always()
258+
steps:
259+
- name: Comment on PR
260+
uses: actions/github-script@v7
261+
with:
262+
script: |
263+
const needs = ${{ toJSON(needs) }};
264+
let status = "✅";
265+
let summary = "All checks passed!";
266+
267+
for (const [job, result] of Object.entries(needs)) {
268+
if (result.result === 'failure') {
269+
status = "❌";
270+
summary = `Job '${job}' failed.`;
271+
break;
272+
} else if (result.result === 'cancelled') {
273+
status = "⚠️";
274+
summary = `Job '${job}' was cancelled.`;
275+
}
276+
}
277+
278+
github.rest.issues.createComment({
279+
issue_number: context.issue.number,
280+
owner: context.repo.owner,
281+
repo: context.repo.repo,
282+
body: `${status} **OpenLake CI Status**: ${summary}\n\n` +
283+
`Run #${context.runNumber} | [View Logs](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`
284+
});

0 commit comments

Comments
 (0)