package build import ( "context" "errors" "fmt" "git.novox.be/novox/mesh-sdk/go/delivery" ) // **A done build is announced again by hand** (recovery of photos and link2pay on novox, 2026-10-29). The controller takes a // build in when a write of the build key reaches its take-in consumer, and composes then; a done key is never written again // by the builder, so a composition or a grant lost afterwards could not be made again by any tool. AnnounceAgain writes the // done key again with its own bytes, as a new revision of the same key: its content, state, artifacts and manifest stay // identical, and only the write is new. Who asked and why is in the builder's log and in the reasks answer. // AnnounceAgain rewrites the done key of module at fp unchanged, as a new revision, and answers the revision. A key that is // not done is refused with why: failed (builder_build_again builds it) or waiting (builder_ask_again). func (b *Builder) AnnounceAgain(ctx context.Context, module, fp, why, by string) BuildAgainResult { b.init() key, err := delivery.BuilderBuildKey(module, fp) if err != nil { return BuildAgainResult{Refused: err.Error()} } value, rev, found, err := b.Claimer.Store.Get(ctx, key) if err != nil { return BuildAgainResult{Refused: fmt.Sprintf("%s cannot be read: %v", key, err)} } if !found { return BuildAgainResult{Refused: fmt.Sprintf("%s has no build key: nothing to announce", key)} } cur, err := delivery.DecodeBuilderBuild(value) if err != nil { return BuildAgainResult{Refused: fmt.Sprintf("%s holds what is not a build: %v", key, err)} } switch cur.State { case delivery.BuildDone: case delivery.BuildFailed: return BuildAgainResult{Refused: fmt.Sprintf("%s is failed, not done: %s builds it again", key, BuildAgainTool)} default: return BuildAgainResult{Refused: fmt.Sprintf("%s is %q, not done: %s asks its waiting asks again", key, cur.State, AskAgainTool)} } // Compare-and-set at the revision read: a build written meanwhile is not overwritten. nrev, err := b.Claimer.Store.Update(ctx, key, value, rev) if err != nil { if errors.Is(err, ErrConflict) { return BuildAgainResult{Refused: fmt.Sprintf("%s changed while it was announced: read it again", key)} } return BuildAgainResult{Refused: fmt.Sprintf("%s could not be written: %v", key, err)} } b.logf("%s: done, announced again by %s (revision %d): %s", key, by, nrev, why) return BuildAgainResult{Revision: nrev} }