File: add_miniforge.py

package info (click to toggle)
pyenv 2.6.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,496 kB
  • sloc: sh: 4,914; python: 410; makefile: 161; ansic: 60
file content (142 lines) | stat: -rwxr-xr-x 5,311 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
'Adds the latest miniforge and mambaforge releases.'
from pathlib import Path
import logging
import os
import string

import requests

logger = logging.getLogger(__name__)
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'))

MINIFORGE_REPO = 'conda-forge/miniforge'
DISTRIBUTIONS = ['miniforge']
DISTRIBUTIONS_PRE25  = ['miniforge', 'mambaforge']

SKIPPED_RELEASES = [
    '4.13.0-0',     #has no Mambaforge. We already generated scripts for Miniforge
    '22.11.1-0',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '22.11.1-1',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '22.11.1-2',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '25.3.0-0',     #marked as prerelease, no Linux version
]

install_script_fmt = """
case "$(anaconda_architecture 2>/dev/null || true)" in
{install_lines}
* )
  {{ echo
    colorize 1 "ERROR"
    echo ": The binary distribution of {flavor} is not available for $(anaconda_architecture 2>/dev/null || true)."
    echo
  }} >&2
  exit 1
  ;;
esac
""".lstrip()

install_line_fmt = """
"{os}-{arch}" )
  install_script "{filename}" "{url}#{sha}" "miniconda" verify_py{py_version}
  ;;
""".strip()

here = Path(__file__).resolve()
out_dir: Path = here.parent.parent / "share" / "python-build"

def download_sha(url):
    logger.info('Downloading SHA file %(url)s', locals())
    tup = tuple(reversed(requests.get(url).text.replace('./', '').rstrip().split()))
    logger.debug('Got %(tup)s', locals())
    return tup

def create_spec(filename, sha, url):
    flavor_with_suffix, version, subversion, os, arch = filename.replace('.sh', '').split('-')
    suffix = flavor_with_suffix[-1]

    if suffix in string.digits:
        flavor = flavor_with_suffix[:-1]
    else:
        flavor = flavor_with_suffix

    spec = {
        'filename': filename,
        'sha': sha,
        'url': url,
        'py_version': py_version(version),
        'flavor': flavor,
        'os': os,
        'arch': arch,
        'installer_filename': f'{flavor_with_suffix.lower()}-{version}-{subversion}',
    }

    logger.debug('Created spec %(spec)s', locals())

    return spec

def version_tuple(version):
    return tuple(int(part) for part in version.split('-')[0].split("."))

def py_version(version):
    """Suffix for `verify_pyXXX` to call in the generated build script"""
    version_tuple_ = version_tuple(version)
    # current version: mentioned under https://github.com/conda-forge/miniforge?tab=readme-ov-file#requirements-and-installers
    # transition points:
    # https://github.com/conda-forge/miniforge/blame/main/Miniforge3/construct.yaml
    # look for "- python <version>" in non-pypy branch and which tag the commit is first in
    if version_tuple_ >= (24,5):
        # yes, they jumped from 3.10 directly to 3.12
        # https://github.com/conda-forge/miniforge/commit/bddad0baf22b37cfe079e47fd1680fdfb2183590
        return "312"
    if version_tuple_ >= (4,14):
        return "310"
    raise ValueError("Bundled Python version unknown for release `%s'"%version)

def supported(filename):
    return ('pypy' not in filename) and ('Windows' not in filename)

def add_version(release, distributions):
    tag_name = release['tag_name']
    download_urls = { f['name']: f['browser_download_url'] for f in release['assets'] }
    # can assume that sha files are named similar to release files so can also check supported(on their names)
    shas = dict([download_sha(url) for (name, url) in download_urls.items() if name.endswith('.sha256') and supported(os.path.basename(name)) and tag_name in name])
    specs = [create_spec(filename, sha, download_urls[filename]) for (filename, sha) in shas.items() if supported(filename)]
    

    for distribution in distributions:
        distribution_specs = [spec for spec in specs if distribution in spec['flavor'].lower()]
        count = len(distribution_specs)

        if count > 0:
            output_file = out_dir / distribution_specs[0]['installer_filename']

            logger.info('Writing %(count)d specs for %(distribution)s to %(output_file)s', locals())

            script_str = install_script_fmt.format(
                install_lines="\n".join([install_line_fmt.format_map(s) for s in distribution_specs]),
                flavor=distribution_specs[0]['flavor'],
            )

            with open(output_file, 'w') as f:
                f.write(script_str)
        else:
            logger.info('Did not find specs for %(distribution)s', locals())

for release in requests.get(f'https://api.github.com/repos/{MINIFORGE_REPO}/releases').json():
    version = release['tag_name']

    if version in SKIPPED_RELEASES:
        continue

    logger.info('Looking for %(version)s in %(out_dir)s', locals())

    # mambaforge is retired https://github.com/conda-forge/miniforge/releases/tag/24.11.2-0
    if version_tuple(version) >= (24,11,2):
        distributions = DISTRIBUTIONS
    else:
        distributions = DISTRIBUTIONS_PRE25

    if any(not list(out_dir.glob(f'{distribution}*-{version}')) for distribution in distributions):
        logger.info('Downloading %(version)s', locals())
        add_version(release, distributions)