1+ name : Core CI & Release
2+
3+ on :
4+ push :
5+ branches : [main]
6+ tags : ['v*']
7+ pull_request :
8+ branches : [main]
9+
10+ jobs :
11+ verify-version :
12+ name : Verify Version Match
13+ if : startsWith(github.ref, 'refs/tags/v')
14+ runs-on : ubuntu-latest
15+ steps :
16+ - uses : actions/checkout@v4
17+ - run : |
18+ TAG_VERSION=${GITHUB_REF_NAME#v}
19+ FILE_VERSION=$(cat VERSION)
20+ if [[ "$TAG_VERSION" != "$FILE_VERSION"* ]]; then
21+ echo "::error::Version Mismatch! Tag '$TAG_VERSION' vs VERSION '$FILE_VERSION'"
22+ exit 1
23+ fi
24+
25+ build-core :
26+ name : Build WASM Core
27+ runs-on : ubuntu-latest
28+ steps :
29+ - uses : actions/checkout@v4
30+ - name : Cache Emscripten
31+ id : cache-emsdk
32+ uses : actions/cache@v4
33+ with :
34+ path : emsdk
35+ key : ${{ runner.os }}-emsdk-4.0.10
36+ - name : Install Emscripten
37+ if : steps.cache-emsdk.outputs.cache-hit != 'true'
38+ run : |
39+ git clone https://github.com/emscripten-core/emsdk.git
40+ cd emsdk && ./emsdk install 4.0.10 && ./emsdk activate 4.0.10
41+ - name : Compile
42+ run : |
43+ source emsdk/emsdk_env.sh
44+ cd j2735codec
45+ make wasm
46+ # Create __init__.py files
47+ touch bindings/python/src/j2735codec/generated/__init__.py
48+ touch bindings/python/src/j2735codec/protobuf/__init__.py
49+
50+ - name : Upload Python Artifacts
51+ uses : actions/upload-artifact@v4
52+ with :
53+ name : python-wasm-binaries
54+ # Use the parent directory to ensure the j2735codec folder structure is preserved
55+ path : j2735codec/bindings/python/src/j2735codec/
56+ include-hidden-files : true
57+
58+ - name : Upload Node Artifacts
59+ uses : actions/upload-artifact@v4
60+ with :
61+ name : node-wasm-binaries
62+ path : j2735codec/bindings/node/src/generated/
63+
64+ test-bindings :
65+ name : Test ${{ matrix.binding }}
66+ needs : build-core
67+ runs-on : ubuntu-latest
68+ strategy :
69+ fail-fast : false
70+ matrix :
71+ binding : [python, node]
72+ steps :
73+ - uses : actions/checkout@v4
74+
75+ - name : Download Artifacts
76+ uses : actions/download-artifact@v4
77+ with :
78+ name : ${{ matrix.binding }}-wasm-binaries
79+ path : j2735codec/bindings/${{ matrix.binding }}/${{ matrix.binding == 'python' && 'src/j2735codec/' || 'src/generated/' }}
80+
81+ - name : Repair and Verify Structure
82+ run : |
83+ if [ "${{ matrix.binding }}" == "python" ]; then
84+ cd j2735codec/bindings/python/src/j2735codec
85+ touch __init__.py generated/__init__.py protobuf/__init__.py
86+ echo "🔍 Verifying Python Structure:"
87+ ls -R
88+ fi
89+
90+ - name : Setup & Test
91+ run : |
92+ if [ "${{ matrix.binding }}" == "python" ]; then
93+ pip install uv
94+ cd j2735codec/bindings/python
95+ uv sync --reinstall-package j2735codec
96+ uv run pytest tests -s
97+ else
98+ npm install
99+ npm run build -w j2735codec
100+ npm test -w j2735codec
101+ fi
102+
103+ publish-release :
104+ name : Publish Release
105+ needs : [verify-version, test-bindings]
106+ if : startsWith(github.ref, 'refs/tags/v')
107+ runs-on : ubuntu-latest
108+ permissions :
109+ contents : write
110+ steps :
111+ - uses : actions/checkout@v4
112+
113+ - name : Download Artifacts
114+ uses : actions/download-artifact@v4
115+ with :
116+ path : artifacts
117+
118+ - name : Build & Stage Assets
119+ run : |
120+ STAGING="$GITHUB_WORKSPACE/dist_release"
121+ mkdir -p "$STAGING"
122+ VERSION=$(cat VERSION)
123+
124+ # --- 2. Build Python Bindings ---
125+ PY_DIR="j2735codec/bindings/python"
126+
127+ # Clean and Recreate Structure
128+ mkdir -p "$PY_DIR/src/j2735codec/generated"
129+ mkdir -p "$PY_DIR/src/j2735codec/protobuf"
130+
131+ # Copy artifacts - using '.' to ensure we copy contents into the pre-made folders
132+ cp -a artifacts/python-wasm-binaries/. "$PY_DIR/src/j2735codec/"
133+
134+ # RE-INJECT Package Markers (Vital for Wheel packaging)
135+ touch "$PY_DIR/src/j2735codec/__init__.py"
136+ touch "$PY_DIR/src/j2735codec/generated/__init__.py"
137+ touch "$PY_DIR/src/j2735codec/protobuf/__init__.py"
138+
139+ # Debug: Log the tree to the console so we can see what's happening
140+ echo "📂 Final Python Build Tree:"
141+ ls -R "$PY_DIR/src/j2735codec/"
142+
143+ cp LICENSE "$PY_DIR/"
144+ cp NOTICE "$PY_DIR/"
145+
146+ pip install uv
147+ cd "$PY_DIR"
148+ uv build --wheel --out-dir ./dist_local
149+
150+ # Verify Wheel Contents before moving (Security Check)
151+ WHL_FILE=$(ls ./dist_local/*.whl)
152+ if ! unzip -l "$WHL_FILE" | grep -q "j2735codec/generated/j2735codec.wasm"; then
153+ echo "::error::WASM file missing from generated wheel!"
154+ exit 1
155+ fi
156+
157+ for f in ./dist_local/*; do cp "$f" "$STAGING/$(basename $f)"; done
158+ cd "$GITHUB_WORKSPACE"
159+
160+ # --- 3. Build Node Bindings ---
161+ JS_DIR="j2735codec/bindings/node"
162+ mkdir -p "$JS_DIR/src/generated/"
163+ cp -a artifacts/node-wasm-binaries/. "$JS_DIR/src/generated/"
164+ cp LICENSE "$JS_DIR/"
165+ cp NOTICE "$JS_DIR/"
166+ npm install
167+ npm run build -w j2735codec
168+ cd "$JS_DIR" && npm pack
169+ for f in *.tgz; do cp "$f" "$STAGING/$f"; done
170+ cd "$GITHUB_WORKSPACE"
171+
172+ # --- 4. Package Python Samples ---
173+ SAMPLE_DIR="etx/examples/python"
174+ if [ -d "$SAMPLE_DIR" ]; then
175+ cp LICENSE "$SAMPLE_DIR/"
176+ cp NOTICE "$SAMPLE_DIR/"
177+
178+ # Inject the language wrapper (the wheel)
179+ mkdir -p "$SAMPLE_DIR/j2735codec"
180+ cp "$STAGING"/*.whl "$SAMPLE_DIR/j2735codec/"
181+
182+ cd "$SAMPLE_DIR"
183+ VERSION=$(cat "$GITHUB_WORKSPACE/VERSION")
184+ ZIP_NAME="python-etx-samples-$VERSION.zip"
185+ zip -r "$STAGING/$ZIP_NAME" . \
186+ -x "*config.json" -x "*/.venv/*" -x "*.venv*" -x "*/.env*" -x "*/__pycache__/*"
187+ cd "$GITHUB_WORKSPACE"
188+ fi
189+
190+ # --- 5. Package Node/TS Samples ---
191+ NODE_SAMPLE_DIR="etx/examples/node"
192+ if [ -d "$NODE_SAMPLE_DIR" ]; then
193+ VERSION=$(cat "$GITHUB_WORKSPACE/VERSION")
194+ PKG_STAGING="$GITHUB_WORKSPACE/node_pkg_temp"
195+ mkdir -p "$PKG_STAGING"
196+
197+ # 1. Copy the source (leaves your repo untouched)
198+ cp -r "$NODE_SAMPLE_DIR"/. "$PKG_STAGING/"
199+
200+ # 2. Rewrite the path to the PORTABLE local path
201+ # This changes the monorepo path to the ZIP-friendly path
202+ sed -i "s|\"j2735codec\": \".*\"|\"j2735codec\": \"file:./j2735codec/j2735codec-$VERSION.tgz\"|g" "$PKG_STAGING/package.json"
203+
204+ # 3. Inject the tarball into the staging area
205+ mkdir -p "$PKG_STAGING/j2735codec"
206+ cp "$STAGING"/j2735codec-$VERSION.tgz "$PKG_STAGING/j2735codec/"
207+
208+ # 4. GENERATE THE LOCKFILE HERE
209+ # This lockfile will now contain the correct, portable reference to the codec
210+ cd "$PKG_STAGING"
211+ npm install --package-lock-only --no-workspaces
212+
213+ # 5. ZIP EVERYTHING
214+ # Now the ZIP contains a package.json and a package-lock.json
215+ # that both point to the local ./j2735codec folder.
216+ ZIP_NAME="node-etx-samples-$VERSION.zip"
217+ zip -r "$STAGING/$ZIP_NAME" . -x "node_modules/*" "dist/*"
218+
219+ # We zip EVERYTHING (*) in this folder, excluding ONLY the junk
220+ zip -r "$STAGING/$ZIP_NAME" . \
221+ -x "certs/*" "dist/*" "node_modules/*" "config.json" ".env*" "npm-debug.log*"
222+
223+ cd "$GITHUB_WORKSPACE"
224+ rm -rf "$PKG_STAGING"
225+ fi
226+
227+ # --- 6. Finalize Staging ---
228+ cp "$PY_DIR/src/j2735codec/generated/j2735codec.wasm" "$STAGING/j2735codec-$VERSION.wasm"
229+ cp LICENSE "$STAGING/"
230+ cp NOTICE "$STAGING/"
231+
232+ # --- 7. FIXED Safe Cleanup ---
233+ echo "🧹 Performing final cleanup..."
234+ # Do NOT use -delete on a broad glob. Delete specific files.
235+ rm -f "$STAGING"/.DS_Store
236+ rm -f "$STAGING"/.env
237+ rm -f "$STAGING"/default.gitignore
238+
239+ echo "✅ Contents of $STAGING for release:"
240+ ls -la "$STAGING"
241+
242+ - name : Create GitHub Release
243+ uses : softprops/action-gh-release@v2
244+ with :
245+ name : " Release ${{ github.ref_name }}"
246+ files : dist_release/*
247+ generate_release_notes : true
0 commit comments