name: "zzz Smoke tests"
on:
workflow_call:
inputs:
docker-image:
required: true
type: string
harbor-prefix:
required: false
type: string
default: ""
cpu-docker-image:
required: false
type: string
default: ""
description: >-
Canonical (non-Harbor-prefixed, publicly pullable) image ref used for the
github_hosted_cpu SKU. GitHub-hosted runners have no Harbor access, so the
Harbor-prefixed docker-image ref cannot be pulled there. Same idea as
docs-latest-public.yaml, which pulls the raw GHCR tag on ubuntu-latest.
package-artifact-name:
required: true
type: string
prebuilt-image:
required: false
type: string
default: ""
description: >-
Image with the packages already installed. When set it replaces
docker-image and the download and install steps are skipped. Empty
means the artifact path, which is what forks always get.
enabled-skus:
required: false
type: string
default: ALL_SKUS_IN_TESTS
description: "Comma-separated SKUs to run, or ALL_SKUS_IN_TESTS to run every SKU in the tests yaml."
product:
required: true
type: string # tt-metalium or tt-nn
upload-coverage:
required: false
type: boolean
default: false
enable-watcher:
description: "Enable watcher"
default: false
type: boolean
enable-kernel-ccache:
description: 'Enable kernel ccache for JIT compilation at runtime'
default: false
type: boolean
test-category:
required: false
type: string
default: merge_gate
description: "Which tests yaml to load: the merge_gate or the pr_gate validation list."
jobs:
load-test-matrix:
runs-on: ubuntu-slim
outputs:
matrix: ${{ steps.build-matrix.outputs.matrix }}
env:
TESTS_YAML_PATH: >-
${{ inputs.product == 'tt-metalium'
&& (inputs.test-category == 'pr_gate'
&& './tests/pipeline_reorg/runtime_validation_pr_gate_tests.yaml'
|| './tests/pipeline_reorg/runtime_validation_merge_gate_tests.yaml')
|| (inputs.test-category == 'pr_gate'
&& './tests/pipeline_reorg/ttnn_validation_pr_gate_tests.yaml'
|| './tests/pipeline_reorg/ttnn_validation_merge_gate_tests.yaml') }}
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
timeout-minutes: 20
with:
sparse-checkout: |
.github
tests/pipeline_reorg
sparse-checkout-cone-mode: true
- name: Install dependencies
run: pip3 install PyYAML
- name: Build test matrix based on enabled SKUs
id: build-matrix
uses: tenstorrent/tt-metal/.github/actions/prepare-test-matrix@main
with:
tests-yaml: "${{ env.TESTS_YAML_PATH }}"
enabled-skus: "${{ inputs.enabled-skus }}"
sku-config: ./.github/sku_config.yaml
event: ${{ github.event_name }}
smoke:
needs: load-test-matrix
if: ${{ needs.load-test-matrix.outputs.matrix != '[]' }}
strategy:
fail-fast: false
matrix:
test-group: ${{ fromJson(needs.load-test-matrix.outputs.matrix) }}
name: ${{ matrix.test-group.name }}
environment: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'mainline' || '' }}
timeout-minutes: 30
runs-on: ${{ matrix.test-group.runs_on }}
container:
# github_hosted_cpu runs on GitHub-hosted runners with no Harbor access, so it
# needs the canonical (publicly pullable) cpu-docker-image ref instead of the
# Harbor-prefixed one (same reason dedicated-merge-gate uses the raw ref).
# A prebuilt image already carries the packages, and is a canonical ghcr
# ref so it needs no Harbor prefix on any SKU.
image: ${{ inputs.prebuilt-image || (matrix.test-group.sku == 'github_hosted_cpu' && (inputs.cpu-docker-image || inputs.docker-image)) || (inputs.docker-image && (contains(join(matrix.test-group.runs_on, ','), 'dedicated-merge-gate') && inputs.docker-image || format('{0}{1}', inputs.harbor-prefix, inputs.docker-image))) || 'docker-image-unresolved!' }}
env:
ASAN_OPTIONS: "color=always"
TSAN_OPTIONS: "color=always"
UBSAN_OPTIONS: "color=always:print_stacktrace=1:halt_on_error=1"
LLVM_PROFILE_FILE: "/work/coverage/%p-%m.profraw"
volumes:
- /work
# No hugepages on viommu runners or on GitHub-hosted CPU-only runners.
- ${{ (!contains(join(matrix.test-group.runs_on, ','), 'viommu') && matrix.test-group.sku != 'github_hosted_cpu' && '/dev/hugepages-1G:/dev/hugepages-1G') || '/no_hugepages' }}
# Memory reduction in WH LB coming up: https://github.com/tenstorrent/metal-internal-workflows/issues/786
# github_hosted_cpu has no /dev/tenstorrent to pass through.
options: ${{ matrix.test-group.sku != 'github_hosted_cpu' && '--device /dev/tenstorrent' || '' }} ${{ contains(matrix.test-group.sku, 'llmbox') && '--memory 256g' || '' }}
defaults:
run:
shell: bash
working-directory: /work # https://github.com/actions/runner/issues/878
steps:
- name: Checkout actions
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
timeout-minutes: 20
with:
sparse-checkout: |
.github/actions/download-artifact-with-retry
.github/scripts/ccache_log_filter.sh
sparse-checkout-cone-mode: false
- uses: ./.github/actions/download-artifact-with-retry
if: ${{ inputs.prebuilt-image == '' }}
timeout-minutes: 15
with:
name: ${{ inputs.package-artifact-name || 'packages artifact unresolved!' }}
path: /work/pkgs/
- name: Check available memory in container for WH LB 2
if: ${{ contains(matrix.test-group.sku, 'llmbox') }}
run: |
# You never know with GH!!!
set -e
# Read memory limit for container cgroup from cgroup v2 or v1
memory_bytes=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || \
cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null)
if [ -z "$memory_bytes" ]; then
echo "Unable to read memory limit from cgroup (v1 or v2)" >&2
exit 1
fi
# 256GB = 274877906944 bytes
max_bytes_wh_lb2=274877906944
if [ "$memory_bytes" -gt "$max_bytes_wh_lb2" ]; then
echo "Error: Container memory limit ${memory_bytes} upcoming max of 256GB (${max_bytes_wh_lb2} bytes)" >&2
exit 1
fi
echo "Memory limit validated: ${memory_bytes} bytes"
- name: Install packages
if: ${{ inputs.prebuilt-image == '' }}
timeout-minutes: 10
run: |
# Ideally only ${{ inputs.product }}-validation, but APT doesn't resolve dependencies from files on disk without being told about them.
# tt-metalium-jit is optional: TT_USE_SYSTEM_SFPI builds don't produce it, since this
# container already has /opt/tenstorrent/sfpi for the JIT-build runtime fallback
# (see tt_metal/jit_build/build.cpp).
shopt -s nullglob
pkgs=(./pkgs/tt-metalium_*.deb ./pkgs/tt-metalium-jit_*.deb ./pkgs/tt-metalium-validation_*.deb ./pkgs/${{ inputs.product }}_*.deb ./pkgs/${{ inputs.product }}-validation_*.deb)
apt install -y "${pkgs[@]}"
- name: Prepare coverage directory
run: |
mkdir -p /work/coverage
- name: Warmup caches
if: false # No need to warmup currently because slow tests will never fail the job
timeout-minutes: 2
env:
GTEST_COLOR: yes
LSAN_OPTIONS: suppressions=/usr/share/tt-metalium/lsan.supp
# TT_METAL_WATCHER: 5
TT_METAL_WATCHER_TEST_MODE: 1
run: |
# Run a single test to warm up caches before the full test suite
/usr/bin/${{ inputs.product }}-validation-smoke --gtest_filter="MeshDispatchFixture.TensixFailOnDuplicateKernelCreationDataflow" || true
- name: Clear coverage from warmup
if: ${{ inputs.upload-coverage }}
run: |
rm -f /work/coverage/*.profraw || true
- name: Set up env vars for watcher
if: ${{ inputs.enable-watcher == true }}
run: |
echo "Enabling TT Metal Watcher"
echo "TT_METAL_WATCHER=2" >> $GITHUB_ENV
echo "TT_METAL_WATCHER_APPEND=1" >> $GITHUB_ENV
echo "TT_METAL_WATCHER_NOINLINE=1" >> $GITHUB_ENV
echo "TT_METAL_WATCHER_DISABLE_ETH=1" >> $GITHUB_ENV
- name: Set up kernel ccache
if: ${{ inputs.enable-kernel-ccache == true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) }}
run: |
echo "Enabling TT Metal kernel ccache"
echo "CCACHE_COMPILERCHECK=content" >> $GITHUB_ENV
echo "CCACHE_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "CCACHE_NOHASHDIR=true" >> $GITHUB_ENV
echo "CCACHE_COMPRESS=true" >> $GITHUB_ENV
echo "CCACHE_COMPRESSLEVEL=3" >> $GITHUB_ENV
echo "CCACHE_LOGFILE=/tmp/ccache.log" >> $GITHUB_ENV
echo "CCACHE_BASEDIR=/work" >> $GITHUB_ENV
echo "TT_METAL_CCACHE_KERNEL_SUPPORT=1" >> $GITHUB_ENV
echo "CCACHE_TEMPDIR=/tmp/ccache" >> $GITHUB_ENV
mkdir -p /tmp/ccache
# Build Redis URL in bash so that the password is never embedded in a
# workflow input string (GHA does not pass secrets through string inputs
# to reusable workflows). This also ensures forks leave CCACHE_REMOTE_STORAGE
# unset — the step is guarded by the fork check above.
REDIS_USER="${{ vars.REDIS_CCACHE_USER }}"
REDIS_PASSWORD="${{ secrets.REDIS_CCACHE_PASSWORD }}"
REDIS_HOST="${{ vars.REDIS_CCACHE_HOST }}"
REDIS_PORT="${{ vars.REDIS_CCACHE_PORT }}"
REDIS_READONLY="${{ vars.REDIS_CCACHE_IS_READONLY }}"
CCACHE_REMOTE_STORAGE="redis://${REDIS_USER}:${REDIS_PASSWORD}@${REDIS_HOST}:${REDIS_PORT}"
# ccache 4.13 bug: the read-only optional trips the write gate whenever
# it has *any* value — even false. Only append it when actually true.
if [ "${REDIS_READONLY}" = "true" ]; then
CCACHE_REMOTE_STORAGE="${CCACHE_REMOTE_STORAGE}|read-only=true"
fi
echo "CCACHE_REMOTE_STORAGE=${CCACHE_REMOTE_STORAGE}" >> $GITHUB_ENV
- name: Run a test
id: test
timeout-minutes: ${{ matrix.test-group.timeout }}
env:
GTEST_COLOR: yes
GTEST_OUTPUT: xml:/work/test-reports/
LSAN_OPTIONS: suppressions=/usr/share/tt-metalium/lsan.supp
TT_METAL_WATCHER_TEST_MODE: 1
TT_LOGGER_LEVEL: warn
run: ${{ matrix.test-group.cmd }}
- name: Merge LLVM coverage
if: ${{ inputs.upload-coverage }}
run: |
shopt -s nullglob || true
files=(/work/coverage/*.profraw)
if [ ${#files[@]} -gt 0 ]; then
llvm-profdata-17 merge -sparse /work/coverage/*.profraw -o /work/coverage/coverage.profdata
else
echo "No .profraw files found; skipping merge"
fi
- name: Export LCOV (llvm-cov)
if: ${{ inputs.upload-coverage }}
run: |
if [ -f /work/coverage/coverage.profdata ]; then
objects=(
/usr/lib/libtt_metal.so
/usr/lib/libtt_stl.so
/usr/lib/_ttnncpp.so
)
test_bin="/usr/bin/${{ inputs.product }}-validation-smoke"
llvm-cov-17 export -format=lcov \
-instr-profile=/work/coverage/coverage.profdata \
"$test_bin" "${objects[@]}" > /work/coverage/coverage.info || {
echo "llvm-cov export failed"; exit 1; }
else
echo "coverage.profdata not found; creating empty LCOV file"
: > /work/coverage/coverage.info
fi
- name: Checkout repository for Codecov mapping
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
timeout-minutes: 20
if: ${{ inputs.upload-coverage }}
with:
submodules: recursive
fetch-depth: 1
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: ${{ !cancelled() && inputs.upload-coverage && (
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)
|| (github.event_name == 'push' && github.ref_name == 'main')
) }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
files: /work/coverage/coverage.info
flags: ${{ inputs.product }}
name: smoke-${{ inputs.product }}-${{ matrix.test-group.sku }}
fail_ci_if_error: true
verbose: true
- name: Test Report
# Because of https://github.com/tenstorrent/tt-metal/issues/19413, only run for our repo
# for now. No forks!
if: ${{ !cancelled() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) }}
# annotate_only keeps this from creating a check run, which is what used to 404 the
# merge gate: the queue's temporary commit could be discarded between the create and
# the update. Annotations attach to this job, so there is no separate SHA to go stale.
continue-on-error: true
uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2
with:
report_paths: /work/test-reports/*.xml
check_name: ${{ inputs.product }} ${{ matrix.test-group.sku }} smoke tests (${{ github.workflow }}, attempt ${{ github.run_attempt }})
annotate_only: true
detailed_summary: true
include_passed: false
fail_on_failure: false
- uses: tenstorrent/tt-metal/.github/actions/upload-artifact-with-job-uuid@main
if: ${{ !cancelled() }}
timeout-minutes: 10
with:
path: /work/test-reports/
prefix: "test_reports_"
- name: Publish ccache summary
if: ${{ !cancelled() && inputs.enable-kernel-ccache == true }}
run: |
echo 'CCache Summary (kernel)
' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
ccache -s >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo ' ' >> $GITHUB_STEP_SUMMARY
# Surface remote storage stats and errors from the ccache log.
# Uses a filter script to avoid overflowing the 1024k step summary limit.
if [ -f /tmp/ccache.log ]; then
echo 'CCache Remote Storage (kernel)
' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
"$GITHUB_WORKSPACE/.github/scripts/ccache_log_filter.sh" /tmp/ccache.log >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo ' ' >> $GITHUB_STEP_SUMMARY
fi