hasLocalModifications checks if the local workflow file has been modified from its source It resolves the source field and imports on the remote content, then compares with local Note: stop-after field is ignored during comparison as it's a deployment-specific setting localWorkflowDir, if non-empty,
(sourceContent, localContent, sourceSpec, localWorkflowDir string, verbose bool)
| 22 | // localWorkflowDir, if non-empty, is passed to import processing so that relative import paths |
| 23 | // whose files exist locally are preserved — giving an accurate comparison against local content. |
| 24 | func hasLocalModifications(sourceContent, localContent, sourceSpec, localWorkflowDir string, verbose bool) bool { |
| 25 | updateMergeLog.Printf("Checking for local modifications: source_spec=%s", sourceSpec) |
| 26 | // Normalize both contents |
| 27 | sourceNormalized := stringutil.NormalizeWhitespace(sourceContent) |
| 28 | localNormalized := stringutil.NormalizeWhitespace(localContent) |
| 29 | |
| 30 | // Remove stop-after field from both contents for comparison |
| 31 | // This field is deployment-specific and should not trigger "local modifications" warnings |
| 32 | sourceNormalized, _ = RemoveFieldFromOnTrigger(sourceNormalized, "stop-after") |
| 33 | localNormalized, _ = RemoveFieldFromOnTrigger(localNormalized, "stop-after") |
| 34 | |
| 35 | // Parse the source spec to get repo and ref information |
| 36 | parsedSourceSpec, err := parseSourceSpec(sourceSpec) |
| 37 | if err != nil { |
| 38 | if verbose { |
| 39 | fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Failed to parse source spec: %v", err))) |
| 40 | } |
| 41 | // Fall back to simple comparison |
| 42 | return sourceNormalized != localNormalized |
| 43 | } |
| 44 | |
| 45 | // Add the source field to the remote content |
| 46 | sourceWithSource, err := UpdateFieldInFrontmatter(sourceNormalized, "source", sourceSpec) |
| 47 | if err != nil { |
| 48 | if verbose { |
| 49 | fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Failed to add source field to remote content: %v", err))) |
| 50 | } |
| 51 | // Fall back to simple comparison |
| 52 | return sourceNormalized != localNormalized |
| 53 | } |
| 54 | |
| 55 | // Resolve imports on the remote content |
| 56 | workflow := &WorkflowSpec{ |
| 57 | RepoSpec: RepoSpec{ |
| 58 | RepoSlug: parsedSourceSpec.Repo, |
| 59 | Version: parsedSourceSpec.Ref, |
| 60 | }, |
| 61 | WorkflowPath: parsedSourceSpec.Path, |
| 62 | } |
| 63 | |
| 64 | sourceResolved, err := processIncludesInContent(sourceWithSource, workflow, parsedSourceSpec.Ref, localWorkflowDir, verbose) |
| 65 | if err != nil { |
| 66 | if verbose { |
| 67 | fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Failed to process imports on remote content: %v", err))) |
| 68 | } |
| 69 | // Use the version with source field but without resolved imports |
| 70 | sourceResolved = sourceWithSource |
| 71 | } |
| 72 | |
| 73 | // Normalize again after processing |
| 74 | sourceResolvedNormalized := stringutil.NormalizeWhitespace(sourceResolved) |
| 75 | if normalized, normalizeErr := UpdateFieldInFrontmatter(sourceResolvedNormalized, "source", "__gh_aw_source__"); normalizeErr == nil { |
| 76 | sourceResolvedNormalized = normalized |
| 77 | } |
| 78 | if normalized, normalizeErr := UpdateFieldInFrontmatter(localNormalized, "source", "__gh_aw_source__"); normalizeErr == nil { |
| 79 | localNormalized = normalized |
| 80 | } |
| 81 |