Skip to content

CI

CI #301

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
merge_group:
workflow_call:
inputs:
checkout_ref:
description: Git ref to validate instead of the triggering commit
required: false
type: string
permissions:
contents: read
packages: write
pull-requests: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Classify changes
runs-on: ubuntu-latest
outputs:
full_ci: ${{ steps.classify.outputs.full_ci }}
workflow_checks: ${{ steps.classify.outputs.workflow_checks }}
steps:
- name: Select validation scope
id: classify
uses: actions/github-script@v8
with:
script: |
let paths;
let forceFullCi = false;
if (context.eventName === 'pull_request') {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 100,
});
paths = files.map(file => file.filename);
} else if (context.eventName === 'merge_group') {
const mergeGroup = context.payload.merge_group;
if (!mergeGroup?.base_sha || !mergeGroup?.head_sha) {
core.warning('Merge group SHAs are unavailable; running full CI.');
core.setOutput('full_ci', 'true');
core.setOutput('workflow_checks', 'false');
return;
}
const comparison = await github.rest.repos.compareCommits({
owner: context.repo.owner,
repo: context.repo.repo,
base: mergeGroup.base_sha,
head: mergeGroup.head_sha,
per_page: 100,
});
const files = comparison.data.files ?? [];
paths = files.map(file => file.filename);
forceFullCi = files.length >= 300;
if (forceFullCi) {
core.warning('Merge group comparison reached the 300-file API limit; running full CI.');
}
} else {
core.setOutput('full_ci', 'true');
core.setOutput('workflow_checks', 'false');
return;
}
const isWorkflow = path => /^\.github\/workflows\/[^/]+\.ya?ml$/.test(path);
const workflowChecks = paths.some(isWorkflow);
const fullCi = forceFullCi || paths.length === 0 ||
paths.some(path => !isWorkflow(path) || path === '.github/workflows/ci.yml');
core.setOutput('full_ci', String(fullCi));
core.setOutput('workflow_checks', String(workflowChecks));
core.info(`Changed files: ${paths.join(', ')}`);
core.info(`Full CI: ${fullCi}; workflow checks: ${workflowChecks}`);
workflow-checks:
name: Validate workflows
needs: [changes]
if: needs.changes.outputs.workflow_checks == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Install actionlint
env:
ACTIONLINT_VERSION: 1.7.12
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
run: |
set -euo pipefail
archive="$RUNNER_TEMP/actionlint.tar.gz"
curl --fail --silent --show-error --location \
--output "$archive" \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
echo "${ACTIONLINT_SHA256} ${archive}" | sha256sum --check
tar -xzf "$archive" -C "$RUNNER_TEMP" actionlint
- name: Validate GitHub Actions workflows
run: |
"$RUNNER_TEMP/actionlint"
ci-image:
name: CI image
needs: [changes]
if: needs.changes.outputs.full_ci == 'true'
runs-on: ubuntu-latest
outputs:
name: ${{ steps.image.outputs.name }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve CI image
id: image
run: |
IMAGE="$(bash scripts/ci-image-name.sh)"
echo "name=$IMAGE" >> "$GITHUB_OUTPUT"
if docker manifest inspect "$IMAGE" >/dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- uses: docker/setup-qemu-action@v4
if: steps.image.outputs.exists != 'true'
- uses: docker/setup-buildx-action@v4
if: steps.image.outputs.exists != 'true'
- name: Build and publish CI image
if: steps.image.outputs.exists != 'true'
uses: docker/build-push-action@v7
with:
context: .
file: Containerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ steps.image.outputs.name }}
ghcr.io/luminusos/aurora-shell-ci:sha-${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
lint:
name: Validate
runs-on: ubuntu-latest
needs: [changes, ci-image]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Install dependencies
run: just deps
- name: Validate
run: just validate
unit-tests:
name: Unit & regression tests
runs-on: ubuntu-latest
needs: [changes, ci-image]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Install dependencies
run: just deps
- name: Run unit tests
run: just test unit
build:
name: Build
runs-on: ubuntu-latest
needs: [changes, ci-image, lint]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Install dependencies
run: just deps
- name: Build and inspect extension packages
run: just package check
- name: Upload extension zips
uses: actions/upload-artifact@v7
with:
name: extension-zip
path: |
dist/target/aurora-shell@luminusos.github.io.shell-extension.zip
dist/target/aurora-shell@luminusos.github.io.development.shell-extension.zip
retention-days: 1
integration-tests:
name: Integration tests
runs-on: ubuntu-latest
needs: [changes, ci-image, build, unit-tests]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
options: --privileged
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.checkout_ref || github.sha }}
- name: Download extension zip
uses: actions/download-artifact@v8
with:
name: extension-zip
path: dist/target/
- name: Run integration tests
env:
DISPLAY: ':0'
GDK_DEBUG: no-portals
LIBGL_ALWAYS_SOFTWARE: '1'
WAYLAND_DISPLAY: gnome-shell-test-display
XDG_RUNTIME_DIR: /tmp/xdg-runtime
XDG_SESSION_ID: aurora_ci
run: |
scripts/run-ci-shell-tests.sh
ci-gate:
name: CI gate
if: always()
runs-on: ubuntu-latest
needs:
- changes
- workflow-checks
- ci-image
- lint
- unit-tests
- build
- integration-tests
steps:
- name: Verify required checks
env:
BUILD_RESULT: ${{ needs.build.result }}
CHANGES_RESULT: ${{ needs.changes.result }}
CI_IMAGE_RESULT: ${{ needs.ci-image.result }}
FULL_CI: ${{ needs.changes.outputs.full_ci }}
INTEGRATION_RESULT: ${{ needs.integration-tests.result }}
LINT_RESULT: ${{ needs.lint.result }}
UNIT_TESTS_RESULT: ${{ needs.unit-tests.result }}
WORKFLOW_CHECKS: ${{ needs.changes.outputs.workflow_checks }}
WORKFLOW_CHECKS_RESULT: ${{ needs.workflow-checks.result }}
run: |
set -euo pipefail
if [[ "$CHANGES_RESULT" != "success" ]]; then
echo "Change classification failed."
exit 1
fi
if [[ "$WORKFLOW_CHECKS" == "true" && "$WORKFLOW_CHECKS_RESULT" != "success" ]]; then
echo "Workflow validation did not pass: ${WORKFLOW_CHECKS_RESULT}."
exit 1
fi
if [[ "$FULL_CI" == "true" ]]; then
for result in \
"$CI_IMAGE_RESULT" \
"$LINT_RESULT" \
"$UNIT_TESTS_RESULT" \
"$BUILD_RESULT" \
"$INTEGRATION_RESULT"; do
if [[ "$result" != "success" ]]; then
echo "A required full-CI job did not pass: ${result}."
exit 1
fi
done
elif [[ "$WORKFLOW_CHECKS" != "true" ]]; then
echo "No validation scope was selected."
exit 1
fi
echo "All required checks passed."