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

ak status - initial commit - proto OK #95

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion ak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
from . import ak_build
from . import ak_suggest
from . import ak_sparse
from . import ak_clone
from . import ak_clone
from . import ak_status
202 changes: 202 additions & 0 deletions ak/ak_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
"""AK."""
import logging

import yaml
from plumbum import cli, local
from plumbum.cmd import cat, git, grep
from plumbum.commands.modifiers import FG
from plumbum.commands.processes import ProcessExecutionError

from .ak_sub import Ak, AkSub

REPO_YAML = "repo.yaml"
SPEC_YAML = "spec.yaml"
FROZEN_YAML = "frozen.yaml"
VENDOR_FOLDER = "external-src"
LOCAL_FOLDER = "local-src"
LINK_FOLDER = "links"
ODOO_FOLDER = "src"
BUILDOUT_SRC = "./buildout.cfg"
PREFIX = "/odoo/"
JOBS = 2

logger = logging.getLogger(__name__)


def get_repo_key_from_spec(key):
if key == "odoo":
# put odoo in a different directory
repo_key = ODOO_FOLDER
elif key[0:2] == "./":
# if prefixed with ./ don't change the path
repo_key = key
else:
# put sources in VENDOR_FOLDERS
repo_key = "./%s/%s" % (VENDOR_FOLDER, key)
return repo_key


@Ak.subcommand("status")
class AkAnalyse(AkSub):
"dependencies status for odoo"

config = cli.SwitchAttr(
["c", "config"], default=SPEC_YAML, help="Config file", group="IO"
)

def _ensure_viable_installation(self, config):
if not local.path(config).is_file():
raise Exception("Config file not found.")

def main(self, *args):
config_file = self.config
self._ensure_viable_installation(config_file)

spec_data = yaml.safe_load(open(self.config).read()) or {}
serie = False
for repo, data in spec_data.items():
logger.debug("********** in", repo)
remote = False
remote_main_branch = False
if "src" in data:
remote = "origin"
serie_candidate = data["src"].split(" ")[-1]
remote_main_branch = serie_candidate
if (
serie_candidate.endswith(".0")
and serie_candidate.split(".")[0].isdigit()
):
serie = serie_candidate

elif "merges" in data:
for index, merge in enumerate(data["merges"]):
if index == 0:
remote = merge.split(" ")[0]
serie_candidate = merge.split(" ")[-1]
remote_main_branch = serie_candidate
if (
serie_candidate.endswith(".0")
and serie_candidate.split(".")[0].isdigit()
):
serie = serie_candidate
if not serie:
raise RuntimeError(f"Unable to figure out Odoo serie for {repo}")

repo_path = get_repo_key_from_spec(repo)
if not local.path(repo_path).is_dir():
print(f"{repo_path} NOT FOUND!")
continue

with local.cwd(repo_path):
local_module_versions = {}
# fisrt we get the current module versions from their manifest
for module in data.get("modules", []):
if local.path(f"{module}/__manifest__.py").is_file():
module_path = f"{module}/__manifest__.py"
elif local.path("__manifest__.py").is_file():
module_path = "__manifest__.py"
else:
continue
version = ((cat < module_path) | grep[f"{serie}."])().split(":")[1]
local_module_versions[module] = (
version.replace('"', "").replace(",", "").strip()
)

# TODO see if we cannot hide the stdout here:
local["git"]["fetch", remote, remote_main_branch] & FG
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to hide from stdout; try:

Suggested change
local["git"]["fetch", remote, remote_main_branch] & FG
local["git"]["fetch", remote, remote_main_branch]()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the optimistic "no freeze" approach can also work for large projects before they are in production.

But note that changes such as the ones I warned about in the account_payment_order module 6 months ago would have easily broken any small project. So may be we could still freeze many projects and simply filter in ak status to report only major changes so the human check is faster.

As for detecting changes between "builds" we might also save the analysis from the click-odoo update command. But as it is md5 based it is much less informative.

As for patched modules: well eventually we could still report any modules version changed in the git log along with changes in the patch branches. it's not really about trusting a version number but t might still be a better than nothing to know some patched module got a major or minor upstream change, or even a patch level change then if we want to be more cautious.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we can check with partner_modules in our instance if some of our other projects are already in a version. It can reduce the cost of each one of us looking all the pr.


# now we will scan the log between the latest upstream and the common ancestor:
current_branch = git["rev-parse", "--abbrev-ref", "HEAD"]().strip()
try:
ancestor = git[
"merge-base", current_branch, f"{remote}/{remote_main_branch}"
]().strip()
except ProcessExecutionError:
logger.debug("Unable to find merge-base")
continue

# now we will scan the change log for module version bumps:
try:
changes = (
git[
"log",
f"{ancestor}..{remote}/{remote_main_branch}",
"--author",
"OCA-git-bot",
]
| grep[f"{serie}.", "-B4"]
)()
except ProcessExecutionError:
logger.debug("UNABLE TO READ CHANGE LOG, LIKELY NO CHANGE")
continue
module_changes = {}
for module, version in local_module_versions.items():
module_changes[module] = []
if module in changes:
module_change = False
upgrade_version = version
lines = changes.splitlines()
lines.reverse()
for line in lines:
if line.strip().startswith(f"{module} "):
new_version = line.split(" ")[-1]
new_version_major = new_version.split(".")[2]
version_major = upgrade_version.split(".")[2]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use python-semver to clean this code

if version_major != new_version_major:
module_change = [
"MAJOR",
upgrade_version,
new_version,
]
upgrade_version = new_version
else:
new_version_minor = new_version.split(".")[3]
version_minor = upgrade_version.split(".")[3]
if version_minor != new_version_minor:
module_change = [
"minor",
upgrade_version,
new_version,
]
upgrade_version = new_version

# we got a version change bump commit,
# but now we should search for the merge commit
# in the commits just before:
elif line.strip().startswith("commit ") and module_change:
bump_commit = line.strip().split(" ")[1]
log = git["log", f"{bump_commit}~", "-n8"]()
scan_commit = False
for log_line in log.splitlines():
if log_line.strip().startswith("commit "):
scan_commit = log_line.strip().split(" ")[1]
elif log_line.strip().startswith("Merge PR #"):
commit = scan_commit
pr = log_line.split("Merge PR #")[1].split(" ")[
0
]
module_change.append(commit)
module_change.append(f"gh pr view {pr}")
break
if not module_changes.get(module):
module_changes[module] = []
module_changes[module].append(module_change)
module_change = False

# print the changes if any:
if module_changes:
repo_printed = False
for module, changes in module_changes.items():
if not changes:
continue
if not repo_printed:
print(f"\nin {repo}:")
repo_printed = True
print(f" {module}:")
for change in changes:
if len(change) < 5:
change.append("undef")
change.append("undef")
print(
f" {change[1]} -> {change[2]} ({change[0]}) - {change[4]} - ({change[3]})"
)