1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 |
#!/usr/bin/env bash
set -euo pipefail
# ──────────────────────────────────────────────────────────────────────────────
# build.sh: builds the project
# Usage:
# ./build.sh build
# ./build.sh clean
# ./build.sh assets
# ./build.sh all # assets + build
# ./build.sh help
# ──────────────────────────────────────────────────────────────────────────────
# ─── Configuration ───────────────────────────────────────────────────────────
BINARY_NAME="srv"
MAIN_PACKAGE="./main.go" # adjust if your entry point is different
EMBED_TARGET_DIR="internal/assets/embed"
# ─── Font configuration ───────────────────────────────────────────────────────
declare -A FONTS
FONTS["DejaVuSansMono.ttf"]="/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf:fonts-dejavu-core"
FONTS["wqy-zenhei.ttc"]="/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc:fonts-wqy-zenhei"
FONTS["MTLc3m.ttf"]="/usr/share/fonts/truetype/motoya-l-cedar/MTLc3m.ttf:fonts-motoya-l-cedar"
FONTS["LexiGulim.ttf"]="/usr/share/fonts/truetype/lexi/LexiGulim.ttf:fonts-lexi-gulim"
FONTS["Symbola_hint.ttf"]="/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf:fonts-symbola"
FONTS["NotoSansDevanagari-Regular.ttf"]="/usr/share/fonts/truetype/noto/NotoSansDevanagari-Regular.ttf:fonts-noto-core"
FONTS["NotoSansBengali-Regular.ttf"]="/usr/share/fonts/truetype/noto/NotoSansBengali-Regular.ttf:fonts-noto-core"
FONTS["NotoSansGurmukhi-Regular.ttf"]="/usr/share/fonts/truetype/noto/NotoSansGurmukhi-Regular.ttf:fonts-noto-core"
# ─── Asset roots (non-font) ───────────────────────────────────────────────────
ASSET_ROOTS=(
"share/defs"
"share/static"
"share/pages"
"share/templates"
"share/translations"
)
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# ─── Helper functions ────────────────────────────────────────────────────────
info() { echo -e "${GREEN}→ $1${NC}"; }
warn() { echo -e "${YELLOW}⚠ $1${NC}" >&2; }
error() { echo -e "${RED}ERROR: $1${NC}" >&2; exit 1; }
ensure_clean_embed_dir() {
mkdir -p "$EMBED_TARGET_DIR/fonts"
touch "$EMBED_TARGET_DIR/fonts/README.md"
}
copy_fonts() {
info "Copying fonts to embed directory..."
local copied=0
for font_file in "${!FONTS[@]}"; do
entry="${FONTS[$font_file]}"
src_path="${entry%%:*}"
pkg="${entry#*:}"
if [[ -f "$src_path" ]]; then
cp "$src_path" "$EMBED_TARGET_DIR/fonts/$font_file"
info " Copied $font_file"
copied=$((copied + 1))
else
warn "Font not found: $font_file"
warn " → Install with: sudo apt install $pkg"
fi
done
# Fonts are embedded via go:embed and are required for PNG rendering. A build
# with an empty font set produces a binary that logs font errors at startup and
# renders degraded PNGs. Fail loudly instead of shipping that silently.
if [[ "$copied" -eq 0 ]]; then
error "No fonts were copied into $EMBED_TARGET_DIR/fonts. Install the font packages listed above (see README) and re-run. Refusing to build without embedded fonts."
fi
if [[ "$copied" -lt "${#FONTS[@]}" ]]; then
warn "Only $copied/${#FONTS[@]} fonts copied; some scripts/glyphs will fall back or render as tofu in PNG output."
fi
}
copy_assets() {
ensure_clean_embed_dir
# Copy regular assets
for src_root in "${ASSET_ROOTS[@]}"; do
if [[ ! -d "$src_root" ]]; then
warn "Asset root not found (skipping): $src_root"
continue
fi
target_dir="${EMBED_TARGET_DIR}/${src_root}"
mkdir -p "$target_dir"
# Use rsync to copy directory structure efficiently
# -a = archive mode (preserves permissions, times, symlinks, etc.)
# --delete = remove files in target that no longer exist in source
# --exclude = optional patterns (add more if needed)
rsync -a --delete \
--exclude='*.tmp' \
--exclude='*.bak' \
--exclude='.DS_Store' \
--exclude='Thumbs.db' \
"${src_root}/" \
"${target_dir}/" \
|| error "rsync failed for $src_root → $target_dir"
info "Synced assets: $src_root/ → $target_dir/"
done
# Copy fonts
copy_fonts
info "All assets prepared for embedding"
}
# ─── Actions ─────────────────────────────────────────────────────────────────
cmd_assets() {
info "Preparing embedded assets..."
copy_assets
info "Assets ready"
}
cmd_build() {
info "Building binary: $BINARY_NAME"
# Optional: force assets preparation before build
cmd_assets
local version
version="$(git describe --tags --dirty --always 2>/dev/null || echo dev)"
local ldflags="-X main.version=${version}"
CGO_CFLAGS="-Wno-return-local-addr" \
go build \
-ldflags "$ldflags" \
-o "$BINARY_NAME" \
"$MAIN_PACKAGE" \
|| error "Build failed"
if [[ -f "$BINARY_NAME" ]]; then
info "Build successful: ./$BINARY_NAME"
else
error "Binary not created"
fi
}
cmd_update_css() {
curl -L -o share/static/terminal.css \
https://raw.githubusercontent.com/buildkite/terminal-to-html/refs/heads/main/internal/assets/terminal.css
sed -i 's@^[.]term-container {@.term-container-disabled {@' share/static/terminal.css
}
cmd_gen() {
info "Generating..."
./"$BINARY_NAME" gen
go fmt internal/options/options.go
info "Generating done"
}
cmd_clean() {
info "Cleaning up..."
rm -f "$BINARY_NAME"
rm -rf "$EMBED_TARGET_DIR"
info "Clean done"
}
cmd_help() {
cat <<EOF
Usage: ./build.sh <command>
Commands:
assets Prepare/copy embeddable assets
build Build the binary (implies assets)
clean Remove build artifacts and copied assets
help Show this help
Environment variables that affect build:
CGO_CFLAGS (already set to -Wno-return-local-addr by default)
EOF
}
# ─── Main dispatcher ─────────────────────────────────────────────────────────
main() {
if [[ $# -eq 0 ]]; then
cmd_help
exit 0
fi
local cmd="$1"
shift
case "$cmd" in
assets) cmd_assets "$@" ;;
build) cmd_build "$@" ;;
gen) cmd_gen "$@" ;;
clean) cmd_clean "$@" ;;
update-css) cmd_update_css "$@" ;;
help|--help|-h)
cmd_help ;;
*)
error "Unknown command: $cmd"
;;
esac
}
main "$@"
|