Skip to content

🐛 Use allowed-failures and skips equally#24

Open
webknjaz wants to merge 2 commits intounstable/v1from
bugfixes/23-allowed-failures
Open

🐛 Use allowed-failures and skips equally#24
webknjaz wants to merge 2 commits intounstable/v1from
bugfixes/23-allowed-failures

Conversation

@webknjaz
Copy link
Member

Before this patch, allowed-failures did not contribute to the overall computed outcome due to a bug in the checking logic. This change refactors the data strucutures used to simplify the check and reduce its complexity in general.

Fixes #23

@webknjaz webknjaz added the bug Something isn't working label Aug 25, 2023
@webknjaz webknjaz self-assigned this Aug 25, 2023
@webknjaz webknjaz force-pushed the bugfixes/23-allowed-failures branch 2 times, most recently from bd0610c to ccb1cc9 Compare July 24, 2025 22:30
Before this patch, `allowed-failures` did not contribute to the
overall computed outcome due to a bug in the checking logic. This
change refactors the data strucutures used to simplify the check and
reduce its complexity in general.

Fixes #23
@webknjaz webknjaz force-pushed the bugfixes/23-allowed-failures branch from 0a2e10a to 646d949 Compare February 8, 2026 21:29
Copilot AI review requested due to automatic review settings February 8, 2026 21:29
@codecov
Copy link

