Skip to content
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
14 changes: 14 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"python.autoComplete.extraPaths": [
"${workspaceFolder}/sources/poky/bitbake/lib",
"${workspaceFolder}/sources/poky/meta/lib"
],
"python.analysis.extraPaths": [
"${workspaceFolder}/sources/poky/bitbake/lib",
"${workspaceFolder}/sources/poky/meta/lib"
],
"files.associations": {
"*.conf": "bitbake",
"*.inc": "bitbake"
}
}
5 changes: 3 additions & 2 deletions scripts/dev/upstream_merge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ The script is user configurable and uses `automation_conf.json` to define variou
- **`build_args`**:
String of extra arguments to pass to the build process (e.g., `"--org"` for NI corporate network builds).

- **`rt_target_IP`**:
The IP address or hostname of the RT target where images will be installed and tested via SSH.
- **`ssh_connection`**:
The SSH connection of the RT target where images will be installed.
Copy link
Contributor

Choose a reason for hiding this comment

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

You can give a usage example something like: [username]@[hostname or IP address]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have made those changes.

Usage example: [username]@[hostname or IP address]

**`Note`**:
- If the configuration file is `automation_conf.json`, you do not need to specify its path explicitly, as it is set as default. However, if you are using a different configuration file, you must provide its path using the `-c` argument.
Expand Down
8 changes: 4 additions & 4 deletions scripts/dev/upstream_merge/automation_conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"upstream_repo_name": "automerge_upstream",
"merge_branch_name": "dev/automerge/ni",
"fork_name": "myfork",
"email_log_level": 1,
"email_log_level": 0,
"log_level": 10,
"email_from": "shreejit.c@emerson.com",
"email_to": "pratheeksha.s.n@emerson.com",
"email_from": "",
"email_to": "",
"username": "",
"build_args":"",
"rt_target_IP":""
"ssh_connection":""
}
2 changes: 1 addition & 1 deletion scripts/dev/upstream_merge/json_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ def __init__(self, automation_conf_path, work_item_id):
self.email_log_level = config.get("email_log_level")
self.log_level = config.get("log_level")
self.build_args = config.get("build_args", "")
self.rt_target_IP = config.get("rt_target_IP")
self.ssh_connection = config.get("ssh_connection")
13 changes: 5 additions & 8 deletions scripts/dev/upstream_merge/log_and_email_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,14 @@ def format_status(status, message):
:param message: Associated message or details.
:return: Tuple of formatted status strings for summary and detailed logs.
"""
error_msg = f" ... ERRORS\n {message or ''}\n\n\n"
ok_no_changes = " ... OK (no changes)"
ok_msg = f" ... OK\n {message}\n\n\n"

if status != 0:
return (" ... ERRORS", error_msg)
return (" Merge Conflict\n", f" Merge Conflict\n {message or ''}\n\n\n")

if message is None:
return (ok_no_changes, f"{ok_no_changes}\n\n\n")
return (" Up to date (no changes)\n", " Up to date (no changes)\n\n\n")

return (" ... OK", ok_msg)
return (" Upstream Merge Successful\n", f"{message}\n\n\n")


def format_merge_report(merge_report, email_log_level, skip_push_and_pr=False):
Expand Down Expand Up @@ -102,7 +99,7 @@ def format_merge_report(merge_report, email_log_level, skip_push_and_pr=False):
for git_obj, (status, message) in merge_report.items():
min_line, additional_line = format_status(status, message)

layer_name = git_obj.local_repo.split("/")[-1]
layer_name = git_obj.local_repo.split("/")[-1].upper()
min_detail += f"{layer_name}\n{min_line}\n"

if status != 0:
Expand Down Expand Up @@ -151,7 +148,7 @@ def format_merge_report(merge_report, email_log_level, skip_push_and_pr=False):

if email_log_level == 0:
return min_detail + "\n\n" + error_detail
return min_detail + "\n\n" + error_detail + "\n\n" + diff_detail
return min_detail + "\n\n\t\t\t\t\t\t\t\t\tERROR DETAILS\n\n" + error_detail + "\n\n\t\t\t\t\t\t\t\t\tSUMMARY\n\n" + diff_detail


def write_log(email_file_name, contents):
Expand Down
23 changes: 12 additions & 11 deletions scripts/dev/upstream_merge/upstream_merge_and_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,27 +120,26 @@ def merge_submodules_with_upstream(
return merge_report


def build_and_test(clean_build, rt_target_IP, build_args):
def build_and_test(clean_build, ssh_connection, build_args):
"""
Build images and run tests on a VM if there are no merge errors.

