Evaluate Submission workflow (jslee02/awesome-robotics-libraries)
The Evaluate Submission workflow from jslee02/awesome-robotics-libraries, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get caching, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Evaluate Submission workflow from the jslee02/awesome-robotics-libraries repository, a real project running GitHub Actions. It is shown here with attribution under its CC0-1.0 license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Evaluate Submission
on:
issues:
types: [edited, labeled]
issue_comment:
types: [created]
jobs:
evaluate:
if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'suggestion'
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
issues: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: pip install pyyaml
- name: Extract GitHub repo from issue body
id: extract
uses: actions/github-script@v9
with:
script: |
const body = context.payload.issue.body || '';
function normalizeGitHubRepo(value) {
if (!value) return '';
const trimmed = value.trim();
const slugMatch = trimmed.match(/^(?:https?:\/\/github\.com\/)?([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+?)(?:\.git|\/)?$/);
return slugMatch ? slugMatch[1] : '';
}
// Try the structured "github" field first (from issue template)
const ghFieldMatch = body.match(/### GitHub Repository\s*\n\s*\n?\s*([\s\S]*?)(?=\n###|$)/);
const ghRepo = normalizeGitHubRepo(ghFieldMatch ? ghFieldMatch[1] : '');
if (ghRepo) {
core.setOutput('repo', ghRepo);
return;
}
// Fallback: extract from any GitHub URL in the body
const urlMatch = body.match(/github\.com\/([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+)/);
if (urlMatch) {
core.setOutput('repo', urlMatch[1]);
return;
}
core.setOutput('repo', '');
- name: Run evaluation
if: steps.extract.outputs.repo != ''
id: eval
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
REPORT=$(python3 scripts/evaluate_entry.py --data-dir data/ "${{ steps.extract.outputs.repo }}" 2>&1) || true
echo "$REPORT" > /tmp/eval-report.md
python3 scripts/evaluate_entry.py --data-dir data/ --json "${{ steps.extract.outputs.repo }}" 2>/dev/null > /tmp/eval-result.json || true
REC=$(python3 -c "import sys,json; print(json.load(open('/tmp/eval-result.json')).get('recommendation','error'))" 2>/dev/null || echo "error")
ARCHIVED=$(python3 -c "import sys,json; print(str(json.load(open('/tmp/eval-result.json')).get('archived',False)).lower())" 2>/dev/null || echo "false")
echo "recommendation=$REC" >> "$GITHUB_OUTPUT"
echo "archived=$ARCHIVED" >> "$GITHUB_OUTPUT"
- name: Post evaluation comment
if: steps.extract.outputs.repo != ''
uses: actions/github-script@v9
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('/tmp/eval-report.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: report
});
- name: Apply label and handle outcome
if: steps.extract.outputs.repo != ''
uses: actions/github-script@v9
with:
script: |
const rec = '${{ steps.eval.outputs.recommendation }}';
const archived = '${{ steps.eval.outputs.archived }}' === 'true';
const issue = context.issue.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Archived rejects close immediately; 0-score rejects stay in incubator queue
let label;
if (rec === 'reject' && archived) {
label = 'rejected';
} else {
const labelMap = {
'accept': 'accepted',
'likely_accept': 'accepted',
'incubator': 'incubator',
'reject': 'incubator',
'duplicate': 'duplicate',
'error': 'needs-review',
};
label = labelMap[rec] || 'needs-review';
}
await github.rest.issues.addLabels({ owner, repo, issue_number: issue, labels: [label] });
if (label === 'accepted') {
await github.rest.actions.createWorkflowDispatch({
owner,
repo,
workflow_id: 'add-accepted-entry.yml',
ref: context.payload.repository.default_branch,
inputs: {
issue_number: String(issue),
},
});
}
// Auto-close duplicates and archived rejects
if (rec === 'duplicate' || (rec === 'reject' && archived)) {
await github.rest.issues.update({
owner, repo, issue_number: issue,
state: 'closed',
state_reason: 'not_planned',
});
}
- name: Post fallback comment (no repo found)
if: steps.extract.outputs.repo == ''
uses: actions/github-script@v9
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'👋 Thanks for the suggestion!',
'',
'I couldn\'t find a GitHub repository in this issue. Could you add the **GitHub Repository** field ',
'(e.g., `owner/repo`) so I can run an automated evaluation?',
'',
'If the project isn\'t on GitHub, please reply confirming that this should be reviewed as a non-GitHub resource and include any public repository or documentation link maintainers should use.',
'',
'This issue is labeled `question` while waiting for author input. If there is no update for about two weeks, automation will mark it stale; after another two weeks without activity, it may close automatically.',
].join('\n')
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-review', 'question']
});
clear-author-question:
if: |
(github.event_name == 'issues' && github.event.action == 'edited') ||
(github.event_name == 'issue_comment' && !github.event.issue.pull_request)
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Clear author-action labels after author update
uses: actions/github-script@v9
with:
script: |
const issue = context.payload.issue;
const labels = (issue.labels || []).map(label => typeof label === 'string' ? label : label.name);
if (!labels.includes('suggestion') || !labels.includes('question')) {
return;
}
const author = issue.user.login;
const sender = context.payload.sender.login;
if (sender !== author) {
return;
}
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue_number = issue.number;
async function removeLabel(label) {
try {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label });
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
await removeLabel('question');
await removeLabel('stale');
The same workflow, on Latchkey
Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.
name: Evaluate Submission on: issues: types: [edited, labeled] issue_comment: types: [created] jobs: evaluate: timeout-minutes: 30 if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'suggestion' runs-on: latchkey-small permissions: actions: write contents: read issues: write steps: - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: cache: 'pip' python-version: "3.12" - run: pip install pyyaml - name: Extract GitHub repo from issue body id: extract uses: actions/github-script@v9 with: script: | const body = context.payload.issue.body || ''; function normalizeGitHubRepo(value) { if (!value) return ''; const trimmed = value.trim(); const slugMatch = trimmed.match(/^(?:https?:\/\/github\.com\/)?([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+?)(?:\.git|\/)?$/); return slugMatch ? slugMatch[1] : ''; } // Try the structured "github" field first (from issue template) const ghFieldMatch = body.match(/### GitHub Repository\s*\n\s*\n?\s*([\s\S]*?)(?=\n###|$)/); const ghRepo = normalizeGitHubRepo(ghFieldMatch ? ghFieldMatch[1] : ''); if (ghRepo) { core.setOutput('repo', ghRepo); return; } // Fallback: extract from any GitHub URL in the body const urlMatch = body.match(/github\.com\/([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+)/); if (urlMatch) { core.setOutput('repo', urlMatch[1]); return; } core.setOutput('repo', ''); - name: Run evaluation if: steps.extract.outputs.repo != '' id: eval env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | REPORT=$(python3 scripts/evaluate_entry.py --data-dir data/ "${{ steps.extract.outputs.repo }}" 2>&1) || true echo "$REPORT" > /tmp/eval-report.md python3 scripts/evaluate_entry.py --data-dir data/ --json "${{ steps.extract.outputs.repo }}" 2>/dev/null > /tmp/eval-result.json || true REC=$(python3 -c "import sys,json; print(json.load(open('/tmp/eval-result.json')).get('recommendation','error'))" 2>/dev/null || echo "error") ARCHIVED=$(python3 -c "import sys,json; print(str(json.load(open('/tmp/eval-result.json')).get('archived',False)).lower())" 2>/dev/null || echo "false") echo "recommendation=$REC" >> "$GITHUB_OUTPUT" echo "archived=$ARCHIVED" >> "$GITHUB_OUTPUT" - name: Post evaluation comment if: steps.extract.outputs.repo != '' uses: actions/github-script@v9 with: script: | const fs = require('fs'); const report = fs.readFileSync('/tmp/eval-report.md', 'utf8'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: report }); - name: Apply label and handle outcome if: steps.extract.outputs.repo != '' uses: actions/github-script@v9 with: script: | const rec = '${{ steps.eval.outputs.recommendation }}'; const archived = '${{ steps.eval.outputs.archived }}' === 'true'; const issue = context.issue.number; const owner = context.repo.owner; const repo = context.repo.repo; // Archived rejects close immediately; 0-score rejects stay in incubator queue let label; if (rec === 'reject' && archived) { label = 'rejected'; } else { const labelMap = { 'accept': 'accepted', 'likely_accept': 'accepted', 'incubator': 'incubator', 'reject': 'incubator', 'duplicate': 'duplicate', 'error': 'needs-review', }; label = labelMap[rec] || 'needs-review'; } await github.rest.issues.addLabels({ owner, repo, issue_number: issue, labels: [label] }); if (label === 'accepted') { await github.rest.actions.createWorkflowDispatch({ owner, repo, workflow_id: 'add-accepted-entry.yml', ref: context.payload.repository.default_branch, inputs: { issue_number: String(issue), }, }); } // Auto-close duplicates and archived rejects if (rec === 'duplicate' || (rec === 'reject' && archived)) { await github.rest.issues.update({ owner, repo, issue_number: issue, state: 'closed', state_reason: 'not_planned', }); } - name: Post fallback comment (no repo found) if: steps.extract.outputs.repo == '' uses: actions/github-script@v9 with: script: | await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: [ '👋 Thanks for the suggestion!', '', 'I couldn\'t find a GitHub repository in this issue. Could you add the **GitHub Repository** field ', '(e.g., `owner/repo`) so I can run an automated evaluation?', '', 'If the project isn\'t on GitHub, please reply confirming that this should be reviewed as a non-GitHub resource and include any public repository or documentation link maintainers should use.', '', 'This issue is labeled `question` while waiting for author input. If there is no update for about two weeks, automation will mark it stale; after another two weeks without activity, it may close automatically.', ].join('\n') }); await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, labels: ['needs-review', 'question'] }); clear-author-question: timeout-minutes: 30 if: | (github.event_name == 'issues' && github.event.action == 'edited') || (github.event_name == 'issue_comment' && !github.event.issue.pull_request) runs-on: latchkey-small permissions: issues: write steps: - name: Clear author-action labels after author update uses: actions/github-script@v9 with: script: | const issue = context.payload.issue; const labels = (issue.labels || []).map(label => typeof label === 'string' ? label : label.name); if (!labels.includes('suggestion') || !labels.includes('question')) { return; } const author = issue.user.login; const sender = context.payload.sender.login; if (sender !== author) { return; } const owner = context.repo.owner; const repo = context.repo.repo; const issue_number = issue.number; async function removeLabel(label) { try { await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }); } catch (error) { if (error.status !== 404) { throw error; } } } await removeLabel('question'); await removeLabel('stale');
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cache dependency installs on the setup step so they are served from cache.
- Add a job timeout so a hung step cannot burn hours of runner time.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Dependency installs
This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.