Skip to content

Commit 8e29f77

Browse files
npownpowclaude
authored
Testing Infra Enhancements (#3040)
1. Refactor internal netflix tests to parameterize orchestrator/backend and moved the tests to this repo 2. Setup local airflow/argo/sfn and k8s/batch to run in GHA 3. Report coverage 4. Split Tiltfile --------- Co-authored-by: npow <npow@netflix.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 253de86 commit 8e29f77

File tree

106 files changed

+7826
-685
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+7826
-685
lines changed

.coveragerc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[run]
2+
source = metaflow
3+
branch = true
4+
parallel = true
5+
omit =
6+
metaflow/tutorials/*
7+
*/test/*
8+
*/__pycache__/*
9+
10+
[report]
11+
show_missing = true
12+
skip_covered = false
13+
skip_empty = true
14+
precision = 1
15+
16+
[html]
17+
directory = htmlcov
18+
title = Metaflow UX Test Coverage
19+
20+
[xml]
21+
output = coverage.xml
22+
23+
[paths]
24+
# Allow coverage combine to reconcile paths across GHA jobs
25+
# (all jobs check out to the same GITHUB_WORKSPACE path so this is a no-op
26+
# in practice, but makes local combine easier if paths differ)
27+
source =
28+
metaflow/
29+
*/metaflow/
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
name: Run Metaflow Core UX Tests
2+
description: >
3+
Checks out metaflow, installs it alongside an optional extension, writes the
4+
provided ux_test_config.yaml, and runs test/ux/core/ with pytest.
5+
Caller is responsible for setting up any external scheduler before invoking
6+
this action, and for setting relevant env vars at the job level.
7+
8+
inputs:
9+
metaflow-ref:
10+
description: metaflow branch, tag, or SHA to check out and test against
11+
default: master
12+
extra-install:
13+
description: >
14+
pip install target for the extension (e.g. "." to install from CWD,
15+
or a git URL). Leave empty to test metaflow alone.
16+
required: false
17+
default: ""
18+
ux-test-config:
19+
description: Full YAML content to write to test/ux/ux_test_config.yaml
20+
required: true
21+
pytest-args:
22+
description: Additional arguments passed verbatim to pytest
23+
default: "-n 4 -v --tb=short --timeout=120 --cov-fail-under=0"
24+
test-files:
25+
description: Space-separated test file names (relative to test/ux/core/)
26+
default: "test_basic.py test_config.py test_dag.py"
27+
use-sudo:
28+
description: >
29+
Run pytest under sudo -E. Required when the scheduler creates
30+
root-owned artifact files that a non-root test process cannot read.
31+
default: "false"
32+
codecov-token:
33+
description: Codecov upload token. Coverage upload is skipped if not provided.
34+
required: false
35+
default: ""
36+
coverage-artifact-suffix:
37+
description: >
38+
Suffix appended to the coverage artifact name to make it unique when
39+
called from a matrix job. Pass the matrix key (e.g. matrix.test) here.
40+
Defaults to empty (artifact name = "coverage-<job>").
41+
required: false
42+
default: ""
43+
conda:
44+
description: >
45+
Set to "false" to skip Miniconda installation and exclude @conda tests.
46+
Defaults to "true" — all orchestrators should verify conda support.
47+
Set to "false" only if the scheduler cannot support conda (e.g. needs
48+
conda inside a Docker container with separate setup).
49+
required: false
50+
default: "true"
51+
52+
runs:
53+
using: composite
54+
steps:
55+
- name: Checkout metaflow
56+
uses: actions/checkout@v4
57+
with:
58+
repository: Netflix/metaflow
59+
ref: ${{ inputs.metaflow-ref }}
60+
path: _metaflow_src
61+
62+
- name: Write ux_test_config.yaml
63+
shell: bash
64+
env:
65+
UX_TEST_CONFIG: ${{ inputs.ux-test-config }}
66+
run: printf '%s' "$UX_TEST_CONFIG" > _metaflow_src/test/ux/ux_test_config.yaml
67+
68+
- name: Install Miniconda
69+
if: ${{ inputs.conda == 'true' }}
70+
uses: conda-incubator/setup-miniconda@v3
71+
with:
72+
auto-activate-base: true
73+
activate-environment: ""
74+
75+
- name: Install metaflow and test dependencies
76+
shell: bash
77+
run: |
78+
pip install -e "_metaflow_src[dev]"
79+
pip install pytest-xdist pytest-timeout pytest-cov omegaconf
80+
if [ -n "${{ inputs.extra-install }}" ]; then
81+
pip install -e "${{ inputs.extra-install }}"
82+
fi
83+
84+
- name: Run UX tests
85+
shell: bash
86+
env:
87+
PYTHONPATH: ${{ github.workspace }}/_metaflow_src
88+
# Pre-set these so test/ux/core/conftest.py's setdefault() calls don't
89+
# override them with devstack-specific values (devtools/.devtools/ and
90+
# profile 'local', which don't exist outside the metaflow devstack).
91+
METAFLOW_HOME: /tmp/metaflow-ux-home
92+
METAFLOW_PROFILE: ""
93+
run: |
94+
files=$(echo "${{ inputs.test-files }}" \
95+
| tr ' ' '\n' \
96+
| sed "s|^|$GITHUB_WORKSPACE/_metaflow_src/test/ux/core/|" \
97+
| tr '\n' ' ')
98+
99+
if [ "${{ inputs.conda }}" = "true" ]; then
100+
mark_args=()
101+
else
102+
mark_args=(-m "not conda")
103+
fi
104+
105+
suffix="${{ inputs.coverage-artifact-suffix }}"
106+
if [ -n "$suffix" ]; then
107+
junit_file="junit-${{ github.job }}-${suffix}.xml"
108+
else
109+
junit_file="junit-${{ github.job }}.xml"
110+
fi
111+
112+
pytest_cmd=(
113+
python -m pytest $files
114+
"${mark_args[@]}"
115+
${{ inputs.pytest-args }}
116+
--cov=metaflow
117+
--cov-report=xml:coverage.xml
118+
--cov-report=html:htmlcov
119+
--junitxml="$junit_file"
120+
)
121+
122+
if [ "${{ inputs.use-sudo }}" = "true" ]; then
123+
sudo -E env "PATH=$PATH" "PYTHONPATH=$PYTHONPATH" "${pytest_cmd[@]}"
124+
else
125+
"${pytest_cmd[@]}"
126+
fi
127+
128+
- name: Set coverage artifact name
129+
if: always()
130+
shell: bash
131+
id: coverage-name
132+
run: |
133+
suffix="${{ inputs.coverage-artifact-suffix }}"
134+
if [ -n "$suffix" ]; then
135+
echo "name=coverage-${{ github.job }}-${suffix}" >> "$GITHUB_OUTPUT"
136+
else
137+
echo "name=coverage-${{ github.job }}" >> "$GITHUB_OUTPUT"
138+
fi
139+
140+
- name: Upload coverage artifact
141+
if: always()
142+
uses: actions/upload-artifact@v4
143+
with:
144+
name: ${{ steps.coverage-name.outputs.name }}
145+
path: |
146+
.coverage
147+
coverage.xml
148+
htmlcov/
149+
if-no-files-found: ignore
150+
include-hidden-files: true
151+
152+
- name: Set JUnit artifact name
153+
if: always()
154+
shell: bash
155+
id: junit_name
156+
run: |
157+
suffix="${{ inputs.coverage-artifact-suffix }}"
158+
if [ -n "$suffix" ]; then
159+
echo "name=junit-${{ github.job }}-${suffix}" >> "$GITHUB_OUTPUT"
160+
echo "file=junit-${{ github.job }}-${suffix}.xml" >> "$GITHUB_OUTPUT"
161+
else
162+
echo "name=junit-${{ github.job }}" >> "$GITHUB_OUTPUT"
163+
echo "file=junit-${{ github.job }}.xml" >> "$GITHUB_OUTPUT"
164+
fi
165+
166+
- name: Upload JUnit XML artifact
167+
if: always()
168+
uses: actions/upload-artifact@v4
169+
with:
170+
name: ${{ steps.junit_name.outputs.name }}
171+
path: ${{ steps.junit_name.outputs.file }}
172+
if-no-files-found: ignore
173+
174+
- name: Upload coverage to Codecov
175+
if: ${{ inputs.codecov-token != '' }}
176+
uses: codecov/codecov-action@v5
177+
with:
178+
token: ${{ inputs.codecov-token }}
179+
files: coverage.xml

0 commit comments

Comments
 (0)