Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 2 additions & 20 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,8 @@ jobs:
- macos-latest
- macos-26-intel
- ubuntu-latest
# - windows-latest # skip for now
# No Windows leg since 2023, and no Windows steps (#1967): adding the leg
# back means writing its steps, as test_conda_no_windows_steps_1967 pins.
# #1633, #1660: the supported tier only. No KTLO 3.10 leg: the list
# util/conda_vendor.py derives omits typing-extensions, which
# pcapkit/utilities/compat.py imports below 3.11, and pins each
Expand Down Expand Up @@ -1032,9 +1033,6 @@ jobs:
ubuntu-latest)
target_platform="linux-64"
;;
windows-latest)
target_platform="win-64"
;;
*)
target_platform=""
;;
Expand Down Expand Up @@ -1083,31 +1081,18 @@ jobs:
# installed here: on ``macos-26-intel`` they resolve a cryptography with
# no Intel wheel, which pip has to compile or fail on.
- name: Install and Setup
if: ${{ matrix.os != 'windows-latest' }}
run: |
set -x

python -m pip install -U pip setuptools wheel

- name: Install and Setup (Windows)
if: ${{ matrix.os == 'windows-latest' }}
run: |
python -m pip install -U pip setuptools wheel

- name: Setup Conda Distribution
if: ${{ matrix.os != 'windows-latest' }}
run: |
set -x

mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

- name: Setup Conda Distribution (Windows)
if: ${{ matrix.os == 'windows-latest' }}
run: |
mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

# With `activate-environment: base`, `python-version` installs that Python
# into base, next to conda itself. conda-forge's conda has no py315 build
# yet, so on 3.15 that fails with "RemoveError: Conda cannot remove
Expand Down Expand Up @@ -1173,9 +1158,6 @@ jobs:
ubuntu-latest)
target_platform="linux-64"
;;
windows-latest)
target_platform="win-64"
;;
*)
target_platform=""
;;
Expand Down
19 changes: 2 additions & 17 deletions .github/workflows/cron-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@ jobs:
- macos-latest
- macos-26-intel
- ubuntu-latest
# - windows-latest # skip for now
# No Windows leg since 2023, and no Windows steps (#1967): adding the leg
# back means writing its steps, as test_conda_no_windows_steps_1967 pins.
# #1633: the supported tier, from .github/python-versions.toml through
# `conda-update` (#1660); create-release.yml's ``conda`` job says why
# there is no KTLO 3.10 leg.
Expand All @@ -433,34 +434,21 @@ jobs:
# #1854: pcapkit is not installed here; see create-release.yml's step of
# the same name.
- name: Install and Setup
if: ${{ matrix.os != 'windows-latest' }}
run: |
set -x

python -m pip install -U pip setuptools wheel

- name: Install and Setup (Windows)
if: ${{ matrix.os == 'windows-latest' }}
run: |
python -m pip install -U pip setuptools wheel

# #1827: pip evaluates conda/requirements.txt's platform markers here,
# on this leg's own runner, so Intel macOS fetches its own cryptography
# pin; the recipe's build.sh installs whatever this downloaded.
- name: Setup Conda Distribution
if: ${{ matrix.os != 'windows-latest' }}
run: |
set -x

mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

- name: Setup Conda Distribution (Windows)
if: ${{ matrix.os == 'windows-latest' }}
run: |
mkdir -p conda/wheels
python -m pip download -r conda/requirements.txt -d conda/wheels

# See create-release.yml's step of the same name for the 3.15 exception.
- name: Conda environment creation and activation
uses: conda-incubator/setup-miniconda@be893c923ea9cf1cf7cd510fbdde27c7e18cbdcb # v4.1.0
Expand Down Expand Up @@ -514,9 +502,6 @@ jobs:
ubuntu-latest)
target_platform="linux-64"
;;
windows-latest)
target_platform="win-64"
;;
*)
target_platform=""
;;
Expand Down
2 changes: 1 addition & 1 deletion tests/project/test_conda_legs_install_no_pcapkit_1854.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def test_each_install_step_upgrades_the_build_tools_alone(self) -> None:
steps = [step for found, job, step in self.steps() if (found, job) == (name, job_id)
and str(step.get('name', '')).startswith('Install and Setup')]
with self.subTest(workflow=name, job=job_id):
self.assertEqual(len(steps), 2, 'the Linux/macOS step and the Windows one')
self.assertEqual(len(steps), 1, 'one step for every leg, with no Windows twin (#1967)')
for step in steps:
with self.subTest(workflow=name, job=job_id, step=step['name']):
self.assertEqual(installs(step), [BUILD_TOOLS])
Expand Down
113 changes: 113 additions & 0 deletions tests/project/test_conda_no_windows_steps_1967.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
"""#1967: the conda jobs keep no step or ``case`` arm for a runner they have no leg on.

``conda`` in :file:`.github/workflows/create-release.yml` and ``conda-dist`` in
:file:`.github/workflows/cron-conda.yml` have had no ``windows-latest`` leg since
2023, yet carried ``Install and Setup (Windows)``, ``Setup Conda Distribution
(Windows)``, the ``matrix.os != 'windows-latest'`` guards on their twins, and a
``windows-latest`` arm in each ``case "${{ matrix.os }}"``: code no run reached.

* :class:`TestNoDeadRunnerCode` pins that no step's ``if:`` names a runner label
the job's matrix lacks, no ``case`` arm maps one, and, with no Windows leg, no
step is Windows-only. So adding a Windows leg back means adding its steps back
deliberately, rather than reviving ones nobody has run.
* :class:`TestTheCheckSeesDeadCode` feeds :func:`dead` the jobs as they stood
before, rebuilt from the current ones, so the pin cannot pass by reading nothing.

"""

