Skip to content
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

feat: add build number editing for v1 recipes #3009

Merged
merged 10 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions conda_forge_tick/migrators/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import re
import typing
from pathlib import Path
from typing import Any, List, Sequence, Set

import dateutil.parser
Expand All @@ -14,7 +15,7 @@
from conda_forge_tick.lazy_json_backends import LazyJson
from conda_forge_tick.make_graph import make_outputs_lut_from_graph
from conda_forge_tick.path_lengths import cyclic_topological_sort
from conda_forge_tick.update_recipe import update_build_number
from conda_forge_tick.update_recipe import update_build_number, v1
from conda_forge_tick.utils import (
frozen_to_json_friendly,
get_bot_run_url,
Expand Down Expand Up @@ -592,25 +593,27 @@
}
return cyclic_topological_sort(graph, top_level)

def set_build_number(self, filename: str) -> None:
def set_build_number(self, filename: str | Path) -> None:
"""Bump the build number of the specified recipe.

Parameters
----------
filename : str
Path the the meta.yaml
"""
with open(filename) as f:
raw = f.read()
filename = Path(filename)
if filename.name == "recipe.yaml":
filename.write_text(v1.update_build_number(filename, self.new_build_number))

Check warning on line 606 in conda_forge_tick/migrators/core.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/migrators/core.py#L606

Added line #L606 was not covered by tests
else:
raw = filename.read_text()

new_myaml = update_build_number(
raw,
self.new_build_number,
build_patterns=self.build_patterns,
)
new_myaml = update_build_number(
raw,
self.new_build_number,
build_patterns=self.build_patterns,
)

with open(filename, "w") as f:
f.write(new_myaml)
filename.write_text(new_myaml)

def new_build_number(self, old_number: int) -> int:
"""Determine the new build number to use.
Expand Down
3 changes: 3 additions & 0 deletions conda_forge_tick/update_recipe/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .build_number import update_build_number

__all__ = ["update_build_number"]
61 changes: 61 additions & 0 deletions conda_forge_tick/update_recipe/v1/build_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Literal

from conda_forge_tick.update_recipe.v1.yaml import _dump_yaml_to_str, _load_yaml

if TYPE_CHECKING:
from pathlib import Path

Check warning on line 9 in conda_forge_tick/update_recipe/v1/build_number.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/update_recipe/v1/build_number.py#L9

Added line #L9 was not covered by tests

logger = logging.getLogger(__name__)

HashType = Literal["md5", "sha256"]


def _update_build_number_in_context(
recipe: dict[str, Any], new_build_number: int
) -> bool:
for key in recipe.get("context", {}):
if key.startswith("build_") or key == "build":
wolfv marked this conversation as resolved.
Show resolved Hide resolved
recipe["context"][key] = new_build_number
return True
return False


def _update_build_number_in_recipe(
recipe: dict[str, Any], new_build_number: int
) -> bool:
is_modified = False
if "build" in recipe and "number" in recipe["build"]:
recipe["build"]["number"] = new_build_number
is_modified = True

if "outputs" in recipe:
for output in recipe["outputs"]:
if "build" in output and "number" in output["build"]:
output["build"]["number"] = new_build_number
is_modified = True

Check warning on line 38 in conda_forge_tick/update_recipe/v1/build_number.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/update_recipe/v1/build_number.py#L35-L38

Added lines #L35 - L38 were not covered by tests

return is_modified


def update_build_number(file: Path, new_build_number: int = 0) -> str:
"""
Update the build number in the recipe file.

Arguments:
----------
* `file` - The path to the recipe file.
* `new_build_number` - The new build number to use. (default: 0)

Returns:
--------
* The updated recipe as a string.
"""
data = _load_yaml(file)
build_number_modified = _update_build_number_in_context(data, new_build_number)
if not build_number_modified:
_update_build_number_in_recipe(data, new_build_number)

return _dump_yaml_to_str(data)
22 changes: 22 additions & 0 deletions conda_forge_tick/update_recipe/v1/yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import io
from pathlib import Path

from ruamel.yaml import YAML

yaml = YAML()
wolfv marked this conversation as resolved.
Show resolved Hide resolved
yaml.preserve_quotes = True
yaml.width = 320
yaml.indent(mapping=2, sequence=4, offset=2)


def _load_yaml(file: Path) -> dict:
"""Load a YAML file."""
with file.open("r") as f:
return yaml.load(f)


def _dump_yaml_to_str(data: dict) -> str:
"""Dump a dictionary to a YAML string."""
with io.StringIO() as f:
yaml.dump(data, f)
return f.getvalue()
10 changes: 10 additions & 0 deletions tests/recipe_v1/build_number/test_1/expected.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# set the build number to something
context:
build: 0

package:
name: recipe_1
version: "0.1.0"

build:
number: ${{ build }}
10 changes: 10 additions & 0 deletions tests/recipe_v1/build_number/test_1/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# set the build number to something
context:
build: 123

package:
name: recipe_1
version: "0.1.0"

build:
number: ${{ build }}
11 changes: 11 additions & 0 deletions tests/recipe_v1/build_number/test_2/expected.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# set the build number to something
package:
name: recipe_1
version: "0.1.0"

# set the build number to something directly in the recipe text
build:
number: 0

source:
- url: foo
11 changes: 11 additions & 0 deletions tests/recipe_v1/build_number/test_2/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# set the build number to something
package:
name: recipe_1
version: "0.1.0"

# set the build number to something directly in the recipe text
build:
number: 321

source:
- url: foo
21 changes: 21 additions & 0 deletions tests/test_recipe_editing_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path

import pytest

from conda_forge_tick.update_recipe.v1 import update_build_number


@pytest.fixture
def data_dir() -> Path:
return Path(__file__).parent / "recipe_v1"


def test_build_number_mod(data_dir: Path) -> None:
tests = data_dir / "build_number"
result = update_build_number(tests / "test_1/recipe.yaml", 0)
expected = tests / "test_1/expected.yaml"
assert result == expected.read_text()

result = update_build_number(tests / "test_2/recipe.yaml", 0)
expected = tests / "test_2/expected.yaml"
assert result == expected.read_text()