From bee4470fb9895b8f2756522daafd795ade368a5f Mon Sep 17 00:00:00 2001 From: Peter Braun Date: Fri, 31 Jul 2026 11:20:18 +0200 Subject: [PATCH] fix: prefer scm_revision as the cache_id if available (#16564) * fix: prefer scm_revision as the cache_id if available * add tests * fix: make sure galaxy requirements are always updated when a new revision is pulled * make sure existing project cache id is used when available --------- Co-authored-by: Liam Allen --- awx/main/models/projects.py | 15 ++++++++------- awx/main/tasks/jobs.py | 4 +++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index e534ff2917..86f00f842a 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -450,13 +450,14 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn @property def cache_id(self): - """This gives the folder name where collections and roles will be saved to so it does not re-download + # Prefer scm_revision as the cache key if available. This guarantees that project changes are tracked correctly + # even over multiple nodes. The scm_revision is a hex string and thus safe to be used as a directory name. + if self.scm_revision: + return self.scm_revision - Normally we want this to track with the last update, because every update should pull new content. - This does not count sync jobs, but sync jobs do not update last_job or current_job anyway. - If cleanup_jobs deletes the last jobs, then we can fallback to using any given heuristic related - to the last job ran. - """ + # If no scm_revision is available (e.g. non-scm projects), use these. current_job_id and last_job_id are global + # IDs in the database. This means that when a project sync runs on one node, all other nodes become outdated, + # resulting in unnecessary re-syncs. if self.current_job_id: return str(self.current_job_id) elif self.last_job_id: @@ -638,7 +639,7 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin, TaskManage @property def cache_id(self): - if self.branch_override or self.job_type == 'check' or (not self.project): + if self.branch_override or (not self.project): return str(self.id) # causes it to not use the cache, basically return self.project.cache_id diff --git a/awx/main/tasks/jobs.py b/awx/main/tasks/jobs.py index c441e6ce1a..1b47de4f45 100644 --- a/awx/main/tasks/jobs.py +++ b/awx/main/tasks/jobs.py @@ -885,7 +885,9 @@ class SourceControlMixin(BaseTask): # Determine whether or not this project sync needs to populate the cache for Ansible content, roles and collections has_cache = os.path.exists(os.path.join(project.get_cache_path(), project.cache_id)) # Galaxy requirements are not supported for manual projects - if project.scm_type and ((not has_cache) or branch_override): + # If a source update is scheduled, always include roles/collections because + # the new revision may have different requirements. + if project.scm_type and ((not has_cache) or branch_override or source_update_tag in sync_needs): sync_needs.extend(['install_roles', 'install_collections']) return sync_needs