from __future__ import annotations

import copy
import re
import unittest
from typing import TYPE_CHECKING

from tests import _workflows

if TYPE_CHECKING:
from typing import Any

#: Test tier (#1958): reads the workflow files, a question about the tree.
__tier__ = 'LATEST'

#: The conda jobs, as (workflow, job).
JOBS = (('create-release.yml', 'conda'), ('cron-conda.yml', 'conda-dist'))
#: A ``matrix.os`` comparison in an ``if:``, capturing the label compared against.
GUARD = re.compile(r"matrix\.os\s*[!=]=\s*'([^']+)'")
#: A ``case "${{ matrix.os }}" in`` block of a step's ``run:``.
CASE = re.compile(r'case "\$\{\{ matrix\.os \}\}" in\n(.*?)\n\s*esac', re.DOTALL)
#: An arm of that block, capturing its pattern.
ARM = re.compile(r'^\s*([^\s()]+)\)\s*$', re.MULTILINE)
#: A Windows runner label, or the step name of a Windows-only step.
WINDOWS = re.compile(r'windows', re.IGNORECASE)
#: An ``if:`` that runs its step on Windows alone.
WINDOWS_ONLY = re.compile(r"(?:matrix|runner)\.os\s*==\s*'[^']*windows", re.IGNORECASE)


def dead(spec: 'dict[str, Any]') -> 'list[str]':
"""What of ``spec``, a job, names a runner its matrix has no leg on."""
labels = list(spec['strategy']['matrix']['os'])
windows_leg = any(WINDOWS.search(label) for label in labels)
found = []
for step in spec['steps']:
name = str(step.get('name') or step.get('uses') or step.get('id'))
condition = str(step.get('if', ''))
for label in GUARD.findall(condition):
if label not in labels:
found.append(f'{name}: `if:` compares matrix.os with {label}, which is no leg')
if not windows_leg and (WINDOWS_ONLY.search(condition) or WINDOWS.search(str(step.get('name', '')))):
found.append(f'{name}: Windows-only, and no leg is Windows')
for block in CASE.findall(str(step.get('run', ''))):
for pattern in ARM.findall(block):
if pattern != '*' and pattern not in labels:
found.append(f'{name}: `case` arm {pattern}, which is no leg')
return found


class TestNoDeadRunnerCode(unittest.TestCase):
"""Each conda job's steps and ``case`` arms cover its legs and nothing else."""

def test_no_step_or_arm_names_a_missing_leg(self) -> None:
for name, job_id in JOBS:
with self.subTest(workflow=name, job=job_id):
self.assertEqual(dead(_workflows.job(name, job_id)), [])


class TestTheCheckSeesDeadCode(unittest.TestCase):
""":func:`dead` reports each kind of dead code the jobs held before #1967."""

def test_it_reports_a_windows_step_guard_and_arm(self) -> None:
for name, job_id in JOBS:
spec = copy.deepcopy(_workflows.job(name, job_id))
install = next(step for step in spec['steps'] if step.get('name') == 'Install and Setup')
install['if'] = "${{ matrix.os != 'windows-latest' }}"
spec['steps'].append({'name': 'Install and Setup (Windows)',
'if': "${{ matrix.os == 'windows-latest' }}",
'run': 'python -m pip install -U pip setuptools wheel\n'})
spec['steps'].append({'name': 'Map the leg', 'run': (
'case "${{ matrix.os }}" in\n'
' windows-latest)\n'
' target_platform="win-64"\n'
' ;;\n'
' *)\n'
' target_platform=""\n'
' ;;\n'
'esac\n')})
with self.subTest(workflow=name, job=job_id):
self.assertEqual(dead(spec), [
'Install and Setup: `if:` compares matrix.os with windows-latest, which is no leg',
'Install and Setup (Windows): `if:` compares matrix.os with windows-latest, which is no leg',
'Install and Setup (Windows): Windows-only, and no leg is Windows',
'Map the leg: `case` arm windows-latest, which is no leg',
])

def test_a_windows_leg_allows_its_own_steps(self) -> None:
spec = {'strategy': {'matrix': {'os': ['ubuntu-latest', 'windows-latest']}},
'steps': [{'name': 'Setup (Windows)', 'if': "${{ matrix.os == 'windows-latest' }}",
'run': 'case "${{ matrix.os }}" in\n windows-latest)\n x=1\n ;;\nesac\n'}]}
self.assertEqual(dead(spec), [])


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion util/conda_vendor.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
#: ``sys.platform``, ``platform.system()`` and ``platform.machine()`` read on its
#: runner: ``ubuntu-latest``, the Intel ``macos-26-intel`` and the arm64
#: ``macos-latest``. Each leg publishes only its own runner's platform (#1822).
#: Their Windows legs are commented out, and with them the recipe's ``bld.bat``.
#: They have no Windows leg (#1967), so the recipe's ``bld.bat`` goes unused.
PLATFORMS = {
'linux-64': ('linux', 'Linux', 'x86_64'),
'osx-64': ('darwin', 'Darwin', 'x86_64'),
Expand Down
Loading