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
160 changes: 156 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,106 @@ on:
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 }}
Expand Down Expand Up @@ -73,7 +165,8 @@ jobs:
lint:
name: Validate
runs-on: ubuntu-latest
needs: [ci-image]
needs: [changes, ci-image]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
Expand All @@ -93,7 +186,8 @@ jobs:
unit-tests:
name: Unit & regression tests
runs-on: ubuntu-latest
needs: [ci-image]
needs: [changes, ci-image]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
Expand All @@ -113,7 +207,8 @@ jobs:
build:
name: Build
runs-on: ubuntu-latest
needs: [ci-image, lint]
needs: [changes, ci-image, lint]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
Expand Down Expand Up @@ -142,7 +237,8 @@ jobs:
integration-tests:
name: Integration tests
runs-on: ubuntu-latest
needs: [ci-image, build, unit-tests]
needs: [changes, ci-image, build, unit-tests]
if: needs.changes.outputs.full_ci == 'true'
container:
image: ${{ needs.ci-image.outputs.name }}
credentials:
Expand Down Expand Up @@ -170,3 +266,59 @@ jobs:
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."
10 changes: 6 additions & 4 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ jobs:
fi
fi

echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
echo "release_name=$RELEASE_NAME" >> "$GITHUB_OUTPUT"
echo "should_release=$SHOULD_RELEASE" >> "$GITHUB_OUTPUT"
echo "tag_name=$RELEASE_NAME" >> "$GITHUB_OUTPUT"
{
echo "head_sha=$HEAD_SHA"
echo "release_name=$RELEASE_NAME"
echo "should_release=$SHOULD_RELEASE"
echo "tag_name=$RELEASE_NAME"
} >> "$GITHUB_OUTPUT"

if [[ "$SHOULD_RELEASE" == "true" ]]; then
echo "Nightly ${RELEASE_NAME} will be built from ${HEAD_SHA}." >> "$GITHUB_STEP_SUMMARY"
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ jobs:
name: extension-zip
path: dist/target/

- name: Resolve latest stable release
id: stable_release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

STABLE_TAG="$(gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq '.tag_name')"
echo "tag=${STABLE_TAG}" >> "$GITHUB_OUTPUT"
echo "Release notes will start at ${STABLE_TAG}." >> "$GITHUB_STEP_SUMMARY"

- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
Expand All @@ -93,6 +104,7 @@ jobs:
dist/target/aurora-shell@luminusos.github.io.shell-extension.zip
dist/target/aurora-shell@luminusos.github.io.development.shell-extension.zip
generate_release_notes: true
previous_tag: ${{ steps.stable_release.outputs.tag }}
prerelease: ${{ github.event_name == 'workflow_dispatch' }}

# An RC supersedes the nightly line: once a release candidate is out,
Expand Down