codecov bot commented Feb 8, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
5 1 4 1
View the top 1 failed test(s) by shortest run time
tests/smoke_test.py::test_smoke[success-despite-failure-and-skip]
Stack Traces | 0.024s run time
allowed_failures = '["failing-job", "skipped-job"]', allowed_skips = ''
jobs = '{"failing-job": {"result": "failure", "outputs": {}}, "succeeding-job": {"result": "success", "outputs": {}}, "skipped-job": {"result": "skipped", "outputs": {}}}'
expected_return_code = 0
expected_outputs = {'failure=false', 'result=success', 'success=true'}
expected_summary_entries = {'All of the required dependency jobs succeeded', 'Some of the allowed to be skipped jobs did not succeed', 'Some of t...re [allowed to fail]', 'skipped-job → ⬜ skipped [allowed to fail]', 'succeeding-job → ✓ success [required to succeed]'}
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3910bb29e0>
tmp_path = PosixPath('.../pytest-of-runner/pytest-0/test_smoke_success_despite_fai0')

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.mark.parametrize(#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mallowed_failures#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mallowed_skips#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mjobs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mexpected_return_code#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mexpected_outputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            #x1B[33m'#x1B[39;49;00m#x1B[33mexpected_summary_entries#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
        (#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                json.dumps([#x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mskipped-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                json.dumps(#x1B[90m#x1B[39;49;00m
                    {#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mfailure#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mskipped-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mskipped#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                    },#x1B[90m#x1B[39;49;00m
                ),#x1B[90m#x1B[39;49;00m
                #x1B[94m0#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mfailure=false#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mresult=success#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33msuccess=true#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                {#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the required dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to fail jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to be skipped jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job → ❌ failure [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mskipped-job → ⬜ skipped [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                },#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33msuccess-despite-failure-and-skip#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mcheck-links-markdown, nightly#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                json.dumps([]),#x1B[90m#x1B[39;49;00m
                json.dumps(#x1B[90m#x1B[39;49;00m
                    {#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mbuild-web#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mcheck-links-book#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mcheck-links-markdown#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mfailure#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mlint-megalinter#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mnightly#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mfailure#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mpublish-web#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mskipped#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-dotnet#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-elixir#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-java#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-js#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-lib#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-php#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-python#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-rust#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mtest-rust-main#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                    },#x1B[90m#x1B[39;49;00m
                ),#x1B[90m#x1B[39;49;00m
                #x1B[94m1#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mfailure=true#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mresult=failure#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33msuccess=false#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                {#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the required to succeed jobs failed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to fail jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to be skipped jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mbuild-web → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mcheck-links-book → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mcheck-links-markdown → ❌ failure [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mlint-megalinter → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mnightly → ❌ failure [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mpublish-web → ⬜ skipped [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-dotnet → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-elixir → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-java → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-js → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-lib → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-php → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-python → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-rust → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mtest-rust-main → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                },#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33mfailure-due-to-skip#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                json.dumps(#x1B[90m#x1B[39;49;00m
                    {#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                    },#x1B[90m#x1B[39;49;00m
                ),#x1B[90m#x1B[39;49;00m
                #x1B[94m0#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mfailure=false#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mresult=success#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33msuccess=true#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                {#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the required dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the allowed to fail dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the allowed to be skipped dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job → ✓ success [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                },#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33msuccess-of-all-allowed-to-skip-or-fail#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                json.dumps(#x1B[90m#x1B[39;49;00m
                    {#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33mfailure#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                    },#x1B[90m#x1B[39;49;00m
                ),#x1B[90m#x1B[39;49;00m
                #x1B[94m1#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mfailure=false#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mresult=success#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33msuccess=true#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                {#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the required dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to fail jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mSome of the allowed to be skipped jobs did not succeed#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mfailing-job → ❌ failure [allowed to fail]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                },#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33msuccess-of-some-allowed-to-skip-or-fail#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                marks=pytest.mark.xfail(reason=#x1B[33m'#x1B[39;49;00m#x1B[33mThis is a bug to fix#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                json.dumps(#x1B[90m#x1B[39;49;00m
                    {#x1B[90m#x1B[39;49;00m
                        #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33mresult#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: #x1B[33m'#x1B[39;49;00m#x1B[33msuccess#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                            #x1B[33m'#x1B[39;49;00m#x1B[33moutputs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m: {},#x1B[90m#x1B[39;49;00m
                        },#x1B[90m#x1B[39;49;00m
                    },#x1B[90m#x1B[39;49;00m
                ),#x1B[90m#x1B[39;49;00m
                #x1B[94m0#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mfailure=false#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33mresult=success#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[33m'#x1B[39;49;00m#x1B[33msuccess=true#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                {#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33mAll of the required dependency jobs succeeded#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                    #x1B[33m'#x1B[39;49;00m#x1B[33msucceeding-job → ✓ success [required to succeed]#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                },#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33meverything-required#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33m{}#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[94m1#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
                #x1B[96mset#x1B[39;49;00m(),#x1B[90m#x1B[39;49;00m
                {#x1B[33m'#x1B[39;49;00m#x1B[33mInvalid input jobs matrix#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m},#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33m'#x1B[39;49;00m#x1B[33mfailure-due-to-empty-jobs#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
        ),#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_smoke#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        allowed_failures: #x1B[96mstr#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        allowed_skips: #x1B[96mstr#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        jobs: #x1B[96mstr#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        expected_return_code: #x1B[96mint#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
        expected_outputs: #x1B[96mset#x1B[39;49;00m[#x1B[96mstr#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        expected_summary_entries: #x1B[96mset#x1B[39;49;00m[#x1B[96mstr#x1B[39;49;00m],#x1B[90m#x1B[39;49;00m
        monkeypatch: pytest.MonkeyPatch,#x1B[90m#x1B[39;49;00m
        tmp_path: pathlib.Path,#x1B[90m#x1B[39;49;00m
    ) -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""Validate all known scenarios."""#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        gh_step_summary_path = tmp_path / #x1B[33m'#x1B[39;49;00m#x1B[33mgh_step_summary#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        gh_output_path = tmp_path / #x1B[33m'#x1B[39;49;00m#x1B[33mgh_output#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        monkeypatch.setenv(#x1B[33m'#x1B[39;49;00m#x1B[33mGITHUB_STEP_SUMMARY#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[96mstr#x1B[39;49;00m(gh_step_summary_path))#x1B[90m#x1B[39;49;00m
        monkeypatch.setenv(#x1B[33m'#x1B[39;49;00m#x1B[33mGITHUB_OUTPUT#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m, #x1B[96mstr#x1B[39;49;00m(gh_output_path))#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        helper_return_code = _invoke_helper_cli(#x1B[90m#x1B[39;49;00m
            [#x1B[90m#x1B[39;49;00m
                sys.executable,#x1B[90m#x1B[39;49;00m
                allowed_failures,#x1B[90m#x1B[39;49;00m
                allowed_skips,#x1B[90m#x1B[39;49;00m
                jobs,#x1B[90m#x1B[39;49;00m
            ],#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
>       #x1B[94massert#x1B[39;49;00m helper_return_code == expected_return_code#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE       assert 1 == 0#x1B[0m

allowed_failures = '["failing-job", "skipped-job"]'
allowed_skips = ''
expected_outputs = {'failure=false', 'result=success', 'success=true'}
expected_return_code = 0
expected_summary_entries = {'All of the required dependency jobs succeeded', 'Some of the allowed to be skipped jobs did not succeed', 'Some of t...re [allowed to fail]', 'skipped-job → ⬜ skipped [allowed to fail]', 'succeeding-job → ✓ success [required to succeed]'}
gh_output_path = PosixPath('.../pytest-of-runner/pytest-0/test_smoke_success_despite_fai0/gh_output')
gh_step_summary_path = PosixPath('.../pytest-of-runner/pytest-0/test_smoke_success_despite_fai0/gh_step_summary')
helper_return_code = 1
jobs       = '{"failing-job": {"result": "failure", "outputs": {}}, "succeeding-job": {"result": "success", "outputs": {}}, "skipped-job": {"result": "skipped", "outputs": {}}}'
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3910bb29e0>
tmp_path   = PosixPath('.../pytest-of-runner/pytest-0/test_smoke_success_despite_fai0')

#x1B[1m#x1B[31mtests/smoke_test.py#x1B[0m:242: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the GitHub Action helper that aggregates needs.*.result values so that allowed-failures and allowed-skips contribute to the overall computed outcome via a unified “allowed outcomes per job” mapping (fixing incorrect aggregation scenarios like Issue #23).

Changes:

  • Replace split all(...) and all(...) aggregation logic with an allowed_outcome_map lookup per job.
  • Add verbose debug print(...) statements to show inputs/intermediate decisions during aggregation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +169 to 172
print(f'{jobs=}')
print(f'{jobs_allowed_to_fail=}')
print(f'{jobs_allowed_to_be_skipped=}')

Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added debug print(f'{jobs=}') (and the following prints) can leak potentially sensitive data into GitHub Actions logs. jobs includes each needed job’s outputs, which may contain secrets depending on workflow usage. Please remove these prints or gate them behind an explicit debug flag and ensure any logged data excludes job outputs.

Suggested change
print(f'{jobs=}')
print(f'{jobs_allowed_to_fail=}')
print(f'{jobs_allowed_to_be_skipped=}')
# Optional debug logging: enable by setting DEBUG_NORMALIZE_NEEDED_JOBS=1.
# Only non-sensitive information (job names and results) is logged; job outputs are excluded.
if os.getenv('DEBUG_NORMALIZE_NEEDED_JOBS') == '1':
jobs_debug_view = {name: {'result': job['result']} for name, job in jobs.items()}
print(f'{jobs_debug_view=}')
print(f'{jobs_allowed_to_fail=}')
print(f'{jobs_allowed_to_be_skipped=}')

Copilot uses AI. Check for mistakes.
if job_name in jobs_allowed_to_be_skipped:
allowed_outcome_map[job_name].add('skipped')
if job_name in jobs_allowed_to_fail:
allowed_outcome_map[job_name].add('failure')
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowed_outcome_map currently allows only {success, failure} for jobs in allowed_failures. This changes existing behavior and breaks the documented/tested semantics where an allowed-failure job may also be skipped without affecting the overall outcome (see tests like success-despite-failure-and-skip). Consider adding 'skipped' (and potentially 'cancelled' to match “don’t succeed” wording) to the allowed outcomes for allowed_failures, while keeping allowed_skips limited to allowing 'skipped' only.

Suggested change
allowed_outcome_map[job_name].add('failure')
allowed_outcome_map[job_name].update({'failure', 'skipped', 'cancelled'})

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +220
print(f'{allowed_outcome_map=}')
print(
f"""{
[
job['result'] == 'success' for name, job in jobs.items()
if name not in (jobs_allowed_to_fail | jobs_allowed_to_be_skipped)
]=
}""",
)
print(
f"""{
[
(name, job['result'] in {'skipped', 'success'}) for name, job in jobs.items()
if name in jobs_allowed_to_be_skipped
]=
}""",
)
job_matrix_succeeded = all(
job['result'] == 'success'
job['result'] in allowed_outcome_map[name]
for name, job in jobs.items()
if name not in (jobs_allowed_to_fail | jobs_allowed_to_be_skipped)
) and all(
job['result'] in {'skipped', 'success'}
for name, job in jobs.items()
if name in jobs_allowed_to_be_skipped
)
print(f'{job_matrix_succeeded=}')
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple additional debug prints here (including printing computed lists and job_matrix_succeeded) that will add noisy log output for every action run. Please remove them or use a structured/conditional debug logger controlled by an input/env var so normal runs stay clean.

Copilot uses AI. Check for mistakes.
@webknjaz webknjaz moved this from 🤔 Brain 🧠 dump 💡 to 🚧 In progress 🚧 in 📅 Procrastinating in public 😵‍💫 Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Possibly incorrect aggregation?

1 participant

Comments