Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kurtmckee committed Oct 13, 2023
0 parents commit f7d3536
Show file tree
Hide file tree
Showing 13 changed files with 470 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: "Test"

on:
push:
branches:
- "main"

jobs:
test-linux:
name: "Test on ${{ matrix.config.os-name }}"
runs-on: "${{ matrix.config.runner }}"
strategy:
matrix:
config:
- os-name: "Linux"
runner: "ubuntu-latest"
test-label: "ci-test-linux"
- os-name: "macOS"
runner: "macos-latest"
test-label: "ci-test-macos"
- os-name: "Windows"
runner: "windows-latest"
test-label: "ci-test-windows"
fail-fast: false

steps:
- name: "Use it!"
id: "finder"
uses: "kurtmckee/setup-python-version-detector@main"

- name: "Print it!"
shell: "bash"
run: |
echo '${{ steps.finder.outputs.python-identifiers }}'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
31 changes: 31 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ci:
autoupdate_schedule: "monthly"

repos:
- repo: "https://github.com/pre-commit/pre-commit-hooks"
rev: "v4.5.0"
hooks:
- id: "trailing-whitespace"
- id: "end-of-file-fixer"
- id: "check-yaml"
- id: "check-added-large-files"

- repo: "https://github.com/python-jsonschema/check-jsonschema"
rev: "0.27.0"
hooks:
- id: "check-github-workflows"
- id: "check-dependabot"

- repo: "local"
hooks:
- id: "sync-identify-code"
name: "Synchronize identify.py source code into 'finder.sh'"
language: "python"
entry: "python src/python_cache_buster/sync_identify_code.py"
files: "^src/python_cache_buster/identify.py$"

- id: "sync-finder-code"
name: "Synchronize finder source code into 'action.yml'"
language: "python"
entry: "python src/python_cache_buster/sync_finder_code.py"
files: "^src/python_cache_buster/finder.*$"
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Identify Python interpreters
############################

*Robust cache-busting based on Python implementations, architectures, and versions.*

----
8 changes: 8 additions & 0 deletions TODO.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* Add shellcheck pre-commit hook for finder.sh
* Add a Powershell syntax checker for finder.ps1
* Expand README
* Create ``releases`` branch for tagging and such
* Add pre-commit hook to check README against latest tagged SHA
* Rename repo...maybe ``python-cache-buster``
* Add test suite that can run locally
* Add test suite that verifies expected outputs in CI
143 changes: 143 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: "Identify installed Python interpreters"
description: |
Find installed Python interpreters and output them as "python-identifiers".
This can be useful for cache busting of tox, virtual environments,
and other items that are sensitive to changes in Python minor versions.
Only Python interpreters named "python" in the $PATH will be found.
outputs:
python-identifiers:
description: |
A string of sorted Python identifiers.
In most cases the identifiers will be paths,
but for system Pythons, 'sysconfig.get_config_var("EXT_SUFFIX")' will be included.
The string will be separated by OS-specific PATH separators;
":" is used on Linux and macOS, and ";" is used on Windows.
value: "${{ steps.final-step.outputs.python-identifiers }}"

runs:
using: "composite"
steps:
- name: "Find Pythons on Linux / macOS"
id: "linux"
if: "runner.os != 'windows'"
shell: "bash"
# Do not edit the 'run' code below.
# It is copied from 'finder.sh' by a pre-commit hook.
# START: finder.sh
run: |
python_code=$(
cat <<'identify.py_SOURCE_CODE'
from __future__ import print_function
import sysconfig
def main():
ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
if ext_suffix is not None:
print(ext_suffix)
else:
# Python 2.7 on GitHub macOS runners
import platform
print(
"." + platform.python_implementation().lower(),
sysconfig.get_config_var("py_version_nodot"),
sysconfig.get_config_var("MACHDEP"),
sep="-"
)
if __name__ == "__main__":
main()
identify.py_SOURCE_CODE
)
# Search paths in $PATH for Python interpreters.
IFS=: read -r -a all_paths <<< "$PATH"
# Find Python interpreters.
paths=()
for path in "${all_paths[@]}"; do
# Interpreters in RUNNER_TOOL_CACHE have directory names that include:
#
# * The implementation (like "Python" or "PyPy")
# * A version (like "3.10.12")
# * The architecture (like "x64")
#
# In such cases, the path can be used as the identifier.
if [[ "${path/#${RUNNER_TOOL_CACHE}/}" != "${path}" ]]; then
# Check for bin/python first;
# this results in duplicate paths which are later removed.
if [[ -x "${path}/bin/python" ]]; then
paths+=("${path}/bin")
elif [[ -x "${path}/python" ]]; then
paths+=("${path}")
fi
else
# System Pythons (e.g. /usr/bin/python) have nothing unique in the path.
# In such cases, it's necessary to run the executable to get something unique.
if [[ -x "${path}/python" ]]; then
paths+=("$(echo "${python_code}" | "${path}/python" -)")
fi
fi
done
# Sort the paths, ensure each path is unique, and create the output result.
result="$(
echo "${paths[*]}" \
| tr ' ' '\n' \
| sort \
| uniq \
| tr '\n' ':'
)"
# Trim trailing colons.
result="${result%:}"
# Output path information.
echo "python-identifiers=${result}" > "$GITHUB_OUTPUT"
# END: finder.sh

