public inbox for kdevops@lists.linux.dev
 help / color / mirror / Atom feed
From: Luis Chamberlain <mcgrof@kernel.org>
To: Chuck Lever <cel@kernel.org>, Daniel Gomez <da.gomez@kruces.com>,
	kdevops@lists.linux.dev
Cc: Luis Chamberlain <mcgrof@kernel.org>
Subject: [PATCH 5/8] bootlinux: add intelligent git repository detection and management
Date: Mon, 11 Aug 2025 15:43:04 -0700	[thread overview]
Message-ID: <20250811224307.2218478-6-mcgrof@kernel.org> (raw)
In-Reply-To: <20250811224307.2218478-1-mcgrof@kernel.org>

Add smart logic to infer when git clone is needed regardless of the
bootlinux_tree_set_by_cli setting. This solves the issue where directories
exist with files (like .config) but no .git repository, which previously
caused the git clone to be skipped.

The new logic:
1. Checks if the target directory exists
2. Checks if .git directory exists within it
3. Infers git clone is needed if directory exists but .git doesn't
4. Clones the repository even when bootlinux_tree_set_by_cli is true
5. Ensures the correct ref is checked out if git exists but on wrong ref

This handles the common case where:
- Directory is pre-created for 9p mount support
- Configuration files are copied before git clone
- bootlinux_tree_set_by_cli persists from previous runs

The implementation also:
- Fetches updates if the target ref doesn't exist locally
- Switches to the correct ref if repository exists but is on wrong branch
- Maintains backward compatibility with existing workflows
- Applies consistently across all build methods (9p, targets, builder)

This makes the system more robust and user-friendly by intelligently
handling partial setups and recovering from incomplete states.

Generated-by: Claude AI
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
 playbooks/roles/bootlinux/tasks/build/9p.yml  | 119 ++++++++++++++----
 .../roles/bootlinux/tasks/build/builder.yml   |  98 +++++++++++++--
 .../roles/bootlinux/tasks/build/targets.yml   |  98 +++++++++++++--
 3 files changed, 273 insertions(+), 42 deletions(-)

diff --git a/playbooks/roles/bootlinux/tasks/build/9p.yml b/playbooks/roles/bootlinux/tasks/build/9p.yml
index 5c9489c3..d0ae61ad 100644
--- a/playbooks/roles/bootlinux/tasks/build/9p.yml
+++ b/playbooks/roles/bootlinux/tasks/build/9p.yml
@@ -26,23 +26,47 @@
   run_once: true
   delegate_to: localhost
 
-- name: Check if target directory exists when using 9p and Linux CLI was set
+- name: Check if target directory exists
   stat:
     path: "{{ bootlinux_9p_host_path }}"
   register: target_directory_stat
   run_once: true
   delegate_to: localhost
+
+- name: Check if .git directory exists in target path
+  stat:
+    path: "{{ bootlinux_9p_host_path }}/.git"
+  register: git_directory_stat
+  run_once: true
+  delegate_to: localhost
   when:
-    - bootlinux_tree_set_by_cli|bool
+    - target_directory_stat.stat.exists
 
-- name: Fail if target directory does not exist when using 9p and Linux CLI was set
-  fail:
-    msg: "The target directory {{ bootlinux_9p_host_path }} does not exist."
+- name: Infer that git clone is needed when .git doesn't exist
+  set_fact:
+    needs_git_clone: true
+  when:
+    - target_directory_stat.stat.exists
+    - not git_directory_stat.stat.exists
   run_once: true
   delegate_to: localhost
+
+- name: Set needs_git_clone when directory doesn't exist
+  set_fact:
+    needs_git_clone: true
   when:
-    - bootlinux_tree_set_by_cli|bool
     - not target_directory_stat.stat.exists
+  run_once: true
+  delegate_to: localhost
+
+- name: Set needs_git_clone to false when .git exists
+  set_fact:
+    needs_git_clone: false
+  when:
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+  run_once: true
+  delegate_to: localhost
 
 - name: Verify target git ref exists before cloning
   command: "git ls-remote {{ target_linux_git }} {{ active_linux_ref | default(target_linux_ref) }}"
@@ -50,7 +74,7 @@
   run_once: true
   delegate_to: localhost
   when:
