Files
temporal/.github/workflows/run-tests.yml
Stephan Behnke 6736479902 Memory watcher for functional tests (#9027)
## What changed?

Added script to take memory report snapshots during test execution and
print report after test success/failure.

## Why?

Gain insights into memory usage; especially when job is OOM killed.



## How did you test it?

see example:
https://github.com/temporalio/temporal/actions/runs/21011468266/job/60407413329?pr=9027#step:9:15

<details><summary>Example</summary>
<p>

```
Memory snapshot at 2026-01-14 22:16:53

Time      Used(%)  Used(MB)  Goroutines  Top Processes
---------------------------------------------------------------
22:06:03    16%      5347         ?     java (2468MB), java (1322MB)
22:06:33    20%      6460         ?     java (2450MB), java (1322MB), compile (376MB)
22:07:03    16%      5418         ?     java (2430MB), java (1323MB)
22:07:33    16%      5401      2781     java (2531MB), java (1333MB)
22:08:06    17%      5702      5861     java (2493MB), java (1339MB), xdc.test (649MB)
22:08:36    18%      6034      5971     java (2528MB), java (1346MB), xdc.test (936MB)
22:09:07    20%      6455      6149     java (2539MB), java (1350MB), xdc.test (1314MB)
22:09:38    26%      8416     45668     xdc.test (3281MB), java (2535MB), java (1375MB)
22:10:10    33%     10596     43034     xdc.test (5657MB), java (2560MB), java (1405MB)
22:10:42    39%     12806     39209     xdc.test (7449MB), java (2551MB), java (1413MB)
22:11:13    42%     13580     33372     xdc.test (8166MB), java (2556MB), java (1413MB)
22:11:44    44%     14196     11067     xdc.test (8168MB), java (2548MB), java (1413MB)
22:12:15    44%     14171     11342     xdc.test (8653MB), java (2555MB), java (1415MB)
22:12:46    44%     14233     11724     xdc.test (8682MB), java (2546MB), java (1421MB)
22:13:17    45%     14536     10186     xdc.test (8858MB), java (2556MB), java (1427MB)
22:13:48    45%     14648     10392     xdc.test (8961MB), java (2555MB), java (1427MB)
22:14:19    45%     14710     10935     xdc.test (9027MB), java (2552MB), java (1428MB)
22:14:49    45%     14716     11060     xdc.test (9020MB), java (2557MB), java (1428MB)
22:15:20    45%     14702     11312     xdc.test (8925MB), java (2557MB), java (1428MB)
22:15:51    46%     14835     11571     xdc.test (9088MB), java (2545MB), java (1429MB)
22:16:22    46%     14839     11964     xdc.test (9082MB), java (2556MB), java (1429MB)
22:16:53    18%      5911      5792     java (2547MB), java (1434MB), xdc.test (628MB)

--- Top Processes ---
    PID %MEM        RSS COMMAND
   2550  7.9    2608560 java
   2679  4.4    1468908 java
  23586  1.9     643644 xdc.test
   1810  0.4     155452 provjobd
   2094  0.4     142716 Runner.Worker
   2079  0.3     113336 Runner.Listener
  23517  0.3      99448 go
   1161  0.2      78640 dockerd
    833  0.1      51476 php-fpm8.3
    910  0.1      51092 containerd
    270  0.1      43536 multipathd
   1417  0.1      36216 python3
    865  0.1      35444 python3
   3362  0.0      22220 go
   1184  0.0      21448 php-fpm8.3
   1185  0.0      21448 php-fpm8.3
    831  0.0      20332 networkd-dispat
   1987  0.0      19000 hca
    203  0.0      16660 systemd-journal

--- Memory Summary ---
               total        used        free      shared  buff/cache   available
Mem:           32098        5914       17968          51        8738       26183
Swap:              0           0           0

--- Go Heap Profile ---
File: xdc.test
Build ID: 2f2150795a26dac09836852e7ea9c9cf9f89fc14
Type: inuse_space
Time: 2026-01-14 22:16:53 UTC
Showing nodes accounting for 37251.65kB, 100% of 37251.65kB total
      flat  flat%   sum%        cum   cum%
 6002.03kB 16.11% 16.11%  6002.03kB 16.11%  go.temporal.io/sdk/internal.newLocalActivityTunnel (inline)
    2565kB  6.89% 23.00%     2565kB  6.89%  runtime.allocm
 2564.16kB  6.88% 29.88%  3635.80kB  9.76%  google.golang.org/protobuf/internal/filedesc.(*Message).unmarshalFull
 2384.64kB  6.40% 36.28%  2384.64kB  6.40%  go.temporal.io/server/common/aggregate.NewMovingWindowAvgImpl (inline)
 2048.35kB  5.50% 41.78%  2048.35kB  5.50%  github.com/aws/aws-sdk-go/aws/endpoints.init
 1537.50kB  4.13% 45.91%  1537.50kB  4.13%  go.uber.org/zap/internal/bufferpool.init.NewPool.func1
 1536.66kB  4.13% 50.03%  1536.66kB  4.13%  runtime.malg
 1072.75kB  2.88% 52.91%  1072.75kB  2.88%  go.temporal.io/sdk/internal.newBaseWorker
 1071.64kB  2.88% 55.79%  1071.64kB  2.88%  google.golang.org/protobuf/internal/strs.(*Builder).grow (inline)
 1045.40kB  2.81% 58.60%  1045.40kB  2.81%  github.com/gocql/gocql.(*Session).scanColumnMetadataSystem
 1024.11kB  2.75% 61.35%  1024.11kB  2.75%  time.newTimer
 1024.09kB  2.75% 64.09%  1024.09kB  2.75%  go.temporal.io/server/common/tasks.(*SequentialScheduler[go.shape.interface { Abort(); Ack(); Cancel(); Execute() error; HandleErr(error) error; IsRetryableError(error) bool; MarkPoisonPill() error; Nack(error); QueueID() interface {}; ReplicationTask() *go.temporal.io/server/api/replication/v1.ReplicationTask; Reschedule(); RetryPolicy() go.temporal.io/server/common/backoff.RetryPolicy; SourceClusterName() string; State() go.temporal.io/server/common/tasks.State; TaskCreationTime() time.Time; TaskID() int64 }]).pollTaskQueue
  532.26kB  1.43% 65.52%   532.26kB  1.43%  google.golang.org/protobuf/reflect/protoregistry.(*Files).RegisterFile.func2
  528.17kB  1.42% 66.94%   528.17kB  1.42%  bufio.NewReaderSize (inline)
  528.17kB  1.42% 68.36%   528.17kB  1.42%  google.golang.org/grpc/internal/transport.newBufWriter (inline)
  516.76kB  1.39% 69.75%   516.76kB  1.39%  runtime.procresize
     514kB  1.38% 71.13%      514kB  1.38%  github.com/gocql/gocql/internal/streams.New (inline)
  513.50kB  1.38% 72.50%   513.50kB  1.38%  go.temporal.io/server/api/history/v1.init
  512.50kB  1.38% 73.88%  7680.97kB 20.62%  go.uber.org/fx/internal/lifecycle.(*Lifecycle).Start
  512.44kB  1.38% 75.26%   512.44kB  1.38%  go/token.init.0
  512.25kB  1.38% 76.63%   512.25kB  1.38%  runtime.gcBgMarkWorker
  512.19kB  1.37% 78.01%   512.19kB  1.37%  github.com/gocql/gocql.(*Conn).executeQuery
  512.17kB  1.37% 79.38%   512.17kB  1.37%  google.golang.org/grpc/metadata.FromOutgoingContext
  512.17kB  1.37% 80.76%   512.17kB  1.37%  google.golang.org/grpc/metadata.MD.Set (inline)
  512.10kB  1.37% 82.13%   512.10kB  1.37%  context.(*cancelCtx).propagateCancel
  512.10kB  1.37% 83.51%   512.10kB  1.37%  go.uber.org/fx.moduleOption.apply
  512.09kB  1.37% 84.88%   512.09kB  1.37%  internal/abi.NewName
  512.07kB  1.37% 86.25%   512.07kB  1.37%  go.uber.org/dig.(*Scope).newGraphNode
  512.06kB  1.37% 87.63%   512.06kB  1.37%  google.golang.org/protobuf/internal/impl.fieldInfoForScalar
  512.05kB  1.37% 89.00%   512.05kB  1.37%  go.temporal.io/server/common/goro.NewAdaptivePool
  512.05kB  1.37% 90.38%   512.05kB  1.37%  go.temporal.io/server/common/nexus.(*EndpointRegistryImpl).setEnabled
  512.05kB  1.37% 91.75%   512.05kB  1.37%  google.golang.org/grpc/internal/transport.newWriteQuota (inline)
  512.05kB  1.37% 93.13%   512.05kB  1.37%  go.temporal.io/sdk/internal.(*baseWorker).runEagerTaskDispatcher
  512.02kB  1.37% 94.50%   512.02kB  1.37%  context.WithValue
  512.02kB  1.37% 95.88%   512.02kB  1.37%  encoding/json.typeFields
  512.02kB  1.37% 97.25%   512.02kB  1.37%  github.com/gocql/gocql.(*ring).addHostIfMissing
  512.02kB  1.37% 98.63%   512.02kB  1.37%  github.com/google/uuid.UUID.String (inline)
  512.02kB  1.37%   100%   512.02kB  1.37%  go.uber.org/dig.newParam
         0     0%   100%   512.10kB  1.37%  context.WithCancel
         0     0%   100%   512.10kB  1.37%  context.withCancel (inline)
         0     0%   100%   512.02kB  1.37%  encoding/json.(*Decoder).Decode
         0     0%   100%   512.02kB  1.37%  encoding/json.(*decodeState).object
         0     0%   100%   512.02kB  1.37%  encoding/json.(*decodeState).unmarshal
         0     0%   100%   512.02kB  1.37%  encoding/json.(*decodeState).value
         0     0%   100%   512.02kB  1.37%  encoding/json.cachedTypeFields
         0     0%   100%   512.02kB  1.37%  encoding/json.newStructEncoder (inline)
         0     0%   100%   512.02kB  1.37%  encoding/json.newTypeEncoder
         0     0%   100%   512.02kB  1.37%  encoding/json.typeEncoder
         0     0%   100%   512.02kB  1.37%  encoding/json.typeEncoder.func1
         0     0%   100%  1557.42kB  4.18%  github.com/gocql/gocql.(*ClusterConfig).CreateSession (inline)
         0     0%   100%   512.19kB  1.37%  github.com/gocql/gocql.(*Query).Iter
         0     0%   100%   512.19kB  1.37%  github.com/gocql/gocql.(*Query).MapScanCAS
         0     0%   100%   512.19kB  1.37%  github.com/gocql/gocql.(*Query).execute
         0     0%   100%  1045.40kB  2.81%  github.com/gocql/gocql.(*Session).KeyspaceMetadata

File: xdc.test
Build ID: 2f2150795a26dac09836852e7ea9c9cf9f89fc14
Type: alloc_space
Time: 2026-01-14 22:16:53 UTC
Showing nodes accounting for 99.45MB, 80.53% of 123.49MB total
Dropped 305 nodes (cum <= 0.62MB)
      flat  flat%   sum%        cum   cum%
   51.44MB 41.65% 41.65%    51.44MB 41.65%  regexp.(*bitState).reset
    5.86MB  4.75% 46.40%     5.86MB  4.75%  go.temporal.io/sdk/internal.newLocalActivityTunnel (inline)
    3.53MB  2.85% 49.26%     4.59MB  3.72%  compress/flate.NewWriter (inline)
    2.50MB  2.03% 51.28%     2.50MB  2.03%  runtime.allocm
    2.50MB  2.03% 53.31%     3.55MB  2.88%  google.golang.org/protobuf/internal/filedesc.(*Message).unmarshalFull
    2.33MB  1.89% 55.20%     2.33MB  1.89%  go.temporal.io/server/common/aggregate.NewMovingWindowAvgImpl (inline)
       2MB  1.62% 56.82%        2MB  1.62%  go.uber.org/zap/internal/bufferpool.init.NewPool.func1
       2MB  1.62% 58.44%        2MB  1.62%  github.com/aws/aws-sdk-go/aws/endpoints.init
    1.52MB  1.23% 59.67%     1.52MB  1.23%  github.com/gocql/gocql.(*Session).scanColumnMetadataSystem
    1.51MB  1.22% 60.89%     1.51MB  1.22%  github.com/grpc-ecosystem/grpc-gateway/v2/runtime.(*ServeMux).Handle
    1.50MB  1.22% 62.11%     1.50MB  1.22%  runtime.malg
    1.50MB  1.21% 63.32%     1.50MB  1.21%  fmt.init.func1
    1.50MB  1.21% 64.54%     1.50MB  1.21%  github.com/gocql/gocql.newFramer (inline)
    1.50MB  1.21% 65.75%        2MB  1.62%  go.uber.org/dig.getParamOrder
    1.07MB  0.86% 66.62%     1.07MB  0.86%  compress/flate.(*compressor).initDeflate (inline)
    1.05MB  0.85% 67.46%     1.05MB  0.85%  go.temporal.io/sdk/internal.newBaseWorker
    1.05MB  0.85% 68.31%     1.05MB  0.85%  google.golang.org/protobuf/internal/strs.(*Builder).grow (inline)
    1.03MB  0.84% 69.15%     1.03MB  0.84%  compress/flate.(*dictDecoder).init (inline)
    1.02MB  0.83% 69.97%     1.02MB  0.83%  google.golang.org/protobuf/reflect/protoregistry.(*Files).RegisterFile.func2
       1MB  0.81% 70.79%        1MB  0.81%  github.com/gocql/gocql/internal/streams.New (inline)
       1MB  0.81% 71.60%        2MB  1.62%  go.uber.org/dig.(*Scope).findAndValidateResults
       1MB  0.81% 72.41%        1MB  0.81%  context.(*cancelCtx).propagateCancel
       1MB  0.81% 73.22%        1MB  0.81%  time.newTimer
       1MB  0.81% 74.03%        1MB  0.81%  go.temporal.io/server/common/tasks.(*SequentialScheduler[go.shape.interface { Abort(); Ack(); Cancel(); Execute() error; HandleErr(error) error; IsRetryableError(error) bool; MarkPoisonPill() error; Nack(error); QueueID() interface {}; ReplicationTask() *go.temporal.io/server/api/replication/v1.ReplicationTask; Reschedule(); RetryPolicy() go.temporal.io/server/common/backoff.RetryPolicy; SourceClusterName() string; State() go.temporal.io/server/common/tasks.State; TaskCreationTime() time.Time; TaskID() int64 }]).pollTaskQueue
       1MB  0.81% 74.84%        7MB  5.67%  go.uber.org/dig.(*Scope).provide
       1MB  0.81% 75.65%        1MB  0.81%  container/list.New (inline)
       1MB  0.81% 76.46%        2MB  1.62%  go.uber.org/dig.newParam
    0.52MB  0.42% 76.88%     1.54MB  1.25%  google.golang.org/protobuf/reflect/protoregistry.(*Files).RegisterFile
    0.51MB  0.41% 77.29%     1.54MB  1.24%  io.ReadAll
    0.50MB  0.41% 77.69%     8.50MB  6.88%  go.uber.org/fx/internal/lifecycle.(*Lifecycle).Start
    0.50MB  0.41% 78.10%        2MB  1.62%  github.com/gocql/gocql.(*Conn).executeQuery
    0.50MB  0.41% 78.50%     1.50MB  1.22%  go.temporal.io/server/common/log.(*zapLogger).cloneWithTags
    0.50MB  0.41% 78.91%    10.98MB  8.89%  go.uber.org/dig.paramObject.Build
    0.50MB   0.4% 79.31%    37.04MB 29.99%  go.uber.org/fx/internal/fxreflect.CallerStack
    0.50MB   0.4% 79.72%     2.50MB  2.02%  go.uber.org/dig.(*graphHolder).EdgesFrom
    0.50MB   0.4% 80.12%     7.50MB  6.07%  go.uber.org/dig.(*Scope).Provide
    0.50MB   0.4% 80.53%        1MB  0.81%  go.uber.org/dig.(*Scope).getAllProviders (inline)
         0     0% 80.53%     1.07MB  0.86%  compress/flate.(*compressor).init
         0     0% 80.53%     1.03MB  0.84%  compress/flate.NewReader
         0     0% 80.53%     1.03MB  0.84%  compress/gzip.(*Reader).Reset
         0     0% 80.53%     1.03MB  0.84%  compress/gzip.(*Reader).readHeader
         0     0% 80.53%     4.59MB  3.72%  compress/gzip.(*Writer).Write
         0     0% 80.53%     1.03MB  0.84%  compress/gzip.NewReader (inline)
         0     0% 80.53%        1MB  0.81%  context.WithCancel
         0     0% 80.53%        1MB  0.81%  context.withCancel (inline)
         0     0% 80.53%        1MB  0.81%  fmt.(*pp).doPrintf
         0     0% 80.53%     1.02MB  0.82%  fmt.(*pp).handleMethods
         0     0% 80.53%     1.52MB  1.23%  fmt.(*pp).printArg
         0     0% 80.53%        2MB  1.62%  fmt.Fprintf
         0     0% 80.53%        1MB  0.81%  fmt.Sprintf
         0     0% 80.53%     1.50MB  1.21%  fmt.newPrinter
         0     0% 80.53%     4.02MB  3.26%  github.com/gocql/gocql.(*ClusterConfig).CreateSession (inline)
         0     0% 80.53%        2MB  1.62%  github.com/gocql/gocql.(*Conn).recv
```

</p>
</details>

---------

Co-authored-by: David Reiss <dnr@dnr.im>
2026-01-15 09:11:24 -08:00

996 lines
38 KiB
YAML

name: All Tests
run-name: ${{ inputs.test_name }}${{ inputs.test_dbs }}
on:
pull_request:
push:
branches:
- main
- cloud/*
- feature/*
- release/*
workflow_dispatch:
inputs:
commit:
description: "Commit SHA"
required: true
run_single_functional_test:
description: "Run a single functional test"
type: boolean
default: false
run_single_unit_test:
description: "Run a single unit test (INSTEAD of functional test)"
type: boolean
default: false
unit_test_directory:
description: "[Unit Test Only] Directory to run unit tests in"
type: string
default: "./temporal"
n_runs:
description: "Number of times to repeat the test per database type (except for unit tests, start with n=25 or lower to avoid OOMKill)"
type: number
default: 1
test_name:
description: "Name of the test to run (i.e. 'TestAcquireShard_DeadlineExceededErrorSuite' or 'TestFunctionalSuite/TestUpdateWorkflow')"
type: string
timeout_minutes:
description: "Test timeout in minutes"
type: number
default: 120
test_runner:
description: "Which runner to use. Choose higher RAM if your n_runs is high."
type: choice
default: "16GB RAM (ubuntu-latest)"
options:
- "16GB RAM (ubuntu-latest)"
- "64GB RAM (ubuntu-latest-16-cores)"
test_dbs:
description: 'List of DBs to test on (i.e. ["sqlite", "cassandra", "mysql8", "postgres12"])'
type: string
default: '["sqlite"]'
concurrency: # Auto-cancel existing runs in the PR when a new commit is pushed
group: run-tests-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
# For workflow_dispatch: use the given commit.
# For pull_request: use the head of the PR branch (not the merge branch which is the default!)
# For push: use the pushed commit.
COMMIT: ${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
PR_BASE_COMMIT: ${{ github.event.pull_request.base.sha }}
DOCKER_COMPOSE_FILE: ./develop/github/docker-compose.yml
TEMPORAL_VERSION_CHECK_DISABLED: 1
MAX_TEST_ATTEMPTS: 3
jobs:
set-up-single-test:
name: Set up single test
runs-on: ubuntu-latest
outputs:
shard_indices: ${{ steps.generate_output.outputs.shard_indices }}
total_shards: ${{ steps.generate_output.outputs.shards }}
github_timeout: ${{ steps.generate_output.outputs.github_timeout }}
test_timeout: ${{ steps.generate_output.outputs.test_timeout }}
single_test_args: ${{ steps.generate_output.outputs.single_test_args }}
runs_on: ${{ steps.generate_output.outputs.runs_on }}
dbs: ${{ inputs.test_dbs }}
modified_unit_test_suites: ${{ env.modified_unit_test_suites }}
modified_integration_test_suites: ${{ env.modified_integration_test_suites }}
modified_functional_test_suites: ${{ env.modified_functional_test_suites }}
modified_functional_ndc_test_suites: ${{ env.modified_functional_ndc_test_suites }}
modified_functional_xdc_test_suites: ${{ env.modified_functional_xdc_test_suites }}
steps:
- id: generate_output
run: |
shards=3
timeout=35 # update this to TEST_TIMEOUT if you update the Makefile
runs_on='["ubuntu-latest"]'
if [[ "${{ inputs.run_single_functional_test }}" == "true" || "${{ inputs.run_single_unit_test }}" == "true" ]]; then
shards=1
timeout=${{ inputs.timeout_minutes }}
single_test_args="-run ${{ inputs.test_name }} -count ${{ inputs.n_runs }}"
if [[ "${{ inputs.test_runner }}" == "64GB RAM (ubuntu-latest-16-cores)" ]]; then
runs_on='[ "ubuntu-latest-16-cores" ]'
fi
fi
{
echo "shard_indices=[ $(seq -s, 0 $((shards-1))) ]"
echo "shards=$shards"
echo "github_timeout=$((timeout+5))"
echo "test_timeout=${timeout}m"
echo "single_test_args=$single_test_args"
echo "runs_on=$runs_on"
} >> "$GITHUB_OUTPUT"
- id: cat_output
run: |
cat "$GITHUB_OUTPUT"
- name: Checkout Code
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
fetch-depth: 0
- name: Fetch base branch
run: git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
- name: Compute merge base
if: ${{ github.event_name == 'pull_request' }}
run: |
MERGE_BASE="$(git merge-base "${{ env.COMMIT }}" "${{ github.event.pull_request.base.ref }}")"
echo "MERGE_BASE=${MERGE_BASE}" >> "$GITHUB_ENV"
set -exuo pipefail
go run ./cmd/tools/test/find_altered_tests.go \
-c unit \
-c integration \
-c functional \
-c functional_ndc \
-c functional_xdc \
-s "${MERGE_BASE}" \
-t "${COMMIT}" | tee -a "$GITHUB_ENV"
shell: bash
pre-build:
name: Pre-build for cache
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
- uses: actions/setup-go@v6
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
id: restore-deps
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- run: make pre-build-functional-test-coverage
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
- name: Save dependencies
uses: actions/cache/save@v4
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test && steps.restore-deps.outputs.cache-hit != 'true' }}
with:
path: ~/go/pkg/mod
key: ${{ steps.restore-deps.outputs.cache-primary-key }}
- name: Save build outputs
uses: actions/cache/save@v4
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
misc-checks:
name: Misc checks
needs: pre-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
# buf-breaking tries to compare HEAD against merge base so we need to be able to find it
fetch-depth: 100
- uses: actions/setup-go@v6
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- uses: arduino/setup-protoc@v3
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- run: GOOS=windows GOARCH=amd64 make clean-bins bins
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
- run: GOOS=darwin GOARCH=arm64 make clean-bins bins
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
- run: make clean-bins ci-build-misc
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
unit-test:
if: ${{ inputs.run_single_functional_test != true }}
name: Unit test
needs: [pre-build, set-up-single-test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- name: Run unit tests
timeout-minutes: 20
run: TEST_TIMEOUT=15m make unit-test-coverage
env:
UNIT_TEST_DIRS: ${{ inputs.unit_test_directory }}
TEST_ARGS: ${{ needs.set-up-single-test.outputs.single_test_args }}
TEST_TIMEOUT: ${{ needs.set-up-single-test.outputs.test_timeout }}
- name: Generate crash report
if: failure()
run:
| # if the tests failed, we would expect one JUnit XML report per attempt; otherwise it must have crashed
[ "$(find .testoutput -maxdepth 1 -name 'junit.*.xml' | wc -l)" -lt "$MAX_TEST_ATTEMPTS" ] &&
CRASH_REPORT_NAME="$GITHUB_JOB" make report-test-crash
- name: Generate test summary
uses: mikepenz/action-junit-report@v5.0.0-rc01
if: failure()
with:
report_paths: ./.testoutput/junit.*.xml
detailed_summary: true
check_annotations: false
annotate_only: true
skip_annotations: true
- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: unit-test
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: unit-test
- name: Upload test results to GitHub
# Can't pin to major because the action linter doesn't recognize the include-hidden-files flag.
uses: actions/upload-artifact@v4.4.3
if: ${{ !cancelled() && !inputs.run_single_unit_test }}
with:
name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--unit-test
path: ./.testoutput/junit.*.xml
include-hidden-files: true
retention-days: 28
# Ensure this doesn't contribute to the junit output.
- name: Flaky Unit Test Detection
if: ${{ !cancelled() && !inputs.run_single_unit_test && env.MODIFIED_TEST_SUITES != '' }}
timeout-minutes: 30
run: |
echo "Detecting flaky unit tests: ${{ needs.set-up-single-test.outputs.modified_unit_test_suites }}"
make unit-test
env:
FAILED_TEST_RETRIES: "0" # not retrying failed tests intentionally here since we're trying to detect flakes
TEST_ARGS: "-run=${{ needs.set-up-single-test.outputs.modified_unit_test_suites }} -count=10"
TEST_TIMEOUT: 35
integration-test:
if: ${{ inputs.run_single_functional_test != true && inputs.run_single_unit_test != true }}
name: Integration test
needs: [pre-build, set-up-single-test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
- name: Start containerized dependencies
uses: hoverkraft-tech/compose-action@v2.0.1
with:
compose-file: ${{ env.DOCKER_COMPOSE_FILE }}
services: |
cassandra
mysql
postgresql
down-flags: -v
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- name: Run integration test
timeout-minutes: 15
run: make integration-test-coverage
- name: Generate crash report
if: failure()
run:
| # if the tests failed, we would expect one JUnit XML report per attempt; otherwise it must have crashed
[ "$(find .testoutput -maxdepth 1 -name 'junit.*.xml' | wc -l)" -lt "$MAX_TEST_ATTEMPTS" ] &&
CRASH_REPORT_NAME="$GITHUB_JOB" make report-test-crash
- name: Generate test summary
uses: mikepenz/action-junit-report@v5.0.0-rc01
if: failure()
with:
report_paths: ./.testoutput/junit.*.xml
detailed_summary: true
check_annotations: false
annotate_only: true
skip_annotations: true
- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: integration-test
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: integration-test
- name: Upload test results to GitHub
# Can't pin to major because the action linter doesn't recognize the include-hidden-files flag.
uses: actions/upload-artifact@v4.4.3
if: ${{ !cancelled() }}
with:
name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--integration-test
path: ./.testoutput/junit.*.xml
include-hidden-files: true
retention-days: 28
# Ensure this doesn't contribute to the junit output.
- name: Flaky Integration Test Detection
if: ${{ env.MODIFIED_TEST_SUITES != '' }}
timeout-minutes: 30
run: |
echo "Detecting flaky integration tests: ${{ needs.set-up-single-test.outputs.modified_integration_test_suites }}"
make integration-test-coverage
env:
FAILED_TEST_RETRIES: "0" # not retrying failed tests intentionally here since we're trying to detect flakes
TEST_ARGS: "-run=${{ needs.set-up-single-test.outputs.modified_integration_test_suites }} -count=5"
- name: Tear down docker compose
if: ${{ always() }}
run: |
docker compose -f ${{ env.DOCKER_COMPOSE_FILE }} down -v
# Root job name includes matrix details so it is unique per shard.
# This MUST stay in sync with the `job_name` passed to the job-id action below.
functional-test:
if: ${{ inputs.run_single_unit_test != true }}
# Display name shown in the UI. The job-id lookup uses this exact value.
name: Functional test (${{ matrix.name }}, shard ${{ matrix.shard_index }})
needs: [pre-build, set-up-single-test]
strategy:
fail-fast: false
matrix:
name:
- cass_es
- cass_es8
- cass_os2
- cass_os3
- sqlite
- mysql8
- postgres12
- postgres12_pgx
shard_index: ${{ fromJson(needs.set-up-single-test.outputs.shard_indices) }}
runs-on: ${{ fromJson(needs.set-up-single-test.outputs.runs_on) }}
include:
- name: cass_es
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, elasticsearch]
es_version: v7
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_es8
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, elasticsearch8]
es_version: v8
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_os2
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch2]
- name: cass_os3
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch3]
- name: sqlite
persistence_type: sql
persistence_driver: sqlite
containers: []
- name: mysql8
persistence_type: sql
persistence_driver: mysql8
containers: [mysql]
- name: postgres12
persistence_type: sql
persistence_driver: postgres12
containers: [postgresql]
- name: postgres12_pgx
persistence_type: sql
persistence_driver: postgres12_pgx
containers: [postgresql]
runs-on: ${{ matrix.runs_on_override || matrix.runs-on }}
env:
TEST_TOTAL_SHARDS: ${{ needs.set-up-single-test.outputs.total_shards }}
TEST_SHARD_INDEX: ${{ matrix.shard_index }}
PERSISTENCE_TYPE: ${{ matrix.persistence_type }}
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
TEST_TIMEOUT: ${{ needs.set-up-single-test.outputs.test_timeout }}
steps:
- uses: ScribeMD/docker-cache@0.3.7
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
with:
key: docker-${{ runner.os }}${{ runner.arch }}-${{ hashFiles(env.DOCKER_COMPOSE_FILE) }}
- uses: actions/checkout@v6
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
- name: Start containerized dependencies
if: ${{ toJson(matrix.containers) != '[]' && (inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER))) }}
uses: hoverkraft-tech/compose-action@v2.0.1
with:
compose-file: ${{ env.DOCKER_COMPOSE_FILE }}
services: "${{ join(matrix.containers, '\n') }}"
down-flags: -v
- uses: actions/setup-go@v6
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- name: Get job ID
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
id: get_job_id
uses: ./.github/actions/get-job-id
with:
job_name: Functional test (${{ matrix.name }}, shard ${{ matrix.shard_index }})
run_id: ${{ github.run_id }}
- name: Run functional test
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
timeout-minutes: ${{ fromJSON(needs.set-up-single-test.outputs.github_timeout) }} # make sure this is larger than the test timeout in the Makefile
run: ./develop/github/monitor_test.sh make functional-test-coverage
env:
TEST_ARGS: ${{ needs.set-up-single-test.outputs.single_test_args }}
TEMPORAL_TEST_OTEL_OUTPUT: ${{ github.workspace }}/.testoutput
TEMPORAL_OTEL_DEBUG: true
- name: Print memory snapshot
if: always()
run: if [ -f /tmp/memory_snapshot.txt ]; then cat /tmp/memory_snapshot.txt; fi
- name: Generate crash report
if: failure()
run:
| # if the tests failed, we would expect one JUnit XML report per attempt; otherwise it must have crashed
[ "$(find .testoutput -maxdepth 1 -name 'junit.*.xml' | wc -l)" -lt "$MAX_TEST_ATTEMPTS" ] &&
CRASH_REPORT_NAME="$GITHUB_JOB" make report-test-crash
- name: Generate test summary
uses: mikepenz/action-junit-report@v5.0.0-rc01
if: failure()
with:
report_paths: ./.testoutput/junit.*.xml
detailed_summary: true
check_annotations: false
annotate_only: true
skip_annotations: true
- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test
- name: Upload test results to GitHub
# Can't pin to major because the action linter doesn't recognize the include-hidden-files flag.
uses: actions/upload-artifact@v4.4.3
if: ${{ !cancelled() && !inputs.run_single_functional_test }}
with:
name: junit-xml--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{github.run_attempt}}--${{matrix.name}}--${{matrix.shard_index}}--functional-test
path: ./.testoutput/junit.*.xml
include-hidden-files: true
retention-days: 28
# Ensure this doesn't contribute to the junit output.
- name: Flaky Functional Test Detection
if: ${{ (env.MODIFIED_TEST_SUITES != '') && (inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER))) }}
timeout-minutes: ${{ fromJSON(needs.set-up-single-test.outputs.github_timeout) }} # make sure this is larger than the test timeout in the Makefile
run: |
echo "Detecting flaky functional tests: ${{ needs.set-up-single-test.outputs.modified_integration_test_suites }}"
make functional-test-coverage
env:
FAILED_TEST_RETRIES: "0" # not retrying failed tests intentionally here since we're trying to detect flakes
TEST_ARGS: "-run=${{ needs.set-up-single-test.outputs.modified_functional_test_suites }} -count=5"
# Upload OpenTelemetry traces.
- name: Upload OpenTelemetry traces
if: always()
uses: actions/upload-artifact@v4
with:
name: traces-json--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{matrix.shard_index}}--${{github.run_attempt}}--${{matrix.name}}--functional-test
path: ./.testoutput/traces.*.json
if-no-files-found: ignore
include-hidden-files: true
retention-days: 28
# XDC matrix job. Include `${{ matrix.name }}` in the display name so each
# matrix variant has a unique name for the job-id lookup.
functional-test-xdc:
if: ${{ inputs.run_single_functional_test != true && inputs.run_single_unit_test != true }}
# Display name shown in the UI. The job-id lookup uses this exact value.
name: Functional test xdc (${{ matrix.name }})
needs: [pre-build, set-up-single-test]
strategy:
fail-fast: false
matrix:
name:
- cass_es
- cass_es8
- cass_os2
- cass_os3
- mysql8
- postgres12
- postgres12_pgx
include:
- name: cass_es
persistence_type: nosql
persistence_driver: elasticsearch
parallel_flags: ""
containers: [cassandra, elasticsearch]
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_es8
persistence_type: nosql
persistence_driver: elasticsearch
parallel_flags: ""
containers: [cassandra, elasticsearch8]
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_os2
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch2]
- name: cass_os3
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch3]
- name: mysql8
persistence_type: sql
persistence_driver: mysql8
parallel_flags: ""
containers: [mysql]
- name: postgres12
persistence_type: sql
persistence_driver: postgres12
parallel_flags: "-parallel=2" # reduce parallelism for postgres
containers: [postgresql]
- name: postgres12_pgx
persistence_type: sql
persistence_driver: postgres12_pgx
parallel_flags: "-parallel=2" # reduce parallelism for postgres
containers: [postgresql]
runs-on: ${{ matrix.runs_on_override || 'ubuntu-latest' }}
env:
PERSISTENCE_TYPE: ${{ matrix.persistence_type }}
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
TEST_PARALLEL_FLAGS: ${{ matrix.parallel_flags }}
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
# Resolve the numeric job ID for this job instance.
# IMPORTANT: `job_name` must exactly match the job's display name above.
- name: Get job ID
id: get_job_id
uses: ./.github/actions/get-job-id
with:
job_name: Functional test xdc (${{ matrix.name }})
run_id: ${{ github.run_id }}
- name: Start containerized dependencies
uses: hoverkraft-tech/compose-action@v2.0.1
with:
compose-file: ${{ env.DOCKER_COMPOSE_FILE }}
services: "${{ join(matrix.containers, '\n') }}"
down-flags: -v
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- name: Run functional test xdc
timeout-minutes: 35 # update this to TEST_TIMEOUT+5 if you update the Makefile
run: ./develop/github/monitor_test.sh make functional-test-xdc-coverage
env:
TEMPORAL_TEST_OTEL_OUTPUT: ${{ github.workspace }}/.testoutput
TEMPORAL_OTEL_DEBUG: true
- name: Print memory snapshot
if: always()
run: if [ -f /tmp/memory_snapshot.txt ]; then cat /tmp/memory_snapshot.txt; fi
- name: Generate crash report
if: failure()
run:
| # if the tests failed, we would expect one JUnit XML report per attempt; otherwise it must have crashed
[ "$(find .testoutput -maxdepth 1 -name 'junit.*.xml' | wc -l)" -lt "$MAX_TEST_ATTEMPTS" ] &&
CRASH_REPORT_NAME="$GITHUB_JOB" make report-test-crash
- name: Generate test summary
uses: mikepenz/action-junit-report@v5.0.0-rc01
if: failure()
with:
report_paths: ./.testoutput/junit.*.xml
detailed_summary: true
check_annotations: false
annotate_only: true
skip_annotations: true
- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test-xdc
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test-xdc
- name: Upload test results to GitHub
# Can't pin to major because the action linter doesn't recognize the include-hidden-files flag.
uses: actions/upload-artifact@v4.4.3
if: ${{ !cancelled() }}
with:
name: junit-xml--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{github.run_attempt}}--${{matrix.name}}--functional-test-xdc
path: ./.testoutput
include-hidden-files: true
retention-days: 28
# Ensure this doesn't contribute to the junit output.
- name: Flaky Functional XDC Test Detection
if: ${{ env.MODIFIED_TEST_SUITES != '' }}
timeout-minutes: 60
run: |
echo "Detecting flaky functional xdc tests: ${{ needs.set-up-single-test.outputs.modified_functional_xdc_test_suites }}"
make functional-test-xdc-coverage
env:
FAILED_TEST_RETRIES: "0" # not retrying failed tests intentionally here since we're trying to detect flakes
TEST_ARGS: "-run=${{ needs.set-up-single-test.outputs.modified_functional_xdc_test_suites }} -count=5"
# Upload OpenTelemetry traces.
- name: Upload OpenTelemetry traces
if: always()
uses: actions/upload-artifact@v4
with:
name: traces-json--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{github.run_attempt}}--${{matrix.name}}--functional-test-xdc
path: ./.testoutput/traces.*.json
if-no-files-found: ignore
retention-days: 28
# NDC matrix job. Include `${{ matrix.name }}` in the display name so each
# matrix variant has a unique name for the job-id lookup.
functional-test-ndc:
if: ${{ inputs.run_single_functional_test != true && inputs.run_single_unit_test != true }}
# Display name shown in the UI. The job-id lookup uses this exact value.
name: Functional test ndc (${{ matrix.name }})
needs: [pre-build, set-up-single-test]
strategy:
fail-fast: false
matrix:
name:
- cass_es
- cass_es8
- cass_os2
- cass_os3
- mysql8
- postgres12
- postgres12_pgx
include:
- name: cass_es
persistence_type: nosql
persistence_driver: elasticsearch
containers: [cassandra, elasticsearch]
es_version: v7
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_es8
persistence_type: nosql
persistence_driver: elasticsearch
containers: [cassandra, elasticsearch8]
es_version: v8
# Cassandra tests need a larger instance
runs_on_override: ubuntu-latest-8-cores
- name: cass_os2
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch2]
- name: cass_os3
persistence_type: nosql
persistence_driver: cassandra
containers: [cassandra, opensearch3]
- name: mysql8
persistence_type: sql
persistence_driver: mysql8
containers: [mysql]
- name: postgres12
persistence_type: sql
persistence_driver: postgres12
containers: [postgresql]
- name: postgres12_pgx
persistence_type: sql
persistence_driver: postgres12_pgx
containers: [postgresql]
runs-on: ${{ matrix.runs_on_override || 'ubuntu-latest' }}
env:
PERSISTENCE_TYPE: ${{ matrix.persistence_type }}
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
ES_VERSION: ${{ matrix.es_version }}
steps:
- uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ env.COMMIT }}
# Resolve the numeric job ID for this job instance.
# IMPORTANT: `job_name` must exactly match the job's display name above.
- name: Get job ID
id: get_job_id
uses: ./.github/actions/get-job-id
with:
job_name: Functional test ndc (${{ matrix.name }})
run_id: ${{ github.run_id }}
- name: Start containerized dependencies
uses: hoverkraft-tech/compose-action@v2.0.1
with:
compose-file: ${{ env.DOCKER_COMPOSE_FILE }}
services: "${{ join(matrix.containers, '\n') }}"
down-flags: -v
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
cache: false # do our own caching
- name: Restore dependencies
uses: actions/cache/restore@v4
with:
path: ~/go/pkg/mod
key: go-${{ runner.os }}${{ runner.arch }}-${{ hashFiles('go.mod') }}-deps-${{ hashFiles('go.sum') }}
- name: Restore build outputs
uses: actions/cache/restore@v4
with:
path: ~/.cache/go-build
key: go-${{ runner.os }}${{ runner.arch }}-build-${{ env.COMMIT }}
- name: Run functional test ndc
timeout-minutes: 15
run: ./develop/github/monitor_test.sh make functional-test-ndc-coverage
env:
TEMPORAL_TEST_OTEL_OUTPUT: ${{ github.workspace }}/.testoutput
TEMPORAL_OTEL_DEBUG: true
- name: Print memory snapshot
if: always()
run: if [ -f /tmp/memory_snapshot.txt ]; then cat /tmp/memory_snapshot.txt; fi
- name: Generate crash report
if: failure()
run:
| # if the tests failed, we would expect one JUnit XML report per attempt; otherwise it must have crashed
[ "$(find .testoutput -maxdepth 1 -name 'junit.*.xml' | wc -l)" -lt "$MAX_TEST_ATTEMPTS" ] &&
CRASH_REPORT_NAME="$GITHUB_JOB" make report-test-crash
- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test-ndc
- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./.testoutput
flags: functional-test-ndc
- name: Upload test results to GitHub
# Can't pin to major because the action linter doesn't recognize the include-hidden-files flag.
uses: actions/upload-artifact@v4.4.3
if: ${{ !cancelled() }}
with:
name: junit-xml--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{github.run_attempt}}--${{matrix.name}}--functional-test-ndc
path: ./.testoutput/junit.*.xml
include-hidden-files: true
retention-days: 28
# Ensure this doesn't contribute to the junit output.
- name: Flaky Functional NDC Test Detection
if: ${{ env.MODIFIED_TEST_SUITES != '' }}
timeout-minutes: 30
run: |
echo "Detecting flaky functional ndc tests: ${{ needs.set-up-single-test.outputs.modified_functional_ndc_test_suites }}"
make functional-test-ndc-coverage
env:
FAILED_TEST_RETRIES: "0" # not retrying failed tests intentionally here since we're trying to detect flakes
TEST_ARGS: "-run=${{ needs.set-up-single-test.outputs.modified_functional_ndc_test_suites }} -count=5"
# Upload OpenTelemetry traces.
- name: Upload OpenTelemetry traces
if: always()
uses: actions/upload-artifact@v4
with:
name: traces-json--${{github.run_id}}--${{ steps.get_job_id.outputs.job_id }}--${{github.run_attempt}}--${{matrix.name}}--functional-test-ndc
path: ./.testoutput/traces.*.json
if-no-files-found: ignore
retention-days: 28
test-status:
if: always()
name: Test Status
needs:
- misc-checks
- unit-test
- integration-test
- functional-test
- functional-test-xdc
- functional-test-ndc
runs-on: ubuntu-latest
env:
RESULTS: ${{ toJSON(needs.*.result) }}
steps:
- name: Check results
run: |
if [[ "${{ inputs.run_single_functional_test }}" != "true" ]]; then
# if in all-tests mode, all statuses must be success
if [[ -n $(echo "$RESULTS" | jq '.[] | select (. != "success")') ]]; then
exit 1
fi
else
# if in single-test mode, skipped status is ok, failure is failure
if [[ -n $(echo "$RESULTS" | jq '.[] | select (. == "failure")') ]]; then
exit 1
fi
fi
notify-failure:
name: Notify Slack on Failure
if: |
always() &&
github.ref == 'refs/heads/main' &&
needs.test-status.result == 'failure'
needs: test-status
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
cache: true
- name: Send Slack notification
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
GH_TOKEN: ${{ github.token }}
run: |
go run ./cmd/tools/ci-notify \
--run-id "${{ github.run_id }}" \
--slack-webhook "$SLACK_WEBHOOK"