SourceImplementationDigest calculates a content-hash of the module source's implementation. Two module sources with the same digest should share implementation-scoped cache identity for SDK operations and module function calls even if they came from different client-specific sources.
(ctx context.Context)
| 1097 | // implementation-scoped cache identity for SDK operations and module function |
| 1098 | // calls even if they came from different client-specific sources. |
| 1099 | func (src *ModuleSource) SourceImplementationDigest(ctx context.Context) (digest.Digest, error) { |
| 1100 | dag, err := CurrentDagqlServer(ctx) |
| 1101 | if err != nil { |
| 1102 | return "", fmt.Errorf("failed to get dag server: %w", err) |
| 1103 | } |
| 1104 | |
| 1105 | var contextDigest string |
| 1106 | if err := dag.Select(ctx, src.ContextDirectory, &contextDigest, dagql.Selector{Field: "digest"}); err != nil { |
| 1107 | return "", fmt.Errorf("failed to get module source context directory digest: %w", err) |
| 1108 | } |
| 1109 | |
| 1110 | inputs := []string{ |
| 1111 | moduleSourceHashMix, |
| 1112 | src.ModuleOriginalName, |
| 1113 | src.SourceRootSubpath, |
| 1114 | src.SourceSubpath, |
| 1115 | contextDigest, |
| 1116 | } |
| 1117 | |
| 1118 | if src.SDK != nil && src.SDK.Debug { |
| 1119 | inputs = append(inputs, rand.Text()) |
| 1120 | } |
| 1121 | |
| 1122 | // Include user defaults in digest so changes to env files invalidate cache |
| 1123 | if src.UserDefaults != nil { |
| 1124 | vars, err := src.UserDefaults.Variables(ctx, false) |
| 1125 | if err != nil { |
| 1126 | // If user defaults fail to load, log the error and skip them from digest calculation |
| 1127 | // FIXME: change the signature to bubble up the error? |
| 1128 | slog.Error("failed to load user defaults for module source", |
| 1129 | "module_name", src.ModuleName, |
| 1130 | "error", err, |
| 1131 | ) |
| 1132 | } else { |
| 1133 | // Sort by variable name for better digest stability |
| 1134 | sort.Slice(vars, func(i, j int) bool { |
| 1135 | return vars[i].Name < vars[j].Name |
| 1136 | }) |
| 1137 | for _, v := range vars { |
| 1138 | inputs = append(inputs, fmt.Sprintf("env:%s=%s", v.Name, v.Value)) |
| 1139 | } |
| 1140 | } |
| 1141 | } |
| 1142 | |
| 1143 | if src.SDK != nil { |
| 1144 | inputs = append(inputs, src.SDK.Source) |
| 1145 | } |
| 1146 | |
| 1147 | inputs = append(inputs, src.IncludePaths...) |
| 1148 | |
| 1149 | for _, dep := range src.Dependencies { |
| 1150 | if dep.Self() == nil { |
| 1151 | continue |
| 1152 | } |
| 1153 | var depDigest string |
| 1154 | if err := dag.Select(ctx, dep, &depDigest, dagql.Selector{Field: "digest"}); err != nil { |
| 1155 | return "", fmt.Errorf("failed to get dependency digest: %w", err) |
| 1156 | } |
no test coverage detected