@@ -301,6 +301,7 @@ func LoadCompose(ctx context.Context, repoPath, workingDir, projectName string,
301301// deployCompose deploys a project as specified by the Docker Compose specification (LoadCompose).
302302func deployCompose (ctx context.Context , dockerCli command.Cli , project * types.Project ,
303303 deployConfig * config.DeployConfig , recreateMode string , services []string ,
304+ needSignal []SignalService ,
304305) error {
305306 var (
306307 err error
@@ -313,6 +314,12 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, project *types.Pr
313314 return err
314315 }
315316
317+ if len (needSignal ) > 0 {
318+ if err := ComposeSignal (ctx , dockerCli , project , needSignal ); err != nil {
319+ return err
320+ }
321+ }
322+
316323 if deployConfig .PruneImages {
317324 beforeImages , err = service .Images (ctx , project .Name , api.ImagesOptions {})
318325 if err != nil {
@@ -416,7 +423,7 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, project *types.Pr
416423func DeployStack (
417424 jobLog * slog.Logger , externalRepoPath string , ctx * context.Context ,
418425 dockerCli * command.Cli , dockerClient * client.Client , payload * webhook.ParsedPayload , deployConfig * config.DeployConfig ,
419- changedFiles []gitInternal. ChangedFile , latestCommit , appVersion string , forceDeploy bool ,
426+ detectedChanges []Change , needSignal [] SignalService , latestCommit , appVersion string , forceDeploy bool ,
420427) error {
421428 startTime := time .Now ()
422429
@@ -520,12 +527,6 @@ func DeployStack(
520527 }
521528 }
522529 } else {
523- detectedChanges , err := ProjectFilesHaveChanges (externalRepoPath , changedFiles , project )
524- if err != nil {
525- errMsg := "failed to check for changed project files"
526- return fmt .Errorf ("%s: %w" , errMsg , err )
527- }
528-
529530 addComposeServiceLabels (project , deployConfig , payload , externalWorkingDir , appVersion , timestamp , ComposeVersion , latestCommit , projectHash )
530531 addComposeVolumeLabels (project , deployConfig , payload , appVersion , timestamp , ComposeVersion , latestCommit , projectHash )
531532
@@ -545,11 +546,21 @@ func DeployStack(
545546 }
546547
547548 stackLog .Debug ("changed project files detected, forcing recreate" , slog .Any ("changes" , detectedChanges ))
549+ case len (needSignal ) > 0 :
550+ stackLog .Debug ("changed project files detected, sending signal to service" ,
551+ slog .Any ("need_signal" , needSignal ))
548552 }
549553
550- stackLog .Info ("deploying stack" , slog .Group ("recreate" , slog .String ("mode" , recreateMode ), slog .Any ("forced_services" , forcedServices .ToSlice ())))
554+ stackLog .Info ("deploying stack" ,
555+ slog .Group ("recreate" ,
556+ slog .String ("mode" , recreateMode ),
557+ slog .Any ("forced_services" , forcedServices .ToSlice ()),
558+ ),
559+ slog .Any ("need_signal" , needSignal ),
560+ )
551561
552- err = deployCompose (* ctx , * dockerCli , project , deployConfig , recreateMode , forcedServices .ToSlice ())
562+ err = deployCompose (* ctx , * dockerCli , project , deployConfig , recreateMode ,
563+ forcedServices .ToSlice (), needSignal )
553564 if err != nil {
554565 prometheus .DeploymentErrorsTotal .WithLabelValues (deployConfig .Name ).Inc ()
555566 return fmt .Errorf ("failed to deploy stack: %w" , err )
@@ -605,7 +616,7 @@ func DestroyStack(
605616 return nil
606617}
607618
608- func getPaths (changedFiles []gitInternal.ChangedFile , basePath string ) []string {
619+ func GetPathsFromGitChangedFiles (changedFiles []gitInternal.ChangedFile , basePath string ) []string {
609620 var absPaths []string
610621
611622 basePath = filepath .Clean (basePath )
@@ -632,7 +643,7 @@ func getPaths(changedFiles []gitInternal.ChangedFile, basePath string) []string
632643}
633644
634645// HasChangedConfigs checks if any files used in docker compose `configs:` definitions have changed using the Git status.
635- func HasChangedConfigs (paths []string , project * types.Project ) ([]string , error ) {
646+ func HasChangedConfigs (paths []string , project * types.Project , ignoreCfg projectIgnoreCfg ) ([]string , [] string ) {
636647 configToServicesMap := make (map [string ][]string )
637648
638649 for name , s := range project .Services {
@@ -641,26 +652,35 @@ func HasChangedConfigs(paths []string, project *types.Project) ([]string, error)
641652 }
642653 }
643654
644- var changedServices []string
655+ var (
656+ changedServices []string
657+ ignoredServices []string
658+ )
645659
646- for name , c := range project .Configs {
660+ for cfgName , c := range project .Configs {
647661 // Changes in config.Content are handled in project hash comparison
648662 if c .File == "" {
649663 continue
650664 }
651665
652666 for _ , p := range paths {
653667 if filesystem .InBasePath (c .File , p ) {
654- changedServices = append (changedServices , configToServicesMap [name ]... )
668+ for _ , svcName := range configToServicesMap [cfgName ] {
669+ if ! checkIsIgnoreByCfg (ignoreCfg , svcName , changeScopeConfigs , cfgName ) {
670+ changedServices = append (changedServices , svcName )
671+ } else {
672+ ignoredServices = append (ignoredServices , svcName )
673+ }
674+ }
655675 }
656676 }
657677 }
658678
659- return slice . Unique (changedServices ), nil
679+ return getChangeAndIgnore (changedServices , ignoredServices )
660680}
661681
662682// HasChangedSecrets checks if any files used in docker compose `secrets:` definitions have changed using the Git status.
663- func HasChangedSecrets (paths []string , project * types.Project ) ([]string , error ) {
683+ func HasChangedSecrets (paths []string , project * types.Project , ignoreCfg projectIgnoreCfg ) ([]string , [] string ) {
664684 secretsToServicesMap := make (map [string ][]string )
665685
666686 for name , s := range project .Services {
@@ -669,46 +689,63 @@ func HasChangedSecrets(paths []string, project *types.Project) ([]string, error)
669689 }
670690 }
671691
672- var changedServices []string
692+ var (
693+ changedServices []string
694+ ignoredServices []string
695+ )
673696
674- for name , s := range project .Secrets {
697+ for secretName , s := range project .Secrets {
675698 if s .File == "" {
676699 continue
677700 }
678701
679702 for _ , p := range paths {
680703 if filesystem .InBasePath (s .File , p ) {
681- changedServices = append (changedServices , secretsToServicesMap [name ]... )
704+ for _ , svcName := range secretsToServicesMap [secretName ] {
705+ if ! checkIsIgnoreByCfg (ignoreCfg , svcName , changeScopeSecrets , secretName ) {
706+ changedServices = append (changedServices , svcName )
707+ } else {
708+ ignoredServices = append (ignoredServices , svcName )
709+ }
710+ }
682711 }
683712 }
684713 }
685714
686- return slice . Unique (changedServices ), nil
715+ return getChangeAndIgnore (changedServices , ignoredServices )
687716}
688717
689718// HasChangedBindMounts checks if any files used in docker compose `volumes:` definitions with type `bind` have changed using the Git status.
690- func HasChangedBindMounts (paths []string , project * types.Project ) ([]string , error ) {
691- var changedServices []string
719+ func HasChangedBindMounts (paths []string , project * types.Project , ignoreCfg projectIgnoreCfg ) ([]string , []string ) {
720+ var (
721+ changedServices []string
722+ ignoredServices []string
723+ )
692724
693725 for _ , s := range project .Services {
694726 out:
695727 for _ , v := range s .Volumes {
696728 if v .Type == "bind" && v .Source != "" {
697729 for _ , p := range paths {
698730 if filesystem .InBasePath (v .Source , p ) {
699- changedServices = append (changedServices , s .Name )
731+ if ! checkIsIgnoreByCfg (ignoreCfg , s .Name , changeScopeBindMounts , v .Target ) {
732+ changedServices = append (changedServices , s .Name )
733+ } else {
734+ ignoredServices = append (ignoredServices , s .Name )
735+ }
736+
700737 break out
701738 }
702739 }
703740 }
704741 }
705742 }
706743
707- return slice . Unique (changedServices ), nil
744+ return getChangeAndIgnore (changedServices , ignoredServices )
708745}
709746
710747// HasChangedEnvFiles checks if any files used in docker compose `env_file:` definitions have changed using the Git status.
711- func HasChangedEnvFiles (paths []string , project * types.Project ) ([]string , error ) {
748+ func HasChangedEnvFiles (paths []string , project * types.Project , _ projectIgnoreCfg ) ([]string , [] string ) {
712749 var changedServices []string
713750
714751 for _ , s := range project .Services {
@@ -728,7 +765,7 @@ func HasChangedEnvFiles(paths []string, project *types.Project) ([]string, error
728765
729766// HasChangedBuildFiles checks if any files used as build context in docker compose `build:` definitions have changed using the Git status.
730767// This includes any file within the build context directory for each service. If a changed file is within a build context, it returns true.
731- func HasChangedBuildFiles (paths []string , project * types.Project ) ([]string , error ) {
768+ func HasChangedBuildFiles (paths []string , project * types.Project , _ projectIgnoreCfg ) ([]string , [] string ) {
732769 var changedServices []string
733770
734771 for _ , s := range project .Services {
@@ -802,42 +839,81 @@ func sortChanges(changes []Change) {
802839 }
803840}
804841
842+ type IgnoredInfo struct {
843+ // Ignored services name
844+ Ignored []string `json:"ignored"`
845+ // Ignored services need to send signal
846+ NeedSendSignal []SignalService `json:"need_signal"`
847+ }
848+
849+ func (i IgnoredInfo ) IsEmpty () bool {
850+ return len (i .Ignored ) == 0 && len (i .NeedSendSignal ) == 0
851+ }
852+
853+ type SignalService struct {
854+ ServiceName string `json:"service_name"`
855+ Signal string `json:"signal"`
856+ }
857+
805858// ProjectFilesHaveChanges checks if any files related to the compose project have changed.
806- func ProjectFilesHaveChanges (repoRootExternal string , changedFiles []gitInternal. ChangedFile , project * types.Project ) ([]Change , error ) {
859+ func ProjectFilesHaveChanges (changePaths [] string , project * types.Project ) ([]Change , IgnoredInfo , error ) {
807860 checks := []struct {
808- name string
809- fn func ([]string , * types.Project ) ([]string , error )
861+ name changeScope
862+ fn func ([]string , * types.Project , projectIgnoreCfg ) ([]string , [] string )
810863 }{
811- {"configs" , HasChangedConfigs },
812- {"secrets" , HasChangedSecrets },
813- {"bindMounts" , HasChangedBindMounts },
814- {"envFiles" , HasChangedEnvFiles },
815- {"buildFiles" , HasChangedBuildFiles },
864+ {changeScopeConfigs , HasChangedConfigs },
865+ {changeScopeSecrets , HasChangedSecrets },
866+ {changeScopeBindMounts , HasChangedBindMounts },
867+ {changeScopeEnvFiles , HasChangedEnvFiles },
868+ {changeScopeBuildFiles , HasChangedBuildFiles },
816869 }
817870
818- paths := getPaths (changedFiles , repoRootExternal )
871+ ignoreCfg , err := getIgnoreRecreateCfgFromProject (project )
872+ if err != nil {
873+ return nil , IgnoredInfo {}, err
874+ }
819875
820- var changes []Change
876+ var (
877+ changes []Change
878+ allChangedServices , allIgnoredServices []string
879+ )
821880
822881 for _ , check := range checks {
823- changedServices , err := check .fn (paths , project )
824- if err != nil {
825- return nil , fmt . Errorf ( "failed to check '%s' for changes: %w" , check . name , err )
826- }
882+ changedServices , ignoredServices := check .fn (changePaths , project , ignoreCfg )
883+
884+ allChangedServices = append ( allChangedServices , changedServices ... )
885+ allIgnoredServices = append ( allIgnoredServices , ignoredServices ... )
827886
828887 if len (changedServices ) > 0 {
829888 slices .Sort (changedServices )
830889
831890 changes = append (changes , Change {
832- Type : check .name ,
891+ Type : string ( check .name ) ,
833892 Services : changedServices ,
834893 })
835894 }
836895 }
837896
838897 sortChanges (changes )
839898
840- return changes , nil
899+ _ , ignores := getChangeAndIgnore (allChangedServices , allIgnoredServices )
900+ slices .Sort (ignores )
901+
902+ retIgnored := IgnoredInfo {}
903+
904+ for _ , svcName := range ignores {
905+ sig := ignoreCfg [svcName ].signal
906+ if sig != "" {
907+ retIgnored .NeedSendSignal = append (retIgnored .NeedSendSignal , SignalService {
908+ ServiceName : svcName ,
909+ Signal : sig ,
910+ })
911+ } else {
912+ retIgnored .Ignored = append (retIgnored .Ignored , svcName )
913+ }
914+ }
915+
916+ return changes , retIgnored , nil
841917}
842918
843919// RestartProject restarts all services in the specified project.
0 commit comments