430 lines
15 KiB
YAML
430 lines
15 KiB
YAML
name: test
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
# Keep every run on main so cancelled checks do not pollute the default branch
|
|
# commit history. PRs and other branches still share a group and cancel stale runs.
|
|
group: ${{ case(github.ref == 'refs/heads/main', format('{0}-{1}', github.workflow, github.run_id), format('{0}-{1}', github.workflow, github.event.pull_request.number || github.ref)) }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
checks: write
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
|
|
jobs:
|
|
unit:
|
|
name: unit (${{ matrix.settings.name }})
|
|
# kilocode_change start
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
settings:
|
|
- name: linux
|
|
host: blacksmith-4vcpu-ubuntu-2404
|
|
- name: windows
|
|
host: blacksmith-4vcpu-windows-2025
|
|
runs-on: ${{ matrix.settings.host }}
|
|
# kilocode_change end
|
|
timeout-minutes: 45 # kilocode_change
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6 # kilocode_change
|
|
with:
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
# kilocode_change start
|
|
- name: Setup Node
|
|
id: setup-node
|
|
continue-on-error: ${{ runner.os == 'Windows' }}
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "24"
|
|
|
|
- name: Retry Setup Node on Windows
|
|
if: runner.os == 'Windows' && steps.setup-node.outcome == 'failure'
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "24"
|
|
# kilocode_change end
|
|
|
|
- name: Setup Bun
|
|
uses: ./.github/actions/setup-bun
|
|
|
|
- name: Configure git identity
|
|
run: |
|
|
git config --global user.email "kilo-maintainer[bot]@users.noreply.github.com"
|
|
git config --global user.name "kilo-maintainer[bot]"
|
|
|
|
- name: Cache Turbo
|
|
uses: actions/cache@v5 # kilocode_change
|
|
with:
|
|
path: node_modules/.cache/turbo
|
|
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-${{ github.sha }}
|
|
restore-keys: |
|
|
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-
|
|
turbo-${{ runner.os }}-
|
|
|
|
- name: Run unit tests
|
|
run: bun turbo test:ci --filter='!@kilocode/kilo-jetbrains'
|
|
env:
|
|
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }}
|
|
|
|
- name: Run HttpApi exerciser gates
|
|
if: runner.os == 'Linux'
|
|
working-directory: packages/opencode
|
|
run: bun run test:httpapi
|
|
|
|
- name: Publish unit reports
|
|
if: always()
|
|
uses: mikepenz/action-junit-report@v6
|
|
with:
|
|
report_paths: packages/*/.artifacts/unit/junit.xml
|
|
check_name: "unit results (${{ matrix.settings.name }})"
|
|
detailed_summary: true
|
|
include_time_in_summary: true
|
|
fail_on_failure: false
|
|
|
|
- name: Upload unit artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@v7 # kilocode_change
|
|
with:
|
|
# kilocode_change start
|
|
name: unit-${{ matrix.settings.name }}-${{ github.run_attempt }}
|
|
include-hidden-files: true
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
path: |
|
|
packages/*/.artifacts/unit/junit.xml
|
|
packages/opencode/.artifacts/test-health/junit.xml
|
|
# kilocode_change end
|
|
|
|
# kilocode_change start
|
|
jetbrains:
|
|
name: jetbrains
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
with:
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Setup Bun
|
|
uses: ./.github/actions/setup-bun
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v4
|
|
with:
|
|
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
|
|
|
- name: Run JetBrains unit tests
|
|
run: bun script/test-ci.ts
|
|
working-directory: packages/kilo-jetbrains
|
|
|
|
- name: Publish JetBrains unit reports
|
|
if: always()
|
|
uses: mikepenz/action-junit-report@v6
|
|
with:
|
|
report_paths: packages/kilo-jetbrains/.artifacts/unit/junit.xml
|
|
check_name: "unit results (jetbrains)"
|
|
detailed_summary: true
|
|
include_time_in_summary: true
|
|
fail_on_failure: false
|
|
|
|
- name: Upload JetBrains unit artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: unit-jetbrains-${{ github.run_attempt }}
|
|
include-hidden-files: true
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
path: packages/kilo-jetbrains/.artifacts/unit/junit.xml
|
|
# kilocode_change end
|
|
|
|
# kilocode_change start
|
|
test-health:
|
|
name: test health
|
|
needs:
|
|
- unit
|
|
- jetbrains
|
|
if: ${{ always() && !cancelled() }}
|
|
continue-on-error: true
|
|
outputs:
|
|
pages-ready: ${{ steps.pages.outcome == 'success' }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
timeout-minutes: 15
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
concurrency:
|
|
group: test-health-${{ github.ref == 'refs/heads/main' && 'main' || github.run_id }}
|
|
cancel-in-progress: false
|
|
steps:
|
|
- name: Download Linux test results
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: unit-linux-${{ github.run_attempt }}
|
|
path: .allure/input/linux
|
|
|
|
- name: Download Windows test results
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: unit-windows-${{ github.run_attempt }}
|
|
path: .allure/input/windows
|
|
|
|
- name: Download JetBrains test results
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: unit-jetbrains-${{ github.run_attempt }}
|
|
path: .allure/input/jetbrains
|
|
|
|
- name: Restore test history
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
mkdir -p .allure/history
|
|
restored=false
|
|
while read -r run; do
|
|
if test "$run" = "$GITHUB_RUN_ID"; then
|
|
continue
|
|
fi
|
|
name="$(
|
|
gh api "repos/$GITHUB_REPOSITORY/actions/runs/$run/artifacts" \
|
|
--jq '[.artifacts[] | select(.expired == false) | select(.name | startswith("allure-history-"))] | sort_by(.created_at) | last | .name // empty'
|
|
)"
|
|
if test -z "$name"; then
|
|
continue
|
|
fi
|
|
gh run download "$run" --repo "$GITHUB_REPOSITORY" --name "$name" --dir .allure/history
|
|
echo "Restored Allure history from run $run"
|
|
restored=true
|
|
break
|
|
done < <(
|
|
gh run list --repo "$GITHUB_REPOSITORY" --workflow test.yml --branch main --status completed --limit 20 \
|
|
--json databaseId --jq '.[].databaseId'
|
|
)
|
|
if test "$restored" = false; then
|
|
echo "No previous Allure history found; starting a new baseline"
|
|
fi
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
|
|
- name: Install Allure Report
|
|
run: |
|
|
archive="$RUNNER_TEMP/allure-2.42.1.tgz"
|
|
curl --fail --location --silent --show-error \
|
|
https://github.com/allure-framework/allure2/releases/download/2.42.1/allure-2.42.1.tgz \
|
|
--output "$archive"
|
|
echo "f8f73bf4bbd2cf5b8eee51d27487aef55c9d2f2650db111a52fa8108866ba86c $archive" | sha256sum --check
|
|
tar -xzf "$archive" -C "$RUNNER_TEMP"
|
|
echo "$RUNNER_TEMP/allure-2.42.1/bin" >> "$GITHUB_PATH"
|
|
|
|
- name: Generate test health reports
|
|
run: |
|
|
mkdir -p .allure/full .allure/reports
|
|
for lane in linux windows jetbrains; do
|
|
mkdir -p ".allure/results/$lane/final" ".allure/results/$lane/retries"
|
|
done
|
|
for lane in linux windows; do
|
|
for report in .allure/input/$lane/*/.artifacts/unit/junit.xml; do
|
|
package="${report#.allure/input/$lane/}"
|
|
package="${package%%/*}"
|
|
cp "$report" ".allure/results/$lane/final/$package.xml"
|
|
done
|
|
retry=".allure/input/$lane/opencode/.artifacts/test-health/junit.xml"
|
|
if test -f "$retry"; then
|
|
cp "$retry" ".allure/results/$lane/retries/opencode.xml"
|
|
fi
|
|
done
|
|
cp .allure/input/jetbrains/junit.xml .allure/results/jetbrains/final/jetbrains.xml
|
|
|
|
for lane in linux windows jetbrains; do
|
|
final=".allure/results/$lane/final"
|
|
retries=".allure/results/$lane/retries"
|
|
history=".allure/history/$lane"
|
|
output=".allure/full/$lane"
|
|
single=".allure/single/$lane"
|
|
|
|
if test -d "$history"; then
|
|
cp -R "$history" "$final/history"
|
|
fi
|
|
|
|
cat > "$final/executor.json" <<EOF
|
|
{
|
|
"name": "GitHub Actions",
|
|
"type": "github",
|
|
"buildOrder": $GITHUB_RUN_NUMBER,
|
|
"buildName": "$lane run $GITHUB_RUN_NUMBER",
|
|
"buildUrl": "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
|
|
}
|
|
EOF
|
|
printf 'environment=%s\nrepository=%s\n' "$lane" "$GITHUB_REPOSITORY" > "$final/environment.properties"
|
|
|
|
# The JUnit plugin keeps the first duplicate as authoritative. Pass
|
|
# final outcomes before failed retry attempts so recovered tests stay green.
|
|
allure generate "$final" "$retries" --clean --name "Kilo tests ($lane)" --output "$output"
|
|
total="$(jq -r '.statistic.total' "$output/widgets/summary.json")"
|
|
if test "$total" -le 0; then
|
|
echo "No tests found in the $lane report" >&2
|
|
exit 1
|
|
fi
|
|
|
|
rm -rf "$history"
|
|
mkdir -p "$history"
|
|
cp -R "$output/history/." "$history/"
|
|
|
|
allure generate "$final" "$retries" --single-file --clean --name "Kilo tests ($lane)" --output "$single"
|
|
cp "$single/index.html" ".allure/reports/$lane.html"
|
|
echo "$lane: $total tests"
|
|
done
|
|
|
|
cat > .allure/reports/README.txt <<'EOF'
|
|
Open linux.html, windows.html, or jetbrains.html directly in a browser.
|
|
Retried marks identify tests that changed status after a retry. Flaky,
|
|
regressed, and fixed marks use the recent history carried between runs.
|
|
EOF
|
|
|
|
mkdir -p .allure/site
|
|
for lane in linux windows jetbrains; do
|
|
cp -R ".allure/full/$lane" ".allure/site/$lane"
|
|
done
|
|
touch .allure/site/.nojekyll
|
|
cat > .allure/site/index.html <<EOF
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Kilo test health</title>
|
|
<style>
|
|
:root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
body { max-width: 960px; margin: 0 auto; padding: 64px 24px; line-height: 1.5; }
|
|
h1 { font-size: clamp(2rem, 7vw, 4rem); line-height: 1; margin-bottom: 16px; }
|
|
.intro { max-width: 720px; color: #888; }
|
|
.reports { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin: 40px 0; }
|
|
.report { border: 1px solid #8886; border-radius: 12px; padding: 24px; color: inherit; text-decoration: none; }
|
|
.report:hover { border-color: #e85d3f; }
|
|
.report strong { display: block; font-size: 1.25rem; margin-bottom: 8px; }
|
|
footer { color: #888; font-size: .9rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>Kilo test health</h1>
|
|
<p class="intro">Allure reports from the latest successful main test run. Use Retried to find tests that recovered within one run, and History to find status or duration changes across recent runs.</p>
|
|
<div class="reports">
|
|
<a class="report" href="linux/"><strong>Linux</strong>Bun and TypeScript tests</a>
|
|
<a class="report" href="windows/"><strong>Windows</strong>Bun and TypeScript tests</a>
|
|
<a class="report" href="jetbrains/"><strong>JetBrains</strong>Gradle and JUnit 5 tests</a>
|
|
</div>
|
|
</main>
|
|
<footer>Generated by <a href="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID">GitHub Actions run $GITHUB_RUN_NUMBER</a>.</footer>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
|
|
- name: Upload Allure reports
|
|
id: reports
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: allure-test-health-${{ github.run_attempt }}
|
|
path: .allure/reports
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
|
|
- name: Save main branch history
|
|
if: github.ref == 'refs/heads/main'
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: allure-history-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: .allure/history
|
|
if-no-files-found: error
|
|
retention-days: 90
|
|
|
|
- name: Upload GitHub Pages site
|
|
id: pages
|
|
if: github.ref == 'refs/heads/main'
|
|
uses: actions/upload-pages-artifact@v5
|
|
with:
|
|
path: .allure/site
|
|
retention-days: 7
|
|
|
|
- name: Add report link to summary
|
|
run: |
|
|
{
|
|
echo "### Allure test health"
|
|
echo
|
|
echo "[Download the single-file Linux, Windows, and JetBrains reports](${{ steps.reports.outputs.artifact-url }})."
|
|
echo
|
|
echo "Open the matching HTML file directly in a browser. Retried tests changed status during the current run; flaky tests changed status across recent runs."
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
deploy-test-health:
|
|
name: deploy test health dashboard
|
|
needs: test-health
|
|
if: ${{ github.ref == 'refs/heads/main' && needs.test-health.outputs.pages-ready == 'true' }}
|
|
continue-on-error: true
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
permissions:
|
|
contents: read
|
|
pages: write
|
|
id-token: write
|
|
concurrency:
|
|
group: test-health-pages
|
|
cancel-in-progress: false
|
|
environment:
|
|
name: github-pages
|
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
steps:
|
|
- name: Deploy test health dashboard
|
|
id: deployment
|
|
uses: actions/deploy-pages@v5
|
|
|
|
- name: Add dashboard link to summary
|
|
run: |
|
|
{
|
|
echo "### Allure test health dashboard"
|
|
echo
|
|
echo "[Open the Linux, Windows, and JetBrains test reports](${{ steps.deployment.outputs.page_url }})."
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
# kilocode_change end
|
|
required:
|
|
name: test (linux)
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
needs:
|
|
- unit
|
|
- jetbrains
|
|
if: always()
|
|
steps:
|
|
- name: Verify upstream test jobs passed
|
|
run: |
|
|
echo "unit=${{ needs.unit.result }}"
|
|
echo "jetbrains=${{ needs.jetbrains.result }}"
|
|
test "${{ needs.unit.result }}" = "success"
|
|
test "${{ needs.jetbrains.result }}" = "success"
|