Skip to content

Nightly version/mx/10 Branch Pipeline #1494

Nightly version/mx/10 Branch Pipeline

Nightly version/mx/10 Branch Pipeline #1494

name: Run Native Pipeline
on:
workflow_dispatch:
inputs:
run_name:
description: "Provide a name for the run"
required: false
default: ""
mendix_version:
description: "Provide the SP version to be used (e.g., 10.14.0.43709) - has to be a released version (Default: latest from Mendix versions.json)"
required: false
default: ""
nt_branch:
description: "Native Template branch/tag to use (Leave empty to auto-select based on Mendix version)"
default: ""
required: false
type: string
LOCAL_TEST_ARTIFACT_RUN_ID:
description: "Workflow run ID for local artifact download (leave empty for normal CI). Use the full run ID from the URL, not the run number displayed in UI."
required: false
default: ""
workspace:
description: "Select a widget to test (Default will run all)"
required: true
default: "*-native"
type: choice
options:
- "*-native"
- js-actions
- accordion-native
- activity-indicator-native
- animation-native
- app-events-native
- background-gradient-native
- background-image-native
- badge-native
- bar-chart-native
- barcode-scanner-native
- bottom-sheet-native
- carousel-native
- color-picker-native
- column-chart-native
- feedback-native
- floating-action-button-native
- gallery-native
- gallery-text-filter-native
- image-native
- intro-screen-native
- line-chart-native
- listview-swipe-native
- maps-native
- pie-doughnut-chart-native
- popup-menu-native
- progress-bar-native
- progress-circle-native
- qr-code-native
- radio-buttons-native
- range-slider-native
- rating-native
- repeater-native
- safe-area-view-native
- signature-native
- slider-native
- switch-native
- toggle-buttons-native
- video-player-native
- web-view-native
# Trigger on PR
pull_request:
# Use default name in case no input
run-name: ${{ github.event.inputs.run_name || 'Run Native Pipeline' }}
env:
LOCAL_TEST_ARTIFACT_RUN_ID: ${{ github.event.inputs.LOCAL_TEST_ARTIFACT_RUN_ID || '' }}
permissions:
packages: write
jobs:
scope:
runs-on: ubuntu-latest
outputs:
scope: ${{ steps.scope.outputs.scope }}
widgets: ${{ steps.scope.outputs.widgets }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 2 # Fetch the latest two commits and its parent commit
- name: "Determine scope"
id: scope
run: |
chmod +x ./.github/scripts/determine-widget-scope.sh
./.github/scripts/determine-widget-scope.sh "${{ github.event_name }}" "${{ github.event.inputs.workspace }}" "${{ github.event.before }}" "${{ github.sha }}"
- name: "Debug Scope Output"
run: |
echo "Scope is: ${{ steps.scope.outputs.scope }}"
echo "Widgets or js actions are: ${{ steps.scope.outputs.widgets }}"
mendix-version:
runs-on: ubuntu-24.04
outputs:
mendix_version: ${{ steps.set-mendix-version.outputs.MENDIX_VERSION }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: "Get Mendix version from JSON"
id: get-mendix-version
uses: notiz-dev/github-action-json-property@7a701887f4b568b23eb7b78bb0fc49aaeb1b68d3 # v0.2.0
with:
path: configs/e2e/mendix-versions.json
prop_path: latest
- name: Set Mendix version
id: set-mendix-version
run: |
if [[ -n "${{ github.event.inputs.mendix_version }}" ]]; then
echo "MENDIX_VERSION=${{ github.event.inputs.mendix_version }}" >> $GITHUB_OUTPUT
else
echo "MENDIX_VERSION=${{ steps.get-mendix-version.outputs.prop }}" >> $GITHUB_OUTPUT
fi
- name: "Debug Mendix Version"
run: |
echo "Mendix Version: ${{ steps.set-mendix-version.outputs.MENDIX_VERSION }}"
determine-nt-version:
needs: [mendix-version]
runs-on: ubuntu-latest
outputs:
nt_branch: ${{ steps.set-output.outputs.nt_branch }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: "Check if nt_branch was specified"
id: check-input
run: |
if [[ -n "${{ github.event.inputs.nt_branch }}" ]]; then
echo "Using specified nt_branch: ${{ github.event.inputs.nt_branch }}"
echo "nt_branch=${{ github.event.inputs.nt_branch }}" >> $GITHUB_OUTPUT
echo "source=input" >> $GITHUB_OUTPUT
else
echo "No nt_branch specified, will determine from mendix_version"
echo "source=auto" >> $GITHUB_OUTPUT
fi
- name: "Download mendix_version.json from native-template repo"
if: steps.check-input.outputs.source == 'auto'
run: |
curl -s -o mendix_version.json https://raw.githubusercontent.com/mendix/native-template/master/mendix_version.json
cat mendix_version.json
- name: "Determine Native Template version based on Mendix version"
if: steps.check-input.outputs.source == 'auto'
id: determine-nt-branch
run: |
pip install requests packaging
python ./.github/scripts/determine-nt-version.py "${{ needs.mendix-version.outputs.mendix_version }}"
- name: "Set output nt_branch"
id: set-output
run: |
if [[ "${{ steps.check-input.outputs.source }}" == "input" ]]; then
echo "nt_branch=${{ github.event.inputs.nt_branch }}" >> $GITHUB_OUTPUT
else
echo "nt_branch=${{ steps.determine-nt-branch.outputs.nt_branch }}" >> $GITHUB_OUTPUT
fi
- name: "Debug final branch output"
run: |
echo "Final nt_branch value: ${{ steps.set-output.outputs.nt_branch }}"
docker-images:
needs: [mendix-version]
runs-on: ubuntu-24.04
steps:
- name: "Login to GitHub Container Registry"
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: "Check if docker image already exists"
run: |
docker manifest inspect ghcr.io/mendix/native-widgets/mxbuild:${{ needs.mendix-version.outputs.mendix_version }} || EXIT_CODE=$?
echo "IMAGE_MISSING=$EXIT_CODE" >> $GITHUB_ENV
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
if: ${{ env.IMAGE_MISSING != 0 }}
- name: "Build mxbuild image"
if: ${{ env.IMAGE_MISSING != 0 }}
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0
with:
file: ./.github/scripts/mxbuild.Dockerfile
context: ./.github/scripts
build-args: |
MENDIX_VERSION=${{ needs.mendix-version.outputs.mendix_version }}
push: true
tags: ghcr.io/mendix/native-widgets/mxbuild:${{ needs.mendix-version.outputs.mendix_version }}
resources:
needs: [scope, mendix-version]
runs-on: ubuntu-24.04
permissions:
packages: read
contents: read
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: "Set up node"
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
with:
node-version-file: .nvmrc
cache: yarn
- name: "Install dependencies"
run: yarn install --immutable
- name: "Force rebuild resources"
run: |
# Build JS actions if needed
if [ "${{ github.event.inputs.workspace }}" = "js-actions" ] || \
[ "${{ github.event.inputs.workspace }}" = "*-native" ] || \
[ "${{ github.event_name }}" = "schedule" ]; then
yarn workspace mobile-resources-native run build
yarn workspace 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
yarn workspace $w run build
done
fi
- name: "Unit test"
run: yarn workspaces foreach ${{ needs.scope.outputs.scope }} run test
- name: "Upload JS actions resources artifact"
if: ${{ github.event.inputs.workspace == 'js-actions' }}
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: resources
path: |
packages/jsActions/mobile-resources-native/dist/**/*
packages/jsActions/nanoflow-actions-native/dist/**/*
- name: "Upload widget and JS actions resources artifact"
if: ${{ github.event.inputs.workspace != 'js-actions' }}
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: resources
path: |
packages/pluggableWidgets/**/dist/*/*.mpk
packages/jsActions/mobile-resources-native/dist/**/*
packages/jsActions/nanoflow-actions-native/dist/**/*
project:
needs: [resources, mendix-version]
runs-on: ubuntu-24.04
container:
image: ghcr.io/mendix/native-widgets/mxbuild:${{ needs.mendix-version.outputs.mendix_version }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Make sure curl is installed"
run: |
apt update && apt upgrade -y
apt install curl -y
- name: "Download test project"
run: curl -L -o project.zip https://github.com/mendix/Native-Mobile-Resources/archive/refs/heads/mx-version/10.zip
- name: "Extract test project"
uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28 # v1.0.0
with:
args: unzip -qq project.zip -d .
- name: "Rename extracted directory" # Doing this since mxbuild fails with - path too long
run: mv Native-Mobile-Resources-mx-version-10 Native-Mobile-Resources-mx10
- name: "Download resources artifact"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: resources
path: resources
- name: "List resources"
run: ls -R resources
- name: "Move widgets"
if: ${{ github.event.inputs.workspace != 'js-actions' }}
shell: bash
run: |
if compgen -G 'resources/pluggableWidgets/**/dist/*/*.mpk' > /dev/null; then
for oldPath in resources/pluggableWidgets/**/dist/*/*.mpk; do
newPath=Native-Mobile-Resources-mx10/widgets/$(basename $oldPath)
mv -f $oldPath $newPath
done
mx update-widgets --loose-version-check Native-Mobile-Resources-mx10/NativeComponentsTestProject.mpr
fi
- name: "Move mobile-resources"
shell: bash
run: |
if compgen -G 'resources/jsActions/mobile-resources-native/*' > /dev/null; then
mv -f resources/jsActions/mobile-resources-native/* Native-Mobile-Resources-mx10/javascriptsource/nativemobileresources/actions/
fi
- name: "Move nanoflow-actions"
shell: bash
run: |
if compgen -G 'resources/jsActions/mobile-resources-native/*' > /dev/null; then
mv -f resources/jsActions/nanoflow-actions-native/* Native-Mobile-Resources-mx10/javascriptsource/nanoflowcommons/actions/
fi
- name: "Force rebuild test project"
run: |
mxbuild -o automation.mda --loose-version-check Native-Mobile-Resources-mx10/NativeComponentsTestProject.mpr
- name: "Upload MDA"
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: mda
path: automation.mda
android-bundle:
needs: [project, mendix-version]
runs-on: ubuntu-24.04
# Skip this job if we're using artifacts from a specific run
if: ${{ github.event.inputs.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
container:
image: ghcr.io/mendix/native-widgets/mxbuild:${{ needs.mendix-version.outputs.mendix_version }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: "Download deployment package"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
- name: "Create Android bundle"
uses: ./.github/actions/create-native-bundle
with:
platform: android
mda-file: automation.mda
ios-bundle:
needs: [project, mendix-version]
runs-on: ubuntu-24.04
# Skip this job if we're using artifacts from a specific run
if: ${{ github.event.inputs.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
container:
image: ghcr.io/mendix/native-widgets/mxbuild:${{ needs.mendix-version.outputs.mendix_version }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: "Download project MDA file"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
- name: "Create iOS bundle"
uses: ./.github/actions/create-native-bundle
with:
platform: ios
mda-file: automation.mda
android-app:
needs: [android-bundle, determine-nt-version]
runs-on: ubuntu-24.04
# Skip this job if we're using artifacts from a specific run, but allow it to run if android-bundle was skipped
if: ${{ github.event.inputs.LOCAL_TEST_ARTIFACT_RUN_ID == '' && always() && (needs.android-bundle.result == 'success') }}
steps:
- name: Debug branch value
run: echo "Using branch ${{ needs.determine-nt-version.outputs.nt_branch }}"
- name: "Check out Native Template for Native Components Test Project"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
repository: mendix/native-template
ref: ${{ needs.determine-nt-version.outputs.nt_branch }}
path: native-template
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
path: native-widgets
- name: "Download Android bundle and assets"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: android-bundle
path: bundles/android
- name: "Set up Node"
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
with:
node-version-file: native-template/.nvmrc
cache: npm
cache-dependency-path: native-template/package-lock.json
- name: "Cache Android Build"
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
with:
path: native-template/android/app/build
key: ${{ runner.os }}-android-build-${{ hashFiles('native-template/android/app/src/**/*.java', 'native-template/android/app/src/**/*.kt', 'native-template/android/app/build.gradle') }}
restore-keys: |
${{ runner.os }}-android-build-
- name: "Copy files to the right location"
run: |
mv bundles/android/index.android.bundle native-template/android/app/src/main/assets/index.android.bundle
cp -r bundles/android/assets/* native-template/android/app/src/main/res/
mv native-widgets/configs/e2e/config.json native-template
mv native-widgets/configs/e2e/google-services.json native-template/android/app
node native-widgets/scripts/test/add-native-dependencies.js
- name: "Install dependencies"
working-directory: native-template
run: npm i
- name: "Setup JDK "
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 #v4
with:
java-version: 21
distribution: temurin
cache: gradle
- name: "Build Android app"
working-directory: native-template/android
run: |
./gradlew assembleAppstoreDebug assembleAppstoreDebugAndroidTest
if [ $? -ne 0 ]; then
echo "Build failed!"
exit 1
fi
- name: "List APK files"
run: |
echo "Listing APK files in the output directory:"
ls -R native-template/android/app/build/outputs/apk/
- name: "Archive Android app"
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: android-app
path: native-template/android/app/build/outputs/apk/**/*.apk
ios-app:
needs: [ios-bundle, determine-nt-version]
runs-on: macos-15
# Skip this job if we're using artifacts from a specific run, but allow it to run if ios-bundle was skipped
if: ${{ github.event.inputs.LOCAL_TEST_ARTIFACT_RUN_ID == '' && always() && (needs.ios-bundle.result == 'success') }}
steps:
- name: "Check out Native Template for Native Components Test Project"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
repository: mendix/native-template
ref: ${{ needs.determine-nt-version.outputs.nt_branch }}
path: native-template
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
path: native-widgets
- name: "Download iOS bundle and assets"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: ios-bundle
path: bundles/ios
- name: "Set up Node"
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4
with:
node-version-file: native-template/.nvmrc
cache: npm
cache-dependency-path: native-template/package-lock.json
- name: "Cache iOS Build"
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
with:
path: native-template/ios/build
key: ${{ runner.os }}-ios-build-${{ hashFiles('native-template/ios/**/*.swift', 'native-template/ios/**/*.h', 'native-template/ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-ios-build-
- name: "Copy files to the right location"
run: |
mv bundles/ios/index.ios.bundle native-template/ios/Bundle/index.ios.bundle
mv bundles/ios/assets/assets native-template/ios/Bundle/
mv native-widgets/configs/e2e/config.json native-template
node native-widgets/scripts/test/add-native-dependencies.js
- name: "Install Node dependencies"
working-directory: native-template
run: npm i
- name: "Setup Pods cache"
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
with:
path: native-template/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: "Install Pod dependencies"
working-directory: native-template/ios
run: pod install
- name: "Build iOS app"
working-directory: native-template/ios
run: xcodebuild -workspace NativeTemplate.xcworkspace -scheme nativeTemplate -configuration Debug -sdk iphonesimulator -derivedDataPath build ONLY_ACTIVE_ARCH=YES VALID_ARCHS="i386 x86_64"
- name: "Archive iOS app"
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: ios-app
path: native-template/ios/build/Build/Products/**/*.app
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') }}
runs-on: ubuntu-24.04
timeout-minutes: 60
strategy:
max-parallel: 5
matrix:
widget: ${{ fromJson(needs.scope.outputs.widgets) }}
fail-fast: false
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: "Download project MDA file"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
# Used only when specific run id is provided when triggering the jbo
- name: "Download Android app from specific run"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID != '' }}
uses: ./.github/actions/use-arficats-from-specific-run
with:
artifact_name: android-app
run_id: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID }}
output_dir: android-app
platform: android
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: "Download Android app"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: android-app
path: android-app
- name: "Setup needed tools"
id: setup-needed-tools
uses: ./.github/actions/setup-tools
with:
mendix_version: ${{ needs.mendix-version.outputs.mendix_version }}
- name: "Start runtime with retry"
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 20
max_attempts: 3
command: |
chmod +x .github/scripts/start-runtime-with-verification.sh
.github/scripts/start-runtime-with-verification.sh automation.mda ${{ needs.mendix-version.outputs.mendix_version }} "${{ steps.setup-needed-tools.outputs.java-path }}" "${{ github.workspace }}"
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: "Run android tests"
uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.0.0
with:
api-level: 35
target: google_apis
arch: x86_64
profile: pixel
emulator-options: "-no-window -gpu swiftshader_indirect -no-boot-anim -no-snapshot -memory 4096 -cores 4"
disable-animations: true
script: |
chmod +x maestro/helpers/prepare_android.sh
chmod +x maestro/run_maestro_widget_tests.sh
bash maestro/helpers/prepare_android.sh
bash maestro/run_maestro_widget_tests.sh android "${{ matrix.widget }}"
- name: Archive test results
uses: ./.github/actions/archive-test-results
with:
platform: android
test-type: ${{ matrix.widget }}
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') }}
runs-on: macos-15
timeout-minutes: 60
strategy:
max-parallel: 5
matrix:
widget: ${{ fromJson(needs.scope.outputs.widgets) }}
fail-fast: false
steps:
- name: "Force cleanup workspace"
run: |
# Kill all Git and Xcode processes aggressively
sudo pkill -9 -f git || true
sudo pkill -9 -f xcodebuild || true
sudo pkill -9 -f node || true
# Wait for processes to be killed
sleep 5
# Clean the workspace contents while preserving directory structure
cd /Users/runner/work/native-widgets
if [ -d "native-widgets" ]; then
cd native-widgets
# Force remove all git state and locks with extreme prejudice
sudo rm -rf .git || true
find . -name "*.lock" -type f -exec sudo rm -f {} \; 2>/dev/null || true
find . -name ".git*" -exec sudo rm -rf {} \; 2>/dev/null || true
# Remove all other contents
sudo find . -mindepth 1 -not -path "./.git*" -delete 2>/dev/null || true
fi
# Final wait and process cleanup
sudo pkill -9 -f git || true
sleep 3
continue-on-error: true
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 1
fetch-tags: false
clean: true
ref: ${{ github.sha }}
- name: "Download project MDA file"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
# Used only for local testing, will be empty on GitHub
- name: "Download iOS app from specific run"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID != '' }}
uses: ./.github/actions/use-arficats-from-specific-run
with:
artifact_name: ios-app
run_id: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID }}
output_dir: ios-app
platform: ios
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: "Download iOS app"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: ios-app
path: ios-app
- name: "Setup needed tools"
id: setup-needed-tools
uses: ./.github/actions/setup-tools
with:
mendix_version: ${{ needs.mendix-version.outputs.mendix_version }}
- name: "Start runtime with retry"
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
command: |
chmod +x .github/scripts/start-runtime-with-verification.sh
.github/scripts/start-runtime-with-verification.sh automation.mda ${{ needs.mendix-version.outputs.mendix_version }} "${{ steps.setup-needed-tools.outputs.java-path }}" "${{ github.workspace }}"
- name: "Setup Xcode CLI Tools"
uses: ./.github/actions/setup-xcode-cli-tools
- name: "Run iOS tests"
run: |
chmod +x maestro/helpers/prepare_ios.sh
chmod +x maestro/run_maestro_widget_tests.sh
bash maestro/helpers/prepare_ios.sh
bash maestro/run_maestro_widget_tests.sh ios "${{ matrix.widget }}"
- name: Archive test results
uses: ./.github/actions/archive-test-results
with:
platform: ios
test-type: ${{ matrix.widget }}
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') }}
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: "Download project MDA file"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
# Used only for local testing, will be empty on GitHub
- name: "Download Android app from specific run"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID != '' }}
uses: ./.github/actions/use-arficats-from-specific-run
with:
artifact_name: android-app
run_id: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID }}
output_dir: android-app
platform: android
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: "Download Android app"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: android-app
path: android-app
- name: "Setup needed tools"
id: setup-needed-tools
uses: ./.github/actions/setup-tools
with:
mendix_version: ${{ needs.mendix-version.outputs.mendix_version }}
- name: "Start runtime with retry"
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
command: |
chmod +x .github/scripts/start-runtime-with-verification.sh
.github/scripts/start-runtime-with-verification.sh automation.mda ${{ needs.mendix-version.outputs.mendix_version }} "${{ steps.setup-needed-tools.outputs.java-path }}" "${{ github.workspace }}"
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: "Run android tests"
uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.0.0
with:
api-level: 35
target: google_apis
arch: x86_64
profile: pixel
emulator-options: "-no-window -gpu swiftshader_indirect -no-boot-anim -no-snapshot -memory 4096 -cores 4"
disable-animations: true
script: |
chmod +x maestro/helpers/prepare_android.sh
chmod +x maestro/run_maestro_jsactions_tests.sh
bash maestro/helpers/prepare_android.sh
bash maestro/run_maestro_jsactions_tests.sh android
- name: Archive test results
uses: ./.github/actions/archive-test-results
with:
platform: android
test-type: js-actions
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') }}
runs-on: macos-15
timeout-minutes: 90
steps:
- name: "Clean up any Git locks"
run: |
# Kill all git and related processes aggressively
sudo pkill -9 -f git || true
sudo pkill -9 -f node || true
# Wait for processes to be killed
sleep 5
# Clean the workspace contents while preserving directory structure
cd /Users/runner/work/native-widgets
if [ -d "native-widgets" ]; then
cd native-widgets
# Force remove git state and locks
sudo rm -rf .git || true
find . -name "*.lock" -type f -exec sudo rm -f {} \; 2>/dev/null || true
find . -name ".git*" -exec sudo rm -rf {} \; 2>/dev/null || true
# Remove all other contents
sudo find . -mindepth 1 -not -path "./.git*" -delete 2>/dev/null || true
fi
# Final cleanup
sudo pkill -9 -f git || true
sleep $((RANDOM % 5 + 3))
continue-on-error: true
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 1
fetch-tags: false
clean: true
- name: "Download project MDA file"
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: mda
# Used only for local testing, will be empty on GitHub
- name: "Download iOS app from specific run"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID != '' }}
uses: ./.github/actions/use-arficats-from-specific-run
with:
artifact_name: ios-app
run_id: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID }}
output_dir: ios-app
platform: ios
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: "Download iOS app"
if: ${{ env.LOCAL_TEST_ARTIFACT_RUN_ID == '' }}
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: ios-app
path: ios-app
- name: "Setup needed tools"
id: setup-needed-tools
uses: ./.github/actions/setup-tools
with:
mendix_version: ${{ needs.mendix-version.outputs.mendix_version }}
- name: "Start runtime with retry"
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
command: |
chmod +x .github/scripts/start-runtime-with-verification.sh
.github/scripts/start-runtime-with-verification.sh automation.mda ${{ needs.mendix-version.outputs.mendix_version }} "${{ steps.setup-needed-tools.outputs.java-path }}" "${{ github.workspace }}"
- name: "Setup Xcode CLI Tools"
uses: ./.github/actions/setup-xcode-cli-tools
- name: "Run iOS tests"
run: |
chmod +x maestro/helpers/prepare_ios.sh
chmod +x maestro/run_maestro_jsactions_tests.sh
bash maestro/helpers/prepare_ios.sh
bash maestro/run_maestro_jsactions_tests.sh ios
- name: Archive test results
uses: ./.github/actions/archive-test-results
with:
platform: ios
test-type: js-actions
compare-screenshots:
needs: [scope, android-widget-tests, ios-widget-tests, android-js-tests, ios-js-tests]
runs-on: ubuntu-latest
if: always()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Check out code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: "Download Android screenshots"
run: |
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
mkdir -p images/actual/android/
for widget in $widgets; do
echo "Downloading android-screenshots-${widget}"
gh run download ${{ github.run_id }} --name android-screenshots-${widget} --dir images/actual/android/ || true
ls -l images/actual/android/
done
# Download JS actions screenshots
echo "Downloading android-screenshots-js-actions"
gh run download ${{ github.run_id }} --name android-screenshots-js-actions --dir images/actual/android/ || true
ls -l images/actual/android/
- name: "Download iOS screenshots"
run: |
widgets=$(echo '${{ needs.scope.outputs.widgets }}' | jq -r '.[]')
mkdir -p images/actual/ios/
for widget in $widgets; do
echo "Downloading ios-screenshots-${widget}"
gh run download ${{ github.run_id }} --name ios-screenshots-${widget} --dir images/actual/ios/ || true
ls -l images/actual/ios/
done
# Download JS actions screenshots
echo "Downloading ios-screenshots-js-actions"
gh run download ${{ github.run_id }} --name ios-screenshots-js-actions --dir images/actual/ios/ || true
ls -l images/actual/ios/
- name: "Setup Node and dependencies"
uses: ./.github/actions/setup-node-with-cache
- name: "Compare Android screenshots"
continue-on-error: true
run: node ${{ github.workspace }}/maestro/helpers/compare_screenshots.js android
- name: "Compare iOS screenshots"
continue-on-error: true
run: node ${{ github.workspace }}/maestro/helpers/compare_screenshots.js ios
- name: "Archive diff results"
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: screenshot-diffs
path: images/diffs
if-no-files-found: ignore
- name: "Archive comparison results"
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4
with:
name: comparison-results
path: compare_output.txt
if-no-files-found: ignore
- name: "Fail if comparisons failed"
run: |
if [ "$ANDROID_COMPARISON_FAILED" == "true" ] || [ "$IOS_COMPARISON_FAILED" == "true" ]; then
echo "One or more comparisons failed."
exit 1
fi