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
266 changes: 160 additions & 106 deletions .github/instructions/coding-guidelines.instructions.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions .github/scripts/compare_coverage_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- Integration tests (subprocess) simulating all gate scenarios listed in the
Coverage Gate implementation spec.

Workflow-level scenarios (unit_tests fails / component_tests fails / both fail) are handled
Workflow-level scenarios (L1 fails / L2 fails / both fail) are handled
by GitHub Actions' implicit success() dependency check on the coverage-gate job
and cannot be tested at the Python script level; they are documented inline.
"""
Expand Down Expand Up @@ -628,12 +628,12 @@ def test_s9_non_dict_json(self):
self.assertEqual(r.returncode, 0, msg=r.stdout + r.stderr)

# ===========================================================================
# SCENARIO 10 — unit_tests job fails
# SCENARIO 10 — L1 (unit) job fails
# Coverage Gate does NOT trigger (workflow-level behaviour).
#
# GitHub Actions: coverage_gate has `needs: [unit_tests, component_tests]`
# GitHub Actions: coverage_gate has `needs: [trigger-L1, trigger-L2]`
# with no custom `if:`. The implicit success() check means coverage_gate
# is SKIPPED whenever unit_tests fails. This cannot be unit-tested here;
# is SKIPPED whenever trigger-L1 fails. This cannot be unit-tested here;
# it is enforced by the workflow graph.
# ===========================================================================

Expand All @@ -652,7 +652,7 @@ def test_s10_unit_artifacts_absent_component_passes(self):
self.assertIn("[WARN]", r.stdout)

# ===========================================================================
# SCENARIO 11 — component_tests job fails (symmetric to scenario 10)
# SCENARIO 11 — L2 (component) job fails (symmetric to scenario 10)
# ===========================================================================

def test_s11_component_artifacts_absent_unit_passes(self):
Expand All @@ -664,7 +664,7 @@ def test_s11_component_artifacts_absent_unit_passes(self):
self.assertIn("[WARN]", r.stdout)

# ===========================================================================
# SCENARIO 12 — Both unit_tests AND component_tests jobs fail
# SCENARIO 12 — Both L1 AND L2 jobs fail
# Coverage Gate does NOT trigger (workflow-level). At script level, both
# .info files are absent → both SKIP → exit 0 (harmless; gate is already
# blocked at the workflow graph layer before the script is ever called).
Expand Down
81 changes: 81 additions & 0 deletions .github/workflows/L1-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: L1-tests

on:
workflow_call:
inputs:
image_tag:
required: true
type: string

permissions:
contents: read
packages: read

defaults:
run:
shell: bash

jobs:
L1-tests:
name: Run unit tests and collect coverage
runs-on: ubuntu-latest
steps:
- name: Log in to the Container registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout Code
uses: actions/checkout@v4

- name: Download build directory
uses: actions/download-artifact@v4
with:
name: build-dir
path: ${{ github.workspace }}/build

- name: Run Unit Tests
run: |
chmod +x ${{ github.workspace }}/build/test/utApp
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace ${{ inputs.image_tag }} \
bash -c "set -e && ctest --test-dir build/test"

- name: Generate Coverage Report
run: |
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace ${{ inputs.image_tag }} \
bash -c " \
set -e \
&& cd build \
&& mkdir -p coverage \
&& gcovr -r .. \
--exclude '.*/test/.*\.h' \
--exclude '.*/test/.*\.cpp' \
--decisions \
--medium-threshold 50 --high-threshold 75 \
--lcov coverage/filtered_coverage.info \
--html-details coverage/index.html \
--cobertura coverage.cobertura.xml \
"

- name: Upload L1 coverage artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts-L1-client
path: ${{ github.workspace }}/build/coverage/
if-no-files-found: error

- name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@51cc3a756ddcd398d447c044c02cb6aa83fdae95
with:
filename: build/coverage.cobertura.xml
badge: true
hide_complexity: true
format: markdown
output: both
thresholds: "50 75"
fail_below_min: false

- name: Add coverage summary to job summary
run: cat code-coverage-results.md >> "$GITHUB_STEP_SUMMARY"
141 changes: 141 additions & 0 deletions .github/workflows/L2-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: L2-tests

on:
workflow_call:
inputs:
image_tag:
required: true
type: string
protocol:
required: true
type: string

permissions:
contents: read
packages: read

defaults:
run:
shell: bash

env:
HUSKY: 0
MOCK_PATH: ${{ github.workspace }}/mock-firebolt
MOCK_BRANCH: topic/changes-for-bidirectional
MOCK_SHA1SUM: 1fec7b75190e75ac8ea8ebf9f3e00c0a070b2566

jobs:
component_tests:
name: Run component tests
runs-on: ubuntu-latest
steps:
- name: Log in to the Container registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout Code
uses: actions/checkout@v4

- name: Download build directory
uses: actions/download-artifact@v4
with:
name: build-dir
path: ${{ github.workspace }}/build

- name: Setup Mock-Firebolt
id: setup-mock
uses: ./.github/actions/setup-mock

- name: Restore Mock-Firebolt Cache
uses: actions/cache@v4
with:
path: ${{ env.MOCK_PATH }}
key: ${{ runner.os }}-mock-${{ steps.setup-mock.outputs.deps_cache_key }}

- name: Run Component Tests
run: |
chmod +x ${{ github.workspace }}/build/test/ctApp
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace -v ${{ env.MOCK_PATH }}:/mock ${{ inputs.image_tag }} \
Comment thread
swethasukumarr marked this conversation as resolved.
./.github/scripts/run-component-tests.sh \
--mock /mock \
--protocol ${{ inputs.protocol }} \
--config /workspace/.github/mock-firebolt/config.json \
--openrpc /workspace/docs/openrpc/the-spec/firebolt-open-rpc.json \
--app-openrpc /workspace/docs/openrpc/the-spec/firebolt-app-open-rpc.json \
--test-exe /workspace/build/test/ctApp

- name: Generate Component Coverage
run: |
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace ${{ inputs.image_tag }} \
bash -c " \
set -e \
&& cd build \
&& mkdir -p coverage \
&& gcovr -r .. \
--exclude '.*/test/.*\.h' \
--exclude '.*/test/.*\.cpp' \
--lcov coverage/filtered_coverage.info \
"

- name: Upload L2 coverage artifact
uses: actions/upload-artifact@v4
with:
name: artifacts-L2-client
path: ${{ github.workspace }}/build/coverage/filtered_coverage.info
if-no-files-found: error

api_test_app:
name: Run API test app
runs-on: ubuntu-latest
steps:
- name: Log in to the Container registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout Code
uses: actions/checkout@v4

- name: Download build directory
uses: actions/download-artifact@v4
with:
name: build-dir
path: ${{ github.workspace }}/build

- name: Build api-test-app standalone
run: |
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace ${{ inputs.image_tag }} \
bash -c " \
set -e \
&& cd test/api_test_app \
&& cmake -B build -S . \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_PREFIX_PATH=/workspace/build/install \
&& cmake --build build --parallel \
"

- name: Setup Mock-Firebolt
id: setup-mock
uses: ./.github/actions/setup-mock

- name: Restore Mock-Firebolt Cache
uses: actions/cache@v4
with:
path: ${{ env.MOCK_PATH }}
key: ${{ runner.os }}-mock-${{ steps.setup-mock.outputs.deps_cache_key }}

- name: Run api-test-app with mock server
run: |
chmod +x ${{ github.workspace }}/test/api_test_app/build/api-test-app
docker run --rm --user "$(id -u):$(id -g)" -v ${{ github.workspace }}:/workspace -v ${{ env.MOCK_PATH }}:/mock ${{ inputs.image_tag }} \
./.github/scripts/run-api-test-app.sh \
--mock /mock \
--config /workspace/.github/mock-firebolt/config.json \
--openrpc /workspace/docs/openrpc/the-spec/firebolt-open-rpc.json \
--app-openrpc /workspace/docs/openrpc/the-spec/firebolt-app-open-rpc.json \
--test-exe /workspace/test/api_test_app/build/api-test-app
16 changes: 8 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ on:

jobs:
release:
if: github.ref_type == 'branch' && (github.ref_name == 'main' || endsWith(github.ref_name, '.rc') || endsWith(github.ref_name, '.x-maintenance'))
if: github.ref_type == 'branch' && (github.ref_name == 'main' || endsWith(github.ref_name, '.rc'))
runs-on: ubuntu-latest
concurrency:
group: release
Expand All @@ -46,11 +46,11 @@ jobs:
run: |
branch="${GITHUB_REF_NAME}"
case "$branch" in
main|*.rc|*.*.x-maintenance)
main|*.rc)
echo "Release allowed from branch: $branch"
;;
*)
echo "Release blocked: branch '$branch' is not allowed. Use 'main', a '*.rc' branch, or a '*.*.x-maintenance' branch."
echo "Release blocked: branch '$branch' is not allowed. Use 'main' or a branch ending in '.rc'."
exit 1
;;
esac
Expand Down Expand Up @@ -135,10 +135,10 @@ jobs:
run: |
if [ "$IS_PROMOTION" = "true" ]; then
if ! git merge-base --is-ancestor "${BASELINE}" HEAD; then
echo "ERROR: ${BASELINE} is not an ancestor of HEAD; select a descendant commit from this branch before promoting to stable."
echo "ERROR: ${BASELINE} is neither an ancestor of nor equal to HEAD; promote from a branch containing the RC commit."
exit 1
fi
echo "Promoting ${BASELINE} to stable release; current HEAD is a descendant."
echo "Promoting ${BASELINE} to stable; the RC commit is HEAD or an ancestor of HEAD."
exit 0
fi
# BASELINE may not be a true git ancestor of HEAD (e.g. it was squash-merged
Expand Down Expand Up @@ -170,7 +170,7 @@ jobs:
- name: Create tag
env:
TAG: ${{ steps.version.outputs.next }}
GH_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
GH_TOKEN: ${{ secrets.AUTOMATION_TOKEN }}
shell: bash
run: |
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
Expand All @@ -182,7 +182,7 @@ jobs:
echo "Tag ${TAG} already exists at the current HEAD; resuming the release."
exit 0
fi
# Identity is just for the annotated tag's metadata -- SEMANTIC_RELEASE_TOKEN is
# Identity is just for the annotated tag's metadata -- AUTOMATION_TOKEN is
# fine for this lookup, it doesn't touch ref creation.
author_login="$(gh api user --jq '.login')"
author_name="$(gh api user --jq '.name // .login')"
Expand All @@ -206,7 +206,7 @@ jobs:

- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
GH_TOKEN: ${{ secrets.AUTOMATION_TOKEN }}
TAG: ${{ steps.version.outputs.next }}
IS_RC: ${{ steps.version.outputs.is_rc }}
ARCHIVE: ${{ steps.asset.outputs.archive }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sync-develop-to-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# The sync logic lives in app-gateway-automation/actions/sync-branches.
#
# Optional secret: SEMANTIC_RELEASE_TOKEN (contents:write + pull-requests:write + issues:write)
# Optional secret: AUTOMATION_TOKEN (contents:write + pull-requests:write + issues:write)
# Used for sync operations when present; falls back to github.token if absent.

name: Sync develop to main
Expand Down Expand Up @@ -39,4 +39,4 @@ jobs:
with:
source_branch: ${{ github.event.inputs.source_branch || 'develop' }}
target_branch: ${{ github.event.inputs.target_branch || 'main' }}
token: ${{ secrets.SEMANTIC_RELEASE_TOKEN || github.token }}
token: ${{ secrets.AUTOMATION_TOKEN || github.token }}
Loading
Loading