Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 14 additions & 112 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,15 @@ permissions:
jobs:
conventional-commits:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Check conventional commits
uses: helpers4/action/conventional-commits@main
with:
checkout: "true"
pr-comment: "error"

- name: Set status
id: status
if: always()
run: |
if [ "${{ job.status }}" == "success" ]; then
echo "status=success" >> $GITHUB_OUTPUT
else
echo "status=failure" >> $GITHUB_OUTPUT
fi

version-bump-check:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down Expand Up @@ -81,16 +67,6 @@ jobs:
fi
exit "$FAILED"

- name: Set status
id: status
if: always()
run: |
if [ "${{ job.status }}" == "success" ]; then
echo "status=success" >> $GITHUB_OUTPUT
else
echo "status=failure" >> $GITHUB_OUTPUT
fi

test-features:
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -150,9 +126,6 @@ jobs:
- features: mistral-dev
baseImage: ubuntu:latest

outputs:
status: ${{ steps.status.outputs.status }}

steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down Expand Up @@ -189,16 +162,18 @@ jobs:

shellcheck:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Run ShellCheck on install scripts
run: |
sudo apt-get update && sudo apt-get install -y shellcheck
find src -name "install.sh" -exec shellcheck -S warning {} +
uses: ludeeus/action-shellcheck@2.0.0
with:
severity: warning
scandir: src
# Preserve the previous scope (install.sh only) — path-keys.sh, sync-files.sh,
# and gh-auth.sh have never been linted here and may have pre-existing issues.
ignore_names: "path-keys.sh sync-files.sh gh-auth.sh"

- name: Verify bootstrap copies match helpers4-common canonical
run: |
Expand All @@ -223,91 +198,18 @@ jobs:
fi
exit $FAILED

- name: Set status
id: status
if: always()
run: |
if [ "${{ job.status }}" == "success" ]; then
echo "status=success" >> $GITHUB_OUTPUT
else
echo "status=failure" >> $GITHUB_OUTPUT
fi

pr-comment:
runs-on: ubuntu-latest
needs: [conventional-commits, version-bump-check, test-features, shellcheck]
if: always()
steps:
- name: Update PR comment
uses: actions/github-script@v9
uses: helpers4/action/pr-status-comment@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const jobs = {
'🧾 Conventional Commits': "${{ needs.conventional-commits.outputs.status || 'unknown' }}",
'🔖 Version Bump': "${{ needs.version-bump-check.outputs.status || 'unknown' }}",
'🧪 Feature Tests': "${{ needs.test-features.result || 'unknown' }}",
'🐚 ShellCheck': "${{ needs.shellcheck.outputs.status || 'unknown' }}",
};

const rows = Object.entries(jobs).map(([job, status]) => {
const icon = status === 'success' ? '✅' :
status === 'failure' ? '❌' :
status === 'skipped' ? '⏭️' : '⚠️';
const badge = status === 'success' ? '`passing`' :
status === 'failure' ? '`failing`' :
status === 'skipped' ? '`skipped`' : '`unknown`';
return `| ${icon} | **${job}** | ${badge} |`;
}).join('\n');

const passedCount = Object.values(jobs).filter(s => s === 'success').length;
const totalJobs = Object.keys(jobs).length;
const allPassed = passedCount === totalJobs;

const statusBanner = allPassed
? '## ✅ PR Validation Passed\n\n> All checks passed!'
: `## ❌ PR Validation Failed\n\n> ${passedCount}/${totalJobs} checks passed`;

const comment = `${statusBanner}

---

### 📋 Pipeline Status

| | Job | Status |
|:---:|-----|:------:|
${rows}

---

<sub>🤖 Generated by **@helpers4** CI • ${new Date().toISOString().split('T')[0]}</sub>`;

try {
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('PR Validation')
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: comment
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
} catch (error) {
console.error('Error updating PR comment:', error);
jobs: |
{
"🧾 Conventional Commits": "${{ needs.conventional-commits.result }}",
"🔖 Version Bump": "${{ needs.version-bump-check.result }}",
"🧪 Feature Tests": "${{ needs.test-features.result }}",
"🐚 ShellCheck": "${{ needs.shellcheck.result }}"
}
66 changes: 29 additions & 37 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,41 +152,33 @@ jobs:
echo "→ created tag ${tag}"
done

- name: Get Trigganator token
id: trigganator
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.TRIGGANATOR_ID }}
private-key: ${{ secrets.TRIGGANATOR_KEY }}
owner: helpers4
repositories: website
permission-contents: write

- name: Trigger website update
trigger-website:
needs: [detect, release]
if: ${{ needs.detect.outputs.count != '0' }}
continue-on-error: true
runs-on: ubuntu-latest
# No checkout, no GITHUB_TOKEN use — the composite action mints its own token via the
# GitHub App credentials passed in `with:`, so the default token needs zero permissions here.
permissions: {}
strategy:
fail-fast: false
# One dispatch per changed feature so the website workflow can react per-feature —
# matrix built from detect's JSON output instead of a bash loop over `gh api`.
matrix:
feature: ${{ fromJson(needs.detect.outputs.changed) }}
steps:
- name: "Trigger website update: ${{ matrix.feature.name }} v${{ matrix.feature.version }}"
continue-on-error: true
env:
CHANGED: ${{ needs.detect.outputs.changed }}
GH_TOKEN: ${{ steps.trigganator.outputs.token }}
run: |
set -euo pipefail
# One dispatch per feature so the website workflow can react per-feature.
echo "$CHANGED" | jq -c '.[]' | while read -r row; do
name=$(jq -r '.name' <<< "$row")
version=$(jq -r '.version' <<< "$row")
echo "→ dispatch website: $name v$version"
# client_payload must be a JSON object, not a stringified one —
# gh api's "field[nested]=value" bracket syntax builds it as such.
# (A previous version passed a pre-serialized JSON string via
# --raw-field, which the API rejects with HTTP 422.)
gh api \
-X POST \
-H "Accept: application/vnd.github+json" \
"/repos/helpers4/website/dispatches" \
-f event_type=devcontainer-release \
-f "client_payload[feature]=$name" \
-f "client_payload[version]=$version" \
-f "client_payload[ref]=${{ github.sha }}" \
-f "client_payload[sha]=${{ github.sha }}" \
-f "client_payload[source]=devcontainer-release" \
|| echo "⚠️ dispatch failed for $name"
done
uses: helpers4/action/trigger-website-update@main
with:
event-type: devcontainer-release
payload: |
{
"feature": "${{ matrix.feature.name }}",
"version": "${{ matrix.feature.version }}",
"ref": "${{ github.sha }}",
"sha": "${{ github.sha }}",
"source": "devcontainer-release"
}
app-id: ${{ vars.TRIGGANATOR_ID }}
app-private-key: ${{ secrets.TRIGGANATOR_KEY }}
Loading