Skip to content

feat(cli): fix issue with content hash handling #74

feat(cli): fix issue with content hash handling

feat(cli): fix issue with content hash handling #74

Workflow file for this run

name: PR
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
title:
name: Title
runs-on: ubuntu-latest
steps:
- name: Check format
id: title
uses: amannn/action-semantic-pull-request@v5
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
test
chore
ci
perf
revert
requireScope: false
subjectPattern: ^[a-z].+$
- name: Process results
id: result
run: |
if [ "${{ steps.title.outcome }}" == "success" ]; then
echo "status=Passed" >> $GITHUB_OUTPUT
echo "details=" >> $GITHUB_OUTPUT
else
echo "status=Failed" >> $GITHUB_OUTPUT
echo "details=Use: type(scope): description" >> $GITHUB_OUTPUT
fi
- name: Update PR comment
uses: actions/github-script@v7
env:
SECTION: PR Title
STATUS: ${{ steps.result.outputs.status }}
DETAILS: ${{ steps.result.outputs.details }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
const marker = "<!-- ci-summary -->";
const detailsMarker = "<!-- details-section -->";
const section = process.env.SECTION;
const status = process.env.STATUS;
const details = process.env.DETAILS;
const runUrl = process.env.RUN_URL;
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number, per_page: 100,
});
const existing = comments.find(c =>
c.user?.login === "github-actions[bot]" && c.body?.includes(marker)
);
let rows = {};
let existingDetails = {};
if (existing?.body) {
const parts = existing.body.split(detailsMarker);
const tableSection = parts[0] || "";
const lines = tableSection.split("\n");
for (const line of lines) {
const match = line.match(/^\| ([^|]+) \| ([^|]+) \|$/);
if (match) {
const name = match[1].trim();
if (name && name !== "Check" && !name.startsWith(":")) {
rows[name] = match[2].trim();
}
}
}
const detailsRegex = /<details>\s*<summary><strong>([^<]+)<\/strong>.*?<\/summary>([\s\S]*?)<\/details>/g;
let detailMatch;
while ((detailMatch = detailsRegex.exec(existing.body)) !== null) {
existingDetails[detailMatch[1].trim()] = detailMatch[0];
}
}
rows[section] = status;
if (status === "Failed" && details) {
existingDetails[section] = `<details>\n<summary><strong>${section}</strong></summary>\n\n${details}\n\nExample: \`feat(cli): add bulletin upload command\`\n\n[View run](${runUrl})\n\n</details>`;
} else {
delete existingDetails[section];
}
const order = ["Lint", "Format", "Typecheck", "Build", "Release", "PR Title", "Labels"];
const sortedKeys = Object.keys(rows).sort((a, b) => {
const ai = order.indexOf(a), bi = order.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
let table = `| Check | Result |\n|:------|:-------|\n`;
for (const key of sortedKeys) {
table += `| ${key} | ${rows[key]} |\n`;
}
const detailsOrder = ["Lint", "Format", "Typecheck", "Build", "Release", "PR Title", "Labels"];
const sortedDetails = Object.keys(existingDetails).sort((a, b) => {
const ai = detailsOrder.indexOf(a), bi = detailsOrder.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
let body = `${marker}\n## CI Summary\n\n${table}`;
if (sortedDetails.length > 0) {
body += `\n${detailsMarker}\n\n---\n\n${sortedDetails.map(k => existingDetails[k]).join("\n\n")}`;
}
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
- name: Fail if title invalid
if: steps.title.outcome == 'failure'
run: exit 1
labels:
name: Labels
runs-on: ubuntu-latest
steps:
- name: Apply labels
id: labels
uses: actions/labeler@v5
continue-on-error: true
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get applied labels
id: result
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const { data: pr } = await github.rest.pulls.get({
owner, repo, pull_number: issue_number
});
const labels = pr.labels.map(l => l.name).join(', ');
if ('${{ steps.labels.outcome }}' === 'success') {
core.setOutput('status', 'Passed');
core.setOutput('details', labels || 'No labels matched');
} else {
core.setOutput('status', 'Failed');
core.setOutput('details', 'Labeler error');
}
- name: Update PR comment
uses: actions/github-script@v7
env:
SECTION: Labels
STATUS: ${{ steps.result.outputs.status }}
DETAILS: ${{ steps.result.outputs.details }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
const marker = "<!-- ci-summary -->";
const detailsMarker = "<!-- details-section -->";
const section = process.env.SECTION;
const status = process.env.STATUS;
const details = process.env.DETAILS;
const runUrl = process.env.RUN_URL;
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number, per_page: 100,
});
const existing = comments.find(c =>
c.user?.login === "github-actions[bot]" && c.body?.includes(marker)
);
let rows = {};
let existingDetails = {};
if (existing?.body) {
const parts = existing.body.split(detailsMarker);
const tableSection = parts[0] || "";
const lines = tableSection.split("\n");
for (const line of lines) {
const match = line.match(/^\| ([^|]+) \| ([^|]+) \|$/);
if (match) {
const name = match[1].trim();
if (name && name !== "Check" && !name.startsWith(":")) {
rows[name] = match[2].trim();
}
}
}
const detailsRegex = /<details>\s*<summary><strong>([^<]+)<\/strong>.*?<\/summary>([\s\S]*?)<\/details>/g;
let detailMatch;
while ((detailMatch = detailsRegex.exec(existing.body)) !== null) {
existingDetails[detailMatch[1].trim()] = detailMatch[0];
}
}
rows[section] = status;
if (status === "Failed") {
existingDetails[section] = `<details>\n<summary><strong>${section}</strong></summary>\n\n${details}\n\n[View run](${runUrl})\n\n</details>`;
} else if (details && details !== 'No labels matched') {
existingDetails[section] = `<details>\n<summary><strong>${section}</strong></summary>\n\n${details}\n\n</details>`;
} else {
delete existingDetails[section];
}
const order = ["Lint", "Format", "Typecheck", "Build", "Release", "PR Title", "Labels"];
const sortedKeys = Object.keys(rows).sort((a, b) => {
const ai = order.indexOf(a), bi = order.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
let table = `| Check | Result |\n|:------|:-------|\n`;
for (const key of sortedKeys) {
table += `| ${key} | ${rows[key]} |\n`;
}
const detailsOrder = ["Lint", "Format", "Typecheck", "Build", "Release", "PR Title", "Labels"];
const sortedDetails = Object.keys(existingDetails).sort((a, b) => {
const ai = detailsOrder.indexOf(a), bi = detailsOrder.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
let body = `${marker}\n## CI Summary\n\n${table}`;
if (sortedDetails.length > 0) {
body += `\n${detailsMarker}\n\n---\n\n${sortedDetails.map(k => existingDetails[k]).join("\n\n")}`;
}
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}