Files

343 lines
13 KiB
YAML

name: CI
on:
pull_request:
push:
branches: [main, master]
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
# Pinned by SHA to prevent tag-swapping supply chain attacks.
# To update: verify the new SHA on the action's releases page,
# then update both the SHA and the version comment.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.8"
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Audit dependencies
run: bun pm ls --all 2>&1 | head -200
- name: Typecheck
run: bun run typecheck
- name: Lint
run: bun run lint
- name: Test (unit + integration, excludes smoke)
run: bun test tests/*.test.ts
- name: Build (node target)
run: bun run build
- name: Build (binary)
run: bun run build:binary
- name: Smoke tests (binary + package)
run: bun test tests/smoke/
check-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.8"
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Test (unit + integration, excludes smoke)
shell: pwsh
run: |
$files = Get-ChildItem -Path tests -Filter *.test.ts | ForEach-Object { $_.FullName }
bun test @files
- name: Build (node target)
run: bun run build
- name: Build (binary)
run: bun run build:binary
- name: Smoke tests (binary + package)
run: bun test tests/smoke/
coordinated:
name: Verified private CLI/API evidence
needs: [check, check-windows]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "22"
# Paid content, private source and content-bearing failure logs stay in
# Toolkit. This public workflow downloads only a strict hash-only receipt.
- name: Require green private run for this exact candidate pair
run: node scripts/verify-coordinated-receipt.mjs
env:
GH_TOKEN: ${{ secrets.TOOLKIT_READ_TOKEN }}
TOOLKIT_COORDINATED_RUN_ID: ${{ vars.TOOLKIT_COORDINATED_RUN_ID }}
E2E_TOOLKIT_SHA: ${{ vars.TOOLKIT_CANDIDATE_SHA }}
E2E_CLI_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
# ── Release jobs (master push only) ──────────────────────────────
version:
needs: [check, check-windows, coordinated]
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.bump.outputs.version }}
should_release: ${{ steps.bump.outputs.should_release }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_TOKEN }}
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.8"
- run: bun install --frozen-lockfile
- name: Bump version
id: bump
run: |
rc=0
output=$(node scripts/auto-version.mjs 2>&1) || rc=$?
if [ "$rc" -eq 1 ]; then
echo "No release needed — skipping."
echo "$output"
echo "should_release=false" >> "$GITHUB_OUTPUT"
exit 0
elif [ "$rc" -ne 0 ]; then
echo "::error::auto-version.mjs crashed (exit $rc):"
echo "$output"
exit 1
fi
version=$(echo "$output" | grep '^NEW_VERSION=' | cut -d= -f2)
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "should_release=true" >> "$GITHUB_OUTPUT"
- name: Commit version bump and tag
if: steps.bump.outputs.should_release == 'true'
env:
VERSION: ${{ steps.bump.outputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add package.json
git commit -m "chore(release): ${VERSION}"
git tag -a "${VERSION}" -m "Release ${VERSION}"
git push origin master "${VERSION}"
build-binaries:
needs: [version]
if: needs.version.outputs.should_release == 'true'
strategy:
matrix:
include:
- os: ubuntu-latest
target: bun-linux-x64
artifact: 10x-linux-x64
- os: ubuntu-latest
target: bun-linux-arm64
artifact: 10x-linux-arm64
- os: macos-latest
target: bun-darwin-arm64
artifact: 10x-darwin-arm64
- os: macos-14
target: bun-darwin-x64
artifact: 10x-darwin-x64
- os: windows-latest
target: bun-windows-x64
artifact: 10x-windows-x64.exe
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ needs.version.outputs.new_version }}
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.8"
- run: bun install --frozen-lockfile
- name: Build binary
run: bun build --compile --minify src/index.ts --target ${{ matrix.target }} --outfile dist/${{ matrix.artifact }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.artifact }}
path: dist/${{ matrix.artifact }}
publish-npm:
needs: [version]
if: needs.version.outputs.should_release == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ needs.version.outputs.new_version }}
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.8"
- run: bun install --frozen-lockfile && bun run build
- name: Publish to npm
run: |
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
npm publish --access public
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
github-release:
needs: [version, build-binaries]
if: needs.version.outputs.should_release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: release-assets
merge-multiple: true
- uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2
with:
tag_name: ${{ needs.version.outputs.new_version }}
generate_release_notes: true
files: release-assets/**/*
notify-slack:
needs: [check, check-windows, coordinated, version, publish-npm, github-release]
if: always() && github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- name: Build notification payload
id: payload
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
MSG=$(printf '%s\n' "$COMMIT_MESSAGE" | head -1 | cut -c1-80)
echo "commit_msg=${MSG}" >> "$GITHUB_OUTPUT"
if [[ "${{ needs.check.result }}" != "success" || "${{ needs.check-windows.result }}" != "success" || "${{ needs.coordinated.result }}" != "success" || "${{ needs.publish-npm.result }}" == "failure" || "${{ needs.version.result }}" == "failure" || "${{ needs.github-release.result }}" == "failure" ]]; then
echo "failed=true" >> "$GITHUB_OUTPUT"
else
echo "failed=false" >> "$GITHUB_OUTPUT"
fi
- name: Post to #observability
uses: slackapi/slack-github-action@v2
with:
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"text": "${{ steps.payload.outputs.failed == 'true' && '🔴' || needs.version.outputs.should_release == 'true' && needs.publish-npm.result == 'success' && '📦' || '🟢' }} 10x-cli pipeline",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${{ steps.payload.outputs.failed == 'true' && '🔴' || needs.version.outputs.should_release == 'true' && needs.publish-npm.result == 'success' && '📦' || '🟢' }} 10x-cli${{ needs.version.outputs.should_release == 'true' && format(' — v{0}', needs.version.outputs.new_version) || '' }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ needs.check.result == 'success' && needs.check-windows.result == 'success' && '✅' || '❌' }} Tests (Linux/Windows) ${{ needs.coordinated.result == 'success' && '✅' || needs.coordinated.result == 'skipped' && '⏭️' || '❌' }} E2E ${{ needs.publish-npm.result == 'success' && '✅' || needs.publish-npm.result == 'skipped' && '⏭️' || '❌' }} npm ${{ needs.github-release.result == 'success' && '✅' || needs.github-release.result == 'skipped' && '⏭️' || '❌' }} Release"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "📝 <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|`${{ github.sha }}`> ${{ steps.payload.outputs.commit_msg }} · 👤 ${{ github.actor }} · <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|🔗 Workflow>${{ steps.payload.outputs.failed == 'true' && ' · 🔔 see #alerting' || '' }}"
}
]
}
]
}
- name: Post to #alerting
if: steps.payload.outputs.failed == 'true'
uses: slackapi/slack-github-action@v2
with:
webhook: ${{ secrets.SLACK_ALERTS_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
{
"text": "🚨 10x-cli pipeline failure",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 10x-cli — FAILURE"
}
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*Tests (Linux/Windows)*\n${{ needs.check.result == 'success' && needs.check-windows.result == 'success' && '✅ passed' || '❌ failed' }}" },
{ "type": "mrkdwn", "text": "*E2E*\n${{ needs.coordinated.result == 'success' && '✅ passed' || needs.coordinated.result == 'skipped' && '⏭️ skipped' || '❌ failed' }}" },
{ "type": "mrkdwn", "text": "*npm*\n${{ needs.publish-npm.result == 'success' && '✅ published' || needs.publish-npm.result == 'skipped' && '⏭️ skipped' || '❌ failed' }}" },
{ "type": "mrkdwn", "text": "*Release*\n${{ needs.github-release.result == 'success' && '✅ created' || needs.github-release.result == 'skipped' && '⏭️ skipped' || '❌ failed' }}" }
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "📝 *Commit:* <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|`${{ github.sha }}`>\n${{ steps.payload.outputs.commit_msg }}\n\n👤 *Author:* ${{ github.actor }}"
},
"accessory": {
"type": "button",
"text": { "type": "plain_text", "text": "View Run" },
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "🔍 <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|Commit diff> · 📜 <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Full logs> · 📦 <https://www.npmjs.com/package/@przeprogramowani/10x-cli|npm package>"
}
]
}
]
}