Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# GitHub Actions Scripts

This directory contains helper scripts used by the CI/CD pipeline.

## determine-widget-scope.sh

Determines which widgets and JS actions should be built and tested based on changed files or manual input.

### Usage

```bash
./determine-widget-scope.sh <event_name> <input_workspace> <before_commit> <current_commit>
```

### Outputs

| Output | Description |
| -------------------- | ---------------------------------------------------- |
| `scope` | pnpm filter scope for building |
| `widgets` | JSON array of widgets to BUILD |
| `widgets_to_test` | JSON array of widgets to TEST (used for test matrix) |
| `js_actions_changed` | Boolean flag indicating if JS actions changed |

### Pipeline Behavior

| Scenario | Widgets Built | Widgets Tested | JS Actions Built | JS Actions Tested |
| ---------------------- | --------------- | --------------- | ---------------- | ----------------- |
| Only JS actions change | All | None | Yes | Yes |
| Only widgets change | Changed only | Changed only | No | No |
| Both change | Changed widgets | Changed widgets | Yes | Yes |
| Full run (`*-native`) | All | All | Yes | Yes |
| Manual: `js-actions` | All | None | Yes | Yes |

### Why build all widgets when only JS actions change?

The JS action tests run against a full test project that requires all widgets to be present. Without building all widgets, the test project would be incomplete and tests would fail.

## Other Scripts

- **determine-nt-version.py** - Determines the Native Template version based on Mendix version
- **mxbuild.Dockerfile** - Docker image for mxbuild
- **setup-runtime.sh** - Sets up the Mendix runtime
- **start-runtime-with-verification.sh** - Starts the runtime with health verification
46 changes: 36 additions & 10 deletions .github/scripts/determine-widget-scope.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,65 @@ if [ "$event_name" == "pull_request" ]; then
selected_workspaces=$(echo $selected_workspaces | xargs)

# Build the final scope and widgets output
# Note: widgets output is used for both BUILDING and the widget TEST MATRIX
# When only JS actions change, widgets_to_test is empty (no widget tests needed)
# but we still need to build all widgets for the test project
if [[ -n "$selected_workspaces" ]] && [[ "$js_actions_changed" == "true" ]]; then
# Both widgets and JS actions changed
# Convert space-separated widget names to JSON array format
widget_array=$(echo "$selected_workspaces" | sed 's/ /","/g')
echo "scope=--all --include '$selected_workspaces mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=[\"$widget_array\",\"mobile-resources-native\",\"nanoflow-actions-native\"]" >> $GITHUB_OUTPUT
echo "widgets=[\"$widget_array\"]" >> $GITHUB_OUTPUT
echo "widgets_to_test=[\"$widget_array\"]" >> $GITHUB_OUTPUT
echo "js_actions_changed=true" >> $GITHUB_OUTPUT
elif [[ -n "$selected_workspaces" ]] && [[ "$js_actions_changed" == "false" ]]; then
# Only widgets changed
widget_array=$(echo "$selected_workspaces" | sed 's/ /","/g')
echo "scope=--all --include '$selected_workspaces'" >> $GITHUB_OUTPUT
echo "widgets=[\"$widget_array\"]" >> $GITHUB_OUTPUT
echo "widgets_to_test=[\"$widget_array\"]" >> $GITHUB_OUTPUT
echo "js_actions_changed=false" >> $GITHUB_OUTPUT
elif [[ -z "$selected_workspaces" ]] && [[ "$js_actions_changed" == "true" ]]; then
# Only JS actions changed
echo "scope=--all --include 'mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=[\"mobile-resources-native\",\"nanoflow-actions-native\"]" >> $GITHUB_OUTPUT
# Only JS actions changed - need to build ALL widgets because JS action tests
# require the full test project with all widgets to function properly
# But widget tests should NOT run (empty widgets_to_test)
echo "scope=--all --include '*-native mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets}" >> $GITHUB_OUTPUT
echo "widgets_to_test=[]" >> $GITHUB_OUTPUT
echo "js_actions_changed=true" >> $GITHUB_OUTPUT
else
# No specific changes detected in widgets or JS actions, run everything
echo "scope=--all --include '*-native mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets_and_js}" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets}" >> $GITHUB_OUTPUT
echo "widgets_to_test=${all_widgets}" >> $GITHUB_OUTPUT
echo "js_actions_changed=true" >> $GITHUB_OUTPUT
fi
else
if [ -n "$input_workspace" ] && [ "$input_workspace" != "*-native" ] && [ "$input_workspace" != "js-actions" ]; then
# Specific widget(s) selected
selected_workspaces=$(echo "$input_workspace" | sed 's/,/ /g')
echo "scope=--all --include '${selected_workspaces}'" >> $GITHUB_OUTPUT
echo "widgets=[\"$input_workspace\"]" >> $GITHUB_OUTPUT
echo "widgets_to_test=[\"$input_workspace\"]" >> $GITHUB_OUTPUT
echo "js_actions_changed=false" >> $GITHUB_OUTPUT
elif [ "$input_workspace" == "js-actions" ]; then
echo "scope=--all --include 'mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=[\"mobile-resources-native\",\"nanoflow-actions-native\"]" >> $GITHUB_OUTPUT
# JS actions selected - need to build ALL widgets because JS action tests
# require the full test project with all widgets to function properly
# But widget tests should NOT run (empty widgets_to_test)
echo "scope=--all --include '*-native mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets}" >> $GITHUB_OUTPUT
echo "widgets_to_test=[]" >> $GITHUB_OUTPUT
echo "js_actions_changed=true" >> $GITHUB_OUTPUT
else
# All widgets (*-native) or default - run everything
echo "scope=--all --include '*-native mobile-resources-native nanoflow-actions-native'" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets_and_js}" >> $GITHUB_OUTPUT
echo "widgets=${all_widgets}" >> $GITHUB_OUTPUT
echo "widgets_to_test=${all_widgets}" >> $GITHUB_OUTPUT
echo "js_actions_changed=true" >> $GITHUB_OUTPUT
fi
fi

