Skip to content

Updated default dtypes for full and arange #264

Updated default dtypes for full and arange

Updated default dtypes for full and arange #264

Workflow file for this run

name: Pull Request
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, labeled]
# Cancel in-progress runs when a new commit is pushed
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
# Auto-label PRs based on changed files
label:
name: Auto-label PR
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Label PR
uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: true
# Run linting and type checks
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: 'npm'
- uses: mlugg/setup-zig@v2
with:
version: "0.15.2"
- name: Install dependencies
run: npm install
- name: Run ESLint + Zig fmt
run: npm run lint
- name: Type check
run: npm run typecheck
- name: Zig dead code check
run: npm run report:zig-dead-code
# Run full test suite with coverage
test:
name: Tests & Coverage
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: 'npm'
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- uses: mlugg/setup-zig@v2
with:
version: "0.15.2"
- name: Install dependencies
run: |
pip install "numpy>=2"
npm install
- name: Build for testing
run: npm run build:test
- name: Run tests with coverage
run: npm run test:coverage
- name: Zig export fn test coverage (≥95%)
run: npm run report:zig-coverage
- name: DType coverage report
id: dtype-coverage
if: always()
run: |
npm run report:dtype-coverage -- --summary 2>&1 | tee dtype-coverage.txt || true
- name: Generate coverage summary
id: coverage
run: |
node -e "
const coverage = require('./coverage/coverage-summary.json');
const total = coverage.total;
const formatMetric = (metric) => {
const pct = metric.pct;
const emoji = pct >= 90 ? '🟢' : pct >= 80 ? '🟡' : pct >= 70 ? '🟠' : '🔴';
return \`\${emoji} \${pct}%\`;
};
let comment = '## 📊 Coverage Report\n\n';
comment += '### Vitest Coverage\n\n';
comment += '| Metric | Coverage | Threshold | Status |\n';
comment += '|--------|----------|-----------|--------|\n';
comment += \`| Statements | \${formatMetric(total.statements)} | 80% | \${total.statements.pct >= 80 ? '✅' : '❌'} |\n\`;
comment += \`| Branches | \${formatMetric(total.branches)} | 75% | \${total.branches.pct >= 75 ? '✅' : '❌'} |\n\`;
comment += \`| Functions | \${formatMetric(total.functions)} | 95% | \${total.functions.pct >= 95 ? '✅' : '❌'} |\n\`;
comment += \`| Lines | \${formatMetric(total.lines)} | 80% | \${total.lines.pct >= 80 ? '✅' : '❌'} |\n\`;
const allPass = total.statements.pct >= 80 &&
total.branches.pct >= 75 &&
total.functions.pct >= 95 &&
total.lines.pct >= 80;
if (allPass) {
comment += '\n✅ All coverage thresholds met!\n';
} else {
comment += '\n⚠️ Some coverage thresholds not met. Please add tests.\n';
}
// Append dtype coverage summary if available
const fs = require('fs');
if (fs.existsSync('dtype-coverage.txt')) {
const dtypeSummary = fs.readFileSync('dtype-coverage.txt', 'utf-8').trim();
if (dtypeSummary) {
comment += '\n### DType Sweep Coverage\n\n';
comment += '<details><summary>Click to expand</summary>\n\n';
comment += '\`\`\`\n' + dtypeSummary + '\n\`\`\`\n';
comment += '</details>\n';
}
}
console.log('COMMENT<<EOF');
console.log(comment);
console.log('EOF');
" > coverage-comment.txt
echo "comment<<EOF" >> $GITHUB_OUTPUT
cat coverage-comment.txt | grep -A 1000 "COMMENT<<EOF" | grep -B 1000 "^EOF$" | grep -v "COMMENT<<EOF" | grep -v "^EOF$" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Comment PR with coverage
continue-on-error: true
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const comment = fs.readFileSync('coverage-comment.txt', 'utf8').trim();
// Find existing coverage comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('📊 Coverage Report')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: comment
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
# Compare committed benchmark results against main baseline
benchmark:
name: Benchmark Regression Check
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch main branch
run: git fetch origin main:refs/remotes/origin/main
- uses: actions/setup-node@v4
with:
node-version: 24
- name: Compare against main baseline
id: compare
run: |
node -e "
const fs = require('fs');
const { execSync } = require('child_process');
// Load PR results (committed in this branch)
if (!fs.existsSync('benchmarks/results/latest-full.json')) {
console.log('No latest-full.json in PR branch — skipping');
fs.writeFileSync('bench-comment.md', '## Benchmark Regression Check\n\nNo benchmark results in this branch.\n');
process.exit(0);
}
const pr = JSON.parse(fs.readFileSync('benchmarks/results/latest-full.json', 'utf8'));
// Load baseline from main
let baseline;
try {
const raw = execSync('git show origin/main:benchmarks/results/latest-full.json', { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 });
baseline = JSON.parse(raw);
} catch {
console.log('No baseline on main — skipping');
fs.writeFileSync('bench-comment.md', '## Benchmark Regression Check\n\nNo baseline found on main.\n');
process.exit(0);
}
const threshold = 1.25;
const prOps = pr.results || [];
const mainOps = baseline.results || [];
const regressions = [];
prOps.forEach(prOp => {
const mainOp = mainOps.find(m => m.name === prOp.name);
if (mainOp && prOp.numpyjs && mainOp.numpyjs) {
const ratio = prOp.numpyjs.mean_ms / mainOp.numpyjs.mean_ms;
if (ratio > threshold) {
regressions.push({
name: prOp.name,
category: prOp.category,
main_ms: mainOp.numpyjs.mean_ms,
pr_ms: prOp.numpyjs.mean_ms,
change: ((ratio - 1) * 100).toFixed(1)
});
}
}
});
let comment = '## Benchmark Regression Check\n\n';
if (regressions.length === 0) {
comment += 'No regressions >25% detected.\n';
} else {
comment += '| Operation | Main (ms) | PR (ms) | Change |\n';
comment += '|-|-|-|-|\n';
regressions.forEach(r => {
comment += \`| \${r.name} | \${r.main_ms.toFixed(4)} | \${r.pr_ms.toFixed(4)} | +\${r.change}% |\n\`;
});
comment += '\n**Warning**: ' + regressions.length + ' operation(s) regressed >25%.\n';
}
fs.writeFileSync('bench-comment.md', comment);
const has = regressions.length > 0 ? 'true' : 'false';
fs.appendFileSync(process.env.GITHUB_OUTPUT, 'has_regression=' + has + '\n');
"
- name: Comment PR
if: always() && hashFiles('bench-comment.md') != ''
continue-on-error: true
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const comment = fs.readFileSync('bench-comment.md', 'utf8');
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Benchmark Regression Check')
);
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: comment
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
- name: Fail if regression detected
if: steps.compare.outputs.has_regression == 'true'
run: |
echo "::error::Performance regression detected (>25%). See PR comment."
exit 1