Multi-language AI translation support #1148
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |