80 lines
3.4 KiB
Go
80 lines
3.4 KiB
Go
package bridge
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
)
|
|
|
|
// Caller calls a module's tool by its manifest key (`<module>.<tool>`) with a JSON body and returns the tool's result:
|
|
// the bridge's process calls through the bus as its own principal, its tools bundle through the runtime (stdio.Ask).
|
|
type Caller func(ctx context.Context, key string, body any) (json.RawMessage, error)
|
|
|
|
// CommitTreesTool is the forge's tool for a commit's trees (mesh-catalog modules/gitea, issue 1234): arguments owner,
|
|
// repo and sha, answer {root, trees}, the root directory spelled "" (1234/AC4).
|
|
const CommitTreesTool = "gitea.gitea_commit_trees"
|
|
|
|
// TreesVia asks the forge for a commit's trees through call.
|
|
func TreesVia(ctx context.Context, call Caller) Trees {
|
|
return func(owner, repo, sha string) (string, map[string]string, error) {
|
|
raw, err := call(ctx, CommitTreesTool, map[string]string{"owner": owner, "repo": repo, "sha": sha})
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
var a struct {
|
|
Root string `json:"root"`
|
|
Trees map[string]string `json:"trees"`
|
|
}
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
return "", nil, fmt.Errorf("the answer of %s is not {root, trees}: %w", CommitTreesTool, err)
|
|
}
|
|
if a.Root == "" {
|
|
return "", nil, fmt.Errorf("the answer of %s names no root tree", CommitTreesTool)
|
|
}
|
|
return a.Root, a.Trees, nil
|
|
}
|
|
}
|
|
|
|
// GetFileTool is the forge's tool for one file at a ref (mesh-catalog modules/gitea tools/index.ts gitea_get_file):
|
|
// arguments owner, repo, path and ref, answer {file: {path, ref, sha, size, content}}, the content decoded to text
|
|
// (client.ts getFile). A path the forge does not have fails with `Gitea API <path>: 404 <body>` (client.ts request).
|
|
const GetFileTool = "gitea.gitea_get_file"
|
|
|
|
// notFound is how the forge's error says 404 (client.ts request: `Gitea API ${path}: ${status} ${text}`; client.ts:670
|
|
// recognises it as /: 404 /).
|
|
var notFound = regexp.MustCompile(`: 404 `)
|
|
|
|
// FilesVia asks the forge for a file through call. A file the forge says is not there is ErrNotFound.
|
|
func FilesVia(ctx context.Context, call Caller) Files {
|
|
return func(owner, repo, path, ref string) ([]byte, error) {
|
|
raw, err := call(ctx, GetFileTool, map[string]string{"owner": owner, "repo": repo, "path": path, "ref": ref})
|
|
if err != nil {
|
|
if notFound.MatchString(err.Error()) {
|
|
return nil, fmt.Errorf("%w: %v", ErrNotFound, err)
|
|
}
|
|
return nil, err
|
|
}
|
|
var a struct {
|
|
File *struct {
|
|
Content *string `json:"content"`
|
|
} `json:"file"`
|
|
}
|
|
if err := json.Unmarshal(raw, &a); err != nil || a.File == nil || a.File.Content == nil {
|
|
return nil, fmt.Errorf("the answer of %s is not {file: {content}}: %v", GetFileTool, err)
|
|
}
|
|
return []byte(*a.File.Content), nil
|
|
}
|
|
}
|
|
|
|
// MergedMessage is the merged message of a merge into the trunk, asked again: built as the bridge builds the one it writes
|
|
// on FORGE (it is that event's body through ConvertKept), from the pull request's owner, repository, number, branch, head
|
|
// and merge commit, which the gate's merges state holds. It reads the forge for every module directory (no kept inputs).
|
|
func MergedMessage(owner, repo string, number int, branch, head, merge string, trees Trees, files Files) (Message, error) {
|
|
body, err := json.Marshal(pull{Owner: owner, Repo: repo, Number: number, Head: branch, HeadSHA: head, Base: Trunk, MergeSHA: merge})
|
|
if err != nil {
|
|
return Message{}, err
|
|
}
|
|
return ConvertKept(MergedSubject, body, trees, files, nil)
|
|
}
|