Skip to content

Multi-language AI translation support #1148

Multi-language AI translation support

Multi-language AI translation support #1148

Workflow file for this run

name: Nextflow Lint
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Nextflow
uses: nf-core/setup-nextflow@v2
- name: Run Nextflow lint
run: |
mkdir -p lint-results
# TODO: Remove '|| true' to fail CI when linting errors are fixed
nextflow lint **/solutions/* -o json > lint-results/lint-output.json 2>&1 || true
- name: Check formatting changes
if: always()
run: |
# Run formatter (modifies files in place)
nextflow lint **/solutions/* -format || true
# Count changed files and save diffs
changed_files=$(git diff --name-only)
if [ -n "$changed_files" ]; then
echo "$changed_files" | wc -l | xargs > lint-results/format-count.txt
echo "$changed_files" > lint-results/format-files.txt
# Save individual diffs for each file
mkdir -p lint-results/diffs
for file in $changed_files; do
# Create safe filename for the diff
safe_name=$(echo "$file" | tr '/' '_')
git diff "$file" > "lint-results/diffs/${safe_name}.diff"
done
else
echo "0" > lint-results/format-count.txt
echo "" > lint-results/format-files.txt
fi
# Reset changes so they don't affect other steps
git checkout -- . || true
- name: Generate markdown report
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const lintOutput = JSON.parse(fs.readFileSync('lint-results/lint-output.json', 'utf8'));
const { summary, errors, warnings } = lintOutput;
const sha = context.sha;
const repo = `${context.repo.owner}/${context.repo.repo}`;
// Handle warnings array (added in future Nextflow version)
const warningsArray = warnings || [];
const warningCount = summary.warnings || 0;
// Read formatting results
const formatCount = parseInt(fs.readFileSync('lint-results/format-count.txt', 'utf8').trim()) || 0;
const formatFiles = fs.readFileSync('lint-results/format-files.txt', 'utf8').trim().split('\n').filter(f => f);
let markdown = '**Nextflow linting complete!**\n\n';
const hasIssues = summary.errors > 0 || warningCount > 0;
if (!hasIssues) {
markdown += `✅ ${summary.filesWithoutErrors} files had no errors\n`;
} else {
if (summary.errors > 0) {
markdown += `❌ ${summary.filesWithErrors} files had ${summary.errors} errors\n`;
}
if (warningCount > 0) {
const filesWithWarnings = summary.filesWithWarnings || 'N/A';
markdown += `⚠️ ${filesWithWarnings} files had ${warningCount} warnings\n`;
}
markdown += `✅ ${summary.filesWithoutErrors} files had no errors\n`;
}
// Add formatting status line
if (formatCount > 0) {
markdown += `🔧 ${formatCount} file${formatCount === 1 ? '' : 's'} would be changed by auto-formatting\n`;
} else {
markdown += `✨ No formatting changes needed\n`;
}
// Add lint issues details section
if (hasIssues) {
markdown += '\n';
// Combine errors and warnings with type labels
const allIssues = [
...errors.map(e => ({ ...e, type: 'Error' })),
...warningsArray.map(w => ({ ...w, type: 'Warning' }))
];
// Group by file
const grouped = {};
for (const issue of allIssues) {
if (!grouped[issue.filename]) {
grouped[issue.filename] = [];
}
grouped[issue.filename].push(issue);
}
// Build table rows
const rows = [];
for (const [filename, fileIssues] of Object.entries(grouped).sort()) {
for (const issue of fileIssues) {
const location = `${issue.startLine}:${issue.startColumn}`;
const link = `[${filename}:${location}](https://github.com/${repo}/blob/${sha}/${filename}#L${issue.startLine})`;
rows.push(`| ${issue.type} | ${link} | ${issue.message} |`);
}
}
const totalIssues = summary.errors + warningCount;
markdown += `<sup>💡 Tip: Click filename locations to go directly to that code.</sup>\n\n`;
markdown += `<details>\n<summary>View all ${totalIssues} issues</summary>\n\n`;
markdown += `| Type | Location | Message |\n|------|----------|---------|\n`;
markdown += rows.join('\n');
markdown += `\n\n</details>\n`;
}
// Add formatting changes details section
if (formatCount > 0) {
markdown += `\n<details>\n<summary>View formatting changes</summary>\n\n`;
markdown += `<table>\n<tr><th>File</th><th>Diff</th></tr>\n`;
const maxDiffLines = 15;
const maxTotalChars = 15000;
let totalChars = 0;
for (const file of formatFiles) {
const fileLink = `<a href="https://github.com/${repo}/blob/${sha}/${file}">${file}</a>`;
const safeName = file.replace(/\//g, '_');
const diffPath = `lint-results/diffs/${safeName}.diff`;
let diffCell = '';
try {
let diffContent = fs.readFileSync(diffPath, 'utf8');
// Skip first 4 lines (diff header with file paths)
const lines = diffContent.split('\n').slice(4);
// Check if we've exceeded total size limit
if (totalChars >= maxTotalChars) {
diffCell = '<em>(truncated)</em>';
} else {
let fileTruncated = false;
if (lines.length > maxDiffLines) {
diffContent = lines.slice(0, maxDiffLines).join('\n');
fileTruncated = true;
} else {
diffContent = lines.join('\n');
}
// Check if adding this would exceed total size
if (totalChars + diffContent.length > maxTotalChars) {
diffCell = '<em>(truncated)</em>';
totalChars = maxTotalChars; // Mark as full
} else {
totalChars += diffContent.length;
let truncMsg = fileTruncated ? '\n... (truncated)' : '';
diffCell = `<details><summary>View</summary>\n\n\`\`\`diff\n${diffContent}${truncMsg}\n\`\`\`\n\n</details>`;
}
}
} catch (e) {
diffCell = '<em>(error)</em>';
}
markdown += `<tr><td>${fileLink}</td><td>${diffCell}</td></tr>\n`;
}
markdown += `</table>\n\n</details>\n`;
}
// Write to file
fs.writeFileSync('lint-results/report.md', markdown);
// Add to job summary
await core.summary
.addRaw(markdown)
.write();
- name: Save PR info
if: always() && github.event_name == 'pull_request'
run: |
echo "${{ github.event.pull_request.number }}" > lint-results/pr-number.txt
echo "${{ github.sha }}" > lint-results/head-sha.txt
- name: Upload lint results
if: always() && github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: lint-results
path: lint-results/
retention-days: 1