:param clean_build: Boolean indicating whether to perform a clean build.
:param rt_target_IP: The target IP address of the RT system.
:param ssh_connection: The SSH connection string for the RT system.
:param build_args: To build with NI specific arguments.
:return: A tuple (status_code, message).
Returns (0, None) on success, or error details on failure.
"""
success = setup_env_and_build_packages(build_args, clean_build)
if success[0] != 0:
return success
success = install_and_test_image(rt_target_IP)
success = install_and_test_image(ssh_connection)
return success


def push_submodules_and_create_PRs(
merge_report,
merge_branch_name,
pr_title,
pr_description,
username
):
Expand All @@ -153,11 +152,11 @@ def push_submodules_and_create_PRs(
:param merge_report: Dictionary mapping GitRepo objects to
(status, message).
:param merge_branch_name: Name of the branch to push and create PRs from.
:param pr_title: Title for the pull request.
:param pr_description: Description for the pull request.
:param username: GitHub username owning the downstream fork.
:return: Dictionary with push and PR results for each sub-module.
"""
current_dir = os.getcwd()
push_and_pr_results = {}
for git_obj, (status, message) in list(merge_report.items()):
if status == 0 and message is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't os.chdir(git_obj.local_repo) meant to cd to the absolute path of the submodule? If that's the case, you won't need to keep switching back to the current_dir at the end of every iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After the final PR is created, the working directory should return to the nilrt directory.

Expand All @@ -166,10 +165,11 @@ def push_submodules_and_create_PRs(
git_obj,
merge_branch_name,
username,
pr_title,
f"{git_obj.local_repo.split('/')[-1]}: Merge latest upstream",
pr_description
)

os.chdir(current_dir)
return push_and_pr_results


Expand All @@ -181,18 +181,20 @@ def get_pr_description(work_item_id):
:return: A formatted string containing the PR checklist and work item.
"""
checklist = (
"# Testing\n"
"- [x] Built pyrex container\n"
"- [x] bitbake packagefeed-ni-core\n"
"- [x] bitbake packagegroup-ni-desirable\n"
"- [x] bitbake package-index && bitbake nilrt-base-system-image\n"
"- [x] Installed BSI on a VM and verified it boots successfully"
)
work_item_line = (
f"\n\nAB#{work_item_id}\n"
f"\n# Justification\nAB#{work_item_id}\n"
if work_item_id is not None else ""
)
return (
f"Merge latest from upstream. No conflicts."
f"{work_item_line}\n\n{checklist}"
f"{work_item_line}\n\n{checklist}\n@ni/rtos"
)


Expand Down Expand Up @@ -293,19 +295,18 @@ def main():
else:
build_and_test_details = build_and_test(
clean_build=skip_merge,
rt_target_IP=json_config_obj.rt_target_IP,
ssh_connection=json_config_obj.ssh_connection,
build_args=json_config_obj.build_args,
)

if build_and_test_details[0] == 0 and not args.skip_push_and_pr:
push_and_pr_results = push_submodules_and_create_PRs(
merge_report,
json_config_obj.merge_branch_name,
"Automated Merge PR",
get_pr_description(json_config_obj.work_item_id),
json_config_obj.username,
)
merge_report["Push and pr"] = push_and_pr_results
merge_report["Push and PR"] = push_and_pr_results

merge_report["Build and Test"] = build_and_test_details

Expand Down
2 changes: 1 addition & 1 deletion scripts/dev/upstream_merge/utils/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def clean_feeds_and_images(args=""):
"""
print("\nCleaning build feeds and images...\n")
return execute_and_stream_cmd_output(
"bash scripts/pipelines/clean_build.core-feeds_and_core-images.sh "
"bash scripts/pipelines/clean.core-feeds_and_core-images.sh "
f"{args}"
)

Expand Down
4 changes: 4 additions & 0 deletions scripts/dev/upstream_merge/utils/git_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,23 @@ def git_diff(
target="HEAD",
compare_with=None,
staged=False,
name_only=False,
capture_output=True
):
"""
Show differences between commits or the working directory.
:param target: Target commit or branch (default: "HEAD").
:param compare_with: Commit or branch to compare with (optional).
:param staged: Whether to show staged changes.
:param name_only: Show only changed file names.
:param capture_output: Whether to capture the command output.
"""
if compare_with:
command = f"git diff {target} {compare_with}"
else:
command = "git diff --staged" if staged else "git diff"
if name_only:
command += " --name-only"
return run_command(command, capture_output)


Expand Down
2 changes: 1 addition & 1 deletion scripts/dev/upstream_merge/utils/git_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def diff(
self
):
"""Check if there are differences in the last merge."""
return git_diff(compare_with="HEAD~1", capture_output=True)
return git_diff(compare_with="HEAD~1", name_only=True, capture_output=True)

def pull(
self,
Expand Down