ci-helper 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ci_helper-0.1.0/.gitignore +5 -0
- ci_helper-0.1.0/LICENSE.txt +21 -0
- ci_helper-0.1.0/PKG-INFO +155 -0
- ci_helper-0.1.0/README.md +133 -0
- ci_helper-0.1.0/ci_distinfo.py +1 -0
- ci_helper-0.1.0/ci_helper/__init__.py +4 -0
- ci_helper-0.1.0/ci_helper/__main__.py +120 -0
- ci_helper-0.1.0/ci_helper/__version__.py +21 -0
- ci_helper-0.1.0/ci_helper/ci_distinfo.py +66 -0
- ci_helper-0.1.0/ci_helper.egg-info/PKG-INFO +155 -0
- ci_helper-0.1.0/ci_helper.egg-info/SOURCES.txt +17 -0
- ci_helper-0.1.0/ci_helper.egg-info/dependency_links.txt +1 -0
- ci_helper-0.1.0/ci_helper.egg-info/entry_points.txt +2 -0
- ci_helper-0.1.0/ci_helper.egg-info/requires.txt +4 -0
- ci_helper-0.1.0/ci_helper.egg-info/top_level.txt +1 -0
- ci_helper-0.1.0/pyproject.toml +2 -0
- ci_helper-0.1.0/setup.cfg +35 -0
- ci_helper-0.1.0/setup.py +29 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Christopher Billington
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ci_helper-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ci-helper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Get info on how a package should be built on CI
|
|
5
|
+
Home-page: http://github.com/chrisjbillington/ci-helper
|
|
6
|
+
Author: Christopher Billington
|
|
7
|
+
Author-email: chrisjbillington@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Source Code, https://github.com/chrisjbillington/ci-helper
|
|
10
|
+
Project-URL: Download, https://github.com/chrisjbillington/ci-helper/releases
|
|
11
|
+
Project-URL: Tracker, https://github.com/chrisjbillington/ci-helper/issues
|
|
12
|
+
Keywords: build setuptools conda
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: setuptools_scm
|
|
19
|
+
Requires-Dist: importlib_metadata
|
|
20
|
+
Requires-Dist: toml
|
|
21
|
+
Requires-Dist: requests
|
|
22
|
+
|
|
23
|
+
# ci-helper
|
|
24
|
+
|
|
25
|
+
Output information to help build a Python package in CI
|
|
26
|
+
|
|
27
|
+
## Motivation
|
|
28
|
+
|
|
29
|
+
This tool exists to address certain issues of bit-rot and deployment of GitHub Actions
|
|
30
|
+
workflows (though the problem exists for CI generally) that build Python packages.
|
|
31
|
+
|
|
32
|
+
It looks up what current Python versions are supported so you can target them in your
|
|
33
|
+
builds without having to maintain a curated list. It determines whether building on
|
|
34
|
+
multiple OSs or Python versions is needed for a Python package, allowing you to deploy a
|
|
35
|
+
generic CI configuration for different types of packages without having to modify it for
|
|
36
|
+
each one specifically when they only vary in this way.
|
|
37
|
+
|
|
38
|
+
Specifically:
|
|
39
|
+
|
|
40
|
+
* It lists all stable and supported Python versions so your CI can target all supported
|
|
41
|
+
versions of Python without having to curate a manual list.
|
|
42
|
+
|
|
43
|
+
* It tells you what the second-most-recent minor version of Python is, so you can use that
|
|
44
|
+
as a sensible default for tools that require a recent-ish Python but are likely not to
|
|
45
|
+
work immediately after a new Python is released.
|
|
46
|
+
|
|
47
|
+
* It tells you whether a Python package is pure Python or not, so that your CI knows
|
|
48
|
+
whether it needs to build on multiple OSs/Python versions.
|
|
49
|
+
|
|
50
|
+
* It tells you whether a Python package's dependencies have environment markers or not, so
|
|
51
|
+
that your CI knows whether it needs to build on multiple OSs/Python versions, for
|
|
52
|
+
package formats that don't support environment markers in dependencies (e.g. conda
|
|
53
|
+
packages).
|
|
54
|
+
|
|
55
|
+
These functions are all serving the goals of:
|
|
56
|
+
|
|
57
|
+
* Minimising how often you need to modify your CI configs just because a new version of
|
|
58
|
+
Python came our or an old version reached end-of-life
|
|
59
|
+
* Minimising (ideally to zero) how much you need to customise an otherwise generic CI
|
|
60
|
+
config when you re-use it for different types of Python packages (e.g. pure vs impure)
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
This package is available on PyPI, install it to your current Python environemnt with
|
|
65
|
+
```bash
|
|
66
|
+
pip install ci-helper
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# The second-most recent minor Python release, a good choice for the version to run
|
|
73
|
+
# tools from:
|
|
74
|
+
$ ci-helper defaultpython
|
|
75
|
+
3.12
|
|
76
|
+
|
|
77
|
+
# All stable, non-end-of-life Python versions:
|
|
78
|
+
$ ci-helper pythons
|
|
79
|
+
3.9,3.10,3.11,3.12,3.13
|
|
80
|
+
|
|
81
|
+
# Same but in the format used by `cibuildwheel`'s `CIBW_BUILD` environment variable (cpython-only for now):
|
|
82
|
+
$ ci-helper pythons --cibw
|
|
83
|
+
cp39-* cp310-* cp311-* cp312-* cp313-*
|
|
84
|
+
|
|
85
|
+
# Info about the source Python project in the current working directory - name, version,
|
|
86
|
+
# whether it's a pure Python package, and whether its build or run requirements contain
|
|
87
|
+
# any # environment markers (that is, whether its requirements vary by platform or
|
|
88
|
+
# Python version):
|
|
89
|
+
$ ci-helper distinfo
|
|
90
|
+
{
|
|
91
|
+
"name": "ci-helper",
|
|
92
|
+
"version": "0.1.dev1+g5043fb5.d20241202",
|
|
93
|
+
"is_pure": true,
|
|
94
|
+
"has_env_markers": false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Same but one field at a time, more convenient for assigning to environment variables:
|
|
98
|
+
$ ci-helper distinfo name
|
|
99
|
+
ci-helper
|
|
100
|
+
$ ci-helper distinfo version
|
|
101
|
+
0.1.0
|
|
102
|
+
$ ci-helper distinfo is_pure
|
|
103
|
+
true
|
|
104
|
+
$ ci-helper distinfo has_env_markers
|
|
105
|
+
false
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Full help text
|
|
109
|
+
|
|
110
|
+
```shell
|
|
111
|
+
$ ci-helper -h
|
|
112
|
+
usage: ci-helper [-h] [--version] {pythons,defaultpython,distinfo} ...
|
|
113
|
+
|
|
114
|
+
positional arguments:
|
|
115
|
+
{pythons,defaultpython,distinfo}
|
|
116
|
+
Action to perform
|
|
117
|
+
pythons Output list of stable Python versions that have not yet reached end of life, see `ci-helper pythons -h`
|
|
118
|
+
defaultpython Output the second-latest stable Python version in X.Y format, useful as a good choice for a default python version
|
|
119
|
+
distinfo Output info about the distribution, see `ci-helper distinfo -h`
|
|
120
|
+
|
|
121
|
+
options:
|
|
122
|
+
-h, --help show this help message and exit
|
|
123
|
+
--version show program's version number and exit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```shell
|
|
127
|
+
$ ci-helper pythons -h
|
|
128
|
+
usage: ci-helper pythons [-h] [--cibw]
|
|
129
|
+
|
|
130
|
+
options:
|
|
131
|
+
-h, --help show this help message and exit
|
|
132
|
+
--cibw Output as a space-separated list in the format `cpXY-* cpXY-*` as appropriate for the CIBW_BUILD environment variable to build for all
|
|
133
|
+
stable CPython versions, otherwise versions are output as a comma-separated list in the format X.Y,X.Y
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```shell
|
|
137
|
+
$ ci-helper defaultpython -h
|
|
138
|
+
usage: ci-helper defaultpython [-h]
|
|
139
|
+
|
|
140
|
+
options:
|
|
141
|
+
-h, --help show this help message and exit
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```shell
|
|
145
|
+
$ ci-helper distinfo -h
|
|
146
|
+
usage: ci-helper distinfo [-h] [{name,version,is_pure,has_env_markers}]
|
|
147
|
+
|
|
148
|
+
positional arguments:
|
|
149
|
+
{name,version,is_pure,has_env_markers}
|
|
150
|
+
Name of field to output as a single json value, if not given, all info is output as json
|
|
151
|
+
|
|
152
|
+
options:
|
|
153
|
+
-h, --help show this help message and exit
|
|
154
|
+
```
|
|
155
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# ci-helper
|
|
2
|
+
|
|
3
|
+
Output information to help build a Python package in CI
|
|
4
|
+
|
|
5
|
+
## Motivation
|
|
6
|
+
|
|
7
|
+
This tool exists to address certain issues of bit-rot and deployment of GitHub Actions
|
|
8
|
+
workflows (though the problem exists for CI generally) that build Python packages.
|
|
9
|
+
|
|
10
|
+
It looks up what current Python versions are supported so you can target them in your
|
|
11
|
+
builds without having to maintain a curated list. It determines whether building on
|
|
12
|
+
multiple OSs or Python versions is needed for a Python package, allowing you to deploy a
|
|
13
|
+
generic CI configuration for different types of packages without having to modify it for
|
|
14
|
+
each one specifically when they only vary in this way.
|
|
15
|
+
|
|
16
|
+
Specifically:
|
|
17
|
+
|
|
18
|
+
* It lists all stable and supported Python versions so your CI can target all supported
|
|
19
|
+
versions of Python without having to curate a manual list.
|
|
20
|
+
|
|
21
|
+
* It tells you what the second-most-recent minor version of Python is, so you can use that
|
|
22
|
+
as a sensible default for tools that require a recent-ish Python but are likely not to
|
|
23
|
+
work immediately after a new Python is released.
|
|
24
|
+
|
|
25
|
+
* It tells you whether a Python package is pure Python or not, so that your CI knows
|
|
26
|
+
whether it needs to build on multiple OSs/Python versions.
|
|
27
|
+
|
|
28
|
+
* It tells you whether a Python package's dependencies have environment markers or not, so
|
|
29
|
+
that your CI knows whether it needs to build on multiple OSs/Python versions, for
|
|
30
|
+
package formats that don't support environment markers in dependencies (e.g. conda
|
|
31
|
+
packages).
|
|
32
|
+
|
|
33
|
+
These functions are all serving the goals of:
|
|
34
|
+
|
|
35
|
+
* Minimising how often you need to modify your CI configs just because a new version of
|
|
36
|
+
Python came our or an old version reached end-of-life
|
|
37
|
+
* Minimising (ideally to zero) how much you need to customise an otherwise generic CI
|
|
38
|
+
config when you re-use it for different types of Python packages (e.g. pure vs impure)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
This package is available on PyPI, install it to your current Python environemnt with
|
|
43
|
+
```bash
|
|
44
|
+
pip install ci-helper
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# The second-most recent minor Python release, a good choice for the version to run
|
|
51
|
+
# tools from:
|
|
52
|
+
$ ci-helper defaultpython
|
|
53
|
+
3.12
|
|
54
|
+
|
|
55
|
+
# All stable, non-end-of-life Python versions:
|
|
56
|
+
$ ci-helper pythons
|
|
57
|
+
3.9,3.10,3.11,3.12,3.13
|
|
58
|
+
|
|
59
|
+
# Same but in the format used by `cibuildwheel`'s `CIBW_BUILD` environment variable (cpython-only for now):
|
|
60
|
+
$ ci-helper pythons --cibw
|
|
61
|
+
cp39-* cp310-* cp311-* cp312-* cp313-*
|
|
62
|
+
|
|
63
|
+
# Info about the source Python project in the current working directory - name, version,
|
|
64
|
+
# whether it's a pure Python package, and whether its build or run requirements contain
|
|
65
|
+
# any # environment markers (that is, whether its requirements vary by platform or
|
|
66
|
+
# Python version):
|
|
67
|
+
$ ci-helper distinfo
|
|
68
|
+
{
|
|
69
|
+
"name": "ci-helper",
|
|
70
|
+
"version": "0.1.dev1+g5043fb5.d20241202",
|
|
71
|
+
"is_pure": true,
|
|
72
|
+
"has_env_markers": false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Same but one field at a time, more convenient for assigning to environment variables:
|
|
76
|
+
$ ci-helper distinfo name
|
|
77
|
+
ci-helper
|
|
78
|
+
$ ci-helper distinfo version
|
|
79
|
+
0.1.0
|
|
80
|
+
$ ci-helper distinfo is_pure
|
|
81
|
+
true
|
|
82
|
+
$ ci-helper distinfo has_env_markers
|
|
83
|
+
false
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Full help text
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
$ ci-helper -h
|
|
90
|
+
usage: ci-helper [-h] [--version] {pythons,defaultpython,distinfo} ...
|
|
91
|
+
|
|
92
|
+
positional arguments:
|
|
93
|
+
{pythons,defaultpython,distinfo}
|
|
94
|
+
Action to perform
|
|
95
|
+
pythons Output list of stable Python versions that have not yet reached end of life, see `ci-helper pythons -h`
|
|
96
|
+
defaultpython Output the second-latest stable Python version in X.Y format, useful as a good choice for a default python version
|
|
97
|
+
distinfo Output info about the distribution, see `ci-helper distinfo -h`
|
|
98
|
+
|
|
99
|
+
options:
|
|
100
|
+
-h, --help show this help message and exit
|
|
101
|
+
--version show program's version number and exit
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```shell
|
|
105
|
+
$ ci-helper pythons -h
|
|
106
|
+
usage: ci-helper pythons [-h] [--cibw]
|
|
107
|
+
|
|
108
|
+
options:
|
|
109
|
+
-h, --help show this help message and exit
|
|
110
|
+
--cibw Output as a space-separated list in the format `cpXY-* cpXY-*` as appropriate for the CIBW_BUILD environment variable to build for all
|
|
111
|
+
stable CPython versions, otherwise versions are output as a comma-separated list in the format X.Y,X.Y
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```shell
|
|
115
|
+
$ ci-helper defaultpython -h
|
|
116
|
+
usage: ci-helper defaultpython [-h]
|
|
117
|
+
|
|
118
|
+
options:
|
|
119
|
+
-h, --help show this help message and exit
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```shell
|
|
123
|
+
$ ci-helper distinfo -h
|
|
124
|
+
usage: ci-helper distinfo [-h] [{name,version,is_pure,has_env_markers}]
|
|
125
|
+
|
|
126
|
+
positional arguments:
|
|
127
|
+
{name,version,is_pure,has_env_markers}
|
|
128
|
+
Name of field to output as a single json value, if not given, all info is output as json
|
|
129
|
+
|
|
130
|
+
options:
|
|
131
|
+
-h, --help show this help message and exit
|
|
132
|
+
```
|
|
133
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ci_helper.ci_distinfo import ci_distinfo
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import subprocess
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .__version__ import __version__
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Command line args that can be used in place of "setup.py" for projects that lack a
|
|
12
|
+
# setup.py, runs a minimal setup.py similar to what pip does for projects with no
|
|
13
|
+
# setup.py.
|
|
14
|
+
_SETUP_PY_STUB = [
|
|
15
|
+
"-c",
|
|
16
|
+
'import sys, setuptools; sys.argv[0] = __file__ = "setup.py"; setuptools.setup()',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def setup_py(project_dir):
|
|
21
|
+
"""Returns a list of command line arguments to be used in place of ["setup.py"]. If
|
|
22
|
+
setup.py exists, then this is just ["setup.py"]. Otherwise, if setup.cfg or
|
|
23
|
+
pyproject.toml exists, returns args that pass a code snippet to Python with "-c" to
|
|
24
|
+
execute a minimal setup.py calling setuptools.setup(). If none of pyproject.toml,
|
|
25
|
+
setup.cfg, or setup.py exists, raises an exception."""
|
|
26
|
+
if Path(project_dir, 'setup.py').exists():
|
|
27
|
+
return ['setup.py']
|
|
28
|
+
elif any(Path(project_dir, s).exists() for s in ['setup.cfg', 'pyproject.toml']):
|
|
29
|
+
return _SETUP_PY_STUB
|
|
30
|
+
msg = f"""{project_dir} does not look like a python project directory: contains no
|
|
31
|
+
setup.py, setup.cfg, or pyproject.toml"""
|
|
32
|
+
raise RuntimeError(' '.join(msg.split()))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_pythons():
|
|
36
|
+
"""Return stable, non-end-of-life Python versions in X.Y format"""
|
|
37
|
+
URL = "https://raw.githubusercontent.com/python/devguide/refs/heads/main/include/release-cycle.json"
|
|
38
|
+
response = requests.get(URL, timeout=30)
|
|
39
|
+
if not response.ok:
|
|
40
|
+
raise ValueError(f"{response.status_code} {response.reason}")
|
|
41
|
+
pythons = response.json()
|
|
42
|
+
pythons = [p for p in pythons if pythons[p]['status'] in ('bugfix', 'security')]
|
|
43
|
+
pythons.sort(key=lambda ver: [int(part) for part in ver.split('.')])
|
|
44
|
+
return pythons
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
# Since setuptools_conda is self-hosting, it needs toml and distlib to read its own
|
|
49
|
+
# requirements just to know that it needs to install toml and distlib! So bootstrap
|
|
50
|
+
# that up if necessary.
|
|
51
|
+
|
|
52
|
+
parser = argparse.ArgumentParser(prog='ci-helper')
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
'--version',
|
|
55
|
+
action='version',
|
|
56
|
+
version=__version__,
|
|
57
|
+
)
|
|
58
|
+
subparsers = parser.add_subparsers(
|
|
59
|
+
dest="command", required=True, help="Action to perform"
|
|
60
|
+
)
|
|
61
|
+
parser_pythons = subparsers.add_parser(
|
|
62
|
+
"pythons",
|
|
63
|
+
help="Output list of stable Python versions that have not yet reached end of "
|
|
64
|
+
+ "life, see `ci-helper pythons -h`",
|
|
65
|
+
)
|
|
66
|
+
parser_pythons.add_argument(
|
|
67
|
+
"--cibw",
|
|
68
|
+
action="store_true",
|
|
69
|
+
help="Output as a space-separated list in the format "
|
|
70
|
+
+ "`cpXY-* cpXY-*` as appropriate for the CIBW_BUILD environment variable "
|
|
71
|
+
+ "to build for all stable CPython versions, otherwise versions are output as "
|
|
72
|
+
"a comma-separated list in the format X.Y,X.Y",
|
|
73
|
+
)
|
|
74
|
+
_ = subparsers.add_parser(
|
|
75
|
+
"defaultpython",
|
|
76
|
+
help="Output the second-latest stable Python version in X.Y format, "
|
|
77
|
+
"useful as a good choice for a default python version",
|
|
78
|
+
)
|
|
79
|
+
parser_distinfo = subparsers.add_parser(
|
|
80
|
+
"distinfo",
|
|
81
|
+
help="Output info about the distribution, see `ci-helper distinfo -h`",
|
|
82
|
+
)
|
|
83
|
+
parser_distinfo.add_argument(
|
|
84
|
+
"field",
|
|
85
|
+
choices=['name', 'version', 'is_pure', 'has_env_markers'],
|
|
86
|
+
nargs="?",
|
|
87
|
+
help="Name of field to output as a single json value, "
|
|
88
|
+
+ "if not given, all info is output as json",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
args = parser.parse_args()
|
|
92
|
+
|
|
93
|
+
if args.command == 'distinfo':
|
|
94
|
+
# distinfo_args = parser_distinfo.parse_args()
|
|
95
|
+
cmd = [sys.executable, *setup_py('.'), '-q', 'ci_distinfo']
|
|
96
|
+
result = subprocess.run(cmd, check=True, capture_output=True)
|
|
97
|
+
info = result.stdout.decode('utf8')
|
|
98
|
+
if args.field is not None:
|
|
99
|
+
info = json.loads(info)
|
|
100
|
+
value = info[args.field]
|
|
101
|
+
if isinstance(value, str):
|
|
102
|
+
print(value)
|
|
103
|
+
else:
|
|
104
|
+
print(json.dumps(value))
|
|
105
|
+
else:
|
|
106
|
+
print(info)
|
|
107
|
+
elif args.command == 'pythons':
|
|
108
|
+
pythons = get_pythons()
|
|
109
|
+
if args.cibw:
|
|
110
|
+
print(' '.join([f"cp{p.replace('.', '')}-*" for p in pythons]))
|
|
111
|
+
else:
|
|
112
|
+
print(','.join(pythons))
|
|
113
|
+
elif args.command == 'defaultpython':
|
|
114
|
+
pythons = get_pythons()
|
|
115
|
+
print(pythons[-2])
|
|
116
|
+
sys.exit(0)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == '__main__':
|
|
120
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
try:
|
|
4
|
+
import importlib.metadata as importlib_metadata
|
|
5
|
+
except ImportError:
|
|
6
|
+
import importlib_metadata
|
|
7
|
+
|
|
8
|
+
VERSION_SCHEME = {
|
|
9
|
+
"version_scheme": os.getenv("SCM_VERSION_SCHEME", "guess-next-dev"),
|
|
10
|
+
"local_scheme": os.getenv("SCM_LOCAL_SCHEME", "node-and-date"),
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
root = Path(__file__).parent.parent
|
|
14
|
+
if (root / '.git').is_dir():
|
|
15
|
+
from setuptools_scm import get_version
|
|
16
|
+
__version__ = get_version(root, **VERSION_SCHEME)
|
|
17
|
+
else:
|
|
18
|
+
try:
|
|
19
|
+
__version__ = importlib_metadata.version(__package__)
|
|
20
|
+
except importlib_metadata.PackageNotFoundError:
|
|
21
|
+
__version__ = None
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from setuptools import Command
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_pyproject_toml_entry(proj, *keys):
|
|
7
|
+
"""Return [build-system] requires as read from proj/pyproject.toml, if any"""
|
|
8
|
+
# this import is here to avoid bootstrapping issues when building wheels for this
|
|
9
|
+
# package itself
|
|
10
|
+
import toml
|
|
11
|
+
pyproject_toml = Path(proj, 'pyproject.toml')
|
|
12
|
+
if not pyproject_toml.exists():
|
|
13
|
+
return None
|
|
14
|
+
config = toml.load(pyproject_toml)
|
|
15
|
+
try:
|
|
16
|
+
for key in keys:
|
|
17
|
+
config = config[key]
|
|
18
|
+
return config
|
|
19
|
+
except KeyError:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def has_environment_markers(setup_requires, install_requires, extras_requires):
|
|
24
|
+
"""Given a list of install_requires and a dict of extras_requires, return if there
|
|
25
|
+
are any environment markers"""
|
|
26
|
+
for item in install_requires:
|
|
27
|
+
if ';' in item:
|
|
28
|
+
return True
|
|
29
|
+
for key in extras_requires:
|
|
30
|
+
if key.startswith(':'):
|
|
31
|
+
# an extras_requires item being used as an environment marker, an old
|
|
32
|
+
# pattern allowed by setuptools
|
|
33
|
+
return True
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ci_distinfo(Command):
|
|
38
|
+
description = "Get package info useful for building on CI"
|
|
39
|
+
user_options = []
|
|
40
|
+
|
|
41
|
+
def initialize_options(self):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def finalize_options(self):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def run(self):
|
|
48
|
+
|
|
49
|
+
setup_requires = get_pyproject_toml_entry('.', 'build-system', 'requires')
|
|
50
|
+
if setup_requires is None:
|
|
51
|
+
setup_requires = self.distribution.setup_requires
|
|
52
|
+
|
|
53
|
+
info = {
|
|
54
|
+
'name': self.distribution.get_name(),
|
|
55
|
+
'version': self.distribution.get_version(),
|
|
56
|
+
'is_pure': not (
|
|
57
|
+
self.distribution.has_ext_modules()
|
|
58
|
+
or self.distribution.has_c_libraries()
|
|
59
|
+
),
|
|
60
|
+
'has_env_markers': has_environment_markers(
|
|
61
|
+
setup_requires,
|
|
62
|
+
self.distribution.install_requires,
|
|
63
|
+
self.distribution.extras_require,
|
|
64
|
+
),
|
|
65
|
+
}
|
|
66
|
+
print(json.dumps(info, indent=4))
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ci-helper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Get info on how a package should be built on CI
|
|
5
|
+
Home-page: http://github.com/chrisjbillington/ci-helper
|
|
6
|
+
Author: Christopher Billington
|
|
7
|
+
Author-email: chrisjbillington@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Source Code, https://github.com/chrisjbillington/ci-helper
|
|
10
|
+
Project-URL: Download, https://github.com/chrisjbillington/ci-helper/releases
|
|
11
|
+
Project-URL: Tracker, https://github.com/chrisjbillington/ci-helper/issues
|
|
12
|
+
Keywords: build setuptools conda
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: setuptools_scm
|
|
19
|
+
Requires-Dist: importlib_metadata
|
|
20
|
+
Requires-Dist: toml
|
|
21
|
+
Requires-Dist: requests
|
|
22
|
+
|
|
23
|
+
# ci-helper
|
|
24
|
+
|
|
25
|
+
Output information to help build a Python package in CI
|
|
26
|
+
|
|
27
|
+
## Motivation
|
|
28
|
+
|
|
29
|
+
This tool exists to address certain issues of bit-rot and deployment of GitHub Actions
|
|
30
|
+
workflows (though the problem exists for CI generally) that build Python packages.
|
|
31
|
+
|
|
32
|
+
It looks up what current Python versions are supported so you can target them in your
|
|
33
|
+
builds without having to maintain a curated list. It determines whether building on
|
|
34
|
+
multiple OSs or Python versions is needed for a Python package, allowing you to deploy a
|
|
35
|
+
generic CI configuration for different types of packages without having to modify it for
|
|
36
|
+
each one specifically when they only vary in this way.
|
|
37
|
+
|
|
38
|
+
Specifically:
|
|
39
|
+
|
|
40
|
+
* It lists all stable and supported Python versions so your CI can target all supported
|
|
41
|
+
versions of Python without having to curate a manual list.
|
|
42
|
+
|
|
43
|
+
* It tells you what the second-most-recent minor version of Python is, so you can use that
|
|
44
|
+
as a sensible default for tools that require a recent-ish Python but are likely not to
|
|
45
|
+
work immediately after a new Python is released.
|
|
46
|
+
|
|
47
|
+
* It tells you whether a Python package is pure Python or not, so that your CI knows
|
|
48
|
+
whether it needs to build on multiple OSs/Python versions.
|
|
49
|
+
|
|
50
|
+
* It tells you whether a Python package's dependencies have environment markers or not, so
|
|
51
|
+
that your CI knows whether it needs to build on multiple OSs/Python versions, for
|
|
52
|
+
package formats that don't support environment markers in dependencies (e.g. conda
|
|
53
|
+
packages).
|
|
54
|
+
|
|
55
|
+
These functions are all serving the goals of:
|
|
56
|
+
|
|
57
|
+
* Minimising how often you need to modify your CI configs just because a new version of
|
|
58
|
+
Python came our or an old version reached end-of-life
|
|
59
|
+
* Minimising (ideally to zero) how much you need to customise an otherwise generic CI
|
|
60
|
+
config when you re-use it for different types of Python packages (e.g. pure vs impure)
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
This package is available on PyPI, install it to your current Python environemnt with
|
|
65
|
+
```bash
|
|
66
|
+
pip install ci-helper
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# The second-most recent minor Python release, a good choice for the version to run
|
|
73
|
+
# tools from:
|
|
74
|
+
$ ci-helper defaultpython
|
|
75
|
+
3.12
|
|
76
|
+
|
|
77
|
+
# All stable, non-end-of-life Python versions:
|
|
78
|
+
$ ci-helper pythons
|
|
79
|
+
3.9,3.10,3.11,3.12,3.13
|
|
80
|
+
|
|
81
|
+
# Same but in the format used by `cibuildwheel`'s `CIBW_BUILD` environment variable (cpython-only for now):
|
|
82
|
+
$ ci-helper pythons --cibw
|
|
83
|
+
cp39-* cp310-* cp311-* cp312-* cp313-*
|
|
84
|
+
|
|
85
|
+
# Info about the source Python project in the current working directory - name, version,
|
|
86
|
+
# whether it's a pure Python package, and whether its build or run requirements contain
|
|
87
|
+
# any # environment markers (that is, whether its requirements vary by platform or
|
|
88
|
+
# Python version):
|
|
89
|
+
$ ci-helper distinfo
|
|
90
|
+
{
|
|
91
|
+
"name": "ci-helper",
|
|
92
|
+
"version": "0.1.dev1+g5043fb5.d20241202",
|
|
93
|
+
"is_pure": true,
|
|
94
|
+
"has_env_markers": false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Same but one field at a time, more convenient for assigning to environment variables:
|
|
98
|
+
$ ci-helper distinfo name
|
|
99
|
+
ci-helper
|
|
100
|
+
$ ci-helper distinfo version
|
|
101
|
+
0.1.0
|
|
102
|
+
$ ci-helper distinfo is_pure
|
|
103
|
+
true
|
|
104
|
+
$ ci-helper distinfo has_env_markers
|
|
105
|
+
false
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Full help text
|
|
109
|
+
|
|
110
|
+
```shell
|
|
111
|
+
$ ci-helper -h
|
|
112
|
+
usage: ci-helper [-h] [--version] {pythons,defaultpython,distinfo} ...
|
|
113
|
+
|
|
114
|
+
positional arguments:
|
|
115
|
+
{pythons,defaultpython,distinfo}
|
|
116
|
+
Action to perform
|
|
117
|
+
pythons Output list of stable Python versions that have not yet reached end of life, see `ci-helper pythons -h`
|
|
118
|
+
defaultpython Output the second-latest stable Python version in X.Y format, useful as a good choice for a default python version
|
|
119
|
+
distinfo Output info about the distribution, see `ci-helper distinfo -h`
|
|
120
|
+
|
|
121
|
+
options:
|
|
122
|
+
-h, --help show this help message and exit
|
|
123
|
+
--version show program's version number and exit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```shell
|
|
127
|
+
$ ci-helper pythons -h
|
|
128
|
+
usage: ci-helper pythons [-h] [--cibw]
|
|
129
|
+
|
|
130
|
+
options:
|
|
131
|
+
-h, --help show this help message and exit
|
|
132
|
+
--cibw Output as a space-separated list in the format `cpXY-* cpXY-*` as appropriate for the CIBW_BUILD environment variable to build for all
|
|
133
|
+
stable CPython versions, otherwise versions are output as a comma-separated list in the format X.Y,X.Y
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```shell
|
|
137
|
+
$ ci-helper defaultpython -h
|
|
138
|
+
usage: ci-helper defaultpython [-h]
|
|
139
|
+
|
|
140
|
+
options:
|
|
141
|
+
-h, --help show this help message and exit
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```shell
|
|
145
|
+
$ ci-helper distinfo -h
|
|
146
|
+
usage: ci-helper distinfo [-h] [{name,version,is_pure,has_env_markers}]
|
|
147
|
+
|
|
148
|
+
positional arguments:
|
|
149
|
+
{name,version,is_pure,has_env_markers}
|
|
150
|
+
Name of field to output as a single json value, if not given, all info is output as json
|
|
151
|
+
|
|
152
|
+
options:
|
|
153
|
+
-h, --help show this help message and exit
|
|
154
|
+
```
|
|
155
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
LICENSE.txt
|
|
3
|
+
README.md
|
|
4
|
+
ci_distinfo.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
setup.cfg
|
|
7
|
+
setup.py
|
|
8
|
+
ci_helper/__init__.py
|
|
9
|
+
ci_helper/__main__.py
|
|
10
|
+
ci_helper/__version__.py
|
|
11
|
+
ci_helper/ci_distinfo.py
|
|
12
|
+
ci_helper.egg-info/PKG-INFO
|
|
13
|
+
ci_helper.egg-info/SOURCES.txt
|
|
14
|
+
ci_helper.egg-info/dependency_links.txt
|
|
15
|
+
ci_helper.egg-info/entry_points.txt
|
|
16
|
+
ci_helper.egg-info/requires.txt
|
|
17
|
+
ci_helper.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ci_helper
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = ci-helper
|
|
3
|
+
description = Get info on how a package should be built on CI
|
|
4
|
+
long_description = file: README.md
|
|
5
|
+
long_description_content_type = text/markdown
|
|
6
|
+
author = Christopher Billington
|
|
7
|
+
author_email = chrisjbillington@gmail.com
|
|
8
|
+
url = http://github.com/chrisjbillington/ci-helper
|
|
9
|
+
project_urls =
|
|
10
|
+
Source Code=https://github.com/chrisjbillington/ci-helper
|
|
11
|
+
Download=https://github.com/chrisjbillington/ci-helper/releases
|
|
12
|
+
Tracker=https://github.com/chrisjbillington/ci-helper/issues
|
|
13
|
+
keywords = build setuptools conda
|
|
14
|
+
license = MIT
|
|
15
|
+
classifiers =
|
|
16
|
+
License :: OSI Approved :: MIT License
|
|
17
|
+
Programming Language :: Python :: 3 :: Only
|
|
18
|
+
|
|
19
|
+
[options]
|
|
20
|
+
packages = find:
|
|
21
|
+
python_requires = >=3.8
|
|
22
|
+
install_requires =
|
|
23
|
+
setuptools_scm
|
|
24
|
+
importlib_metadata
|
|
25
|
+
toml
|
|
26
|
+
requests
|
|
27
|
+
|
|
28
|
+
[options.entry_points]
|
|
29
|
+
console_scripts =
|
|
30
|
+
ci-helper = ci_helper.__main__:main
|
|
31
|
+
|
|
32
|
+
[egg_info]
|
|
33
|
+
tag_build =
|
|
34
|
+
tag_date = 0
|
|
35
|
+
|
ci_helper-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
from setuptools import setup
|
|
4
|
+
import sysconfig
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Normally packages don't have to do this - the dist_conda command should be
|
|
8
|
+
# automatically available. But since we're installing it, it isn't there yet!
|
|
9
|
+
from ci_helper.ci_distinfo import ci_distinfo
|
|
10
|
+
CMDCLASS = {'ci_distinfo': ci_distinfo}
|
|
11
|
+
|
|
12
|
+
VERSION_SCHEME = {
|
|
13
|
+
"version_scheme": os.getenv("SCM_VERSION_SCHEME", "guess-next-dev"),
|
|
14
|
+
"local_scheme": os.getenv("SCM_LOCAL_SCHEME", "node-and-date"),
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
SITE_PACKAGES = sysconfig.get_path('purelib')
|
|
18
|
+
|
|
19
|
+
# Add the dist_conda command to the distlib vendored in setuptools:
|
|
20
|
+
setuptools_distutils = Path(SITE_PACKAGES) / 'setuptools' / '_distutils' / 'command'
|
|
21
|
+
DATA_FILES = [
|
|
22
|
+
(str(setuptools_distutils.relative_to(sys.prefix)), ["ci_distinfo.py"]),
|
|
23
|
+
]
|
|
24
|
+
setup(
|
|
25
|
+
use_scm_version=VERSION_SCHEME,
|
|
26
|
+
cmdclass=CMDCLASS,
|
|
27
|
+
data_files=DATA_FILES,
|
|
28
|
+
)
|
|
29
|
+
|