Repository navigation
CI #301
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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." |