Skip to content
Open
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
190 changes: 172 additions & 18 deletions .github/workflows/build-and-test-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
#
# A shared run timestamp keeps results under the same tests/runs/<timestamp>/
# tree; the publish job republishes failure artifacts from all test legs.
#
# The builds set EDG_BUILD_IDENTIFIER so that identical sources produce
# byte-identical build products. Each test leg keys a cache entry on the
# SHA-256 of the build products it downloaded plus the git trees of
# everything else that affects the results (tests/, dev_tools/, bases/, and
# this workflow, computed once by the prepare job). When a leg passes, it
# saves that entry along with its coverage report; a later run on the same
# ref (a re-run, or a push that leaves the build products and test inputs
# unchanged) restores it, skips edgy, and reuses the coverage report.
# actions/cache scopes entries to the ref that saved them (plus the default
# branch), so one pull request can never vouch for another's build.
#
# The bench job runs the cachegrind benchmarks the way
# `edg-docker-bench --posture=release` does and uploads them as bench-results
# (c/ and cpp/ runs, for comparison against the checked-in
Expand All @@ -34,13 +46,6 @@
name: Build & Test CI

on:
push:
branches:
- main
# The nightly collect-changes job's commits only move Changes entries.
paths-ignore:
- '**/Changes'
- '**/pending_changes/**'
pull_request:
merge_group:

Expand All @@ -61,19 +66,40 @@ jobs:
timeout-minutes: 5
outputs:
run_timestamp: ${{ steps.timestamp.outputs.run_timestamp }}
test_inputs: ${{ steps.test-inputs.outputs.test_inputs }}
steps:
- name: Declare shared edgy run timestamp
id: timestamp
run: |
# Match edgutil.RUN_TIMESTAMP_FORMAT (%Y.%m.%d-%H.%M.%S).
echo "run_timestamp=$(date -u +'%Y.%m.%d-%H.%M.%S')" >> "${GITHUB_OUTPUT}"

# Only the git trees are needed, so skip blobs and most of the
# working tree.
- name: Check out repository metadata
uses: actions/checkout@v7
with:
persist-credentials: false
filter: blob:none
sparse-checkout: .github

- name: Hash test inputs
id: test-inputs
run: |
# Everything besides the build products that can change a test
# leg's results. The workflow file covers the container image and
# the edgy options.
hash="$(git rev-parse HEAD:tests HEAD:dev_tools HEAD:bases \
HEAD:.github/workflows/build-and-test-ci.yml |
sha256sum | cut -c1-64)"
echo "test_inputs=${hash}" >> "${GITHUB_OUTPUT}"

build-gcc:
name: Build linux-gcc-debug
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: ghcr.io/edgcpp/ci-runner@sha256:14b3948c2ce12cfa5472e5a6c468393d15426f3a67685cc35d830ea5db2acdf8
image: ghcr.io/edgcpp/ci-runner@sha256:e83cc8c97556cdcb42353915a5164a051ba9c219d5b822f6859c875e07e9851d
defaults:
run:
shell: edg-container-exec "bash -eo pipefail {0}"
Expand All @@ -87,7 +113,9 @@ jobs:
- name: Configure linux-gcc-debug
env:
EDG_BASE: ${{ github.workspace }}/bases/docker/dev-env/gcc
run: cmake --preset linux-gcc-debug -DEDG_CPFE_USE_GCOV=ON
run: >-
cmake --preset linux-gcc-debug -DEDG_CPFE_USE_GCOV=ON
-DEDG_BUILD_IDENTIFIER=ci

- name: Build linux-gcc-debug
env:
Expand Down Expand Up @@ -118,7 +146,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: ghcr.io/edgcpp/ci-runner@sha256:14b3948c2ce12cfa5472e5a6c468393d15426f3a67685cc35d830ea5db2acdf8
image: ghcr.io/edgcpp/ci-runner@sha256:e83cc8c97556cdcb42353915a5164a051ba9c219d5b822f6859c875e07e9851d
defaults:
run:
shell: edg-container-exec "bash -eo pipefail {0}"
Expand All @@ -132,7 +160,9 @@ jobs:
- name: Configure linux-i686-gcc-debug
env:
EDG_BASE: ${{ github.workspace }}/bases/docker/dev-env/gcc-i686
run: cmake --preset linux-i686-gcc-debug -DEDG_CPFE_USE_GCOV=ON
run: >-
cmake --preset linux-i686-gcc-debug -DEDG_CPFE_USE_GCOV=ON
-DEDG_BUILD_IDENTIFIER=ci

