@@ -162,24 +162,25 @@ This command is in beta and subject to change.`,
162162 // Detect local changes as a patch
163163 patch := detectPatch (workflowDir )
164164
165+ var workspacePatchKey string
165166 if patch != nil {
167+ if err := validateWorkspacePatch (patch ); err != nil {
168+ return err
169+ }
170+ workspacePatchKey = patchWorkspaceCacheKey (patch )
166171 fmt .Printf ("Base: %s\n " , patch .baseBranch )
167172 fmt .Printf ("Merge base: %s\n " , patch .mergeBase )
168173 fmt .Printf ("Patch size: %d bytes\n " , len (patch .content ))
174+ fmt .Printf ("Cache key: %s\n " , workspacePatchKey )
169175
170- hash := sha256 .Sum256 ([]byte (patch .content ))
171- patchHash := fmt .Sprintf ("%x" , hash )[:16 ]
172- cacheKey := fmt .Sprintf ("patch/%s/%s" , patch .mergeBase [:12 ], patchHash )
173- fmt .Printf ("Cache key: %s\n " , cacheKey )
174-
175- if err := api .UploadCacheEntry (ctx , tokenVal , orgID , cacheKey , []byte (patch .content )); err != nil {
176+ if err := api .UploadCacheEntry (ctx , tokenVal , orgID , workspacePatchKey , []byte (patch .content )); err != nil {
176177 return fmt .Errorf ("failed to upload patch: %w" , err )
177178 }
178179 fmt .Println ("Patch uploaded to Depot Cache" )
179180
180181 // Inject patch step into each selected job that has actions/checkout
181182 for _ , jobName := range selectedJobs {
182- injectPatchStep (jobs , jobName , patch .mergeBase , cacheKey )
183+ injectPatchStep (jobs , jobName , patch .mergeBase , workspacePatchKey )
183184 }
184185 }
185186
@@ -203,7 +204,9 @@ This command is in beta and subject to change.`,
203204 if sshAfterStep > 0 {
204205 fmt .Printf ("Inserting tmate step after step %d\n " , sshAfterStep )
205206 }
206- if headSHA , err := resolveHEAD (workflowDir ); err == nil {
207+ headSHA , headErr := resolveHEAD (workflowDir )
208+ headOK := headErr == nil
209+ if headOK {
207210 fmt .Printf ("HEAD: %s\n " , headSHA )
208211 }
209212 fmt .Println ()
@@ -218,12 +221,7 @@ This command is in beta and subject to change.`,
218221 Repo : repo ,
219222 WorkflowContent : []string {string (yamlBytes )},
220223 }
221-
222- if patch != nil {
223- req .Sha = & patch .mergeBase
224- } else if headSHA , err := resolveHEAD (workflowDir ); err == nil {
225- req .Sha = & headSHA
226- }
224+ setRunRequestGitContext (req , patch , headSHA , headOK , workspacePatchKey )
227225
228226 resp , err := api .CIRun (ctx , tokenVal , orgID , req )
229227 if err != nil {
@@ -300,6 +298,50 @@ type patchInfo struct {
300298 content string
301299}
302300
301+ // validateWorkspacePatch returns an error if a detected patch cannot be uploaded safely.
302+ // patch must be non-nil (callers invoke this only when patch != nil).
303+ func validateWorkspacePatch (patch * patchInfo ) error {
304+ if patch == nil {
305+ return fmt .Errorf ("internal error: validateWorkspacePatch called with nil patch" )
306+ }
307+ if patch .mergeBase == "" {
308+ return fmt .Errorf ("cannot upload workspace patch: empty merge base" )
309+ }
310+ return nil
311+ }
312+
313+ // patchWorkspaceCacheKey returns the Depot generic cache key for the workspace patch
314+ // (same value for upload, injectPatchStep, and RunRequest.workspace_patch_cache_key).
315+ func patchWorkspaceCacheKey (patch * patchInfo ) string {
316+ if patch == nil {
317+ return ""
318+ }
319+ prefix := patch .mergeBase
320+ if len (prefix ) > 12 {
321+ prefix = prefix [:12 ]
322+ }
323+ sum := sha256 .Sum256 ([]byte (patch .content ))
324+ patchHash := fmt .Sprintf ("%x" , sum )[:16 ]
325+ return fmt .Sprintf ("patch/%s/%s" , prefix , patchHash )
326+ }
327+
328+ // setRunRequestGitContext sets RunRequest Sha and, when a workspace patch was uploaded,
329+ // WorkspacePatchCacheKey. workspacePatchKey must be the exact string passed to UploadCacheEntry
330+ // and injectPatchStep (empty when patch == nil).
331+ func setRunRequestGitContext (req * civ1.RunRequest , patch * patchInfo , headSHA string , headOK bool , workspacePatchKey string ) {
332+ if patch != nil {
333+ mb := patch .mergeBase
334+ req .Sha = & mb
335+ key := workspacePatchKey
336+ req .WorkspacePatchCacheKey = & key
337+ return
338+ }
339+ if headOK {
340+ sha := headSHA
341+ req .Sha = & sha
342+ }
343+ }
344+
303345// findMergeBase picks the best base commit for patch generation.
304346//
305347// If the current branch has been pushed (origin/<branch> exists locally),
@@ -406,7 +448,7 @@ func resolveJobDeps(allJobs map[string]interface{}, requested []string) map[stri
406448 return needed
407449}
408450
409- func injectPatchStep (jobs map [string ]interface {}, jobName , mergeBase , cacheKey string ) {
451+ func injectPatchStep (jobs map [string ]interface {}, jobName , mergeBase , workspacePatchKey string ) {
410452 jobRaw , ok := jobs [jobName ]
411453 if ! ok {
412454 return
@@ -476,7 +518,7 @@ curl -fsSL "$PATCH_URL" -o /tmp/local.patch
476518echo "Applying patch..."
477519git apply --allow-empty /tmp/local.patch
478520rm /tmp/local.patch
479- echo "Patch applied successfully"` , cacheKey , cacheBaseURL ),
521+ echo "Patch applied successfully"` , workspacePatchKey , cacheBaseURL ),
480522 "env" : map [string ]interface {}{
481523 "DEPOT_TOKEN" : "${{ secrets.DEPOT_TOKEN }}" ,
482524 },
0 commit comments