Add bbsim-tests action

This is for the initial action. Workflow to follow.

Signed-off-by: Eric Ball <eball@linuxfoundation.org>
Change-Id: I70f46f6e8933a2a8a465c7b6b96767e8ee618c97
diff --git a/.github/actions/bbsim-tests/EXTERNAL-USAGE.md b/.github/actions/bbsim-tests/EXTERNAL-USAGE.md
new file mode 100644
index 0000000..e0ca577
--- /dev/null
+++ b/.github/actions/bbsim-tests/EXTERNAL-USAGE.md
@@ -0,0 +1,273 @@
+# Using BBSim Tests Action from External Repositories
+
+## Overview
+
+This action is specifically designed to be used as a **shared/reusable action** from external repositories. All implementation details are self-contained within the `action.yaml` file, with no dependencies on external script files that could cause path resolution issues.
+
+## Why This Matters
+
+When a GitHub Action is called from an external repository (e.g., `uses: opencord/shared-workflows/.github/actions/bbsim-tests@master`), the action runs in the context of the **calling repository**, not the action's repository. This means:
+
+- ❌ Relative paths like `./execute_test.sh` won't work
+- ❌ `${{ github.action_path }}/script.sh` may not be accessible
+- ✅ Inline scripts work perfectly
+- ✅ Scripts created at runtime in `/tmp` work perfectly
+
+## Implementation Approach
+
+### The Problem (Original Approach)
+
+```yaml
+# This DOES NOT work from external repositories
+- name: Execute test
+  shell: bash
+  run: |
+    bash ${{ github.action_path }}/execute_test.sh
+```
+
+When called from `opencord/bbsim` repository, `github.action_path` points to a directory that doesn't exist in the caller's context.
+
+### The Solution (Current Approach)
+
+```yaml
+# This WORKS from external repositories
+- name: Create test execution script
+  shell: bash
+  run: |
+    cat > /tmp/execute_test.sh <<'SCRIPT'
+    #!/bin/bash
+    # ... entire script embedded here ...
+    SCRIPT
+    chmod +x /tmp/execute_test.sh
+
+- name: Execute test
+  shell: bash
+  run: |
+    bash /tmp/execute_test.sh
+```
+
+The script is created dynamically at runtime, ensuring it's always available regardless of where the action is called from.
+
+## Usage Pattern
+
+### From External Repository (Standard Usage)
+
+```yaml
+name: BBSim Tests
+
+on:
+  push:
+    branches: [master]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+**No special steps required!** The action handles everything automatically.
+
+### Repository Setup Required
+
+The calling repository needs:
+- GitHub Actions enabled
+- Sufficient runner resources (minimum 8GB RAM, 2 CPUs)
+- Network access to pull Docker images and Helm charts
+
+The calling repository does **NOT** need:
+- The `shared-workflows` repository checked out locally
+- Any of the action's script files
+- Special permissions or configurations
+
+## What Gets Checked Out
+
+When the action runs, it automatically checks out these repositories into the runner's workspace:
+
+1. **voltha-system-tests** - Contains the Robot Framework test suites
+2. **voltha-helm-charts** - Contains Helm charts for deployment (if using patches or release branches)
+3. **gerrit-project** (optional) - If testing a specific component patch
+
+The calling repository is **not** automatically checked out. If you need files from your repository, add a checkout step:
+
+```yaml
+steps:
+  - name: Checkout calling repository
+    uses: actions/checkout@v4
+    with:
+      path: my-repo
+
+  - name: Run BBSim Tests
+    uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+    with:
+      branch: master
+      test-targets: |
+        ...
+```
+
+## File System Layout
+
+When the action runs, the file system looks like this:
+
+```
+/home/runner/work/
+└── <calling-repo-name>/
+    ├── <calling-repo-name>/        (only if explicitly checked out)
+    ├── voltha-system-tests/        (checked out by action)
+    ├── voltha-helm-charts/         (checked out by action, if needed)
+    ├── <gerrit-project>/           (checked out by action, if specified)
+    ├── bin/                        (created by action for tools)
+    ├── logs/                       (created by action for test results)
+    └── tmp/
+        └── execute_test.sh         (created by action at runtime)
+```
+
+## Environment Variables
+
+The action sets these environment variables automatically:
+
+- `GITHUB_WORKSPACE` - Base workspace directory
+- `KUBECONFIG` - Kubernetes configuration file location
+- `VOLTCONFIG` - VOLTHA CLI configuration location
+- `PATH` - Extended with tool directories
+- `DIAGS_PROFILE` - VOLTHA diagnostics profile
+- `SSHPASS` - ONOS SSH password
+
+All test-specific variables are passed to the execution script via environment.
+
+## Troubleshooting External Usage
+
+### Issue: Action not found
+
+**Error**: `Unable to resolve action 'opencord/shared-workflows/.github/actions/bbsim-tests@master'`
+
+**Solutions**:
+1. Verify the repository path is correct
+2. Check that the action exists at that path in the repository
+3. Ensure you have access to the repository (public or proper permissions)
+4. Try using a specific commit SHA instead of branch name
+
+### Issue: Permission denied errors
+
+**Error**: `Permission denied` when running tests
+
+**Solutions**:
+1. Ensure the runner has Docker access
+2. Check that the runner has sufficient permissions to create kind clusters
+3. Verify network access to required registries
+
+### Issue: Tests can't find voltha-system-tests
+
+**Error**: `No such file or directory: voltha-system-tests`
+
+**Solutions**:
+1. This should not happen - the action checks out the repository automatically
+2. If it does occur, check the action's checkout step logs
+3. Verify network connectivity to GitHub/Gerrit
+
+## Version Pinning
+
+For production use, always pin to a specific version:
+
+### Using Tags (Recommended)
+```yaml
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@v1.0.0
+```
+
+### Using Commit SHA (Most Secure)
+```yaml
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@abc123def456...
+```
+
+### Using Branch (Development Only)
+```yaml
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+```
+
+## Benefits of This Approach
+
+✅ **Portability**: Works from any repository without modifications
+✅ **Self-Contained**: No external dependencies to manage
+✅ **Maintainability**: Single file to update (action.yaml)
+✅ **Testability**: Easy to test from different repositories
+✅ **Security**: No risk of path traversal issues
+✅ **Transparency**: All logic visible in one place
+
+## Real-World Example
+
+See the actual usage in the BBSim repository:
+
+**Repository**: `opencord/bbsim`
+**Workflow File**: `.github/workflows/bbsim-basic-test.yaml`
+
+```yaml
+name: BBSim Tests
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+
+jobs:
+  bbsim-tests:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+This works perfectly without any additional setup!
+
+## Developer Notes
+
+If you're modifying this action:
+
+1. **Never use relative paths** to script files - they won't work from external repos
+2. **Always test from an external repository** before considering the change complete
+3. **Keep all logic in action.yaml** or create files dynamically at runtime
+4. **Use absolute paths** when referring to files created by the action itself
+5. **Document any new inputs** thoroughly in README.md
+
+## Migration from Jenkins
+
+Unlike Jenkins shared libraries, GitHub Actions composite actions run in the caller's context. This means:
+
+| Jenkins | GitHub Actions |
+|---------|----------------|
+| Library code runs in shared space | Action code runs in caller's workspace |
+| Can reference shared files directly | Must inline or create files at runtime |
+| `@Library('shared')` imports | `uses: org/repo/.github/actions/name` |
+| Groovy functions | Composite action steps |
+
+The conversion required embedding all shell script logic directly into the action definition to ensure cross-repository compatibility.
+
+## Support
+
+If you encounter issues using this action from your repository:
+
+1. Check this document first
+2. Review the action's README.md
+3. Examine the workflow logs in GitHub Actions UI
+4. Verify your test-targets YAML syntax
+5. Ensure your runner has sufficient resources
+
+For bugs or feature requests, open an issue in the `shared-workflows` repository.
\ No newline at end of file
diff --git a/.github/actions/bbsim-tests/QUICKSTART.md b/.github/actions/bbsim-tests/QUICKSTART.md
new file mode 100644
index 0000000..1b0c7c0
--- /dev/null
+++ b/.github/actions/bbsim-tests/QUICKSTART.md
@@ -0,0 +1,341 @@
+# BBSim Tests - Quick Start Guide
+
+This guide will help you get started with the BBSim Tests GitHub Action in under 5 minutes.
+
+## Prerequisites
+
+- A GitHub repository with GitHub Actions enabled
+- Basic understanding of VOLTHA and BBSim
+- Sufficient runner resources (minimum 8GB RAM, 2 CPUs)
+
+## Minimal Example
+
+Create `.github/workflows/bbsim-test.yaml` in your repository:
+
+```yaml
+name: BBSim Tests
+
+on:
+  push:
+    branches: [master]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: master
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+That's it! Commit and push this file to trigger your first BBSim test.
+
+**Note**: This action is designed as a **shared/reusable action**. You don't need to check out the `shared-workflows` repository - just reference it directly with `uses:`. All dependencies and scripts are handled automatically.
+
+## What This Does
+
+1. **Installs dependencies**: kubectl, helm, kind, kail, voltctl
+2. **Creates a Kubernetes cluster**: 3-node kind cluster
+3. **Deploys VOLTHA**: Complete VOLTA stack with BBSim
+4. **Runs tests**: Executes the specified Robot Framework tests
+5. **Collects results**: Uploads logs and test reports as artifacts
+
+## Common Test Targets
+
+### DT Workflow
+```yaml
+- target: functional-single-kind-dt
+  workflow: dt
+  flags: ""
+  teardown: true
+  logging: true
+  vgcEnabled: false
+```
+
+### ATT Workflow
+```yaml
+- target: functional-single-kind-att
+  workflow: att
+  flags: ""
+  teardown: true
+  logging: true
+  vgcEnabled: false
+```
+
+### TT Workflow
+```yaml
+- target: functional-single-kind-tt
+  workflow: tt
+  flags: ""
+  teardown: true
+  logging: true
+  vgcEnabled: false
+```
+
+### Sanity Tests (Quick)
+```yaml
+- target: sanity-single-kind
+  workflow: dt
+  flags: ""
+  teardown: true
+  logging: true
+  vgcEnabled: false
+```
+
+## Running Multiple Tests
+
+Just add more entries to the `test-targets` list:
+
+```yaml
+test-targets: |
+  - target: sanity-single-kind
+    workflow: dt
+    flags: ""
+    teardown: true
+    logging: true
+    vgcEnabled: false
+  - target: functional-single-kind-dt
+    workflow: dt
+    flags: ""
+    teardown: true
+    logging: true
+    vgcEnabled: false
+```
+
+## Enabling Debug Logging
+
+Set `log-level` to `DEBUG`:
+
+```yaml
+- name: Run BBSim Tests
+  uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+  with:
+    branch: master
+    log-level: DEBUG
+    test-targets: |
+      ...
+```
+
+## Enabling Monitoring
+
+Add `with-monitoring: true` to collect memory consumption metrics:
+
+```yaml
+- name: Run BBSim Tests
+  uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+  with:
+    branch: master
+    with-monitoring: true
+    test-targets: |
+      ...
+```
+
+## Testing Multiple OLTs
+
+Set the `olts` parameter:
+
+```yaml
+- name: Run BBSim Tests
+  uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+  with:
+    branch: master
+    olts: "2"
+    test-targets: |
+      ...
+```
+
+## Viewing Results
+
+After the workflow completes:
+
+1. Go to the **Actions** tab in your repository
+2. Click on the workflow run
+3. Scroll to the bottom to see **Artifacts**
+4. Download the test results artifact
+5. Open `report.html` in a browser to see the Robot Framework report
+
+## Manual Trigger
+
+Add `workflow_dispatch` to trigger tests manually:
+
+```yaml
+name: BBSim Tests
+
+on:
+  workflow_dispatch:
+    inputs:
+      branch:
+        description: 'Branch to test'
+        required: true
+        default: 'master'
+      log_level:
+        description: 'Log level'
+        required: false
+        default: 'WARN'
+        type: choice
+        options:
+          - DEBUG
+          - INFO
+          - WARN
+          - ERROR
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ inputs.branch }}
+          log-level: ${{ inputs.log_level }}
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+Now you can trigger tests from the GitHub UI!
+
+## Troubleshooting
+
+### Test Fails Immediately
+
+Check that your runner has enough resources:
+- Minimum: 8GB RAM, 2 CPUs
+- Recommended: 16GB RAM, 4 CPUs
+
+Free up disk space before running:
+```yaml
+- name: Free disk space
+  run: |
+    sudo rm -rf /usr/share/dotnet
+    sudo rm -rf /opt/ghc
+    sudo rm -rf /usr/local/share/boost
+    sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+```
+
+### Cluster Creation Fails
+
+The kind cluster name might conflict. Try a unique name:
+```yaml
+with:
+  cluster-name: "my-test-cluster"
+```
+
+### Tests Time Out
+
+Increase the job timeout:
+```yaml
+jobs:
+  test:
+    timeout-minutes: 480  # 8 hours
+```
+
+### Need Help?
+
+1. Check the [README.md](README.md) for detailed documentation
+1. Look at [example-workflow.yaml](example-workflow.yaml) for complete examples
+1. Check GitHub Actions logs for specific error messages
+
+## Important Notes
+
+### External Usage
+This action is designed to be called from external repositories. You don't need to:
+- ❌ Check out the `shared-workflows` repository
+- ❌ Copy any script files
+- ❌ Install dependencies manually
+
+The action is **fully self-contained** and handles everything automatically.
+
+### Version Pinning
+For production use, pin to a specific version:
+```yaml
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@v1.0.0  # Recommended
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@abc123  # Most secure
+uses: opencord/shared-workflows/.github/actions/bbsim-tests@main  # Development only
+```
+
+## Next Steps
+
+- **Customize**: Adjust parameters for your specific needs
+- **Schedule**: Add `schedule` triggers for nightly tests
+- **Integrate**: Add to PR workflows for automated testing
+- **Monitor**: Enable monitoring to track resource usage
+- **Optimize**: Adjust timeouts and resource limits
+- **Read**: Check [EXTERNAL-USAGE.md](EXTERNAL-USAGE.md) for detailed information
+
+## Full Example with All Options
+
+```yaml
+name: Complete BBSim Tests
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+  schedule:
+    - cron: '0 2 * * *'
+  workflow_dispatch:
+
+jobs:
+  bbsim-tests:
+    runs-on: ubuntu-latest
+    timeout-minutes: 360
+    
+    steps:
+      - name: Free up disk space
+        run: |
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+      
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: master
+          log-level: INFO
+          cluster-name: kind-ci
+          docker-registry: linuxfoundation.jfrog.io/voltha-docker
+          olts: "2"
+          with-monitoring: true
+          enable-mac-learning: false
+          extra-helm-flags: "--set global.some_option=value"
+          extra-robot-args: "-v some_var:some_value"
+          test-targets: |
+            - target: sanity-single-kind
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+      
+      - name: Upload results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+```
+
+---
+
+**Happy Testing!** 🚀
diff --git a/.github/actions/bbsim-tests/README.md b/.github/actions/bbsim-tests/README.md
new file mode 100644
index 0000000..4731187
--- /dev/null
+++ b/.github/actions/bbsim-tests/README.md
@@ -0,0 +1,404 @@
+# BBSim Tests GitHub Action
+
+This action runs VOLTHA end-to-end tests using BBSim (Broadband Simulator) to simulate OLT/ONU devices. It's a conversion of the Jenkins pipeline from `ci-management/jjb/pipeline/voltha/bbsim-tests.groovy` to GitHub Actions.
+
+## Important Note
+
+This action is designed to be used as a **shared/reusable action** from external repositories. All test execution logic is embedded directly in the `action.yaml` file (no external script dependencies), making it compatible with GitHub Actions' composite action model when called from other repositories.
+
+## Overview
+
+The action performs the following steps:
+
+1. **Setup Environment**: Installs all required dependencies (kubectl, helm, kind, kail, voltctl)
+2. **Checkout Repositories**: Clones voltha-system-tests and voltha-helm-charts
+3. **Build Components**: Optionally builds VOLTHA components from Gerrit patches
+4. **Create Kubernetes Cluster**: Sets up a kind cluster for testing
+5. **Deploy Infrastructure**: Deploys VOLTHA, ONOS, and BBSim instances
+6. **Run Tests**: Executes Robot Framework test suites
+7. **Collect Artifacts**: Gathers logs, test results, and metrics
+
+## Inputs
+
+### Required Inputs
+
+- **`branch`**: Branch to test (e.g., `master`, `voltha-2.15`)
+- **`test-targets`**: YAML string defining test targets to run (see format below)
+
+### Optional Inputs
+
+- **`gerrit-project`**: Gerrit project name if building a patch (default: `""`)
+- **`gerrit-refspec`**: Gerrit refspec if building a patch (default: `""`)
+- **`voltha-system-tests-change`**: Gerrit change number for voltha-system-tests (default: `""`)
+- **`voltha-helm-charts-change`**: Gerrit change number for voltha-helm-charts (default: `""`)
+- **`extra-helm-flags`**: Additional Helm flags for deployment (default: `""`)
+- **`log-level`**: Log level for VOLTHA components: DEBUG, INFO, WARN, ERROR (default: `"WARN"`)
+- **`timeout`**: Timeout in minutes for the entire action (default: `"240"`)
+- **`cluster-name`**: Name of the kind cluster (default: `"kind-ci"`)
+- **`docker-registry`**: Docker registry to use (default: `"linuxfoundation.jfrog.io/voltha-docker"`)
+- **`olts`**: Number of OLTs to simulate (default: `"1"`)
+- **`with-monitoring`**: Enable monitoring with Prometheus (default: `false`)
+- **`enable-mac-learning`**: Enable MAC learning in VOLTHA (default: `false`)
+- **`extra-robot-args`**: Additional arguments for Robot Framework (default: `""`)
+
+## Outputs
+
+- **`test-results`**: Path to test results directory
+
+## Test Targets Format
+
+The `test-targets` input should be a YAML array with the following structure:
+
+```yaml
+- target: functional-single-kind-dt
+  workflow: dt
+  flags: "--set someFlag=value"
+  teardown: true
+  logging: true
+  vgcEnabled: false
+- target: functional-single-kind-att
+  workflow: att
+  flags: ""
+  teardown: true
+  logging: true
+  vgcEnabled: false
+```
+
+### Test Target Fields
+
+- **`target`**: Make target from voltha-system-tests (e.g., `functional-single-kind-dt`)
+- **`workflow`**: VOLTHA workflow type (e.g., `dt`, `att`, `tt`)
+- **`flags`**: Additional Helm flags specific to this test (optional)
+- **`teardown`**: Whether to tear down and redeploy VOLTHA (default: `true`)
+- **`logging`**: Enable test logging (default: `true`)
+- **`vgcEnabled`**: Enable VOLTHA Go Controller (default: `false`)
+
+## Usage Examples
+
+### Basic Usage (from External Repository)
+
+This is the most common usage pattern - calling the action from another repository:
+
+```yaml
+name: BBSim Tests
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+
+jobs:
+  bbsim-tests:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+**Note**: Replace `@master` with a specific version tag or commit SHA for production use.
+
+### Usage from Local Repository
+
+If you're developing or testing the action within the `shared-workflows` repository itself:
+
+```yaml
+jobs:
+  bbsim-tests:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+### Advanced Usage with Multiple Tests
+
+```yaml
+name: BBSim Multi-Workflow Tests
+
+on:
+  schedule:
+    - cron: '0 2 * * *'  # Run nightly
+
+jobs:
+  bbsim-tests:
+    runs-on: ubuntu-latest
+    timeout-minutes: 300
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Run BBSim Tests
+        uses: ./.github/actions/bbsim-tests
+        with:
+          branch: master
+          log-level: INFO
+          olts: "2"
+          with-monitoring: true
+          extra-helm-flags: "--set global.some_option=true"
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-att
+              workflow: att
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-tt
+              workflow: tt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Publish Test Results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-results
+          path: logs/
+```
+
+### Testing a Gerrit Patch
+
+```yaml
+name: Test Gerrit Patch
+
+on:
+  workflow_dispatch:
+    inputs:
+      gerrit_project:
+        description: 'Gerrit project name'
+        required: true
+        type: string
+      gerrit_refspec:
+        description: 'Gerrit refspec'
+        required: true
+        type: string
+
+jobs:
+  test-patch:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests with Patch
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          gerrit-project: ${{ inputs.gerrit_project }}
+          gerrit-refspec: ${{ inputs.gerrit_refspec }}
+          test-targets: |
+            - target: sanity-single-kind
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+### Testing with voltha-system-tests Changes
+
+```yaml
+name: Test System Tests Changes
+
+on:
+  pull_request:
+    paths:
+      - 'tests/**'
+
+jobs:
+  test-changes:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@master
+        with:
+          branch: master
+          voltha-system-tests-change: "refs/changes/12/34512/1"
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+```
+
+## How It Works
+
+This action is implemented as a **composite action** with all logic embedded directly in the `action.yaml` file. When called from an external repository:
+
+1. The test execution script is created dynamically at runtime in `/tmp/execute_test.sh`
+2. No local file paths or relative references are needed
+3. All dependencies are installed fresh in each run
+4. The action is fully self-contained and portable
+
+This design ensures the action works correctly whether called from:
+- External repositories (most common use case)
+- The shared-workflows repository itself (for development/testing)
+- Forked repositories
+
+## Dependencies
+
+This action automatically installs the following dependencies:
+
+- **kubectl**: Kubernetes CLI
+- **helm**: Kubernetes package manager
+- **kind**: Kubernetes in Docker (for local cluster)
+- **kail**: Kubernetes log viewer
+- **voltctl**: VOLTHA CLI tool
+- **Python 3**: For Robot Framework tests
+- **System packages**: curl, wget, git, make, jq, sshpass, rsync
+
+## Artifacts
+
+The action automatically uploads the following artifacts:
+
+- Robot Framework test reports (HTML)
+- Robot Framework test logs
+- Robot Framework output XML
+- VOLTHA pod logs
+- Kubernetes events and pod information
+- Memory consumption metrics (if monitoring enabled)
+- Combined startup logs (compressed)
+
+Artifacts are retained for 30 days and can be downloaded from the GitHub Actions UI.
+
+## Architecture
+
+### Components
+
+1. **BBSim**: Simulates OLT/ONU devices
+2. **VOLTHA**: Open source VOLT management system
+3. **ONOS**: SDN controller
+4. **Kafka**: Message broker for VOLTHA
+5. **ETCD**: Key-value store for VOLTHA
+6. **Robot Framework**: Test automation framework
+
+### Namespaces
+
+- **default**: Infrastructure components (optional monitoring)
+- **voltha**: VOLTHA stack and BBSim instances
+
+### Port Forwarding
+
+The action sets up the following port forwards:
+
+- **9092**: Kafka (voltha-infra-kafka)
+- **30115**: ONOS SSH
+- **30120**: ONOS REST API
+- **31301**: Prometheus (if monitoring enabled)
+- **50075+**: BBSim DMI ports (one per OLT instance)
+- **8181**: VOLTHA Go Controller (if VGC enabled)
+
+## Troubleshooting
+
+### Tests Fail to Start
+
+- Check that the cluster was created successfully
+- Verify that all pods are running: `kubectl get pods -A`
+- Check pod logs: `kubectl logs -n voltha <pod-name>`
+
+### Timeout Issues
+
+- Increase the `timeout` input value
+- Check resource constraints on the runner
+- Verify network connectivity
+
+### Image Pull Errors
+
+- Verify the `docker-registry` input is correct
+- Check if images exist for the specified branch
+- Ensure the runner has network access to the registry
+
+### Port Forward Issues
+
+Port forwards may fail if:
+- Ports are already in use
+- Pods are not ready
+- Network policies block access
+
+Check port-forward processes: `ps aux | grep port-forward`
+
+### Test Failures
+
+1. Check the Robot Framework logs in artifacts
+2. Review VOLTHA pod logs
+3. Check Kubernetes events
+4. Verify test configuration matches the workflow
+
+## Migration from Jenkins
+
+This action replaces the Jenkins pipeline `bbsim-tests.groovy`. Key differences:
+
+1. **No shared library**: All functionality is self-contained
+2. **Declarative syntax**: Uses GitHub Actions YAML instead of Groovy
+3. **Native artifact handling**: Uses GitHub Actions artifact upload
+4. **Simplified configuration**: Direct input parameters instead of Jenkins parameters
+5. **Better isolation**: Each run gets a fresh environment
+
+### Parameter Mapping
+
+| Jenkins Parameter | GitHub Action Input |
+|------------------|---------------------|
+| `branch` | `branch` |
+| `testTargets` | `test-targets` |
+| `gerritProject` | `gerrit-project` |
+| `gerritRefspec` | `gerrit-refspec` |
+| `volthaSystemTestsChange` | `voltha-system-tests-change` |
+| `volthaHelmChartsChange` | `voltha-helm-charts-change` |
+| `extraHelmFlags` | `extra-helm-flags` |
+| `logLevel` | `log-level` |
+| `timeout` | `timeout` |
+| `registry` | `docker-registry` |
+| `olts` | `olts` |
+| `withMonitoring` | `with-monitoring` |
+| `enableMacLearning` | `enable-mac-learning` |
+| `extraRobotArgs` | `extra-robot-args` |
+
+## Contributing
+
+When modifying this action:
+
+1. Test changes locally using [act](https://github.com/nektos/act)
+2. Update this README with any new inputs or behavior changes
+3. Follow the existing code structure and style
+4. Add comments for complex logic
+5. Update version numbers appropriately
+
+## License
+
+Copyright 2026 The Linux Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/.github/actions/bbsim-tests/action.yaml b/.github/actions/bbsim-tests/action.yaml
new file mode 100644
index 0000000..72803ac
--- /dev/null
+++ b/.github/actions/bbsim-tests/action.yaml
@@ -0,0 +1,734 @@
+---
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: 2026 Open Networking Foundation Contributors
+
+name: "bbsim-tests"
+description: "VOLTHA BBSim E2E Tests - Simulates OLT/ONUs using bbsim and runs robot framework tests"
+
+inputs:
+  branch:
+    description: "Branch to test (master, voltha-2.15, etc.)"
+    required: true
+    type: string
+  test-targets:
+    description: "YAML string defining test targets to run"
+    required: true
+    type: string
+  gerrit-project:
+    description: "Gerrit project name if building a patch"
+    required: false
+    type: string
+    default: ""
+  gerrit-refspec:
+    description: "Gerrit refspec if building a patch"
+    required: false
+    type: string
+    default: ""
+  voltha-system-tests-change:
+    description: "Gerrit change number for voltha-system-tests"
+    required: false
+    type: string
+    default: ""
+  voltha-helm-charts-change:
+    description: "Gerrit change number for voltha-helm-charts"
+    required: false
+    type: string
+    default: ""
+  extra-helm-flags:
+    description: "Additional Helm flags for deployment"
+    required: false
+    type: string
+    default: ""
+  log-level:
+    description: "Log level for VOLTHA components (DEBUG, INFO, WARN, ERROR)"
+    required: false
+    type: string
+    default: "WARN"
+  timeout:
+    description: "Timeout in minutes for the entire action"
+    required: false
+    type: string
+    default: "240"
+  cluster-name:
+    description: "Name of the kind cluster"
+    required: false
+    type: string
+    default: "kind-ci"
+  docker-registry:
+    description: "Docker registry to use"
+    required: false
+    type: string
+    default: "linuxfoundation.jfrog.io/voltha-docker"
+  olts:
+    description: "Number of OLTs to simulate"
+    required: false
+    type: string
+    default: "1"
+  with-monitoring:
+    description: "Enable monitoring with prometheus"
+    required: false
+    type: boolean
+    default: false
+  enable-mac-learning:
+    description: "Enable MAC learning in VOLTHA"
+    required: false
+    type: boolean
+    default: false
+  extra-robot-args:
+    description: "Additional arguments for Robot Framework"
+    required: false
+    type: string
+    default: ""
+
+outputs:
+  test-results:
+    description: "Path to test results"
+    value: ${{ steps.test-execution.outputs.results-path }}
+
+runs:
+  using: "composite"
+  steps:
+    # -----------------------------------------------------------------------
+    # Setup environment variables
+    # -----------------------------------------------------------------------
+    - name: Setup environment
+      shell: bash
+      run: |
+        echo "KUBECONFIG=$HOME/.kube/kind-${{ inputs.cluster-name }}" >> $GITHUB_ENV
+        echo "VOLTCONFIG=$HOME/.volt/config" >> $GITHUB_ENV
+        echo "PATH=$PATH:$GITHUB_WORKSPACE/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" >> $GITHUB_ENV
+        echo "DIAGS_PROFILE=VOLTHA_PROFILE" >> $GITHUB_ENV
+        echo "SSHPASS=karaf" >> $GITHUB_ENV
+        mkdir -p $GITHUB_WORKSPACE/bin
+
+    # -----------------------------------------------------------------------
+    # Install dependencies
+    # -----------------------------------------------------------------------
+    - name: Install system dependencies
+      shell: bash
+      run: |
+        echo "Installing system dependencies..."
+        sudo apt-get update
+        sudo apt-get install -y \
+          curl \
+          wget \
+          git \
+          make \
+          jq \
+          sshpass \
+          python3 \
+          python3-pip \
+          python3-venv \
+          rsync
+
+    # -----------------------------------------------------------------------
+    # Install kubectl
+    # -----------------------------------------------------------------------
+    - name: Install kubectl
+      shell: bash
+      run: |
+        echo "Installing kubectl..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kubectl" ]; then
+          KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
+          curl -Lo "$GITHUB_WORKSPACE/bin/kubectl" \
+            "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
+          chmod +x "$GITHUB_WORKSPACE/bin/kubectl"
+        fi
+        kubectl version --client
+
+    # -----------------------------------------------------------------------
+    # Install Helm
+    # -----------------------------------------------------------------------
+    - name: Install Helm
+      shell: bash
+      run: |
+        echo "Installing Helm..."
+        if ! command -v helm &> /dev/null; then
+          curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
+        fi
+        helm version
+
+    # -----------------------------------------------------------------------
+    # Install kind
+    # -----------------------------------------------------------------------
+    - name: Install kind
+      shell: bash
+      run: |
+        echo "Installing kind..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kind" ]; then
+          KIND_VERSION="v0.20.0"
+          curl -Lo "$GITHUB_WORKSPACE/bin/kind" \
+            "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64"
+          chmod +x "$GITHUB_WORKSPACE/bin/kind"
+        fi
+        kind version
+
+    # -----------------------------------------------------------------------
+    # Install kail
+    # -----------------------------------------------------------------------
+    - name: Install kail
+      shell: bash
+      run: |
+        echo "Installing kail..."
+        if [ ! -f "$GITHUB_WORKSPACE/bin/kail" ]; then
+          KAIL_VERSION="v0.17.4"
+          wget -O /tmp/kail.tar.gz \
+            "https://github.com/boz/kail/releases/download/${KAIL_VERSION}/kail_${KAIL_VERSION}_linux_amd64.tar.gz"
+          tar -xzf /tmp/kail.tar.gz -C "$GITHUB_WORKSPACE/bin" kail
+          chmod +x "$GITHUB_WORKSPACE/bin/kail"
+          rm /tmp/kail.tar.gz
+        fi
+
+    # -----------------------------------------------------------------------
+    # Install voltctl
+    # -----------------------------------------------------------------------
+    - name: Install voltctl
+      shell: bash
+      run: |
+        echo "Installing voltctl..."
+        if [ "${{ inputs.gerrit-project }}" != "voltctl" ]; then
+          if [ ! -f "$GITHUB_WORKSPACE/bin/voltctl" ]; then
+            VOLTCTL_VERSION="1.8.45"
+            curl -Lo "$GITHUB_WORKSPACE/bin/voltctl" \
+              "https://github.com/opencord/voltctl/releases/download/v${VOLTCTL_VERSION}/voltctl-${VOLTCTL_VERSION}-linux-amd64"
+            chmod +x "$GITHUB_WORKSPACE/bin/voltctl"
+          fi
+        fi
+
+    # -----------------------------------------------------------------------
+    # Checkout VOLTHA repositories
+    # -----------------------------------------------------------------------
+    - name: Checkout voltha-system-tests
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/voltha-system-tests
+        ref: ${{ inputs.branch }}
+        path: voltha-system-tests
+
+    - name: Apply voltha-system-tests patch
+      if: inputs.voltha-system-tests-change != ''
+      shell: bash
+      working-directory: voltha-system-tests
+      run: |
+        echo "Applying voltha-system-tests change ${{ inputs.voltha-system-tests-change }}"
+        git fetch https://gerrit.lfbroadband.org/voltha-system-tests \
+          refs/changes/${{ inputs.voltha-system-tests-change }}
+        git checkout FETCH_HEAD
+
+    - name: Checkout voltha-helm-charts
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/voltha-helm-charts
+        ref: ${{ inputs.branch }}
+        path: voltha-helm-charts
+
+    - name: Apply voltha-helm-charts patch
+      if: inputs.voltha-helm-charts-change != ''
+      shell: bash
+      working-directory: voltha-helm-charts
+      run: |
+        echo "Applying voltha-helm-charts change ${{ inputs.voltha-helm-charts-change }}"
+        git fetch https://gerrit.lfbroadband.org/voltha-helm-charts \
+          refs/changes/${{ inputs.voltha-helm-charts-change }}
+        git checkout FETCH_HEAD
+
+    # -----------------------------------------------------------------------
+    # Checkout and build gerrit project if specified
+    # -----------------------------------------------------------------------
+    - name: Checkout gerrit project
+      if: inputs.gerrit-project != ''
+      uses: actions/checkout@v4
+      with:
+        repository: opencord/${{ inputs.gerrit-project }}
+        ref: ${{ inputs.gerrit-refspec }}
+        path: ${{ inputs.gerrit-project }}
+
+    - name: Build gerrit project
+      if: inputs.gerrit-project != ''
+      shell: bash
+      working-directory: ${{ inputs.gerrit-project }}
+      run: |
+        echo "Building ${{ inputs.gerrit-project }}..."
+        if [ -f "Makefile" ]; then
+          make docker-build
+        fi
+
+    - name: Build and install voltctl from source
+      if: inputs.gerrit-project == 'voltctl'
+      shell: bash
+      working-directory: voltctl
+      run: |
+        echo "Building voltctl from source..."
+        make build
+        cp voltctl "$GITHUB_WORKSPACE/bin/"
+        chmod +x "$GITHUB_WORKSPACE/bin/voltctl"
+
+    # -----------------------------------------------------------------------
+    # Create kind cluster
+    # -----------------------------------------------------------------------
+    - name: Check if kind cluster exists
+      id: cluster-check
+      shell: bash
+      run: |
+        if kind get clusters | grep -q "^${{ inputs.cluster-name }}$"; then
+          echo "exists=true" >> $GITHUB_OUTPUT
+        else
+          echo "exists=false" >> $GITHUB_OUTPUT
+        fi
+
+    - name: Create kind cluster
+      if: steps.cluster-check.outputs.exists == 'false'
+      shell: bash
+      run: |
+        echo "Creating kind cluster ${{ inputs.cluster-name }}..."
+        cat <<EOF > /tmp/kind-config.yaml
+        kind: Cluster
+        apiVersion: kind.x-k8s.io/v1alpha4
+        nodes:
+          - role: control-plane
+          - role: worker
+          - role: worker
+        EOF
+
+        kind create cluster --name ${{ inputs.cluster-name }} --config /tmp/kind-config.yaml
+
+        # Configure kubeconfig
+        mkdir -p $HOME/.kube
+        kind export kubeconfig --name ${{ inputs.cluster-name }} --kubeconfig $HOME/.kube/kind-${{ inputs.cluster-name }}
+
+        # Wait for cluster to be ready
+        kubectl wait --for=condition=Ready nodes --all --timeout=300s
+
+    # -----------------------------------------------------------------------
+    # Load images to kind if building a component
+    # -----------------------------------------------------------------------
+    - name: Load images to kind
+      if: inputs.gerrit-project != '' && inputs.gerrit-project != 'voltctl'
+      shell: bash
+      run: |
+        echo "Loading images to kind cluster..."
+        PROJECT="${{ inputs.gerrit-project }}"
+        IMAGE_NAME="${PROJECT//-/_}"
+
+        # Find the built image
+        IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "voltha/${PROJECT}|opencord/${PROJECT}" | head -1)
+
+        if [ -n "$IMAGE" ]; then
+          echo "Loading image: $IMAGE"
+          kind load docker-image "$IMAGE" --name ${{ inputs.cluster-name }}
+        else
+          echo "Warning: No image found for project $PROJECT"
+        fi
+
+    # -----------------------------------------------------------------------
+    # Setup Helm repositories
+    # -----------------------------------------------------------------------
+    - name: Setup Helm repositories
+      shell: bash
+      run: |
+        echo "Adding Helm repositories..."
+        helm repo add onf https://charts.lfbroadband.org
+        helm repo add cord https://charts.lfbroadband.org
+        helm repo update
+
+    # -----------------------------------------------------------------------
+    # Run tests
+    # -----------------------------------------------------------------------
+    - name: Install PyYAML for test parsing
+      shell: bash
+      run: |
+        pip3 install pyyaml
+
+    - name: Create test execution script
+      shell: bash
+      run: |
+        # Create the test execution script inline
+        cat > /tmp/execute_test.sh <<'EXECUTE_TEST_SCRIPT'
+        #!/bin/bash
+        set -euo pipefail
+
+        # Get environment variables
+        TEST_TARGET="${TEST_TARGET:-functional-single-kind-dt}"
+        WORKFLOW="${WORKFLOW:-dt}"
+        TEST_FLAGS="${TEST_FLAGS:-}"
+        TEARDOWN="${TEARDOWN:-true}"
+        TEST_LOGGING="${TEST_LOGGING:-True}"
+        VGC_ENABLED="${VGC_ENABLED:-false}"
+        INFRA_NAMESPACE="${INFRA_NAMESPACE:-default}"
+        VOLTHA_NAMESPACE="${VOLTHA_NAMESPACE:-voltha}"
+        LOGS_DIR="${LOGS_DIR:-$GITHUB_WORKSPACE/logs}"
+        EXTRA_HELM_FLAGS="${EXTRA_HELM_FLAGS:-}"
+        LOG_LEVEL="${LOG_LEVEL:-WARN}"
+        DOCKER_REGISTRY="${DOCKER_REGISTRY:-linuxfoundation.jfrog.io/voltha-docker}"
+        OLTS="${OLTS:-1}"
+        WITH_MONITORING="${WITH_MONITORING:-false}"
+        ENABLE_MAC_LEARNING="${ENABLE_MAC_LEARNING:-false}"
+        EXTRA_ROBOT_ARGS="${EXTRA_ROBOT_ARGS:-}"
+        BRANCH="${BRANCH:-master}"
+        GERRIT_PROJECT="${GERRIT_PROJECT:-}"
+
+        # Helper functions
+        banner() {
+            echo ""
+            echo "** -----------------------------------------------------------------------"
+            echo "** $1"
+            echo "** -----------------------------------------------------------------------"
+            echo ""
+        }
+
+        cleanup_port_forward() {
+            echo "Cleaning up port-forward processes..."
+            pkill -f "kubectl.*port-forward" || true
+        }
+
+        get_pods_info() {
+            local logs_dir=$1
+            mkdir -p "$logs_dir"
+            kubectl get pods --all-namespaces -o wide > "$logs_dir/pods.txt" || true
+            kubectl get nodes -o wide > "$logs_dir/nodes.txt" || true
+            kubectl describe pods -n "$VOLTHA_NAMESPACE" > "$logs_dir/voltha-pods-describe.txt" || true
+            kubectl describe pods -n "$INFRA_NAMESPACE" > "$logs_dir/infra-pods-describe.txt" || true
+        }
+
+        # Stage: Cleanup (if teardown enabled)
+        if [ "$TEARDOWN" = "true" ]; then
+            banner "Cleanup - Helm Teardown"
+            cleanup_port_forward
+            for namespace in default "$INFRA_NAMESPACE" "$VOLTHA_NAMESPACE"; do
+                echo "Cleaning up Helm releases in namespace: $namespace"
+                helm list -n "$namespace" -q | xargs -r helm uninstall -n "$namespace" || true
+            done
+            echo "Waiting for pods to terminate..."
+            kubectl wait --for=delete pods --all -n "$VOLTHA_NAMESPACE" --timeout=120s || true
+            kubectl wait --for=delete pods --all -n "$INFRA_NAMESPACE" --timeout=120s || true
+        fi
+
+        # Stage: Deploy Common Infrastructure
+        banner "Deploying Common Infrastructure"
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Deploying monitoring stack..."
+            helm install nem-monitoring onf/nem-monitoring \
+                --set prometheus.alertmanager.enabled=false \
+                --set prometheus.pushgateway.enabled=false \
+                --set kpi_exporter.enabled=false \
+                --set dashboards.xos=false \
+                --set dashboards.onos=false \
+                --set dashboards.aaa=false \
+                --set dashboards.voltha=false \
+                --wait || true
+        fi
+
+        # Stage: Deploy VOLTHA
+        if [ "$TEARDOWN" = "true" ]; then
+            banner "Deploying VOLTHA"
+            ONOS_LOG="${LOGS_DIR}/onos-voltha-startup-combined.log"
+            mkdir -p "$LOGS_DIR"
+            touch "$ONOS_LOG"
+            _TAG=kail-startup kail -n "${INFRA_NAMESPACE}" -n "${VOLTHA_NAMESPACE}" > "$ONOS_LOG" &
+            KAIL_PID=$!
+
+            LOCAL_CHARTS="false"
+            if [ -d "$GITHUB_WORKSPACE/voltha-helm-charts" ]; then
+                LOCAL_CHARTS="true"
+            fi
+
+            HELM_FLAGS="--set global.log_level=${LOG_LEVEL^^}"
+            if [ "$VGC_ENABLED" != "true" ]; then
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosSshPort=30115"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosApiPort=30120"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.onosOfPort=31653"
+                HELM_FLAGS="$HELM_FLAGS --set onos-classic.individualOpenFlowNodePorts=true"
+            fi
+            if [ -n "$EXTRA_HELM_FLAGS" ]; then
+                HELM_FLAGS="$HELM_FLAGS $EXTRA_HELM_FLAGS"
+            fi
+            if [ -n "$TEST_FLAGS" ]; then
+                HELM_FLAGS="$HELM_FLAGS $TEST_FLAGS"
+            fi
+
+            if [ -n "$GERRIT_PROJECT" ] && [ "$GERRIT_PROJECT" != "voltctl" ]; then
+                IMAGE_TAG="citest"
+                case "$GERRIT_PROJECT" in
+                    voltha-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha.image.tag=$IMAGE_TAG"
+                        ;;
+                    voltha-openolt-adapter)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha-adapter-openolt.image.tag=$IMAGE_TAG"
+                        ;;
+                    voltha-openonu-adapter-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha-adapter-openonu.image.tag=$IMAGE_TAG"
+                        ;;
+                    ofagent-go)
+                        HELM_FLAGS="$HELM_FLAGS --set voltha.ofagent.image.tag=$IMAGE_TAG"
+                        ;;
+                    bbsim)
+                        HELM_FLAGS="$HELM_FLAGS --set bbsim.image.tag=$IMAGE_TAG"
+                        ;;
+                esac
+            fi
+
+            echo "Deploying VOLTHA with workflow: $WORKFLOW"
+            echo "Helm flags: $HELM_FLAGS"
+
+            if [ "$LOCAL_CHARTS" = "true" ]; then
+                echo "Using local charts from $GITHUB_WORKSPACE/voltha-helm-charts"
+                helm dependency update "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-infra" || true
+                helm dependency update "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-stack" || true
+                helm install voltha-infra "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-infra" \
+                    -n "$INFRA_NAMESPACE" --create-namespace --wait --timeout 120m
+                helm install voltha "$GITHUB_WORKSPACE/voltha-helm-charts/voltha-stack" \
+                    -n "$VOLTHA_NAMESPACE" --create-namespace \
+                    --set global.stack_name=voltha \
+                    --set global.voltha_infra_name=voltha-infra \
+                    --set global.voltha_infra_namespace="$INFRA_NAMESPACE" \
+                    $(echo "$HELM_FLAGS") --wait --timeout 120m
+            else
+                echo "Using charts from Helm repository"
+                helm install voltha-infra onf/voltha-infra \
+                    -n "$INFRA_NAMESPACE" --create-namespace --wait --timeout 120m
+                helm install voltha onf/voltha-stack \
+                    -n "$VOLTHA_NAMESPACE" --create-namespace \
+                    --set global.stack_name=voltha \
+                    --set global.voltha_infra_name=voltha-infra \
+                    --set global.voltha_infra_namespace="$INFRA_NAMESPACE" \
+                    $(echo "$HELM_FLAGS") --wait --timeout 120m
+            fi
+
+            for i in $(seq 0 $((OLTS - 1))); do
+                echo "Deploying bbsim${i}..."
+                if [ "$LOCAL_CHARTS" = "true" ]; then
+                    helm install "bbsim${i}" "$GITHUB_WORKSPACE/voltha-helm-charts/bbsim" \
+                        -n "$VOLTHA_NAMESPACE" --set olt_id="${i}" --wait || true
+                else
+                    helm install "bbsim${i}" onf/bbsim \
+                        -n "$VOLTHA_NAMESPACE" --set olt_id="${i}" --wait || true
+                fi
+            done
+
+            if [ -n "${KAIL_PID:-}" ]; then
+                kill "$KAIL_PID" || true
+                wait "$KAIL_PID" 2>/dev/null || true
+            fi
+            gzip -f "$ONOS_LOG" || true
+
+            echo "Setting up port forwarding..."
+            kubectl port-forward --address 0.0.0.0 -n "$INFRA_NAMESPACE" svc/voltha-infra-kafka 9092:9092 &
+            bbsim_dmi_port=50075
+            for i in $(seq 0 $((OLTS - 1))); do
+                kubectl port-forward --address 0.0.0.0 -n "$VOLTHA_NAMESPACE" "svc/bbsim${i}" "${bbsim_dmi_port}:50075" &
+                ((bbsim_dmi_port++))
+            done
+            if [ "$WITH_MONITORING" = "true" ]; then
+                kubectl port-forward --address 0.0.0.0 -n default svc/nem-monitoring-prometheus-server 31301:80 &
+            fi
+            if [ "$VGC_ENABLED" = "true" ]; then
+                kubectl port-forward --address 0.0.0.0 -n "$VOLTHA_NAMESPACE" svc/voltha-voltha-go-controller 8181:8181 &
+            fi
+            sleep 5
+        fi
+
+        # Stage: Run Tests
+        banner "Running test ${TEST_TARGET} on workflow ${WORKFLOW}"
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Collecting initial memory consumption..."
+            mkdir -p "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}"
+            cd "$GITHUB_WORKSPACE/voltha-system-tests"
+            if [ -f "requirements.txt" ]; then
+                python3 -m venv .venv || true
+                source .venv/bin/activate || true
+                pip install -r requirements.txt || true
+                if [ -f "scripts/mem_consumption.py" ]; then
+                    python scripts/mem_consumption.py \
+                        -o "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}" \
+                        -a 0.0.0.0:31301 -n "$VOLTHA_NAMESPACE" || true
+                fi
+            fi
+        fi
+
+        echo "Running Robot Framework tests..."
+        mkdir -p "$LOGS_DIR"
+        export ROBOT_MISC_ARGS="-d ${LOGS_DIR} ${EXTRA_ROBOT_ARGS}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v ONOS_SSH_PORT:30115"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v ONOS_REST_PORT:30120"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v NAMESPACE:${VOLTHA_NAMESPACE}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v INFRA_NAMESPACE:${INFRA_NAMESPACE}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v container_log_dir:${LOGS_DIR}"
+        ROBOT_MISC_ARGS="${ROBOT_MISC_ARGS} -v logging:${TEST_LOGGING}"
+        export ROBOT_MISC_ARGS
+        export KVSTOREPREFIX="voltha/voltha_voltha"
+        cd "$GITHUB_WORKSPACE/voltha-system-tests"
+        make "${TEST_TARGET}" || TEST_RESULT=$?
+
+        get_pods_info "$LOGS_DIR"
+
+        if [ "$WITH_MONITORING" = "true" ]; then
+            echo "Collecting final memory consumption..."
+            cd "$GITHUB_WORKSPACE/voltha-system-tests"
+            source .venv/bin/activate || true
+            if [ -f "scripts/mem_consumption.py" ]; then
+                python scripts/mem_consumption.py \
+                    -o "$GITHUB_WORKSPACE/voltha-pods-mem-consumption-${WORKFLOW}" \
+                    -a 0.0.0.0:31301 -n "$VOLTHA_NAMESPACE" || true
+            fi
+        fi
+
+        echo "Compressing logs..."
+        cd "$LOGS_DIR"
+        gzip *-combined.log 2>/dev/null || true
+        banner "Test execution completed"
+        exit ${TEST_RESULT:-0}
+        EXECUTE_TEST_SCRIPT
+
+        chmod +x /tmp/execute_test.sh
+
+    - name: Execute tests
+      id: test-execution
+      shell: bash
+      run: |
+        echo "Executing tests..."
+
+        # Create logs directory
+        mkdir -p $GITHUB_WORKSPACE/logs
+        echo "results-path=$GITHUB_WORKSPACE/logs" >> $GITHUB_OUTPUT
+
+        # Parse test targets from YAML
+        cat > /tmp/test-targets.yaml <<'EOF'
+        ${{ inputs.test-targets }}
+        EOF
+
+        # Run each test target
+        python3 - <<'PYTHON_SCRIPT'
+        import yaml
+        import subprocess
+        import os
+        import sys
+
+        with open('/tmp/test-targets.yaml', 'r') as f:
+            tests = yaml.safe_load(f)
+
+        if not tests:
+            print("No tests defined")
+            sys.exit(0)
+
+        workspace = os.environ['GITHUB_WORKSPACE']
+        test_failures = []
+
+        for idx, test in enumerate(tests):
+            target = test.get('target', '')
+            workflow = test.get('workflow', '')
+            flags = test.get('flags', '')
+            teardown = test.get('teardown', True)
+            logging = test.get('logging', True)
+            vgc_enabled = test.get('vgcEnabled', False)
+
+            print(f"\n** -----------------------------------------------------------------------")
+            print(f"** Executing test {target} on workflow {workflow}")
+            print(f"** -----------------------------------------------------------------------\n")
+
+            # Set environment variables
+            env = os.environ.copy()
+            env['TEST_TARGET'] = target
+            env['WORKFLOW'] = workflow
+            env['TEST_FLAGS'] = flags
+            env['TEARDOWN'] = str(teardown).lower()
+            env['TEST_LOGGING'] = 'True' if logging else 'False'
+            env['VGC_ENABLED'] = 'true' if vgc_enabled else 'false'
+            env['INFRA_NAMESPACE'] = 'default'
+            env['VOLTHA_NAMESPACE'] = 'voltha'
+            env['LOGS_DIR'] = f"{workspace}/logs/{target}"
+            env['EXTRA_HELM_FLAGS'] = "${{ inputs.extra-helm-flags }}"
+            env['LOG_LEVEL'] = "${{ inputs.log-level }}"
+            env['DOCKER_REGISTRY'] = "${{ inputs.docker-registry }}"
+            env['OLTS'] = "${{ inputs.olts }}"
+            env['WITH_MONITORING'] = "${{ inputs.with-monitoring }}"
+            env['ENABLE_MAC_LEARNING'] = "${{ inputs.enable-mac-learning }}"
+            env['EXTRA_ROBOT_ARGS'] = "${{ inputs.extra-robot-args }}"
+            env['BRANCH'] = "${{ inputs.branch }}"
+            env['GERRIT_PROJECT'] = "${{ inputs.gerrit-project }}"
+
+            # Create logs directory for this test
+            os.makedirs(env['LOGS_DIR'], exist_ok=True)
+
+            # Execute the test using the inline script
+            script_path = "/tmp/execute_test.sh"
+
+            print(f"Running test script: {script_path}")
+
+            try:
+                result = subprocess.run(
+                    ['/bin/bash', script_path],
+                    env=env,
+                    check=True
+                )
+                print(f"\nTest {target} completed successfully\n")
+            except subprocess.CalledProcessError as e:
+                print(f"\nTest {target} failed with exit code {e.returncode}\n")
+                test_failures.append(target)
+                # Continue with other tests
+                continue
+            except Exception as e:
+                print(f"\nTest {target} failed with exception: {e}\n")
+                test_failures.append(target)
+                continue
+
+        if test_failures:
+            print(f"\n** Failed tests: {', '.join(test_failures)}")
+            sys.exit(1)
+        else:
+            print("\n** All tests passed successfully")
+
+        PYTHON_SCRIPT
+
+    # -----------------------------------------------------------------------
+    # Collect artifacts
+    # -----------------------------------------------------------------------
+    - name: Collect pod information
+      if: always()
+      shell: bash
+      run: |
+        echo "Collecting pod information..."
+        mkdir -p $GITHUB_WORKSPACE/logs/artifacts
+
+        kubectl get pods --all-namespaces -o wide > $GITHUB_WORKSPACE/logs/artifacts/pods.txt || true
+        kubectl get nodes -o wide > $GITHUB_WORKSPACE/logs/artifacts/nodes.txt || true
+        kubectl get events --all-namespaces --sort-by='.lastTimestamp' > $GITHUB_WORKSPACE/logs/artifacts/events.txt || true
+
+    - name: Collect VOLTHA logs
+      if: always()
+      shell: bash
+      run: |
+        echo "Collecting VOLTHA logs..."
+        kubectl logs -n voltha -l app.kubernetes.io/part-of=voltha \
+          > $GITHUB_WORKSPACE/logs/artifacts/voltha.log || true
+
+    - name: Cleanup port-forward processes
+      if: always()
+      shell: bash
+      run: |
+        echo "Cleaning up port-forward processes..."
+        pkill -f "kubectl.*port-forward" || true
+
+    - name: Upload test results
+      if: always()
+      uses: actions/upload-artifact@v4
+      with:
+        name: bbsim-test-results-${{ github.run_id }}
+        path: |
+          ${{ github.workspace }}/logs/**/*.log
+          ${{ github.workspace }}/logs/**/*.gz
+          ${{ github.workspace }}/logs/**/*.txt
+          ${{ github.workspace }}/logs/**/*.html
+          ${{ github.workspace }}/logs/**/*.xml
+          ${{ github.workspace }}/logs/**/voltha-pods-mem-consumption-*/*
+        retention-days: 30
+        if-no-files-found: warn
+
+    # -----------------------------------------------------------------------
+    # Cleanup
+    # -----------------------------------------------------------------------
+    - name: Delete kind cluster
+      if: always()
+      shell: bash
+      run: |
+        # Note: Cluster deletion is optional and can be controlled by workflow
+        echo "To delete cluster manually, run: kind delete cluster --name ${{ inputs.cluster-name }}"
diff --git a/.github/actions/bbsim-tests/example-workflow.yaml b/.github/actions/bbsim-tests/example-workflow.yaml
new file mode 100644
index 0000000..3654bb6
--- /dev/null
+++ b/.github/actions/bbsim-tests/example-workflow.yaml
@@ -0,0 +1,362 @@
+---
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: 2025 Open Networking Foundation Contributors
+
+name: BBSim Tests Example
+
+on:
+  # Run on push to main branches
+  push:
+    branches:
+      - master
+      - "voltha-*"
+
+  # Run on pull requests
+  pull_request:
+    branches:
+      - master
+      - "voltha-*"
+
+  # Allow manual triggering with custom parameters
+  workflow_dispatch:
+    inputs:
+      branch:
+        description: "Branch to test"
+        required: true
+        default: "master"
+        type: choice
+        options:
+          - master
+          - voltha-2.15
+      workflow_type:
+        description: "Workflow to test"
+        required: true
+        default: "all"
+        type: choice
+        options:
+          - all
+          - dt
+          - att
+          - tt
+      log_level:
+        description: "Log level"
+        required: false
+        default: "WARN"
+        type: choice
+        options:
+          - DEBUG
+          - INFO
+          - WARN
+          - ERROR
+      with_monitoring:
+        description: "Enable monitoring"
+        required: false
+        default: false
+        type: boolean
+
+  # Run nightly
+  schedule:
+    - cron: "0 2 * * *" # 2 AM UTC daily
+
+jobs:
+  # -----------------------------------------------------------------------
+  # Job: DT Workflow Tests
+  # -----------------------------------------------------------------------
+  dt-tests:
+    name: DT Workflow Tests
+    runs-on: ubuntu-latest
+    timeout-minutes: 240
+    if: |
+      github.event_name == 'schedule' ||
+      (github.event_name == 'workflow_dispatch' &&
+       (github.event.inputs.workflow_type == 'dt' || github.event.inputs.workflow_type == 'all')) ||
+      github.event_name == 'push' ||
+      github.event_name == 'pull_request'
+
+    steps:
+      - name: Free up disk space
+        run: |
+          echo "Disk space before cleanup:"
+          df -h
+
+          # Remove unnecessary software
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+
+          echo "Disk space after cleanup:"
+          df -h
+
+      - name: Run DT BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ github.event.inputs.branch || 'master' }}
+          log-level: ${{ github.event.inputs.log_level || 'WARN' }}
+          with-monitoring: ${{ github.event.inputs.with_monitoring || false }}
+          test-targets: |
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: dt-test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+
+  # -----------------------------------------------------------------------
+  # Job: ATT Workflow Tests
+  # -----------------------------------------------------------------------
+  att-tests:
+    name: ATT Workflow Tests
+    runs-on: ubuntu-latest
+    timeout-minutes: 240
+    if: |
+      github.event_name == 'schedule' ||
+      (github.event_name == 'workflow_dispatch' &&
+       (github.event.inputs.workflow_type == 'att' || github.event.inputs.workflow_type == 'all'))
+
+    steps:
+      - name: Free up disk space
+        run: |
+          echo "Disk space before cleanup:"
+          df -h
+
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+
+          echo "Disk space after cleanup:"
+          df -h
+
+      - name: Run ATT BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ github.event.inputs.branch || 'master' }}
+          log-level: ${{ github.event.inputs.log_level || 'WARN' }}
+          with-monitoring: ${{ github.event.inputs.with_monitoring || false }}
+          test-targets: |
+            - target: functional-single-kind-att
+              workflow: att
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: att-test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+
+  # -----------------------------------------------------------------------
+  # Job: TT Workflow Tests
+  # -----------------------------------------------------------------------
+  tt-tests:
+    name: TT Workflow Tests
+    runs-on: ubuntu-latest
+    timeout-minutes: 240
+    if: |
+      github.event_name == 'schedule' ||
+      (github.event_name == 'workflow_dispatch' &&
+       (github.event.inputs.workflow_type == 'tt' || github.event.inputs.workflow_type == 'all'))
+
+    steps:
+      - name: Free up disk space
+        run: |
+          echo "Disk space before cleanup:"
+          df -h
+
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+
+          echo "Disk space after cleanup:"
+          df -h
+
+      - name: Run TT BBSim Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ github.event.inputs.branch || 'master' }}
+          log-level: ${{ github.event.inputs.log_level || 'WARN' }}
+          with-monitoring: ${{ github.event.inputs.with_monitoring || false }}
+          test-targets: |
+            - target: functional-single-kind-tt
+              workflow: tt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: tt-test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+
+  # -----------------------------------------------------------------------
+  # Job: Multi-workflow Tests (run multiple workflows in sequence)
+  # -----------------------------------------------------------------------
+  multi-workflow-tests:
+    name: Multi-Workflow Tests
+    runs-on: ubuntu-latest
+    timeout-minutes: 480
+    if: github.event_name == 'workflow_dispatch' && github.event.inputs.workflow_type == 'all'
+
+    steps:
+      - name: Free up disk space
+        run: |
+          echo "Disk space before cleanup:"
+          df -h
+
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+
+          echo "Disk space after cleanup:"
+          df -h
+
+      - name: Run All Workflow Tests
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ github.event.inputs.branch || 'master' }}
+          log-level: ${{ github.event.inputs.log_level || 'INFO' }}
+          with-monitoring: true
+          olts: "2"
+          test-targets: |
+            - target: sanity-single-kind
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-dt
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-att
+              workflow: att
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+            - target: functional-single-kind-tt
+              workflow: tt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: multi-workflow-test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+
+  # -----------------------------------------------------------------------
+  # Job: Test Gerrit Patch
+  # -----------------------------------------------------------------------
+  test-patch:
+    name: Test Gerrit Patch
+    runs-on: ubuntu-latest
+    timeout-minutes: 240
+    # Only run when triggered manually with patch information
+    if: |
+      github.event_name == 'workflow_dispatch' &&
+      github.event.inputs.gerrit_project != '' &&
+      github.event.inputs.gerrit_refspec != ''
+
+    steps:
+      - name: Free up disk space
+        run: |
+          sudo rm -rf /usr/share/dotnet
+          sudo rm -rf /opt/ghc
+          sudo rm -rf /usr/local/share/boost
+          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+
+      - name: Test Patch with BBSim
+        uses: opencord/shared-workflows/.github/actions/bbsim-tests@main
+        with:
+          branch: ${{ github.event.inputs.branch || 'master' }}
+          gerrit-project: ${{ github.event.inputs.gerrit_project }}
+          gerrit-refspec: ${{ github.event.inputs.gerrit_refspec }}
+          log-level: DEBUG
+          test-targets: |
+            - target: sanity-single-kind
+              workflow: dt
+              flags: ""
+              teardown: true
+              logging: true
+              vgcEnabled: false
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: patch-test-results-${{ github.run_id }}
+          path: logs/
+          retention-days: 30
+
+  # -----------------------------------------------------------------------
+  # Job: Report Results
+  # -----------------------------------------------------------------------
+  report:
+    name: Test Results Summary
+    runs-on: ubuntu-latest
+    needs: [dt-tests, att-tests, tt-tests]
+    if: always()
+
+    steps:
+      - name: Download all artifacts
+        uses: actions/download-artifact@v4
+        with:
+          path: all-results
+
+      - name: Generate summary report
+        run: |
+          echo "# BBSim Test Results Summary" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "## Test Execution" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
+          echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
+          echo "| DT Tests | ${{ needs.dt-tests.result }} |" >> $GITHUB_STEP_SUMMARY
+          echo "| ATT Tests | ${{ needs.att-tests.result }} |" >> $GITHUB_STEP_SUMMARY
+          echo "| TT Tests | ${{ needs.tt-tests.result }} |" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+
+          # List available artifacts
+          echo "## Artifacts" >> $GITHUB_STEP_SUMMARY
+          echo "" >> $GITHUB_STEP_SUMMARY
+          if [ -d "all-results" ]; then
+            find all-results -type f -name "*.html" | while read file; do
+              echo "- $(basename $file)" >> $GITHUB_STEP_SUMMARY
+            done
+          fi
+
+      - name: Check overall status
+        run: |
+          if [[ "${{ needs.dt-tests.result }}" != "success" ]] || \
+             [[ "${{ needs.att-tests.result }}" != "success" && "${{ needs.att-tests.result }}" != "skipped" ]] || \
+             [[ "${{ needs.tt-tests.result }}" != "success" && "${{ needs.tt-tests.result }}" != "skipped" ]]; then
+            echo "One or more test jobs failed"
+            exit 1
+          fi
+          echo "All executed tests passed successfully"