- name: Build linux-i686-gcc-debug
env:
Expand Down Expand Up @@ -168,7 +198,7 @@ jobs:
- edg_x86_64
- edg_x86_64_cp
container:
image: ghcr.io/edgcpp/ci-runner@sha256:14b3948c2ce12cfa5472e5a6c468393d15426f3a67685cc35d830ea5db2acdf8
image: ghcr.io/edgcpp/ci-runner@sha256:e83cc8c97556cdcb42353915a5164a051ba9c219d5b822f6859c875e07e9851d
# Required for setarch ... -R (disable ASLR) used by the test harness.
options: --privileged
defaults:
Expand All @@ -194,7 +224,38 @@ jobs:
# Artifact round-trip does not reliably preserve +x.
chmod -R a+x build/gcc/bin

- name: Compute test cache key
id: key
env:
CONFIG: ${{ matrix.config }}
TEST_INPUTS: ${{ needs.prepare.outputs.test_inputs }}
run: |
# Hash the names and contents of the downloaded build products.
build="$(cd build/gcc &&
find . -type f -print0 | LC_ALL=C sort -z |
xargs -0 sha256sum | sha256sum | cut -c1-64)"
echo "Build products: ${build}"
echo "key=tested-${CONFIG}-${TEST_INPUTS}-${build}" \
>> "${GITHUB_OUTPUT}"

- name: Look for an earlier passing run of this build
id: tested
uses: actions/cache/restore@v6
with:
key: ${{ steps.key.outputs.key }}
path: .tmp/tested