-    - not bootlinux_tree_set_by_cli|bool
+    - needs_git_clone|bool
   tags: [ 'clone']
 
 - name: Fail if git ref does not exist
@@ -68,20 +92,11 @@
       - If using A/B testing with different refs, ensure shallow cloning is disabled
       - The repository URL '{{ target_linux_git }}' is correct and accessible
   when:
-    - not bootlinux_tree_set_by_cli|bool
+    - needs_git_clone|bool
     - ref_check.rc != 0
   run_once: true
   delegate_to: localhost
 
-- name: Check if target directory exists for dirty check
-  stat:
-    path: "{{ bootlinux_9p_host_path }}"
-  register: git_dir_stat
-  run_once: true
-  delegate_to: localhost
-  when:
-    - not bootlinux_tree_set_by_cli|bool
-
 - name: Check if git tree is dirty
   command: "git -C {{ bootlinux_9p_host_path }} status --porcelain"
   register: git_status
@@ -90,9 +105,9 @@
   run_once: true
   delegate_to: localhost
   when:
-    - not bootlinux_tree_set_by_cli|bool
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
 
 - name: Fail if git tree has local modifications
   fail:
@@ -109,9 +124,9 @@
       Modified files:
       {{ git_status.stdout }}
   when:
-    - not bootlinux_tree_set_by_cli|bool
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
     - git_status.stdout | length > 0
   run_once: true
   delegate_to: localhost
@@ -129,9 +144,65 @@
   until: not result.failed
   tags: [ 'clone']
   when:
-    - not bootlinux_tree_set_by_cli|bool
+    - needs_git_clone|bool
+  run_once: true
+  delegate_to: localhost
+
+- name: Get current git ref when git exists but clone wasn't needed
+  command: "git -C {{ bootlinux_9p_host_path }} rev-parse HEAD"
+  register: current_ref
+  changed_when: false
+  run_once: true
+  delegate_to: localhost
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Get target ref SHA
+  command: "git -C {{ bootlinux_9p_host_path }} rev-parse {{ active_linux_ref | default(target_linux_ref) }}"
+  register: target_ref_sha
+  changed_when: false
+  failed_when: false
   run_once: true
   delegate_to: localhost
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Fetch updates if target ref doesn't exist locally
+  command: "git -C {{ bootlinux_9p_host_path }} fetch origin"
+  run_once: true
+  delegate_to: localhost
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Get target ref SHA after fetch
+  command: "git -C {{ bootlinux_9p_host_path }} rev-parse {{ active_linux_ref | default(target_linux_ref) }}"
+  register: target_ref_sha_after_fetch
+  changed_when: false
+  run_once: true
+  delegate_to: localhost
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Checkout target ref if not on correct ref
+  command: "git -C {{ bootlinux_9p_host_path }} checkout {{ active_linux_ref | default(target_linux_ref) }}"
+  run_once: true
+  delegate_to: localhost
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - (target_ref_sha.rc == 0 and current_ref.stdout != target_ref_sha.stdout) or
+      (target_ref_sha.rc != 0 and target_ref_sha_after_fetch is defined and target_ref_sha_after_fetch.rc == 0)
 
 - name: Copy kernel delta if requested on the control node
   template:
diff --git a/playbooks/roles/bootlinux/tasks/build/builder.yml b/playbooks/roles/bootlinux/tasks/build/builder.yml
index d36815ed..1213c56f 100644
--- a/playbooks/roles/bootlinux/tasks/build/builder.yml
+++ b/playbooks/roles/bootlinux/tasks/build/builder.yml
@@ -10,9 +10,43 @@
     - target_linux_install_b4
     - ansible_os_family == "Debian"
 
+- name: Check if target directory exists
+  stat:
+    path: "{{ target_linux_dir_path }}"
+  register: target_directory_stat
+
+- name: Check if .git directory exists in target path
+  stat:
+    path: "{{ target_linux_dir_path }}/.git"
+  register: git_directory_stat
+  when:
+    - target_directory_stat.stat.exists
+
+- name: Infer that git clone is needed when .git doesn't exist
+  set_fact:
+    needs_git_clone: true
+  when:
+    - target_directory_stat.stat.exists
+    - not git_directory_stat.stat.exists
+
+- name: Set needs_git_clone when directory doesn't exist
+  set_fact:
+    needs_git_clone: true
+  when:
+    - not target_directory_stat.stat.exists
+
+- name: Set needs_git_clone to false when .git exists
+  set_fact:
+    needs_git_clone: false
+  when:
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
 - name: Verify target git ref exists before cloning
   command: "git ls-remote {{ target_linux_git }} {{ target_linux_ref }}"
   register: ref_check
