-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NEW: Migrate NVIDIA redist feedstocks to use cf-nvidia-tools #3580
base: main
Are you sure you want to change the base?
Changes from 1 commit
fbb63d5
cce6d62
0cab166
7543074
6f4ffc0
ced739d
2e023ae
ee7195b
3fbdb6a
9df8324
51abc14
a6d1d56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,245 @@ | ||||||||||
import os.path | ||||||||||
import logging | ||||||||||
import copy | ||||||||||
from typing import Any | ||||||||||
|
||||||||||
from conda_forge_tick.contexts import ClonedFeedstockContext | ||||||||||
import conda_forge_tick.migrators | ||||||||||
|
||||||||||
from conda_forge_tick.migrators_types import AttrsTypedDict, MigrationUidTypedDict | ||||||||||
from conda_forge_tick.utils import ( | ||||||||||
yaml_safe_dump, | ||||||||||
yaml_safe_load, | ||||||||||
get_bot_run_url, | ||||||||||
) | ||||||||||
|
||||||||||
|
||||||||||
def _file_contains(filename, string): | ||||||||||
with open(filename, "r") as f: | ||||||||||
return string in f.read() | ||||||||||
|
||||||||||
|
||||||||||
def _insert_requirements_build(meta): | ||||||||||
insert_before: bool = True | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think we can add this after compilers and like (assuming I'm understanding the intent of this parameter correctly)
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a function parameter. It is a variable which holds the state of "whether we are currently inserting lines into the variable named 'before'". We're not going to do that because trying to locate the compiler packages and insert the new dependency after them is technically challenging and not necessary for a working migration. Adding the complexity of inserting after compiler packages instead of just inserting into requirements.build makes additional assumptions including 1. that the package has compiler packages listed in the top-level requirements.build section 2. the compiler packages are already sorted to the top of the requirements.build section |
||||||||||
before: list[str] = [] | ||||||||||
after: list[str] = [] | ||||||||||
requirements_found = False | ||||||||||
build_found = False | ||||||||||
with open(meta, "r") as f: | ||||||||||
for line in f: | ||||||||||
if build_found: | ||||||||||
insert_before = False | ||||||||||
elif line.startswith("requirements"): | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was intentional. Once you find the requirements.build section, you don't need to do any more searching for it because you already know where to split the text. Maybe I could refactor to not execute insert_before=False, but I'm not sure it's worth the effort? |
||||||||||
requirements_found = True | ||||||||||
elif requirements_found and line.lstrip().startswith("build"): | ||||||||||
build_found = True | ||||||||||
|
||||||||||
if insert_before: | ||||||||||
before.append(line) | ||||||||||
else: | ||||||||||
after.append(line) | ||||||||||
|
||||||||||
if not (build_found or requirements_found): | ||||||||||
return | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So is the idea of this check that we are skipping metapackages? Or are there other cases this would be used for? May help to have a comment above clarifying intent There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we didn't find the requirements.build section, then we cannot add our new dependency to the file. |
||||||||||
|
||||||||||
with open(meta, "w") as f: | ||||||||||
f.writelines(before + [" - cf-nvidia-tools # [linux]\n"] + after) | ||||||||||
|
||||||||||
|
||||||||||
def _insert_build_script(meta): | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about adding this to the recipe's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. There's no reason to wait until the test phase to check whether the binaries have the expected glibc symbols. We can save everyone time by failing as early as possible. Additionally, I think that the environment variable c_stdlib_version is not set during the test phase, and it's required for this check. |
||||||||||
insert_before: bool = True | ||||||||||
before: list[str] = [] | ||||||||||
after: list[str] = [] | ||||||||||
script_found = False | ||||||||||
build_found = False | ||||||||||
with open(meta, "r") as f: | ||||||||||
for line in f: | ||||||||||
if not insert_before: | ||||||||||
pass | ||||||||||
elif line.startswith("build"): | ||||||||||
build_found = True | ||||||||||
elif build_found and line.lstrip().startswith("script"): | ||||||||||
script_found = True | ||||||||||
elif build_found and script_found: | ||||||||||
if line.lstrip().startswith("-"): | ||||||||||
# inside script | ||||||||||
pass | ||||||||||
else: | ||||||||||
# empty script section? | ||||||||||
insert_before = False | ||||||||||
if insert_before: | ||||||||||
before.append(line) | ||||||||||
else: | ||||||||||
after.append(line) | ||||||||||
|
||||||||||
if not (script_found or build_found): | ||||||||||
return | ||||||||||
|
||||||||||
with open(meta, "w") as f: | ||||||||||
f.writelines( | ||||||||||
before | ||||||||||
+ [' - check-glibc "$PREFIX"/lib/*.so.* "$PREFIX"/bin/* # [linux]\n'] | ||||||||||
+ after | ||||||||||
) | ||||||||||
|
||||||||||
|
||||||||||
class AddNVIDIATools(conda_forge_tick.migrators.Migrator): | ||||||||||
"""Add the cf-nvidia-tools package to NVIDIA redist feedstocks. | ||||||||||
|
||||||||||
In order to ensure that NVIDIA's redistributed binaries (redists) are being packaged | ||||||||||
correctly, NVIDIA has created a package containing a collection of tools to perform | ||||||||||
common actions for NVIDIA recipes. | ||||||||||
|
||||||||||
At this time, the package may be used to check Linux binaries for their minimum glibc | ||||||||||
requirement in order to ensure that the correct metadata is being used in the conda | ||||||||||
package. | ||||||||||
|
||||||||||
This migrator will attempt to add this glibc check to all feedstocks which download any | ||||||||||
artifacts from https://developer.download.nvidia.com. The check involves adding | ||||||||||
something like: | ||||||||||
|
||||||||||
```bash | ||||||||||
check-glibc "$PREFIX"/lib/*.so.* "$PREFIX"/bin/* | ||||||||||
``` | ||||||||||
|
||||||||||
to the build script after the package artifacts have been installed. | ||||||||||
|
||||||||||
> [!NOTE] | ||||||||||
> A human needs to verify that the glob expression is checking all of the correct | ||||||||||
> artifacts! | ||||||||||
|
||||||||||
More information about cf-nvidia-tools is available in the feedstock's | ||||||||||
[README](https://github.com/conda-forge/cf-nvidia-tools-feedstock/tree/main/recipe). | ||||||||||
|
||||||||||
Please ping carterbox for questions. | ||||||||||
""" | ||||||||||
|
||||||||||
name = "NVIDIA Tools Migrator" | ||||||||||
|
||||||||||
rerender = True | ||||||||||
|
||||||||||
max_solver_attempts = 3 | ||||||||||
|
||||||||||
migrator_version = 0 | ||||||||||
|
||||||||||
allow_empty_commits = False | ||||||||||
|
||||||||||
allowed_schema_versions = [0] | ||||||||||
|
||||||||||
def filter(self, attrs: "AttrsTypedDict", not_bad_str_start: str = "") -> bool: | ||||||||||
"""If true don't act upon node | ||||||||||
|
||||||||||
Parameters | ||||||||||
---------- | ||||||||||
attrs : dict | ||||||||||
The node attributes | ||||||||||
not_bad_str_start : str, optional | ||||||||||
If the 'bad' notice starts with the string then it is not | ||||||||||
to be excluded. For example, rebuild migrations don't need | ||||||||||
to worry about if the upstream can be fetched. Defaults to ``''`` | ||||||||||
|
||||||||||
Returns | ||||||||||
------- | ||||||||||
bool : | ||||||||||
True if node is to be skipped | ||||||||||
""" | ||||||||||
return ( | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the filter method always needs calls to super and to check the schema version. Check some of the other migrators for examples. |
||||||||||
attrs["archived"] | ||||||||||
or "https://developer.download.nvidia.com" not in attrs["source"]["url"] | ||||||||||
) | ||||||||||
|
||||||||||
def migrate( | ||||||||||
self, recipe_dir: str, attrs: AttrsTypedDict, **kwargs: Any | ||||||||||
) -> MigrationUidTypedDict: | ||||||||||
"""Perform the migration, updating the ``meta.yaml`` | ||||||||||
|
||||||||||
Parameters | ||||||||||
---------- | ||||||||||
recipe_dir : str | ||||||||||
The directory of the recipe | ||||||||||
attrs : dict | ||||||||||
The node attributes | ||||||||||
|
||||||||||
Returns | ||||||||||
------- | ||||||||||
namedtuple or bool: | ||||||||||
If namedtuple continue with PR, if False scrap local folder | ||||||||||
""" | ||||||||||
# STEP 0: Bump the build number | ||||||||||
self.set_build_number(os.path.join(recipe_dir, "meta.yaml")) | ||||||||||
|
||||||||||
# STEP 1: Add cf-nvidia-tools to build requirements | ||||||||||
meta = os.path.join(recipe_dir, "meta.yaml") | ||||||||||
if _file_contains(meta, "cf-nvidia-tools"): | ||||||||||
logging.debug("cf-nvidia-tools already in meta.yaml; not adding again.") | ||||||||||
else: | ||||||||||
_insert_requirements_build(meta) | ||||||||||
logging.debug("cf-nvidia-tools added to meta.yaml.") | ||||||||||
|
||||||||||
# STEP 2: Add check-glibc to the build script | ||||||||||
build = os.path.join(recipe_dir, "build.sh") | ||||||||||
if os.path.isfile(build): | ||||||||||
if _file_contains(build, "check-glibc"): | ||||||||||
logging.debug( | ||||||||||
"build.sh already contains check-glibc; not adding again." | ||||||||||
) | ||||||||||
else: | ||||||||||
with open(build, "a") as file: | ||||||||||
file.write('\ncheck-glibc "$PREFIX"/lib/*.so.* "$PREFIX"/bin/*\n') | ||||||||||
logging.debug("Added check-glibc to build.sh") | ||||||||||
else: | ||||||||||
if _file_contains(meta, "check-glibc"): | ||||||||||
logging.debug( | ||||||||||
"meta.yaml already contains check-glibc; not adding again." | ||||||||||
) | ||||||||||
else: | ||||||||||
_insert_build_script(meta) | ||||||||||
logging.debug("Added check-glibc to meta.yaml") | ||||||||||
|
||||||||||
# STEP 3: Remove os_version keys from conda-forge.yml | ||||||||||
config = os.path.join(recipe_dir, "..", "conda-forge.yml") | ||||||||||
with open(config) as f: | ||||||||||
y = yaml_safe_load(f) | ||||||||||
y_orig = copy.deepcopy(y) | ||||||||||
y.pop("os_version", None) | ||||||||||
if y_orig != y: | ||||||||||
with open(config, "w") as f: | ||||||||||
yaml_safe_dump(y, f) | ||||||||||
Comment on lines
+198
to
+206
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we are dropping the Wonder if it makes to add/confirm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. It's not necessary to force use an old container. The tool fails if c_stdlib_version is not set, so indirectly this is already checked? |
||||||||||
|
||||||||||
return self.migrator_uid(attrs) | ||||||||||
|
||||||||||
def pr_body( | ||||||||||
self, feedstock_ctx: ClonedFeedstockContext, add_label_text=True | ||||||||||
) -> str: | ||||||||||
"""Create a PR message body | ||||||||||
|
||||||||||
Returns | ||||||||||
------- | ||||||||||
body: str | ||||||||||
The body of the PR message | ||||||||||
:param feedstock_ctx: | ||||||||||
""" | ||||||||||
body = f"{AddNVIDIATools.__doc__}\n\n" | ||||||||||
|
||||||||||
if add_label_text: | ||||||||||
body += ( | ||||||||||
"If this PR was opened in error or needs to be updated please add " | ||||||||||
"the `bot-rerun` label to this PR. The bot will close this PR and " | ||||||||||
"schedule another one. If you do not have permissions to add this " | ||||||||||
"label, you can use the phrase " | ||||||||||
"<code>@<space/>conda-forge-admin, please rerun bot</code> " | ||||||||||
"in a PR comment to have the `conda-forge-admin` add it for you.\n\n" | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wonder if we should add some text on how to handle issues or ask for help in resolving There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please look at line 113. The description directs users (us) to ping me for questions. |
||||||||||
) | ||||||||||
|
||||||||||
body += ( | ||||||||||
"<sub>" | ||||||||||
"This PR was created by the [regro-cf-autotick-bot](https://github.com/regro/cf-scripts). " | ||||||||||
"The **regro-cf-autotick-bot** is a service to automatically " | ||||||||||
"track the dependency graph, migrate packages, and " | ||||||||||
"propose package version updates for conda-forge. " | ||||||||||
"Feel free to drop us a line if there are any " | ||||||||||
"[issues](https://github.com/regro/cf-scripts/issues)! " | ||||||||||
+ f"This PR was generated by {get_bot_run_url()} - please use this URL for debugging." | ||||||||||
+ "</sub>" | ||||||||||
) | ||||||||||
return body |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should append this migrator to the list after the noarch python migrator. There are some conventions in the code about where each kind of migrator goes in the list.