- name: Reuse earlier results
if: ${{ steps.tested.outputs.cache-hit == 'true' }}
run: |
echo "::notice::This build already passed with the same test" \
"inputs: $(cat .tmp/tested/ci-meta.json)"
mkdir -p coverage
cp .tmp/tested/coverage/*.json coverage/ || true

- name: Run edgy (${{ matrix.config }})
id: edgy
if: ${{ steps.tested.outputs.cache-hit != 'true' }}
env:
RUN_TIMESTAMP: ${{ needs.prepare.outputs.run_timestamp }}
CONFIG: ${{ matrix.config }}
Expand Down Expand Up @@ -230,7 +291,7 @@ jobs:
# Coverage problems must never fail the test leg, hence
# continue-on-error.
- name: Install gcovr
if: ${{ !cancelled() }}
if: ${{ !cancelled() && steps.tested.outputs.cache-hit != 'true' }}
continue-on-error: true
run: |
python3 -m venv "${RUNNER_TEMP}/gcovr"
Expand All @@ -242,7 +303,7 @@ jobs:
# hot loops exceed gcovr's "suspicious hits" threshold; warn about them
# instead of failing.
- name: Generate coverage report
if: ${{ !cancelled() }}
if: ${{ !cancelled() && steps.tested.outputs.cache-hit != 'true' }}
continue-on-error: true
env:
CONFIG: ${{ matrix.config }}
Expand All @@ -269,6 +330,37 @@ jobs:
if-no-files-found: warn
retention-days: 1

# Record the pass (with its coverage report, so a run that skips edgy
# still contributes coverage). Without a report, don't record it.
- name: Prepare record of this passing run
id: record
if: >-
${{ steps.edgy.outcome == 'success' &&
hashFiles(format('coverage/{0}.json', matrix.config)) != '' }}
env:
CONFIG: ${{ matrix.config }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
TESTED_SHA: ${{ github.sha }}
run: |
mkdir -p .tmp/tested/coverage
cp "coverage/${CONFIG}.json" .tmp/tested/coverage/
python3 - > .tmp/tested/ci-meta.json <<'EOF'
import json, os
print(json.dumps({
'tested_sha': os.environ['TESTED_SHA'],
'run_id': int(os.environ['RUN_ID']),
'run_attempt': int(os.environ['RUN_ATTEMPT']),
}))
EOF

- name: Save record of this passing run
if: ${{ steps.record.outcome == 'success' }}
uses: actions/cache/save@v6
with:
key: ${{ steps.key.outputs.key }}
path: .tmp/tested

test-i686:
name: Test ${{ matrix.config }}
needs:
Expand All @@ -283,7 +375,7 @@ jobs:
- edg_x86_32
- edg_x86_32_cp
container:
image: ghcr.io/edgcpp/ci-runner@sha256:14b3948c2ce12cfa5472e5a6c468393d15426f3a67685cc35d830ea5db2acdf8
image: ghcr.io/edgcpp/ci-runner@sha256:e83cc8c97556cdcb42353915a5164a051ba9c219d5b822f6859c875e07e9851d
# Required for setarch ... -R (disable ASLR) used by the test harness.
options: --privileged
defaults:
Expand All @@ -309,7 +401,38 @@ jobs:
# Artifact round-trip does not reliably preserve +x.
chmod -R a+x build/gcc-i686/bin

- name: Compute test cache key
id: key
env:
CONFIG: ${{ matrix.config }}
TEST_INPUTS: ${{ needs.prepare.outputs.test_inputs }}
run: |
# Hash the names and contents of the downloaded build products.
build="$(cd build/gcc-i686 &&
find . -type f -print0 | LC_ALL=C sort -z |
xargs -0 sha256sum | sha256sum | cut -c1-64)"
echo "Build products: ${build}"
echo "key=tested-${CONFIG}-${TEST_INPUTS}-${build}" \
>> "${GITHUB_OUTPUT}"

- name: Look for an earlier passing run of this build
id: tested
uses: actions/cache/restore@v6
with:
key: ${{ steps.key.outputs.key }}
path: .tmp/tested

- name: Reuse earlier results
if: ${{ steps.tested.outputs.cache-hit == 'true' }}
run: |
echo "::notice::This build already passed with the same test" \
"inputs: $(cat .tmp/tested/ci-meta.json)"
mkdir -p coverage
cp .tmp/tested/coverage/*.json coverage/ || true

- name: Run edgy (${{ matrix.config }})
id: edgy
if: ${{ steps.tested.outputs.cache-hit != 'true' }}
env:
RUN_TIMESTAMP: ${{ needs.prepare.outputs.run_timestamp }}
CONFIG: ${{ matrix.config }}
Expand Down Expand Up @@ -345,7 +468,7 @@ jobs:
# Coverage problems must never fail the test leg, hence
# continue-on-error.
- name: Install gcovr
if: ${{ !cancelled() }}
if: ${{ !cancelled() && steps.tested.outputs.cache-hit != 'true' }}
continue-on-error: true
run: |
python3 -m venv "${RUNNER_TEMP}/gcovr"
Expand All @@ -357,7 +480,7 @@ jobs:
# hot loops exceed gcovr's "suspicious hits" threshold; warn about them
# instead of failing.
- name: Generate coverage report
if: ${{ !cancelled() }}
if: ${{ !cancelled() && steps.tested.outputs.cache-hit != 'true' }}
continue-on-error: true
env:
CONFIG: ${{ matrix.config }}
Expand All @@ -384,6 +507,37 @@ jobs:
if-no-files-found: warn
retention-days: 1

# Record the pass (with its coverage report, so a run that skips edgy
# still contributes coverage). Without a report, don't record it.
- name: Prepare record of this passing run
id: record
if: >-
${{ steps.edgy.outcome == 'success' &&
hashFiles(format('coverage/{0}.json', matrix.config)) != '' }}
env:
CONFIG: ${{ matrix.config }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
TESTED_SHA: ${{ github.sha }}
run: |
mkdir -p .tmp/tested/coverage
cp "coverage/${CONFIG}.json" .tmp/tested/coverage/
python3 - > .tmp/tested/ci-meta.json <<'EOF'
import json, os
print(json.dumps({
'tested_sha': os.environ['TESTED_SHA'],
'run_id': int(os.environ['RUN_ID']),
'run_attempt': int(os.environ['RUN_ATTEMPT']),
}))
EOF

- name: Save record of this passing run
if: ${{ steps.record.outcome == 'success' }}
uses: actions/cache/save@v6
with:
key: ${{ steps.key.outputs.key }}
path: .tmp/tested

publish-test-results:
name: Publish aggregated test results
needs:
Expand Down Expand Up @@ -508,7 +662,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: ghcr.io/edgcpp/ci-runner@sha256:14b3948c2ce12cfa5472e5a6c468393d15426f3a67685cc35d830ea5db2acdf8
image: ghcr.io/edgcpp/ci-runner@sha256:e83cc8c97556cdcb42353915a5164a051ba9c219d5b822f6859c875e07e9851d
defaults:
run:
shell: edg-container-exec "bash -eo pipefail {0}"
Expand Down
26 changes: 6 additions & 20 deletions .github/workflows/commit-messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
# See https://edgcpp.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# Check the messages of the new commits pushed to main and of every pull
# request's commits with edg-check-commit-messages (the same policy the
# pre-push hook applies, from edgchanges). Only commits with a violation are
# reported.
# Check the messages of every pull request's commits and of every merge queue
# entry with edg-check-commit-messages (the same policy the pre-push hook
# applies, from edgchanges). Only commits with a violation are reported.
#
# A pull request is checked from its base commit to its head commit, and a
# merge queue entry from the branch it is merged into to its tip (so the
# commits the queue creates are checked too). A push is checked from the
# previous tip of main. If that tip is unavailable (e.g. after a force-push),
# the job fails rather than check nothing.
# commits the queue creates are checked too). Commits land on main only
# through the merge queue, so pushes to main are not checked again.
#
# The checker and the commits come from one checkout of github.sha, the same
# commit GitHub loads this workflow from. For a pull request that is the merge
Expand All @@ -26,9 +24,6 @@
name: Commit Messages

on:
push:
branches:
- main
pull_request:
merge_group:

Expand Down Expand Up @@ -63,21 +58,12 @@ jobs:
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
QUEUE_BASE_SHA: ${{ github.event.merge_group.base_sha }}
QUEUE_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
ALLOW_MERGE_COMMITS: ${{ vars.ALLOW_MERGE_COMMITS }}
run: |
if [[ "${EVENT_NAME}" == pull_request ]]; then
range=("${PR_BASE_SHA}..${PR_HEAD_SHA}")
elif [[ "${EVENT_NAME}" == merge_group ]]; then
range=("${QUEUE_BASE_SHA}..${QUEUE_HEAD_SHA}")
elif [[ "${PUSH_BEFORE_SHA}" != 0000000000000000000000000000000000000000 ]] &&
git cat-file -e "${PUSH_BEFORE_SHA}^{commit}" 2> /dev/null; then
range=("${PUSH_BEFORE_SHA}..HEAD")
else
echo "::error::No previous tip of the branch to compare against" \
"(push before: ${PUSH_BEFORE_SHA}); the pushed commits" \
"were not checked."
exit 1
range=("${QUEUE_BASE_SHA}..${QUEUE_HEAD_SHA}")
fi
options=()
if [[ "${ALLOW_MERGE_COMMITS}" == true ]]; then
Expand Down
11 changes: 0 additions & 11 deletions .github/workflows/dev-tools-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,6 @@
name: Dev Tools Tests

on:
push:
branches:
- main
paths:
- .github/workflows/dev-tools-tests.yml
- dev_tools/bin/edg-check-commit-messages
- dev_tools/bin/edg-check-file-changes
- dev_tools/bin/edg-collect-changes
- dev_tools/pylibs/edgchanges/**
- dev_tools/pylibs/edgutil/**
- dev_tools/tests/**
pull_request:
paths:
- .github/workflows/dev-tools-tests.yml
Expand Down
Loading
Loading