@@ -571,6 +571,30 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng
571571
572572 // Check if update is needed
573573 if ! force && currentRef == latestRef {
574+ // Download the source content to check if local file has been modified
575+ sourceContent , err := downloadWorkflowContent (sourceSpec .Repo , sourceSpec .Path , currentRef , verbose )
576+ if err != nil {
577+ // If we can't download for comparison, just show the up-to-date message
578+ if verbose {
579+ fmt .Fprintln (os .Stderr , console .FormatWarningMessage (fmt .Sprintf ("Failed to download source for comparison: %v" , err )))
580+ }
581+ fmt .Fprintln (os .Stderr , console .FormatInfoMessage (fmt .Sprintf ("Workflow %s is already up to date (%s)" , wf .Name , currentRef )))
582+ return nil
583+ }
584+
585+ // Read current workflow content
586+ currentContent , err := os .ReadFile (wf .Path )
587+ if err != nil {
588+ return fmt .Errorf ("failed to read current workflow: %w" , err )
589+ }
590+
591+ // Check if local file differs from source
592+ if hasLocalModifications (string (sourceContent ), string (currentContent ), wf .SourceSpec , verbose ) {
593+ fmt .Fprintln (os .Stderr , console .FormatInfoMessage (fmt .Sprintf ("Workflow %s is already up to date (%s)" , wf .Name , currentRef )))
594+ fmt .Fprintln (os .Stderr , console .FormatWarningMessage (fmt .Sprintf ("⚠️ Local copy of %s has been modified from source" , wf .Name )))
595+ return nil
596+ }
597+
574598 fmt .Fprintln (os .Stderr , console .FormatInfoMessage (fmt .Sprintf ("Workflow %s is already up to date (%s)" , wf .Name , currentRef )))
575599 return nil
576600 }
@@ -670,6 +694,64 @@ func normalizeWhitespace(content string) string {
670694 return normalized
671695}
672696
697+ // hasLocalModifications checks if the local workflow file has been modified from its source
698+ // It resolves the source field and imports on the remote content, then compares with local
699+ func hasLocalModifications (sourceContent , localContent , sourceSpec string , verbose bool ) bool {
700+ // Normalize both contents
701+ sourceNormalized := normalizeWhitespace (sourceContent )
702+ localNormalized := normalizeWhitespace (localContent )
703+
704+ // Parse the source spec to get repo and ref information
705+ parsedSourceSpec , err := parseSourceSpec (sourceSpec )
706+ if err != nil {
707+ if verbose {
708+ fmt .Fprintln (os .Stderr , console .FormatVerboseMessage (fmt .Sprintf ("Failed to parse source spec: %v" , err )))
709+ }
710+ // Fall back to simple comparison
711+ return sourceNormalized != localNormalized
712+ }
713+
714+ // Add the source field to the remote content
715+ sourceWithSource , err := UpdateFieldInFrontmatter (sourceNormalized , "source" , sourceSpec )
716+ if err != nil {
717+ if verbose {
718+ fmt .Fprintln (os .Stderr , console .FormatVerboseMessage (fmt .Sprintf ("Failed to add source field to remote content: %v" , err )))
719+ }
720+ // Fall back to simple comparison
721+ return sourceNormalized != localNormalized
722+ }
723+
724+ // Resolve imports on the remote content
725+ workflow := & WorkflowSpec {
726+ RepoSpec : RepoSpec {
727+ RepoSlug : parsedSourceSpec .Repo ,
728+ Version : parsedSourceSpec .Ref ,
729+ },
730+ WorkflowPath : parsedSourceSpec .Path ,
731+ }
732+
733+ sourceResolved , err := processIncludesInContent (sourceWithSource , workflow , parsedSourceSpec .Ref , verbose )
734+ if err != nil {
735+ if verbose {
736+ fmt .Fprintln (os .Stderr , console .FormatVerboseMessage (fmt .Sprintf ("Failed to process imports on remote content: %v" , err )))
737+ }
738+ // Use the version with source field but without resolved imports
739+ sourceResolved = sourceWithSource
740+ }
741+
742+ // Normalize again after processing
743+ sourceResolvedNormalized := normalizeWhitespace (sourceResolved )
744+
745+ // Compare the normalized contents
746+ hasModifications := sourceResolvedNormalized != localNormalized
747+
748+ if verbose && hasModifications {
749+ fmt .Fprintln (os .Stderr , console .FormatVerboseMessage ("Local modifications detected" ))
750+ }
751+
752+ return hasModifications
753+ }
754+
673755// MergeWorkflowContent performs a 3-way merge of workflow content using git merge-file
674756// It returns the merged content, whether conflicts exist, and any error
675757func MergeWorkflowContent (base , current , new , oldSourceSpec , newRef string , verbose bool ) (string , bool , error ) {
0 commit comments