+  when:
+    - needs_git_clone|bool
 
 - name: Fail if git ref does not exist
   fail:
@@ -29,21 +63,18 @@
       - If using A/B testing with different refs, ensure shallow cloning is disabled
       - The repository URL '{{ target_linux_git }}' is correct and accessible
   when:
+    - needs_git_clone|bool
     - ref_check.rc != 0
 
-- name: Check if target directory exists for dirty check
-  stat:
-    path: "{{ target_linux_dir_path }}"
-  register: git_dir_stat
-
 - name: Check if git tree is dirty
   command: "git -C {{ target_linux_dir_path }} status --porcelain"
   register: git_status
   changed_when: false
   failed_when: false
   when:
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
 
 - name: Fail if git tree has local modifications
   fail:
@@ -60,8 +91,9 @@
       Modified files:
       {{ git_status.stdout }}
   when:
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
     - git_status.stdout | length > 0
 
 - name: Clone {{ target_linux_tree }}
@@ -75,6 +107,54 @@
   retries: 3
   delay: 5
   until: result is succeeded
+  when:
+    - needs_git_clone|bool
+
+- name: Get current git ref when git exists but clone wasn't needed
+  command: "git -C {{ target_linux_dir_path }} rev-parse HEAD"
+  register: current_ref
+  changed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Get target ref SHA
+  command: "git -C {{ target_linux_dir_path }} rev-parse {{ target_linux_ref }}"
+  register: target_ref_sha
+  changed_when: false
+  failed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Fetch updates if target ref doesn't exist locally
+  command: "git -C {{ target_linux_dir_path }} fetch origin"
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Get target ref SHA after fetch
+  command: "git -C {{ target_linux_dir_path }} rev-parse {{ target_linux_ref }}"
+  register: target_ref_sha_after_fetch
+  changed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Checkout target ref if not on correct ref
+  command: "git -C {{ target_linux_dir_path }} checkout {{ target_linux_ref }}"
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - (target_ref_sha.rc == 0 and current_ref.stdout != target_ref_sha.stdout) or
+      (target_ref_sha.rc != 0 and target_ref_sha_after_fetch is defined and target_ref_sha_after_fetch.rc == 0)
 
 - name: Copy the kernel delta to the builder
   ansible.builtin.template:
diff --git a/playbooks/roles/bootlinux/tasks/build/targets.yml b/playbooks/roles/bootlinux/tasks/build/targets.yml
index 414602ab..87393c74 100644
--- a/playbooks/roles/bootlinux/tasks/build/targets.yml
+++ b/playbooks/roles/bootlinux/tasks/build/targets.yml
@@ -10,10 +10,44 @@
     - target_linux_install_b4
     - ansible_facts['os_family']|lower != 'debian'
 
+- name: Check if target directory exists
+  stat:
+    path: "{{ target_linux_dir_path }}"
+  register: target_directory_stat
+
+- name: Check if .git directory exists in target path
+  stat:
+    path: "{{ target_linux_dir_path }}/.git"
+  register: git_directory_stat
+  when:
+    - target_directory_stat.stat.exists
+
+- name: Infer that git clone is needed when .git doesn't exist
+  set_fact:
+    needs_git_clone: true
+  when:
+    - target_directory_stat.stat.exists
+    - not git_directory_stat.stat.exists
+
+- name: Set needs_git_clone when directory doesn't exist
+  set_fact:
+    needs_git_clone: true
+  when:
+    - not target_directory_stat.stat.exists
+
+- name: Set needs_git_clone to false when .git exists
+  set_fact:
+    needs_git_clone: false
+  when:
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
 - name: Verify target git ref exists before cloning
   command: "git ls-remote {{ target_linux_git }} {{ target_linux_ref }}"
   register: ref_check
   tags: [ 'clone']
