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

Run auto-tick with a single package only #2813

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
151 changes: 126 additions & 25 deletions conda_forge_tick/auto_tick.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import traceback
import typing
from dataclasses import dataclass
from typing import Literal, cast
from typing import AnyStr, Literal, cast
from urllib.error import URLError
from uuid import uuid4

Expand Down Expand Up @@ -41,6 +41,7 @@
)
from conda_forge_tick.lazy_json_backends import (
LazyJson,
does_key_exist_in_hashmap,
get_all_keys_for_hashmap,
lazy_json_transaction,
remove_key_for_hashmap,
Expand All @@ -50,7 +51,6 @@
PR_LIMIT,
load_migrators,
)
from conda_forge_tick.migration_runner import run_migration
from conda_forge_tick.migrators import MigrationYaml, Migrator, Version
from conda_forge_tick.migrators.version import VersionMigrationError
from conda_forge_tick.os_utils import eval_cmd
Expand All @@ -69,6 +69,7 @@
sanitize_string,
)

from .migration_runner import run_migration
from .migrators_types import MigrationUidTypedDict
from .models.pr_json import PullRequestData, PullRequestInfoSpecial, PullRequestState

Expand Down Expand Up @@ -847,10 +848,11 @@
return good_prs, break_loop


def _is_migrator_done(_mg_start, good_prs, time_per, pr_limit):
def _is_migrator_done(
_mg_start, good_prs, time_per, pr_limit, git_backend: GitPlatformBackend
):
curr_time = time.time()
backend = github_backend()
api_req = backend.get_api_requests_left()
api_req = git_backend.get_api_requests_left()

Check warning on line 855 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L855

Added line #L855 was not covered by tests

if curr_time - START_TIME > TIMEOUT:
logger.info(
Expand Down Expand Up @@ -885,7 +887,27 @@
return False


def _run_migrator(migrator, mctx, temp, time_per, git_backend: GitPlatformBackend):
def _run_migrator(
migrator: Migrator,
mctx: MigratorSessionContext,
temp: list[AnyStr],
time_per: float,
git_backend: GitPlatformBackend,
feedstock: str | None = None,
) -> int:
"""
Run a migrator.

:param migrator: The migrator to run.
:param mctx: The migrator session context.
:param temp: The list of temporary files.
:param time_per: The time limit of this migrator.
:param git_backend: The GitPlatformBackend instance to use.
:param feedstock: The feedstock to update, if None, all feedstocks are updated. Does not contain the `-feedstock`
suffix.

:return: The number of "good" PRs created by the migrator.
"""
_mg_start = time.time()

migrator_name = get_migrator_name(migrator)
Expand All @@ -907,6 +929,15 @@

possible_nodes = list(migrator.order(effective_graph, mctx.graph))

if feedstock:
if feedstock not in possible_nodes:
logger.info(

Check warning on line 934 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L932-L934

Added lines #L932 - L934 were not covered by tests
f"Feedstock {feedstock}-feedstock is not a candidate for migration of {migrator_name}. "
f"If you want to investigate this, run the make-migrators command."
)
return 0
possible_nodes = [feedstock]

Check warning on line 939 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L938-L939

Added lines #L938 - L939 were not covered by tests

# version debugging info
if isinstance(migrator, Version):
print("possible version migrations:", flush=True)
Expand Down Expand Up @@ -939,7 +970,9 @@
flush=True,
)

if _is_migrator_done(_mg_start, good_prs, time_per, migrator.pr_limit):
if _is_migrator_done(

Check warning on line 973 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L973

Added line #L973 was not covered by tests
_mg_start, good_prs, time_per, migrator.pr_limit, git_backend
):
return 0

for node_name in possible_nodes:
Expand All @@ -956,7 +989,9 @@
):
# Don't let CI timeout, break ahead of the timeout so we make certain
# to write to the repo
if _is_migrator_done(_mg_start, good_prs, time_per, migrator.pr_limit):
if _is_migrator_done(

Check warning on line 992 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L992

Added line #L992 was not covered by tests
_mg_start, good_prs, time_per, migrator.pr_limit, git_backend
):
break

base_branches = migrator.get_possible_feedstock_branches(attrs)
Expand Down Expand Up @@ -1051,18 +1086,27 @@
resource.setrlimit(resource.RLIMIT_AS, (limit_int, limit_int))


def _update_nodes_with_bot_rerun(gx: nx.DiGraph):
"""Go through all the open PRs and check if they are rerun"""
def _update_nodes_with_bot_rerun(gx: nx.DiGraph, feedstock: str | None = None):
"""
Go through all the open PRs and check if they are rerun

:param gx: the dependency graph
:param feedstock: The feedstock to update. If None, all feedstocks are updated. Does not contain the `-feedstock`
suffix.
"""

print("processing bot-rerun labels", flush=True)

for i, (name, node) in enumerate(gx.nodes.items()):
nodes = gx.nodes.items() if not feedstock else [(feedstock, gx.nodes[feedstock])]

Check warning on line 1100 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1100

Added line #L1100 was not covered by tests

for i, (name, node) in enumerate(nodes):

Check warning on line 1102 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1102

Added line #L1102 was not covered by tests
# logger.info(
# f"node: {i} memory usage: "
# f"{psutil.Process().memory_info().rss // 1024 ** 2}MB",
# )
with node["payload"] as payload:
if payload.get("archived", False):
logger.debug(f"skipping archived package {name}")

Check warning on line 1109 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1109

Added line #L1109 was not covered by tests
continue
with payload["pr_info"] as pri, payload["version_pr_info"] as vpri:
# reset bad
Expand Down Expand Up @@ -1112,12 +1156,24 @@
return version


def _update_nodes_with_new_versions(gx):
"""Updates every node with it's new version (when available)"""
def _update_nodes_with_new_versions(gx: nx.DiGraph, feedstock: str | None = None):
"""
Updates every node with its new version (when available)

:param gx: the dependency graph
:param feedstock: the feedstock to update, if None, all feedstocks are updated. Does not contain the `-feedstock`
suffix.
"""

print("updating nodes with new versions", flush=True)

version_nodes = get_all_keys_for_hashmap("versions")
if feedstock and not does_key_exist_in_hashmap("versions", feedstock):
logger.warning(f"Feedstock {feedstock}-feedstock not found in versions hashmap")
return

Check warning on line 1172 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1170-L1172

Added lines #L1170 - L1172 were not covered by tests

version_nodes = (

Check warning on line 1174 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1174

Added line #L1174 was not covered by tests
get_all_keys_for_hashmap("versions") if not feedstock else [feedstock]
)

for node in version_nodes:
version_data = LazyJson(f"versions/{node}.json").data
Expand All @@ -1143,13 +1199,42 @@
vpri["new_version"] = version_from_data


def _remove_closed_pr_json():
def _remove_closed_pr_json(feedstock: str | None = None):
"""
Remove the pull request information for closed PRs.

:param feedstock: The feedstock to remove the PR information for. If None, all PR information is removed. If you pass
a feedstock, closed pr_json files are not removed because this would require iterating all pr_json files. Does not
contain the `-feedstock` suffix.
"""
print("collapsing closed PR json", flush=True)

if feedstock:
pr_info_nodes = (

Check warning on line 1213 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1212-L1213

Added lines #L1212 - L1213 were not covered by tests
[feedstock] if does_key_exist_in_hashmap("pr_info", feedstock) else []
)
version_pr_info_nodes = (

Check warning on line 1216 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1216

Added line #L1216 was not covered by tests
[feedstock]
if does_key_exist_in_hashmap("version_pr_info", feedstock)
else []
)

if not pr_info_nodes:
logger.warning(

Check warning on line 1223 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1222-L1223

Added lines #L1222 - L1223 were not covered by tests
f"Feedstock {feedstock}-feedstock not found in pr_info hashmap"
)
if not version_pr_info_nodes:
logger.warning(

Check warning on line 1227 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1226-L1227

Added lines #L1226 - L1227 were not covered by tests
f"Feedstock {feedstock}-feedstock not found in version_pr_info hashmap"
)
else:
pr_info_nodes = get_all_keys_for_hashmap("pr_info")
version_pr_info_nodes = get_all_keys_for_hashmap("version_pr_info")

Check warning on line 1232 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1231-L1232

Added lines #L1231 - L1232 were not covered by tests

# first we go from nodes to pr json and update the pr info and remove the data
name_nodes = [
("pr_info", get_all_keys_for_hashmap("pr_info")),
("version_pr_info", get_all_keys_for_hashmap("version_pr_info")),
("pr_info", pr_info_nodes),
("version_pr_info", version_pr_info_nodes),
]
for name, nodes in name_nodes:
for node in nodes:
Expand Down Expand Up @@ -1182,6 +1267,11 @@

# at this point, any json blob referenced in the pr info is state != closed
# so we can remove anything that is empty or closed
if feedstock:
logger.info(

Check warning on line 1271 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1270-L1271

Added lines #L1270 - L1271 were not covered by tests
"Since you requested a run for a specific package, we are not removing closed pr_json files."
)
return

Check warning on line 1274 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1274

Added line #L1274 was not covered by tests
nodes = get_all_keys_for_hashmap("pr_json")
for node in nodes:
pr = LazyJson(f"pr_json/{node}.json")
Expand All @@ -1192,22 +1282,32 @@
)


def _update_graph_with_pr_info():
_remove_closed_pr_json()
def _update_graph_with_pr_info(feedstock: str | None = None):
"""
:param feedstock: The feedstock to update the graph for. If None, all feedstocks are updated. Does not contain the
`-feedstock` suffix.
"""
_remove_closed_pr_json(feedstock)

Check warning on line 1290 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1290

Added line #L1290 was not covered by tests
gx = load_existing_graph()
_update_nodes_with_bot_rerun(gx)
_update_nodes_with_new_versions(gx)
_update_nodes_with_bot_rerun(gx, feedstock)
_update_nodes_with_new_versions(gx, feedstock)

Check warning on line 1293 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1292-L1293

Added lines #L1292 - L1293 were not covered by tests
dump_graph(gx)


def main(ctx: CliContext) -> None:
def main(ctx: CliContext, feedstock: str | None = None) -> None:
"""
Run the main bot logic.

:param ctx: The CLI context.
:param feedstock: If not None, only the given feedstock is updated. Does not contain the `-feedstock` suffix.
"""
global START_TIME
START_TIME = time.time()

_setup_limits()

with fold_log_lines("updating graph with PR info"):
_update_graph_with_pr_info()
_update_graph_with_pr_info(feedstock)

Check warning on line 1310 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1310

Added line #L1310 was not covered by tests
deploy(ctx, dirs_to_deploy=["version_pr_info", "pr_json", "pr_info"])

# record tmp dir so we can be sure to clean it later
Expand All @@ -1227,6 +1327,7 @@
smithy_version=smithy_version,
pinning_version=pinning_version,
)
# TODO: this does not support --online
migrators = load_migrators()

# compute the time per migrator
Expand Down Expand Up @@ -1260,7 +1361,7 @@

for mg_ind, migrator in enumerate(migrators):
good_prs = _run_migrator(
migrator, mctx, temp, time_per_migrator[mg_ind], git_backend
migrator, mctx, temp, time_per_migrator[mg_ind], git_backend, feedstock
)
if good_prs > 0:
pass
Expand All @@ -1275,5 +1376,5 @@
# ],
# )

logger.info("API Calls Remaining: %d", github_backend().get_api_requests_left())
logger.info("API Calls Remaining: %s", git_backend.get_api_requests_left())

Check warning on line 1379 in conda_forge_tick/auto_tick.py

View check run for this annotation

Codecov / codecov/patch

conda_forge_tick/auto_tick.py#L1379

Added line #L1379 was not covered by tests
logger.info("Done")
28 changes: 21 additions & 7 deletions conda_forge_tick/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
import os
import time
from typing import Optional

import click
from click import Context, IntRange
Expand Down Expand Up @@ -131,31 +130,46 @@ def make_graph(
@job_option
@n_jobs_option
@click.argument(
"package",
"feedstock",
required=False,
default=None,
type=str,
)
@pass_context
def update_upstream_versions(
ctx: CliContext, job: int, n_jobs: int, package: Optional[str]
ctx: CliContext, job: int, n_jobs: int, feedstock: str | None
) -> None:
"""
Update the upstream versions of feedstocks in the graph.

If PACKAGE is given, only update that package, otherwise update all packages.
If FEEDSTOCK is given, only update that feedstock, otherwise update all feedstocks.
The FEEDSTOCK argument should omit the `-feedstock` suffix.
"""
from . import update_upstream_versions

check_job_param_relative(job, n_jobs)

update_upstream_versions.main(ctx, job=job, n_jobs=n_jobs, package=package)
update_upstream_versions.main(ctx, job=job, n_jobs=n_jobs, feedstock=feedstock)


@main.command(name="auto-tick")
@click.argument(
"feedstock",
required=False,
beckermr marked this conversation as resolved.
Show resolved Hide resolved
default=None,
type=str,
)
@pass_context
def auto_tick(ctx: CliContext) -> None:
def auto_tick(ctx: CliContext, feedstock: str | None) -> None:
"""
Run the main bot logic that runs all migrations, updates the graph accordingly, and opens the corresponding PRs.

If FEEDSTOCK is given, only run the bot for that feedstock, otherwise run the bot for all feedstocks.
The FEEDSTOCK argument should omit the `-feedstock` suffix.
"""
from . import auto_tick

auto_tick.main(ctx)
auto_tick.main(ctx, feedstock=feedstock)


@main.command(name="make-status-report")
Expand Down
2 changes: 1 addition & 1 deletion conda_forge_tick/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ def format_field(key: str, value: str) -> str:
return f"{key}:\n{value}"
return f"{key}: {value}"

output += "".join(format_field(key, value) for key, value in data.items())
output += "\n".join(format_field(key, value) for key, value in data.items())
output += f"\n{border}"

logger.debug(output)
Expand Down
11 changes: 11 additions & 0 deletions conda_forge_tick/lazy_json_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,17 @@ def get_all_keys_for_hashmap(name):
return backend.hkeys(name)


def does_key_exist_in_hashmap(name: str, key: str) -> bool:
"""
Check if a key exists in a hashmap, using the primary backend.
:param name: The hashmap name.
:param key: The key to check.
:return: True if the key exists, False otherwise.
"""
backend = LAZY_JSON_BACKENDS[CF_TICK_GRAPH_DATA_PRIMARY_BACKEND]()
return backend.hexists(name, key)


@contextlib.contextmanager
def lazy_json_transaction():
try:
Expand Down
Loading