Fix the PATH mangling in action.yml (#2723)
* Fix the PATH mangling in action.yml Let's put the `uv` on PATH question aside, and fix #2663. This change ensures that the PATH entries added by Github when you request `shell: bash` don't make it into the pwsh environment. It also keeps the uv binary on PATH if installed via `extras:`. Would love to get this fixed soon, then we can figure out the right approach regarding `uv` discovery. * Modify script to allow testing of the GHA action on a PR Cherry-picked from #2718 and modified so we can test the action.yml file
This commit is contained in:
+25
-21
@@ -69,49 +69,53 @@ runs:
|
||||
venv_path = Path(r"${{ runner.temp }}") / "cibw"
|
||||
if venv_path.exists():
|
||||
shutil.rmtree(venv_path)
|
||||
|
||||
builder = EnvBuilder()
|
||||
builder.create(venv_path)
|
||||
exposed_binaries = {"cibuildwheel"}
|
||||
|
||||
if "uv" in EXTRAS:
|
||||
exposed_binaries.add("uv")
|
||||
clean_bin_path = builder.bin_path.parent / f"{builder.bin_path.name}.clean"
|
||||
clean_bin_path.mkdir()
|
||||
|
||||
for path in list(builder.bin_path.iterdir()):
|
||||
if path.stem in exposed_binaries:
|
||||
try:
|
||||
os.symlink(path, clean_bin_path / path.name)
|
||||
except OSError:
|
||||
import shutil
|
||||
|
||||
shutil.copy2(path, clean_bin_path / path.name)
|
||||
full_path = f"{clean_bin_path}{os.pathsep}{os.environ['PATH']}"
|
||||
|
||||
cibw_bin = [p for p in builder.bin_path.glob("cibuildwheel*") if p.stem == "cibuildwheel"][0]
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "at") as f:
|
||||
f.write(f"updated-path={full_path}\n")
|
||||
f.write(f"cibw-bin={cibw_bin}\n")
|
||||
f.write(f"prepend-path={clean_bin_path}\n")
|
||||
|
||||
print("::endgroup::")
|
||||
EOF
|
||||
shell: bash
|
||||
|
||||
# Redirecting stderr to stdout to fix interleaving issue in Actions.
|
||||
- run: >
|
||||
cibuildwheel
|
||||
"${{ inputs.package-dir }}"
|
||||
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}}
|
||||
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}}
|
||||
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}}
|
||||
2>&1
|
||||
env:
|
||||
PATH: "${{ steps.cibw.outputs.updated-path }}"
|
||||
- run: |
|
||||
export PATH="${{ steps.cibw.outputs.prepend-path }}:$PATH"
|
||||
|
||||
"${{ steps.cibw.outputs.cibw-bin }}" \
|
||||
"${{ inputs.package-dir }}" \
|
||||
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}} \
|
||||
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}} \
|
||||
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}} \
|
||||
2>&1
|
||||
shell: bash
|
||||
if: runner.os != 'Windows'
|
||||
|
||||
# Windows needs powershell to interact nicely with Meson
|
||||
- run: >
|
||||
cibuildwheel
|
||||
"${{ inputs.package-dir }}"
|
||||
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}}
|
||||
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}}
|
||||
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}}
|
||||
env:
|
||||
PATH: "${{ steps.cibw.outputs.updated-path }}"
|
||||
- run: |
|
||||
$env:PATH = "${{ steps.cibw.outputs.prepend-path }};$env:PATH"
|
||||
& "${{ steps.cibw.outputs.cibw-bin }}" `
|
||||
"${{ inputs.package-dir }}" `
|
||||
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}} `
|
||||
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}} `
|
||||
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}}
|
||||
shell: pwsh
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
@@ -39,10 +39,10 @@ class CIService(typing.NamedTuple):
|
||||
name: str
|
||||
dst_config_path: str
|
||||
badge_md: str
|
||||
config_file_transform: typing.Callable[[str], str] = lambda x: x # identity by default
|
||||
config_file_transform: typing.Callable[[str, str], str] = lambda x, _: x # identity by default
|
||||
|
||||
|
||||
def github_config_file_transform(content: str) -> str:
|
||||
def github_config_file_transform(content: str, git_ref: str) -> str:
|
||||
# one of the the github configs only builds on main, so we need to remove that restriction
|
||||
# so our example build will run on the test branch.
|
||||
#
|
||||
@@ -61,6 +61,22 @@ def github_config_file_transform(content: str) -> str:
|
||||
"push:",
|
||||
content,
|
||||
)
|
||||
|
||||
# use the version of cibuildwheel from the current commit, not the latest
|
||||
# release
|
||||
# replace:
|
||||
# """
|
||||
# uses: pypa/cibuildwheel@v3.3.1
|
||||
# """
|
||||
# with:
|
||||
# """
|
||||
# uses: pypa/cibuildwheel@<latest commit hash>
|
||||
# """
|
||||
content = re.sub(
|
||||
r"uses: pypa/cibuildwheel@v.*",
|
||||
f"uses: pypa/cibuildwheel@{git_ref}",
|
||||
content,
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
@@ -110,20 +126,22 @@ def ci_service_for_config_file(config_file: Path) -> CIService:
|
||||
|
||||
@click.command()
|
||||
@click.argument("config_files", nargs=-1, type=click.Path())
|
||||
def run_example_ci_configs(config_files=None):
|
||||
def run_example_ci_configs(
|
||||
config_files: list[str],
|
||||
) -> None:
|
||||
"""
|
||||
Test the example configs. If no files are specified, will test
|
||||
examples/*-minimal.yml
|
||||
"""
|
||||
|
||||
if len(config_files) == 0:
|
||||
config_files = Path("examples").glob("*-minimal.yml")
|
||||
config_file_paths = list(Path("examples").glob("*-minimal.yml"))
|
||||
else:
|
||||
config_files = [Path(f) for f in config_files]
|
||||
config_file_paths = [Path(f) for f in config_files]
|
||||
|
||||
# check each CI service has at most 1 config file
|
||||
configs_by_service = set()
|
||||
for config_file in config_files:
|
||||
for config_file in config_file_paths:
|
||||
service = ci_service_for_config_file(config_file)
|
||||
if service.name in configs_by_service:
|
||||
msg = "You cannot specify more than one config per CI service"
|
||||
@@ -137,6 +155,9 @@ def run_example_ci_configs(config_files=None):
|
||||
previous_branch = shell(
|
||||
"git rev-parse --abbrev-ref HEAD", check=True, capture_output=True, encoding="utf8"
|
||||
).stdout.strip()
|
||||
git_ref = shell(
|
||||
"git rev-parse HEAD", check=True, capture_output=True, encoding="utf8"
|
||||
).stdout.strip()
|
||||
|
||||
timestamp = time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime())
|
||||
branch_name = f"example-config-test---{previous_branch}-{timestamp}"
|
||||
@@ -147,14 +168,14 @@ def run_example_ci_configs(config_files=None):
|
||||
example_project = Path("example_root")
|
||||
generate_basic_project(example_project)
|
||||
|
||||
for config_file in config_files:
|
||||
for config_file in config_file_paths:
|
||||
service = ci_service_for_config_file(config_file)
|
||||
dst_config_file = example_project / service.dst_config_path
|
||||
|
||||
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
contents = config_file.read_text(encoding="utf8")
|
||||
contents = service.config_file_transform(contents)
|
||||
contents = service.config_file_transform(contents, git_ref)
|
||||
dst_config_file.write_text(contents, encoding="utf8")
|
||||
|
||||
subprocess.run(["git", "add", example_project], check=True)
|
||||
@@ -178,14 +199,14 @@ def run_example_ci_configs(config_files=None):
|
||||
print("> ")
|
||||
print("> | Service | Config | Status |")
|
||||
print("> |---|---|---|")
|
||||
for config_file in config_files:
|
||||
for config_file in config_file_paths:
|
||||
service = ci_service_for_config_file(config_file)
|
||||
badge = service.badge_md.format(
|
||||
branch=branch_name, branch_escaped=quote(branch_name, safe="")
|
||||
)
|
||||
print(f"> | {service.name} | `{config_file}` | {badge} |")
|
||||
print("> ")
|
||||
print("> Generated by `bin/run_example_ci_config.py`")
|
||||
print(f"> Generated by `{' '.join(sys.argv)}`")
|
||||
print()
|
||||
print("---")
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user