- name: "Find Pythons on Windows"
id: "windows"
if: "runner.os == 'windows'"
shell: "powershell"
# Do not edit the 'run' code below.
# It is copied from 'finder.ps1' by a pre-commit hook.
# START: finder.ps1
run: |
$true | Out-Null # No-op to prevent YAML syntax errors.
# Search paths in $PATH for Python interpreters.
$all_paths = $env:PATH -split ";"
# Find Python interpreters.
$paths = @()
foreach ($path in $all_paths) {
# Only consider paths in RUNNER_TOOL_CACHE.
if ($path.StartsWith($env:RUNNER_TOOL_CACHE)) {
if (Test-Path "$path\python.exe") {
$paths += $path
}
}
}
# Sort the paths, ensure each path is unique, and create the output result.
$result = (
$paths `
| Sort-Object `
| Get-Unique
) -join ";"
# Output path information.
Write-Output "python-identifiers=$result" > "$env:GITHUB_OUTPUT"
# END: finder.ps1

- name: "Output"
id: "final-step"
shell: "bash"
run: |
echo 'python-identifiers=${{ steps.linux.outputs.python-identifiers }}${{ steps.windows.outputs.python-identifiers }}' > "$GITHUB_OUTPUT"
20 changes: 20 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[tool.poetry]
name = "python_cache_buster"
version = "1.0.0"
description = ""
authors = ["Kurt McKee <[email protected]>"]
license = "MIT"
readme = "README.rst"

[tool.poetry.scripts]
identify = "python_cache_buster.identify:main"
sync-finder-code = "python_cache_buster.sync_finder_code:main"
sync-identify-code = "python_cache_buster.sync_identify_code:main"

[tool.poetry.dependencies]
python = ">=3.8"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file.
23 changes: 23 additions & 0 deletions src/python_cache_buster/finder.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Search paths in $PATH for Python interpreters.
$all_paths = $env:PATH -split ";"

# Find Python interpreters.
$paths = @()
foreach ($path in $all_paths) {
# Only consider paths in RUNNER_TOOL_CACHE.
if ($path.StartsWith($env:RUNNER_TOOL_CACHE)) {
if (Test-Path "$path\python.exe") {
$paths += $path
}
}
}

# Sort the paths, ensure each path is unique, and create the output result.
$result = (
$paths `
| Sort-Object `
| Get-Unique
) -join ";"

# Output path information.
Write-Output "python-identifiers=$result"
71 changes: 71 additions & 0 deletions src/python_cache_buster/finder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
python_code=$(
cat <<'identify.py_SOURCE_CODE'
from __future__ import print_function
import sysconfig
def main():
ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
if ext_suffix is not None:
print(ext_suffix)
else:
# Python 2.7 on GitHub macOS runners
import platform
print(
"." + platform.python_implementation().lower(),
sysconfig.get_config_var("py_version_nodot"),
sysconfig.get_config_var("MACHDEP"),
sep="-"
)
if __name__ == "__main__":
main()
identify.py_SOURCE_CODE
)

# Search paths in $PATH for Python interpreters.
IFS=: read -r -a all_paths <<< "$PATH"

# Find Python interpreters.
paths=()
for path in "${all_paths[@]}"; do
# Interpreters in RUNNER_TOOL_CACHE have directory names that include:
#
# * The implementation (like "Python" or "PyPy")
# * A version (like "3.10.12")
# * The architecture (like "x64")
#
# In such cases, the path can be used as the identifier.
if [[ "${path/#${RUNNER_TOOL_CACHE}/}" != "${path}" ]]; then
# Check for bin/python first;
# this results in duplicate paths which are later removed.
if [[ -x "${path}/bin/python" ]]; then
paths+=("${path}/bin")
elif [[ -x "${path}/python" ]]; then
paths+=("${path}")
fi
else
# System Pythons (e.g. /usr/bin/python) have nothing unique in the path.
# In such cases, it's necessary to run the executable to get something unique.
if [[ -x "${path}/python" ]]; then
paths+=("$(echo "${python_code}" | "${path}/python" -)")
fi
fi
done

# Sort the paths, ensure each path is unique, and create the output result.
result="$(
echo "${paths[*]}" \
| tr ' ' '\n' \
| sort \
| uniq \
| tr '\n' ':'
)"

# Trim trailing colons.
result="${result%:}"

# Output path information.
echo "python-identifiers=${result}"
22 changes: 22 additions & 0 deletions src/python_cache_buster/identify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from __future__ import print_function

import sysconfig


def main():
ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
if ext_suffix is not None:
print(ext_suffix)
else:
# Python 2.7 on GitHub macOS runners
import platform
print(
"." + platform.python_implementation().lower(),
sysconfig.get_config_var("py_version_nodot"),
sysconfig.get_config_var("MACHDEP"),
sep="-"
)


if __name__ == "__main__":
main()
Loading

0 comments on commit f7d3536

Please sign in to comment.