ci: improve workflow configuration and reliability

- Replace global read-all permissions with scoped permissions (contents: read, actions: write)
- Fix cache configuration to exclude node_modules and include package-lock.json
- Improve CI workflow resolution with flexible path matching and pagination
- Verify version instead of committing version bumps from CI
- Detect prereleases and publish with appropriate npm tags (next vs latest)
- Use generic test suite description in release notes to avoid drift
This commit is contained in:
2025-12-11 19:28:48 +02:00
parent 5bc95f0bcd
commit 4cc202c687
2 changed files with 43 additions and 41 deletions

View File

@@ -71,36 +71,28 @@ jobs:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with: with:
script: | script: |
// Find CI workflow by name or filename const wfList = await github.rest.actions.listRepoWorkflows({
const { data: allWorkflows } = await github.rest.actions.listRepoWorkflows({
owner: context.repo.owner,
repo: context.repo.repo
});
const ciWorkflow = allWorkflows.workflows.find(w =>
w.name === 'CI' ||
w.path === '.github/workflows/test.yml'
);
if (!ciWorkflow) {
core.setFailed('Could not find CI workflow');
return;
}
const { data: workflows } = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
workflow_id: ciWorkflow.id,
head_sha: context.sha,
status: 'completed'
}); });
const wf =
const latestRun = workflows.workflow_runs[0]; wfList.data.workflows.find(w => w.path.endsWith('/test.yml')) ||
if (!latestRun || latestRun.conclusion !== 'success') { wfList.data.workflows.find(w => (w.name || '').toLowerCase() === 'ci');
core.setFailed(`CI workflow has not passed for this commit. Status: ${latestRun?.conclusion || 'not found'}`); if (!wf) core.setFailed('CI workflow not found (test.yml or CI).');
const { data } = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: wf.id,
head_sha: context.sha,
status: 'completed',
per_page: 1
});
const latestRun = data.workflow_runs?.[0];
if (!latestRun) core.setFailed('No completed CI runs found for this commit.');
if (latestRun.conclusion !== 'success') {
core.setFailed(`CI workflow conclusion: ${latestRun.conclusion}`);
} }
console.log(`CI status: ${latestRun.conclusion}`)
console.log(`CI workflow ID: ${ciWorkflow.id}, Status: ${latestRun?.conclusion || 'not found'}`)
security: security:
name: 🔒 Security Scan name: 🔒 Security Scan
@@ -197,22 +189,24 @@ jobs:
- name: 🏗️ Build Parser - name: 🏗️ Build Parser
run: npm run build run: npm run build
- name: 📋 Update Package Version - name: 🔎 Verify package.json version matches input
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: | run: |
VERSION="${{ github.event.inputs.version }}" INPUT="${{ github.event.inputs.version }}"
npm version ${VERSION} --no-git-tag-version EXPECTED="v${INPUT#v}"
git config user.name "github-actions[bot]" PKG="v$(node -p "require('./package.json').version")"
git config user.email "github-actions[bot]@users.noreply.github.com" if [ "$PKG" != "$EXPECTED" ]; then
git add package.json package-lock.json echo "package.json version ($PKG) does not match requested release ($EXPECTED)."
git commit -m "chore: bump version to ${VERSION}" || true echo "Bump package.json in a PR before running workflow_dispatch."
exit 1
fi
- name: 🏷️ Create Tag - name: 🏷️ Create Tag
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: | run: |
VERSION="v${{ github.event.inputs.version }}" VERSION="v${{ github.event.inputs.version }}"
git tag ${VERSION} git tag "${VERSION#v}"
git push origin ${VERSION} git push origin "${VERSION#v}"
- name: 📝 Generate Release Notes - name: 📝 Generate Release Notes
id: release_notes id: release_notes
@@ -237,7 +231,7 @@ jobs:
echo "" echo ""
echo "- Initial release of tree-sitter-shellspec" echo "- Initial release of tree-sitter-shellspec"
echo "- Complete ShellSpec grammar support" echo "- Complete ShellSpec grammar support"
echo "- 59 comprehensive test cases" echo "- Comprehensive test suite with broad coverage"
echo "- Real-world compatibility with official ShellSpec examples" echo "- Real-world compatibility with official ShellSpec examples"
} >> release_notes.md } >> release_notes.md
fi fi
@@ -262,7 +256,14 @@ jobs:
generate_release_notes: false generate_release_notes: false
- name: 📦 Publish to npm - name: 📦 Publish to npm
run: npm publish --access public run: |
if [[ "${{ needs.validate.outputs.version }}" == *"-"* ]]; then
PUBLISH_TAG="next"
else
PUBLISH_TAG="latest"
fi
echo "Publishing with tag: $PUBLISH_TAG"
npm publish --access public --tag "$PUBLISH_TAG"
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -12,7 +12,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
actions: write
jobs: jobs:
test: test:
@@ -89,9 +91,8 @@ jobs:
id: cache-parser id: cache-parser
with: with:
path: | path: |
src/ build/
node_modules/ key: ${{ runner.os }}-parser-${{ matrix.node-version }}-${{ hashFiles('package-lock.json', 'src/parser.c', 'binding.gyp', 'src/**/*.cc', 'src/**/*.h') }}
key: ${{ runner.os }}-parser-${{ matrix.node-version }}-${{ hashFiles('src/parser.c', 'binding.gyp', 'package.json') }}
- name: Build Parser - name: Build Parser
if: steps.cache-parser.outputs.cache-hit != 'true' if: steps.cache-parser.outputs.cache-hit != 'true'