+  when:
+    - needs_git_clone|bool
 
 - name: Fail if git ref does not exist
   fail:
@@ -30,21 +64,18 @@
       - If using A/B testing with different refs, ensure shallow cloning is disabled
       - The repository URL '{{ target_linux_git }}' is correct and accessible
   when:
+    - needs_git_clone|bool
     - ref_check.rc != 0
 
-- name: Check if target directory exists for dirty check
-  stat:
-    path: "{{ target_linux_dir_path }}"
-  register: git_dir_stat
-
 - name: Check if git tree is dirty
   command: "git -C {{ target_linux_dir_path }} status --porcelain"
   register: git_status
   changed_when: false
   failed_when: false
   when:
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
 
 - name: Fail if git tree has local modifications
   fail:
@@ -61,8 +92,9 @@
       Modified files:
       {{ git_status.stdout }}
   when:
-    - git_dir_stat.stat.exists
-    - git_dir_stat.stat.isdir
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
     - git_status.stdout | length > 0
 
 - name: git clone {{ target_linux_tree }} on the target nodes
@@ -77,6 +109,54 @@
   register: result
   until: not result.failed
   tags: [ 'clone']
+  when:
+    - needs_git_clone|bool
+
+- name: Get current git ref when git exists but clone wasn't needed
+  command: "git -C {{ target_linux_dir_path }} rev-parse HEAD"
+  register: current_ref
+  changed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Get target ref SHA
+  command: "git -C {{ target_linux_dir_path }} rev-parse {{ target_linux_ref }}"
+  register: target_ref_sha
+  changed_when: false
+  failed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+
+- name: Fetch updates if target ref doesn't exist locally
+  command: "git -C {{ target_linux_dir_path }} fetch origin"
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Get target ref SHA after fetch
+  command: "git -C {{ target_linux_dir_path }} rev-parse {{ target_linux_ref }}"
+  register: target_ref_sha_after_fetch
+  changed_when: false
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - target_ref_sha.rc != 0
+
+- name: Checkout target ref if not on correct ref
+  command: "git -C {{ target_linux_dir_path }} checkout {{ target_linux_ref }}"
+  when:
+    - not needs_git_clone|bool
+    - target_directory_stat.stat.exists
+    - git_directory_stat.stat.exists
+    - (target_ref_sha.rc == 0 and current_ref.stdout != target_ref_sha.stdout) or
+      (target_ref_sha.rc != 0 and target_ref_sha_after_fetch is defined and target_ref_sha_after_fetch.rc == 0)
 
 - name: Copy kernel delta if requested on the target nodes
   template:
-- 
2.47.2


  parent reply	other threads:[~2025-08-11 22:43 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-11 22:42 [PATCH 0/8] linux-ab enhancements + monitor support Luis Chamberlain
2025-08-11 22:43 ` [PATCH 1/8] bootlinux: use different kernel for A/B testing by default Luis Chamberlain
2025-08-11 22:43 ` [PATCH 2/8] bootlinux: add support for custom refs on dev kernels on the CLI Luis Chamberlain
2025-08-11 22:43 ` [PATCH 3/8] bootlinux: add git ref verification before cloning Luis Chamberlain
2025-08-11 22:43 ` [PATCH 4/8] bootlinux: add git dirty check " Luis Chamberlain
2025-08-11 22:43 ` Luis Chamberlain [this message]
2025-08-11 22:43 ` [PATCH 6/8] bootlinux: enhance A/B testing and repository management Luis Chamberlain
2025-08-11 22:43 ` [PATCH 7/8] fstests: add make target for running tests on all hosts Luis Chamberlain
2025-08-11 22:43 ` [PATCH 8/8] monitoring: integrate monitoring collection into fstests workflow Luis Chamberlain
2025-08-11 22:46   ` Luis Chamberlain
2025-08-12  0:49     ` Luis Chamberlain
2025-08-14  0:59       ` Luis Chamberlain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250811224307.2218478-6-mcgrof@kernel.org \
    --to=mcgrof@kernel.org \
    --cc=cel@kernel.org \
    --cc=da.gomez@kruces.com \
    --cc=kdevops@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox