@@ -5,12 +5,17 @@ name: Check toolchain
55# deliberately not restated here, so there is only one copy of the numbers.
66#
77# This is the only workflow here that runs automatically on pull requests.
8- # "Build plugin artifacts" and "Update libs from CodeOnTheGo" are both
8+ # "Build addon artifacts" and "Update libs from CodeOnTheGo" are both
99# workflow_dispatch-only, which is why toolchain drift previously reached
1010# main with no CI signal at all.
1111#
1212# It is pure text inspection — no JDK, no Gradle, no network — so it costs a
1313# few seconds and is safe to make a required check.
14+ #
15+ # It also lints the template bundle sources (ADFA-6252). Those checks are
16+ # borrowed from dev-assets' lint-templates.yml, which guards core.cgt the same
17+ # way. They are cheap, and since this is the only workflow that runs on a pull
18+ # request, it is the only place a broken bundle can be caught before merge.
1419
1520on :
1621 pull_request :
@@ -53,3 +58,109 @@ jobs:
5358
5459 - name : Check addon names and metadata
5560 run : uv run --directory tools/addons addons --root "$GITHUB_WORKSPACE" check
61+
62+ # From here down: the template bundle sources. `addons check` already
63+ # asserts the structure (templates.json parses, every path it names
64+ # exists and carries a template.json, the addon.json block is present).
65+ # These add the syntax checks it does not do, on the file contents.
66+ - name : Install the template lint tools
67+ run : |
68+ sudo apt-get update -qq
69+ sudo apt-get install -y -qq libxml2-utils jq zip
70+ pip install --quiet json5
71+
72+ - name : Reject junk files committed under templates/
73+ run : |
74+ set -e
75+ BAD=$(git ls-files templates/ | grep -E '(^|/)(\.gradle|build|local\.properties)(/|$)|\.(swp|swo|bak)$|(^|/)\.DS_Store$' || true)
76+ if [ -n "$BAD" ]; then
77+ echo "::error::Junk files committed under templates/:"
78+ echo "$BAD"
79+ exit 1
80+ fi
81+
82+ - name : Reject a committed bundle or provenance record
83+ run : |
84+ set -e
85+ # Both are generated at publish time. A committed one goes stale
86+ # silently and would ship instead of a fresh build (ADFA-6252).
87+ BAD=$(git ls-files templates/ | grep -E '\.cgt$|(^|/)cgt-build\.properties$' || true)
88+ if [ -n "$BAD" ]; then
89+ echo "::error::A .cgt or cgt-build.properties is committed; both are generated:"
90+ echo "$BAD"
91+ exit 1
92+ fi
93+
94+ - name : Validate XML under templates/
95+ run : |
96+ set -e
97+ fail=0
98+ while IFS= read -r f; do
99+ if ! xmllint --noout "$f" 2>/dev/null; then
100+ echo "::error file=$f::Invalid XML"
101+ fail=1
102+ fi
103+ done < <(find templates -type f -name '*.xml')
104+ exit $fail
105+
106+ - name : Validate each templates.json (strict JSON)
107+ run : |
108+ set -e
109+ fail=0
110+ while IFS= read -r f; do
111+ if ! jq empty "$f" 2>/dev/null; then
112+ echo "::error file=$f::Invalid JSON"
113+ fail=1
114+ fi
115+ done < <(find templates -maxdepth 2 -type f -name 'templates.json')
116+ exit $fail
117+
118+ - name : Validate each template.json (JSON5)
119+ run : |
120+ set -e
121+ fail=0
122+ while IFS= read -r f; do
123+ if ! python3 -c "import json5,sys; json5.load(open(sys.argv[1]))" "$f" >/dev/null 2>&1; then
124+ echo "::error file=$f::Invalid JSON5"
125+ fail=1
126+ fi
127+ done < <(find templates -type f -path '*/template/template.json')
128+ exit $fail
129+
130+ - name : Pebble brace balance check
131+ run : |
132+ set -e
133+ # Counts delimiters only. It cannot tell whether an identifier is
134+ # declared, and the renderer runs with strictVariables(true), so an
135+ # undeclared one still fails on the device at project generation.
136+ fail=0
137+ while IFS= read -r f; do
138+ opens=$(grep -o '{{' "$f" | wc -l | tr -d ' ')
139+ closes=$(grep -o '}}' "$f" | wc -l | tr -d ' ')
140+ if [ "$opens" != "$closes" ]; then
141+ echo "::error file=$f::Pebble brace imbalance: $opens '{{' vs $closes '}}'"
142+ fail=1
143+ fi
144+ done < <(find templates -type f -name '*.peb')
145+ exit $fail
146+
147+ - name : Build every template bundle
148+ run : |
149+ set -euo pipefail
150+ # Proves the bundle packages and that templates.json names only paths
151+ # that exist: build-cgt.sh derives its file list from that manifest and
152+ # fails when one is missing.
153+ ./scripts/build-templates.sh
154+ for cgt in dist/*.cgt; do
155+ [ -e "$cgt" ] || continue
156+ slug="$(basename "$cgt" .cgt)"
157+ # The IDE looks templates.json up by that exact bare name, so a
158+ # nested one means no template is ever found.
159+ unzip -l "$cgt" | grep -qE '[[:space:]]templates\.json$' \
160+ || { echo "::error::${slug}.cgt has no bare templates.json entry"; exit 1; }
161+ # Repository metadata must not reach a user-facing download.
162+ if unzip -l "$cgt" | grep -qE '[[:space:]](addon\.json|.*\.html|icon_(day|night)\.png)$'; then
163+ echo "::error::${slug}.cgt contains gallery metadata that belongs only in the repository"
164+ exit 1
165+ fi
166+ done
0 commit comments