Skip to content

Commit 4c68a82

Browse files
akoclaude
andcommitted
Add GitHub workflows for MDL syntax checks and nightly releases
- Add MDL syntax check step to push-test.yml (runs mxcli check on all doctype-test .mdl files for every push/PR) - Add nightly.yml workflow for daily pre-release builds from main (skips if no new commits, rolling single nightly release) - Add proposal document for full mx check integration (Slice 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 40d1883 commit 4c68a82

3 files changed

Lines changed: 357 additions & 0 deletions

File tree

.github/workflows/nightly.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Nightly Release
2+
3+
on:
4+
schedule:
5+
# Every day at 02:00 UTC
6+
- cron: '0 2 * * *'
7+
workflow_dispatch: # Allow manual trigger
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
nightly:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- uses: actions/setup-go@v4
21+
with:
22+
go-version: '1.26'
23+
24+
- name: Check for new commits since last nightly
25+
id: check
26+
run: |
27+
LAST_NIGHTLY=$(gh release view nightly --json publishedAt -q .publishedAt 2>/dev/null || echo "")
28+
if [ -n "$LAST_NIGHTLY" ]; then
29+
COMMITS=$(git log --since="$LAST_NIGHTLY" --oneline origin/main | wc -l)
30+
if [ "$COMMITS" -eq 0 ]; then
31+
echo "No new commits since last nightly, skipping."
32+
echo "skip=true" >> "$GITHUB_OUTPUT"
33+
exit 0
34+
fi
35+
echo "$COMMITS new commit(s) since last nightly."
36+
fi
37+
echo "skip=false" >> "$GITHUB_OUTPUT"
38+
env:
39+
GH_TOKEN: ${{ github.token }}
40+
41+
- name: Build release binaries
42+
if: steps.check.outputs.skip != 'true'
43+
run: VERSION=nightly-$(date -u +%Y%m%d)-$(git rev-parse --short HEAD) make release
44+
45+
- name: Generate changelog
46+
if: steps.check.outputs.skip != 'true'
47+
id: changelog
48+
run: |
49+
{
50+
echo "body<<NIGHTLY_EOF"
51+
echo "**Nightly build from \`main\` — $(date -u +%Y-%m-%d)**"
52+
echo ""
53+
echo "Commit: [\`$(git rev-parse --short HEAD)\`](https://github.com/${{ github.repository }}/commit/$(git rev-parse HEAD))"
54+
echo ""
55+
echo "### Recent changes"
56+
echo ""
57+
git log --oneline -20 origin/main
58+
echo ""
59+
echo "> This is an automated pre-release build. Use tagged releases for production."
60+
echo "NIGHTLY_EOF"
61+
} >> "$GITHUB_OUTPUT"
62+
63+
- name: Delete previous nightly release
64+
if: steps.check.outputs.skip != 'true'
65+
run: gh release delete nightly --cleanup-tag -y 2>/dev/null || true
66+
env:
67+
GH_TOKEN: ${{ github.token }}
68+
69+
- name: Create nightly release
70+
if: steps.check.outputs.skip != 'true'
71+
run: |
72+
gh release create nightly \
73+
--prerelease \
74+
--title "Nightly $(date -u +%Y-%m-%d)" \
75+
--notes "${{ steps.changelog.outputs.body }}" \
76+
bin/mxcli-*
77+
env:
78+
GH_TOKEN: ${{ github.token }}

.github/workflows/push-test.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,19 @@ jobs:
1616
run: make test
1717
- name: Lint Go
1818
run: make lint-go
19+
- name: Check MDL example scripts
20+
run: |
21+
FAILED=0
22+
for f in mdl-examples/doctype-tests/*.mdl; do
23+
[[ "$f" == *.test.mdl ]] && continue
24+
NAME=$(basename "$f")
25+
echo "::group::$NAME"
26+
if ./bin/mxcli check "$f"; then
27+
echo "PASS: $NAME"
28+
else
29+
echo "::error file=$f::Syntax check failed: $NAME"
30+
FAILED=1
31+
fi
32+
echo "::endgroup::"
33+
done
34+
exit $FAILED
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Proposal: GitHub Actions MDL Integration Tests
2+
3+
**Status:** Draft
4+
**Date:** 2026-03-16
5+
**Author:** AI-assisted design
6+
7+
---
8+
9+
## Summary
10+
11+
Add a GitHub Actions workflow that validates MDL example scripts against a real Mendix project after every merge to `main`. This catches parser regressions, executor bugs, and invalid BSON generation automatically.
12+
13+
The proposal is split into two slices:
14+
1. **Slice 1** — Syntax check only (`mxcli check`), no Mendix tooling required
15+
2. **Slice 2** — Full integration with `mx check` against a real Mendix project
16+
17+
## Motivation
18+
19+
The `mdl-examples/doctype-tests/` directory contains 15+ MDL scripts covering domain models, microflows, pages, security, navigation, and more. These scripts are the canonical reference for what the MDL parser and executor support.
20+
21+
Today, regressions in the parser or executor can go unnoticed until someone manually runs the scripts. A CI workflow catches these automatically:
22+
23+
| Failure mode | Caught by |
24+
|---|---|
25+
| ANTLR grammar regression | `mxcli check` (Slice 1) |
26+
| Parser crash on valid MDL | `mxcli check` (Slice 1) |
27+
| Executor crash | `mxcli exec` (Slice 2) |
28+
| Invalid BSON generation | `mx check` (Slice 2) |
29+
| Missing references / broken associations | `mx check` (Slice 2) |
30+
31+
## Slice 1: Syntax Validation (no Mendix tooling)
32+
33+
**Goal:** Verify all MDL example scripts parse without errors.
34+
35+
`mxcli check` validates syntax using the ANTLR4 parser. It requires no Mendix project, no mxbuild, and no JDK — just the compiled `mxcli` binary.
36+
37+
### Workflow
38+
39+
```yaml
40+
# .github/workflows/mdl-check.yml
41+
name: MDL Syntax Check
42+
43+
on:
44+
push:
45+
branches: [main]
46+
pull_request:
47+
48+
jobs:
49+
mdl-syntax-check:
50+
runs-on: ubuntu-latest
51+
steps:
52+
- uses: actions/checkout@v4
53+
54+
- uses: actions/setup-go@v4
55+
with:
56+
go-version: '1.26'
57+
58+
- name: Build mxcli
59+
run: make build
60+
61+
- name: Check MDL example scripts
62+
run: |
63+
FAILED=0
64+
for f in mdl-examples/doctype-tests/*.mdl; do
65+
[[ "$f" == *.test.mdl ]] && continue
66+
NAME=$(basename "$f")
67+
echo "::group::$NAME"
68+
if ./bin/mxcli check "$f"; then
69+
echo "PASS: $NAME"
70+
else
71+
echo "::error file=$f::Syntax check failed: $NAME"
72+
FAILED=1
73+
fi
74+
echo "::endgroup::"
75+
done
76+
exit $FAILED
77+
```
78+
79+
### What this covers
80+
81+
- All 15 doctype-test `.mdl` files are parsed
82+
- Skips `.test.mdl` files (these use test-specific annotations)
83+
- Runs on both PRs and merges — catches regressions before merge
84+
- No external dependencies beyond Go
85+
- Adds ~30 seconds to CI
86+
87+
### What this does NOT cover
88+
89+
- Execution against a real project (no BSON validation)
90+
- `mx check` validation (no Mendix tooling)
91+
- Runtime behavior (no Docker/PostgreSQL)
92+
93+
## Slice 2: Full Integration with `mx check`
94+
95+
**Goal:** Execute MDL scripts against a blank Mendix project and validate with `mx check`.
96+
97+
### Prerequisites
98+
99+
| Dependency | Size | Source |
100+
|---|---|---|
101+
| mxbuild | ~500 MB | Mendix CDN (cached) |
102+
| JDK 21 | ~200 MB | Eclipse Temurin (actions/setup-java) |
103+
| Blank Mendix project | ~5 MB | Created via `mx create-project` |
104+
105+
### How it works
106+
107+
1. **Build** `mxcli`
108+
2. **Download mxbuild** from Mendix CDN using `mxcli setup mxbuild --version $VERSION`
109+
3. **Create a blank project** using `mx create-project --app-name mdl-test`
110+
4. **For each `.mdl` script:**
111+
- Copy the blank project to a temp directory (fresh state)
112+
- Execute the script with `mxcli exec script.mdl -p $PROJECT`
113+
- Validate with `mx check $PROJECT`
114+
5. **Report** pass/fail per script with GitHub Actions annotations
115+
116+
### Workflow
117+
118+
```yaml
119+
# .github/workflows/mdl-integration.yml
120+
name: MDL Integration Tests
121+
122+
on:
123+
push:
124+
branches: [main]
125+
126+
jobs:
127+
mdl-integration:
128+
runs-on: ubuntu-latest
129+
env:
130+
MENDIX_VERSION: "11.6.3"
131+
132+
steps:
133+
- uses: actions/checkout@v4
134+
135+
- uses: actions/setup-go@v4
136+
with:
137+
go-version: '1.26'
138+
139+
- uses: actions/setup-java@v4
140+
with:
141+
distribution: temurin
142+
java-version: '21'
143+
144+
- name: Build mxcli
145+
run: make build
146+
147+
- name: Cache mxbuild
148+
uses: actions/cache@v4
149+
with:
150+
path: ~/.mxcli/mxbuild
151+
key: mxbuild-${{ env.MENDIX_VERSION }}-amd64
152+
153+
- name: Download mxbuild
154+
run: ./bin/mxcli setup mxbuild --version $MENDIX_VERSION
155+
156+
- name: Create blank Mendix project
157+
run: |
158+
mkdir -p /tmp/mdl-test-source
159+
cd /tmp/mdl-test-source
160+
~/.mxcli/mxbuild/$MENDIX_VERSION/modeler/mx \
161+
create-project --app-name mdl-test
162+
163+
- name: Run MDL scripts and validate with mx check
164+
run: |
165+
PROJECT_DIR=/tmp/mdl-test-source
166+
MX=~/.mxcli/mxbuild/$MENDIX_VERSION/modeler/mx
167+
FAILED=0
168+
PASSED=0
169+
SKIPPED=0
170+
171+
for f in mdl-examples/doctype-tests/*.mdl; do
172+
[[ "$f" == *.test.mdl ]] && continue
173+
NAME=$(basename "$f")
174+
echo "::group::$NAME"
175+
176+
# Fresh copy for each script
177+
WORKDIR=$(mktemp -d)
178+
cp -r "$PROJECT_DIR"/* "$WORKDIR/"
179+
WORK_MPR="$WORKDIR/mdl-test.mpr"
180+
181+
# Execute MDL
182+
echo "Executing $NAME..."
183+
if ! ./bin/mxcli exec "$f" -p "$WORK_MPR" 2>&1; then
184+
echo "::error file=$f::exec failed: $NAME"
185+
FAILED=$((FAILED + 1))
186+
rm -rf "$WORKDIR"
187+
echo "::endgroup::"
188+
continue
189+
fi
190+
191+
# Validate with mx check
192+
echo "Validating with mx check..."
193+
if ! "$MX" check "$WORK_MPR" 2>&1; then
194+
echo "::error file=$f::mx check failed: $NAME"
195+
FAILED=$((FAILED + 1))
196+
else
197+
echo "PASS: $NAME"
198+
PASSED=$((PASSED + 1))
199+
fi
200+
201+
rm -rf "$WORKDIR"
202+
echo "::endgroup::"
203+
done
204+
205+
echo ""
206+
echo "Results: $PASSED passed, $FAILED failed"
207+
exit $FAILED
208+
```
209+
210+
### Runtime estimate
211+
212+
| Step | Duration |
213+
|---|---|
214+
| Build mxcli | ~60s |
215+
| Download mxbuild (cold) | ~90s |
216+
| Download mxbuild (cached) | ~5s |
217+
| Create blank project | ~10s |
218+
| Per MDL script (exec + mx check) | ~15-30s |
219+
| **Total (15 scripts, cached)** | **~6-8 min** |
220+
221+
### Mendix version management
222+
223+
The `MENDIX_VERSION` env var pins which mxbuild to use. When upgrading:
224+
1. Update the env var in the workflow
225+
2. The cache key includes the version, so a new download is triggered automatically
226+
227+
An alternative is to store the version in a `.mendix-version` file at the repo root and read it in CI, making it easier to keep in sync with the test project.
228+
229+
## Scope of MDL files tested
230+
231+
| File | Domain |
232+
|---|---|
233+
| `01-domain-model-examples.mdl` | Entities, enumerations, associations, views, ALTER, generalization |
234+
| `02-microflow-examples.mdl` | Microflows, activities, expressions, error handling |
235+
| `03-page-examples.mdl` | Pages, widgets, layouts, data views |
236+
| `04-math-examples.mdl` | Mathematical expressions and operations |
237+
| `05-database-connection-examples.mdl` | External SQL connectivity |
238+
| `06-rest-client-examples.mdl` | REST client operations |
239+
| `07-java-action-examples.mdl` | Java action calls |
240+
| `08-security-examples.mdl` | Module/user roles, access rules |
241+
| `09-constant-examples.mdl` | Constants |
242+
| `10-odata-examples.mdl` | OData services |
243+
| `11-navigation-examples.mdl` | Navigation profiles, menus |
244+
| `12-styling-examples.mdl` | Styling and theming |
245+
| `13-business-events-examples.mdl` | Business event services |
246+
| `14-project-settings-examples.mdl` | Project settings |
247+
| `15-fragment-examples.mdl` | Page fragments/snippets |
248+
249+
Not all scripts may pass `mx check` immediately — some may use features that produce warnings vs errors. The workflow treats `mx check` exit code as the pass/fail signal.
250+
251+
## Implementation plan
252+
253+
1. **Slice 1 first** — add `mdl-check.yml` to `.github/workflows/`, merge, verify it passes on `main`
254+
2. **Fix any syntax failures** uncovered by Slice 1
255+
3. **Slice 2** — add `mdl-integration.yml`, initially with just `01-domain-model-examples.mdl` to validate the setup
256+
4. **Expand Slice 2** — add remaining scripts one-by-one, fixing executor/BSON issues as found
257+
5. **Optional: version file** — add `.mendix-version` to decouple from hardcoded env var
258+
259+
## Open questions
260+
261+
- **Which scripts to include in Slice 2?** Some scripts (e.g., `05-database-connection-examples.mdl`) may require external services. These could be excluded or run conditionally.
262+
- **Failure tolerance:** Should `mx check` warnings be treated as failures? The `mx check` command distinguishes errors (non-zero exit) from warnings (zero exit with output).
263+
- **Parallel execution:** Scripts are independent — could run in a matrix strategy for faster feedback, at the cost of more `mx create-project` calls.

0 commit comments

Comments
 (0)