Skip to content

Commit 5315d23

Browse files
authored
Merge pull request #177 from barbatos2011/feat/build-pipeline-phase4
build: Phase 4-5 — SSH target, jar-wrap image, cache mgmt, MCP, host builder
2 parents 7fd98c6 + 79b65cf commit 5315d23

74 files changed

Lines changed: 6416 additions & 162 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/build.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var (
3232
buildImageTag string
3333
buildImageOverride string
3434
buildPlatform string
35+
buildImageStrategy string
3536
)
3637

3738
var buildCmd = &cobra.Command{
@@ -69,8 +70,10 @@ func init() {
6970
"Git revision to build (HEAD, branch, tag, or sha)")
7071
buildCmd.Flags().StringVar(&buildArtifactKind, "artifact", "jar",
7172
"Artifact kind: 'jar' or 'image'")
72-
buildCmd.Flags().StringVar(&buildJDKVersion, "jdk", "8",
73-
"JDK version for the builder container (8|11|17|21)")
73+
buildCmd.Flags().StringVar(&buildJDKVersion, "jdk", "",
74+
"JDK version for the builder container (8|11|17|21). "+
75+
"Empty defaults from platform: linux/amd64→8, linux/arm64→17 "+
76+
"(java-tron's published compat matrix).")
7477
buildCmd.Flags().StringVar(&buildGradleTask, "gradle-task", "",
7578
"Gradle task name (defaults: 'shadowJar' for jar, 'dockerBuild' for image)")
7679
buildCmd.Flags().StringArrayVar(&buildGradleArgs, "gradle-arg", nil,
@@ -85,6 +88,11 @@ func init() {
8588
buildCmd.Flags().StringVar(&buildPlatform, "platform", "",
8689
"Docker --platform for the builder container (linux/amd64 or linux/arm64). "+
8790
"Empty = host arch. Cross-arch builds use QEMU emulation.")
91+
buildCmd.Flags().StringVar(&buildImageStrategy, "image-strategy", "",
92+
"For --artifact image: 'gradle' (default — invoke source's gradle dockerBuild "+
93+
"task; requires the source to ship one) or 'jar-wrap' (produce JAR via "+
94+
"the artifact=jar path, then COPY into a trond-embedded Dockerfile — "+
95+
"works for stock java-tron).")
8896
rootCmd.AddCommand(buildCmd)
8997
}
9098

@@ -112,6 +120,7 @@ func runBuild(cmd *cobra.Command, _ []string) error {
112120
ImageTag: buildImageTag,
113121
BuilderImageOverride: buildImageOverride,
114122
Platform: buildPlatform,
123+
ImageStrategy: buildImageStrategy,
115124
}
116125

117126
// SIGINT-aware context. Build container + git subprocesses all

cmd/build_flags_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type flagsCaptureRunner struct {
2424

2525
func (f *flagsCaptureRunner) RunDockerBuild(
2626
_ context.Context,
27-
sourcePath, _ string,
27+
sourcePath, _ /* outDir */, _ /* outTmpPath */ string,
2828
gradleTask string,
2929
gradleArgs []string,
3030
env map[string]string,

cmd/build_inspect.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
10+
"github.com/tronprotocol/tron-deployment/internal/build"
11+
"github.com/tronprotocol/tron-deployment/internal/output"
12+
)
13+
14+
// `trond build inspect <cache-key>` returns the full manifest plus
15+
// the computed artifact size + orphan flag for a single cache entry.
16+
// Designed for the agent workflow where `trond build list -o json`
17+
// returned a cache key and the agent now wants the full record.
18+
//
19+
// The cache key may be a full key (e.g. `260585c9397b-bd0861e68+...`)
20+
// or an unambiguous prefix (e.g. `260585`). Ambiguous prefixes
21+
// return AMBIGUOUS_PREFIX with the candidate list in the message so
22+
// the operator can disambiguate.
23+
//
24+
// Output schema: schemas/output/build-inspect.schema.json.
25+
26+
var buildInspectCmd = &cobra.Command{
27+
Use: "inspect <cache-key>",
28+
Short: "Show full details for one cached build",
29+
Long: `Look up a cached build by its cache key (or an unambiguous prefix)
30+
and print the full manifest plus the computed artifact size and orphan
31+
state. Mirrors the per-entry shape produced by 'trond build list -o
32+
json' so downstream tools can parse either output identically.`,
33+
Example: ` # Full key.
34+
trond build inspect 260585c9397b-bd0861e68+dirty-a71dd635-x25f78389
35+
36+
# Unambiguous prefix.
37+
trond build inspect 260585c9 -o json`,
38+
Args: cobra.ExactArgs(1),
39+
RunE: runBuildInspect,
40+
}
41+
42+
func init() {
43+
buildCmd.AddCommand(buildInspectCmd)
44+
}
45+
46+
func runBuildInspect(cmd *cobra.Command, args []string) error {
47+
entry, err := build.InspectEntry(cmd.Context(), args[0])
48+
if err != nil {
49+
switch {
50+
case errors.Is(err, build.ErrNoMatch):
51+
return output.NewErrorf("NOT_FOUND", output.ExitGeneralError,
52+
"no cache entry matches %q", args[0]).
53+
WithSuggestions("Run 'trond build list' to see available cache keys")
54+
case errors.Is(err, build.ErrAmbiguousPrefix):
55+
return output.NewErrorf("AMBIGUOUS_PREFIX", output.ExitValidationError,
56+
"%s", err.Error()).
57+
WithSuggestions("Re-run with a longer prefix or the full cache key")
58+
default:
59+
return output.NewErrorf("INSPECT_ERROR", output.ExitGeneralError,
60+
"inspect cache entry: %s", err.Error())
61+
}
62+
}
63+
64+
outputFmt, _ := cmd.Flags().GetString("output")
65+
if outputFmt == "json" {
66+
return output.WriteJSON(os.Stdout, entry)
67+
}
68+
printInspectText(entry)
69+
return nil
70+
}
71+
72+
func printInspectText(e *build.Entry) {
73+
fmt.Printf("cache_key: %s\n", e.CacheKey)
74+
if e.Orphaned {
75+
fmt.Println("status: ORPHAN (artifact missing on disk)")
76+
} else {
77+
fmt.Println("status: OK")
78+
}
79+
fmt.Printf("artifact_kind: %s\n", e.ArtifactKind)
80+
if e.ArtifactKind == "jar" {
81+
fmt.Printf("artifact_path: %s\n", e.ArtifactPath)
82+
fmt.Printf("sha256: %s\n", e.SHA256)
83+
} else {
84+
fmt.Printf("image_tag: %s\n", e.ImageTag)
85+
fmt.Printf("image_id: %s\n", e.ImageID)
86+
}
87+
fmt.Printf("size: %s\n", humanBytes(e.SizeBytes))
88+
fmt.Printf("source_path: %s\n", e.SourcePath)
89+
fmt.Printf("source_revision: %s\n", e.SourceRevision)
90+
if e.Dirty {
91+
fmt.Printf("patch_hash: %s (dirty tree)\n", e.PatchHash)
92+
}
93+
fmt.Printf("builder: %s\n", e.Builder)
94+
fmt.Printf("builder_image: %s\n", e.BuilderImage)
95+
fmt.Printf("jdk_version: %s\n", e.JDKVersion)
96+
fmt.Printf("platform: %s\n", e.Platform)
97+
fmt.Printf("gradle_task: %s\n", e.GradleTask)
98+
if len(e.GradleArgs) > 0 {
99+
fmt.Printf("gradle_args: %v\n", e.GradleArgs)
100+
}
101+
fmt.Printf("duration_ms: %d\n", e.DurationMs)
102+
fmt.Printf("created_at: %s\n", e.CreatedAt.Local().Format("2006-01-02 15:04:05 MST"))
103+
}

cmd/build_list.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"sort"
7+
"text/tabwriter"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/tronprotocol/tron-deployment/internal/build"
12+
"github.com/tronprotocol/tron-deployment/internal/output"
13+
)
14+
15+
// `trond build list` enumerates the on-disk build cache so operators
16+
// (and agents via `--output json`) can see what artifacts are taking
17+
// space, when they were built, and from which source revision —
18+
// without poking under the state directory by hand.
19+
//
20+
// Default behavior hides orphans (manifests whose artifact has been
21+
// deleted out-of-band). `--include-orphans` surfaces them too, which
22+
// is the data shape `trond build prune --orphan` operates on.
23+
//
24+
// Output schema: schemas/output/build-list.schema.json.
25+
26+
var (
27+
buildListFilter string
28+
buildListSort string
29+
buildListIncludeOrphans bool
30+
)
31+
32+
var buildListCmd = &cobra.Command{
33+
Use: "list",
34+
Short: "List cached build artifacts",
35+
Long: `Walk the trond build cache directory and emit one row per
36+
cached artifact (JAR or image). Useful for finding the cache key to
37+
reference in trond build inspect / prune, or for spotting orphaned
38+
manifests whose underlying artifact has been deleted out-of-band.`,
39+
Example: ` # Table view, newest first.
40+
trond build list
41+
42+
# Just images, JSON for piping into jq.
43+
trond build list --filter image -o json
44+
45+
# Sort by size to find the biggest cache hogs.
46+
trond build list --sort size`,
47+
RunE: runBuildList,
48+
}
49+
50+
func init() {
51+
buildListCmd.Flags().StringVar(&buildListFilter, "filter", "all",
52+
"Artifact kind to include: 'all' (default), 'jar', or 'image'")
53+
buildListCmd.Flags().StringVar(&buildListSort, "sort", "newest",
54+
"Sort order: 'newest' (default), 'oldest', or 'size' (largest first)")
55+
buildListCmd.Flags().BoolVar(&buildListIncludeOrphans, "include-orphans", false,
56+
"Include cache entries whose underlying artifact is missing")
57+
buildCmd.AddCommand(buildListCmd)
58+
}
59+
60+
func runBuildList(cmd *cobra.Command, _ []string) error {
61+
opts := []build.ListOption{}
62+
if buildListIncludeOrphans {
63+
opts = append(opts, build.IncludeOrphans())
64+
}
65+
entries, err := build.ListEntries(cmd.Context(), opts...)
66+
if err != nil {
67+
return output.NewErrorf("LIST_ERROR", output.ExitGeneralError,
68+
"list build cache: %s", err.Error())
69+
}
70+
71+
// Apply --filter and --sort here at the CLI layer so the
72+
// library's ListEntries stays a thin walker. (--filter is
73+
// post-walk: walking is cheap, image-size lookups already
74+
// happened; filtering after is straightforward.)
75+
entries = filterEntriesByKind(entries, buildListFilter)
76+
if err := sortEntries(entries, buildListSort); err != nil {
77+
return output.NewError("VALIDATION_ERROR", output.ExitValidationError, err.Error())
78+
}
79+
80+
outputFmt, _ := cmd.Flags().GetString("output")
81+
if outputFmt == "json" {
82+
return output.WriteJSON(os.Stdout, map[string]any{
83+
"entries": entries,
84+
"count": len(entries),
85+
})
86+
}
87+
return printBuildListTable(entries)
88+
}
89+
90+
func filterEntriesByKind(entries []*build.Entry, filter string) []*build.Entry {
91+
if filter == "" || filter == "all" {
92+
return entries
93+
}
94+
out := make([]*build.Entry, 0, len(entries))
95+
for _, e := range entries {
96+
if e.ArtifactKind == filter {
97+
out = append(out, e)
98+
}
99+
}
100+
return out
101+
}
102+
103+
func sortEntries(entries []*build.Entry, order string) error {
104+
switch order {
105+
case "", "newest":
106+
// ListEntries already returned newest-first; no-op.
107+
return nil
108+
case "oldest":
109+
sort.SliceStable(entries, func(i, j int) bool {
110+
return entries[i].CreatedAt.Before(entries[j].CreatedAt)
111+
})
112+
return nil
113+
case "size":
114+
sort.SliceStable(entries, func(i, j int) bool {
115+
return entries[i].SizeBytes > entries[j].SizeBytes
116+
})
117+
return nil
118+
default:
119+
return fmt.Errorf("invalid --sort %q (want: newest|oldest|size)", order)
120+
}
121+
}
122+
123+
func printBuildListTable(entries []*build.Entry) error {
124+
if len(entries) == 0 {
125+
fmt.Println("No cached builds.")
126+
return nil
127+
}
128+
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
129+
fmt.Fprintln(tw, "CACHE_KEY\tKIND\tSOURCE_REV\tSIZE\tCREATED\tARTIFACT")
130+
for _, e := range entries {
131+
artifact := e.ImageTag
132+
if e.ArtifactKind == "jar" {
133+
artifact = e.ArtifactPath
134+
}
135+
marker := ""
136+
if e.Orphaned {
137+
marker = " (orphan)"
138+
}
139+
fmt.Fprintf(tw, "%s\t%s%s\t%s\t%s\t%s\t%s\n",
140+
e.CacheKey,
141+
e.ArtifactKind, marker,
142+
shortRev(e.SourceRevision),
143+
humanBytes(e.SizeBytes),
144+
e.CreatedAt.Local().Format("2006-01-02 15:04"),
145+
artifact,
146+
)
147+
}
148+
return tw.Flush()
149+
}
150+
151+
// shortRev trims a 40-char git sha to 12 chars for table readability.
152+
func shortRev(rev string) string {
153+
if len(rev) > 12 {
154+
return rev[:12]
155+
}
156+
return rev
157+
}
158+
159+
// humanBytes formats a byte count as a short string (e.g. "615MB").
160+
// Operators don't need bytes precision in a table — JSON output
161+
// still carries the raw size_bytes for tooling.
162+
func humanBytes(n int64) string {
163+
const (
164+
kib = 1024
165+
mib = kib * 1024
166+
gib = mib * 1024
167+
)
168+
switch {
169+
case n >= gib:
170+
return fmt.Sprintf("%.1fGB", float64(n)/float64(gib))
171+
case n >= mib:
172+
return fmt.Sprintf("%dMB", n/mib)
173+
case n >= kib:
174+
return fmt.Sprintf("%dKB", n/kib)
175+
case n == 0:
176+
return "-"
177+
default:
178+
return fmt.Sprintf("%dB", n)
179+
}
180+
}

0 commit comments

Comments
 (0)