echo "Determined scope: $(cat $GITHUB_OUTPUT | grep scope= | cut -d= -f2)"
echo "Widgets: $(cat $GITHUB_OUTPUT | grep widgets= | cut -d= -f2)"
echo "Determined scope: $(cat $GITHUB_OUTPUT | grep 'scope=' | cut -d= -f2)"
echo "Widgets to build: $(cat $GITHUB_OUTPUT | grep '^widgets=' | cut -d= -f2)"
echo "Widgets to test: $(cat $GITHUB_OUTPUT | grep 'widgets_to_test=' | cut -d= -f2)"
echo "JS actions changed: $(cat $GITHUB_OUTPUT | grep 'js_actions_changed=' | cut -d= -f2)"
49 changes: 25 additions & 24 deletions .github/workflows/NativePipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ jobs:
outputs:
scope: ${{ steps.scope.outputs.scope }}
widgets: ${{ steps.scope.outputs.widgets }}
widgets_to_test: ${{ steps.scope.outputs.widgets_to_test }}
js_actions_changed: ${{ steps.scope.outputs.js_actions_changed }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
Expand All @@ -101,7 +103,9 @@ jobs:
- name: "Debug Scope Output"
run: |
echo "Scope is: ${{ steps.scope.outputs.scope }}"
echo "Widgets or js actions are: ${{ steps.scope.outputs.widgets }}"
echo "Widgets to build: ${{ steps.scope.outputs.widgets }}"
echo "Widgets to test: ${{ steps.scope.outputs.widgets_to_test }}"
echo "JS actions changed: ${{ steps.scope.outputs.js_actions_changed }}"
mendix-version:
runs-on: ubuntu-24.04
outputs:
Expand Down Expand Up @@ -215,23 +219,20 @@ jobs:
run: pnpm install --frozen-lockfile
- name: "Force rebuild resources"
run: |
# Build JS actions if needed
if [ "${{ github.event.inputs.workspace }}" = "js-actions" ] || \
# Build JS actions if needed (when js_actions_changed is true OR when workspace explicitly includes them)
if [ "${{ needs.scope.outputs.js_actions_changed }}" = "true" ] || \
[ "${{ github.event.inputs.workspace }}" = "js-actions" ] || \
[ "${{ github.event.inputs.workspace }}" = "*-native" ] || \
[ "${{ github.event_name }}" = "schedule" ]; then
pnpm --filter=mobile-resources-native run build
pnpm --filter=nanoflow-actions-native run build
fi

# Build widgets if needed (any specific widget, *-native, or nightly)
if [ "${{ github.event.inputs.workspace }}" != "js-actions" ] || \
[ "${{ github.event.inputs.workspace }}" = "*-native" ] || \
[ "${{ github.event_name }}" = "schedule" ]; then
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
for w in $widgets; do
pnpm --filter=$w run build
done
fi
# Build widgets from scope
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
for w in $widgets; do
pnpm --filter=$w run build
done
- name: "Unit test"
run: pnpm -r --filter="${{ needs.scope.outputs.scope }}" run test
- name: "Upload JS actions resources artifact"
Expand Down Expand Up @@ -487,14 +488,14 @@ jobs:

android-widget-tests:
needs: [scope, mendix-version, project, android-app]
# Run if project succeeds and either android-app succeeds OR we're using custom artifacts (android-app was skipped)
if: ${{ (github.event.inputs.workspace != 'js-actions' || github.event_name == 'schedule') && always() && needs.project.result == 'success' && (needs.android-app.result == 'success' || needs.android-app.result == 'skipped') }}
# Run if widgets need testing (widgets_to_test is not empty) and project succeeds
if: ${{ needs.scope.outputs.widgets_to_test != '[]' && always() && needs.project.result == 'success' && (needs.android-app.result == 'success' || needs.android-app.result == 'skipped') }}
runs-on: ubuntu-24.04
timeout-minutes: 60
strategy:
max-parallel: 5
matrix:
widget: ${{ fromJson(needs.scope.outputs.widgets) }}
widget: ${{ fromJson(needs.scope.outputs.widgets_to_test) }}
fail-fast: false
steps:
- name: "Check out code"
Expand Down Expand Up @@ -563,14 +564,14 @@ jobs:

ios-widget-tests:
needs: [scope, mendix-version, project, ios-app]
# Run if project succeeds and either ios-app succeeds OR we're using custom artifacts (ios-app was skipped)
if: ${{ (github.event.inputs.workspace != 'js-actions' || github.event_name == 'schedule') && always() && needs.project.result == 'success' && (needs.ios-app.result == 'success' || needs.ios-app.result == 'skipped') }}
# Run if widgets need testing (widgets_to_test is not empty) and project succeeds
if: ${{ needs.scope.outputs.widgets_to_test != '[]' && always() && needs.project.result == 'success' && (needs.ios-app.result == 'success' || needs.ios-app.result == 'skipped') }}
runs-on: macos-15
timeout-minutes: 60
strategy:
max-parallel: 5
matrix:
widget: ${{ fromJson(needs.scope.outputs.widgets) }}
widget: ${{ fromJson(needs.scope.outputs.widgets_to_test) }}
fail-fast: false
steps:
- name: "Force cleanup workspace"
Expand Down Expand Up @@ -659,8 +660,8 @@ jobs:

android-js-tests:
needs: [scope, mendix-version, project, android-app]
# Run if project succeeds and either android-app succeeds OR we're using custom artifacts (android-app was skipped)
if: ${{ (github.event.inputs.workspace == '*-native' || github.event_name == 'schedule' || github.event.inputs.workspace == 'js-actions' || contains(needs.scope.outputs.widgets, 'mobile-resources-native') || contains(needs.scope.outputs.widgets, 'nanoflow-actions-native')) && always() && needs.project.result == 'success' && (needs.android-app.result == 'success' || needs.android-app.result == 'skipped') }}
# Run if JS actions changed and project succeeds and either android-app succeeds OR we're using custom artifacts (android-app was skipped)
if: ${{ needs.scope.outputs.js_actions_changed == 'true' && always() && needs.project.result == 'success' && (needs.android-app.result == 'success' || needs.android-app.result == 'skipped') }}
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
Expand Down Expand Up @@ -729,8 +730,8 @@ jobs:

ios-js-tests:
needs: [scope, mendix-version, project, ios-app]
# Run if project succeeds and either ios-app succeeds OR we're using custom artifacts (ios-app was skipped)
if: ${{ (github.event.inputs.workspace == '*-native' || github.event_name == 'schedule' || github.event.inputs.workspace == 'js-actions' || contains(needs.scope.outputs.widgets, 'mobile-resources-native') || contains(needs.scope.outputs.widgets, 'nanoflow-actions-native')) && always() && needs.project.result == 'success' && (needs.ios-app.result == 'success' || needs.ios-app.result == 'skipped') }}
# Run if JS actions changed and project succeeds and either ios-app succeeds OR we're using custom artifacts (ios-app was skipped)
if: ${{ needs.scope.outputs.js_actions_changed == 'true' && always() && needs.project.result == 'success' && (needs.ios-app.result == 'success' || needs.ios-app.result == 'skipped') }}
runs-on: macos-15
timeout-minutes: 90
steps:
Expand Down Expand Up @@ -827,7 +828,7 @@ jobs:

- name: "Download Android screenshots"
run: |
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
widgets=$(echo '${{ needs.scope.outputs.widgets_to_test }}' | jq -r '.[]')
mkdir -p images/actual/android/
for widget in $widgets; do
echo "Downloading android-screenshots-${widget}"
Expand All @@ -841,7 +842,7 @@ jobs:

- name: "Download iOS screenshots"
run: |
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
widgets=$(echo '${{ needs.scope.outputs.widgets_to_test }}' | jq -r '.[]')
mkdir -p images/actual/ios/
for widget in $widgets; do
echo "Downloading ios-screenshots-${widget}"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,5 @@
"react-native-snap-carousel@3.9.1": "patches/react-native-snap-carousel+3.9.1.patch"
}
},
"packageManager": "pnpm@10.13.1"
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
}
Loading