5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
116 lines
5.7 KiB
YAML
116 lines
5.7 KiB
YAML
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
name: PR Linter
|
|
|
|
# This workflow runs on pull requests that are opened, edited, or have new commits pushed.
|
|
on:
|
|
pull_request:
|
|
types: [opened, edited, synchronize]
|
|
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
jobs:
|
|
lint-pr:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout Code
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Validate PR Title, Description, and Commits
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
// Get the PR context
|
|
const pr = context.payload.pull_request;
|
|
if (!pr) {
|
|
core.setFailed("Could not get PR from context.");
|
|
return;
|
|
}
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
|
|
let errors = [];
|
|
|
|
// 1. Validate PR Title
|
|
const title = pr.title;
|
|
const titleRegex = /^\[(OPIK-\d+|DND-\d+|DEV-\d+|CUST-\d+|issue-\d+|NA)\](\s*\[(BE|FE|DOCS|SDK|GHA|CI|HELM)\])*\s*.+$/;
|
|
if (!titleRegex.test(title)) {
|
|
errors.push(
|
|
"❌ **Invalid Title Format.** Your PR title must include a ticket/issue number and may optionally include component tags (`[FE]`, `[BE]`, etc.).\n\n" +
|
|
" - **Internal contributors: Open a JIRA ticket and link to it:** `[OPIK-xxxx]` or `[CUST-xxxx]` or `[DND-xxxx]` or `[DEV-xxxx] [COMPONENT] Your change`\n" +
|
|
" - **External contributors: Open a Github Issue and link to it via its number:** `[issue-xxxx] [COMPONENT] Your change`\n" +
|
|
" - **No ticket:** Use `[NA] [COMPONENT] Your change` (Issues section not required)\n\n" +
|
|
" *Example: `[issue-3108] [BE] [FE] Fix authentication bug` or `[OPIK-1234] Fix bug` or `[NA] Update README`*"
|
|
);
|
|
}
|
|
|
|
// 2. Validate PR Description
|
|
const body = pr.body || "";
|
|
const requiredHeadings = ["## Details", "## Change checklist", "## Issues", "## Testing", "## Documentation"];
|
|
if (body.length === 0) {
|
|
errors.push("❌ **Missing Description.** The PR description cannot be empty.");
|
|
} else {
|
|
for (const heading of requiredHeadings) {
|
|
if (!body.includes(heading)) {
|
|
errors.push(`❌ **Missing Section.** The description is missing the \`${heading}\` section.`);
|
|
}
|
|
}
|
|
|
|
// Helper function to get content of a markdown section
|
|
const getSectionContent = (sectionTitle) => {
|
|
const startIndex = body.indexOf(sectionTitle);
|
|
if (startIndex === -1) return null;
|
|
const nextHeaderIndex = body.indexOf("## ", startIndex + sectionTitle.length);
|
|
const endIndex = nextHeaderIndex !== -1 ? nextHeaderIndex : body.length;
|
|
return body.substring(startIndex + sectionTitle.length, endIndex).trim();
|
|
};
|
|
|
|
const detailsContent = getSectionContent("## Details");
|
|
if (detailsContent !== null && detailsContent.length === 0) {
|
|
errors.push("❌ **Incomplete Details Section.** The `## Details` section cannot be empty.");
|
|
}
|
|
|
|
const issuesContent = getSectionContent("## Issues");
|
|
const issuesRegex = /(#\d+|OPIK-\d+|DND-\d+|DEV-\d+|CUST-\d+)/;
|
|
const isNATicket = title.startsWith("[NA]");
|
|
if (issuesContent !== null && !isNATicket && !issuesRegex.test(issuesContent)) {
|
|
errors.push(
|
|
"❌ **Incomplete Issues Section.** You must reference at least one GitHub issue (`#xxxx`), Jira ticket (`OPIK-xxxx`), CUST ticket (`CUST-xxxx`), DEV ticket (`DEV-xxxx`), or DND ticket (`DND-xxxx`) under the `## Issues` section."
|
|
);
|
|
}
|
|
}
|
|
|
|
// 3. Find all existing linter comments across all pages
|
|
const COMMENT_MARKER = "<!-- pr-linter-comment -->";
|
|
const allComments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: pr.number });
|
|
const markerComments = allComments.filter(c => c.body && c.body.includes(COMMENT_MARKER));
|
|
|
|
// 4. Final step: upsert/delete comment and/or fail the job
|
|
if (errors.length > 0) {
|
|
const commentBody = COMMENT_MARKER + "\n### 📋 PR Linter Failed\n\n" + errors.join("\n\n---\n\n");
|
|
|
|
const logMessage = "PR Linter Failed:\n\n" + errors.join("\n\n")
|
|
.replace(/❌ \*\*/g, '❌ ')
|
|
.replace(/\*\*`/g, '`')
|
|
.replace(/`\*\*/g, '`')
|
|
.replace(/###\s/g, '')
|
|
.replace(/---\n\n/g, '');
|
|
|
|
if (markerComments.length > 0) {
|
|
// Keep the newest, delete any duplicates
|
|
const [keep, ...duplicates] = [...markerComments].sort((a, b) => b.id - a.id);
|
|
await github.rest.issues.updateComment({ owner, repo, comment_id: keep.id, body: commentBody });
|
|
await Promise.all(duplicates.map(c => github.rest.issues.deleteComment({ owner, repo, comment_id: c.id })));
|
|
} else {
|
|
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: commentBody });
|
|
}
|
|
|
|
core.setFailed(logMessage);
|
|
} else {
|
|
await Promise.all(markerComments.map(c => github.rest.issues.deleteComment({ owner, repo, comment_id: c.id })));
|
|
console.log("✅ PR linting passed!");
|
|
}
|