@@ -1111,6 +1111,29 @@ <h1>Usage Guide</h1>
11111111 < li > < a href ="#sdk-options "> Options Reference</ a > </ li >
11121112 </ ul >
11131113 </ div >
1114+ < div class ="sidebar-group open ">
1115+ < button class ="sidebar-heading " aria-expanded ="true ">
1116+ GitHub Actions
1117+ < svg
1118+ class ="sidebar-chevron "
1119+ viewBox ="0 0 24 24 "
1120+ fill ="none "
1121+ stroke ="currentColor "
1122+ stroke-width ="2 "
1123+ >
1124+ < path d ="M6 9l6 6 6-6 " />
1125+ </ svg >
1126+ </ button >
1127+ < ul >
1128+ < li > < a href ="#gha-overview "> Overview</ a > </ li >
1129+ < li > < a href ="#gha-quick-start "> Quick Start</ a > </ li >
1130+ < li > < a href ="#gha-inputs "> Inputs</ a > </ li >
1131+ < li > < a href ="#gha-secrets "> Secrets Setup</ a > </ li >
1132+ < li > < a href ="#gha-examples "> Examples</ a > </ li >
1133+ < li > < a href ="#gha-reports "> Reports</ a > </ li >
1134+ < li > < a href ="#gha-runners "> Runner Requirements</ a > </ li >
1135+ </ ul >
1136+ </ div >
11141137 </ aside >
11151138
11161139 <!-- CONTENT -->
@@ -2929,6 +2952,315 @@ <h3>TypeScript types</h3>
29292952< span class ="p "> }</ span > < span class ="k "> from</ span > < span class ="s "> 'appclaw'</ span > < span class ="p "> ;</ span > </ pre >
29302953 </ div >
29312954 </ section >
2955+
2956+ <!-- ============================================ -->
2957+ <!-- GITHUB ACTIONS -->
2958+ <!-- ============================================ -->
2959+ < section id ="gha-overview " class ="reveal ">
2960+ < h2 > GitHub Actions</ h2 >
2961+ < p >
2962+ Run AppClaw mobile UI automation flows and AI-driven goals directly in GitHub Actions —
2963+ Android emulator or iOS simulator included, zero boilerplate.
2964+ </ p >
2965+ < p >
2966+ Available on the
2967+ < a href ="https://github.com/marketplace/actions/appclaw-mobile-tests " target ="_blank "> GitHub Marketplace</ a >
2968+ as < strong > AppClaw Mobile Tests</ strong > .
2969+ </ p >
2970+
2971+ < div class ="code-block ">
2972+ < div class ="code-block-header ">
2973+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
2974+ < span class ="code-block-label "> workflow.yml</ span >
2975+ </ div >
2976+ < pre > < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
2977+ < span class ="k "> with:</ span >
2978+ < span class ="k "> flow:</ span > < span class ="s "> flows/login.yaml</ span >
2979+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
2980+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span > </ pre >
2981+ </ div >
2982+ </ section >
2983+
2984+ < section id ="gha-quick-start " class ="reveal ">
2985+ < h2 > Quick Start</ h2 >
2986+
2987+ < h3 > Android — run a YAML flow</ h3 >
2988+ < div class ="code-block ">
2989+ < div class ="code-block-header ">
2990+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
2991+ < span class ="code-block-label "> android-flow.yml</ span >
2992+ </ div >
2993+ < pre > < span class ="k "> name:</ span > < span class ="s "> Mobile Tests</ span >
2994+ < span class ="k "> on:</ span > < span class ="p "> [</ span > < span class ="s "> push</ span > < span class ="p "> ,</ span > < span class ="s "> pull_request</ span > < span class ="p "> ]</ span >
2995+
2996+ < span class ="k "> jobs:</ span >
2997+ < span class ="k "> test:</ span >
2998+ < span class ="k "> runs-on:</ span > < span class ="s "> ubuntu-latest</ span >
2999+ < span class ="k "> steps:</ span >
3000+ - < span class ="k "> uses:</ span > < span class ="s "> actions/checkout@v4</ span >
3001+
3002+ - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3003+ < span class ="k "> with:</ span >
3004+ < span class ="k "> flow:</ span > < span class ="s "> flows/login.yaml</ span >
3005+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3006+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span > </ pre >
3007+ </ div >
3008+
3009+ < h3 > Android — natural language goal</ h3 >
3010+ < div class ="code-block ">
3011+ < div class ="code-block-header ">
3012+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3013+ < span class ="code-block-label "> android-goal.yml</ span >
3014+ </ div >
3015+ < pre > - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3016+ < span class ="k "> with:</ span >
3017+ < span class ="k "> goal:</ span > < span class ="s "> 'Open YouTube, search for Appium 3.0, verify the first result is visible'</ span >
3018+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3019+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span > </ pre >
3020+ </ div >
3021+
3022+ < h3 > iOS — run a YAML flow</ h3 >
3023+ < div class ="code-block ">
3024+ < div class ="code-block-header ">
3025+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3026+ < span class ="code-block-label "> ios-flow.yml</ span >
3027+ </ div >
3028+ < pre > < span class ="k "> jobs:</ span >
3029+ < span class ="k "> test:</ span >
3030+ < span class ="k "> runs-on:</ span > < span class ="s "> macos-14</ span > < span class ="c "> # iOS requires macOS (Apple Silicon)</ span >
3031+ < span class ="k "> steps:</ span >
3032+ - < span class ="k "> uses:</ span > < span class ="s "> actions/checkout@v4</ span >
3033+
3034+ - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3035+ < span class ="k "> with:</ span >
3036+ < span class ="k "> flow:</ span > < span class ="s "> flows/ios-login.yaml</ span >
3037+ < span class ="k "> platform:</ span > < span class ="s "> ios</ span >
3038+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span > </ pre >
3039+ </ div >
3040+ </ section >
3041+
3042+ < section id ="gha-inputs " class ="reveal ">
3043+ < h2 > Inputs</ h2 >
3044+ < p > All inputs are passed via the < code > with:</ code > block in your workflow.</ p >
3045+
3046+ < table >
3047+ < thead >
3048+ < tr >
3049+ < th > Input</ th >
3050+ < th > Required</ th >
3051+ < th > Default</ th >
3052+ < th > Description</ th >
3053+ </ tr >
3054+ </ thead >
3055+ < tbody >
3056+ < tr > < td > < code > flow</ code > </ td > < td > one of*</ td > < td > —</ td > < td > Path to a YAML flow file relative to repo root</ td > </ tr >
3057+ < tr > < td > < code > goal</ code > </ td > < td > one of*</ td > < td > —</ td > < td > Natural language goal executed by the LLM agent</ td > </ tr >
3058+ < tr > < td > < code > platform</ code > </ td > < td > no</ td > < td > < code > android</ code > </ td > < td > Target platform: < code > android</ code > or < code > ios</ code > </ td > </ tr >
3059+ < tr > < td > < code > provider</ code > </ td > < td > no</ td > < td > < code > gemini</ code > </ td > < td > LLM provider: < code > gemini</ code > , < code > anthropic</ code > , < code > openai</ code > , < code > groq</ code > </ td > </ tr >
3060+ < tr > < td > < code > api-key</ code > </ td > < td > < strong > yes</ strong > </ td > < td > —</ td > < td > LLM API key — stored as < code > LLM_API_KEY</ code > </ td > </ tr >
3061+ < tr > < td > < code > model</ code > </ td > < td > no</ td > < td > < em > provider default</ em > </ td > < td > LLM model ID to pin (e.g. < code > gemini-2.0-flash</ code > )</ td > </ tr >
3062+ < tr > < td > < code > agent-mode</ code > </ td > < td > no</ td > < td > < code > dom</ code > </ td > < td > < code > dom</ code > (element locators) or < code > vision</ code > (screenshot AI)</ td > </ tr >
3063+ < tr > < td > < code > max-steps</ code > </ td > < td > no</ td > < td > < code > 30</ code > </ td > < td > Maximum agent steps before the run fails</ td > </ tr >
3064+ < tr > < td > < code > step-delay</ code > </ td > < td > no</ td > < td > < code > 500</ code > </ td > < td > Milliseconds between steps</ td > </ tr >
3065+ < tr > < td > < code > android-api-level</ code > </ td > < td > no</ td > < td > < code > 33</ code > </ td > < td > Android emulator API level (33 = Android 13)</ td > </ tr >
3066+ < tr > < td > < code > android-profile</ code > </ td > < td > no</ td > < td > < code > pixel_6</ code > </ td > < td > Android AVD hardware profile</ td > </ tr >
3067+ < tr > < td > < code > android-target</ code > </ td > < td > no</ td > < td > < code > default</ code > </ td > < td > Emulator target: < code > default</ code > or < code > google_apis</ code > </ td > </ tr >
3068+ < tr > < td > < code > cloud-provider</ code > </ td > < td > no</ td > < td > < em > local</ em > </ td > < td > Cloud provider: < code > lambdatest</ code > . Leave empty for local.</ td > </ tr >
3069+ < tr > < td > < code > lambdatest-username</ code > </ td > < td > no**</ td > < td > —</ td > < td > LambdaTest account username</ td > </ tr >
3070+ < tr > < td > < code > lambdatest-access-key</ code > </ td > < td > no**</ td > < td > —</ td > < td > LambdaTest access key</ td > </ tr >
3071+ < tr > < td > < code > lambdatest-device-name</ code > </ td > < td > no**</ td > < td > —</ td > < td > Cloud device name (e.g. < code > Pixel 7</ code > )</ td > </ tr >
3072+ < tr > < td > < code > lambdatest-os-version</ code > </ td > < td > no**</ td > < td > —</ td > < td > Cloud OS version (e.g. < code > 13</ code > , < code > 16</ code > )</ td > </ tr >
3073+ < tr > < td > < code > lambdatest-app</ code > </ td > < td > no</ td > < td > —</ td > < td > LambdaTest app ID (< code > lt://APP...</ code > )</ td > </ tr >
3074+ < tr > < td > < code > report</ code > </ td > < td > no</ td > < td > < code > true</ code > </ td > < td > Upload HTML report as workflow artifact</ td > </ tr >
3075+ < tr > < td > < code > report-name</ code > </ td > < td > no</ td > < td > < code > appclaw-report</ code > </ td > < td > Name of the uploaded artifact</ td > </ tr >
3076+ < tr > < td > < code > appclaw-version</ code > </ td > < td > no</ td > < td > < code > latest</ code > </ td > < td > npm package version to pin</ td > </ tr >
3077+ </ tbody >
3078+ </ table >
3079+ < p > * Provide either < code > flow</ code > < strong > or</ strong > < code > goal</ code > , not both.</ p >
3080+ < p > ** Required when < code > cloud-provider: lambdatest</ code > .</ p >
3081+ </ section >
3082+
3083+ < section id ="gha-secrets " class ="reveal ">
3084+ < h2 > Secrets Setup</ h2 >
3085+ < p >
3086+ Go to your repo → < strong > Settings → Secrets and variables → Actions → New repository secret</ strong > :
3087+ </ p >
3088+ < table >
3089+ < thead >
3090+ < tr > < th > Secret name</ th > < th > Description</ th > </ tr >
3091+ </ thead >
3092+ < tbody >
3093+ < tr > < td > < code > LLM_API_KEY</ code > </ td > < td > Your API key — works for any provider (Gemini, Anthropic, OpenAI, Groq)</ td > </ tr >
3094+ < tr > < td > < code > LT_USERNAME</ code > </ td > < td > LambdaTest username (only if using cloud devices)</ td > </ tr >
3095+ < tr > < td > < code > LT_ACCESS_KEY</ code > </ td > < td > LambdaTest access key (only if using cloud devices)</ td > </ tr >
3096+ < tr > < td > < code > LT_APP_ID</ code > </ td > < td > LambdaTest app ID (only if using cloud devices)</ td > </ tr >
3097+ </ tbody >
3098+ </ table >
3099+ </ section >
3100+
3101+ < section id ="gha-examples " class ="reveal ">
3102+ < h2 > Examples</ h2 >
3103+
3104+ < h3 > Parallel matrix — run multiple flows concurrently</ h3 >
3105+ < div class ="code-block ">
3106+ < div class ="code-block-header ">
3107+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3108+ < span class ="code-block-label "> matrix-parallel.yml</ span >
3109+ </ div >
3110+ < pre > < span class ="k "> jobs:</ span >
3111+ < span class ="k "> test:</ span >
3112+ < span class ="k "> runs-on:</ span > < span class ="s "> ubuntu-latest</ span >
3113+ < span class ="k "> strategy:</ span >
3114+ < span class ="k "> fail-fast:</ span > < span class ="s "> false</ span >
3115+ < span class ="k "> matrix:</ span >
3116+ < span class ="k "> flow:</ span >
3117+ - < span class ="s "> flows/login.yaml</ span >
3118+ - < span class ="s "> flows/search.yaml</ span >
3119+ - < span class ="s "> flows/checkout.yaml</ span >
3120+ < span class ="k "> steps:</ span >
3121+ - < span class ="k "> uses:</ span > < span class ="s "> actions/checkout@v4</ span >
3122+
3123+ - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3124+ < span class ="k "> with:</ span >
3125+ < span class ="k "> flow:</ span > < span class ="s "> ${{ matrix.flow }}</ span >
3126+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3127+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span >
3128+ < span class ="k "> report-name:</ span > < span class ="s "> report-${{ strategy.job-index }}</ span > </ pre >
3129+ </ div >
3130+
3131+ < h3 > LambdaTest cloud devices</ h3 >
3132+ < p > Run iOS tests on Ubuntu — no macOS runner needed.</ p >
3133+ < div class ="code-block ">
3134+ < div class ="code-block-header ">
3135+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3136+ < span class ="code-block-label "> lambdatest.yml</ span >
3137+ </ div >
3138+ < pre > < span class ="k "> jobs:</ span >
3139+ < span class ="k "> test:</ span >
3140+ < span class ="k "> runs-on:</ span > < span class ="s "> ubuntu-latest</ span >
3141+ < span class ="k "> steps:</ span >
3142+ - < span class ="k "> uses:</ span > < span class ="s "> actions/checkout@v4</ span >
3143+
3144+ - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3145+ < span class ="k "> with:</ span >
3146+ < span class ="k "> flow:</ span > < span class ="s "> flows/ios-login.yaml</ span >
3147+ < span class ="k "> platform:</ span > < span class ="s "> ios</ span >
3148+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span >
3149+ < span class ="k "> cloud-provider:</ span > < span class ="s "> lambdatest</ span >
3150+ < span class ="k "> lambdatest-username:</ span > < span class ="s "> ${{ secrets.LT_USERNAME }}</ span >
3151+ < span class ="k "> lambdatest-access-key:</ span > < span class ="s "> ${{ secrets.LT_ACCESS_KEY }}</ span >
3152+ < span class ="k "> lambdatest-device-name:</ span > < span class ="s "> 'iPhone 14'</ span >
3153+ < span class ="k "> lambdatest-os-version:</ span > < span class ="s "> '16'</ span >
3154+ < span class ="k "> lambdatest-app:</ span > < span class ="s "> ${{ secrets.LT_APP_ID }}</ span > </ pre >
3155+ </ div >
3156+
3157+ < h3 > Vision mode (screenshot-based AI)</ h3 >
3158+ < div class ="code-block ">
3159+ < div class ="code-block-header ">
3160+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3161+ < span class ="code-block-label "> vision-mode.yml</ span >
3162+ </ div >
3163+ < pre > - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3164+ < span class ="k "> with:</ span >
3165+ < span class ="k "> flow:</ span > < span class ="s "> flows/onboarding.yaml</ span >
3166+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3167+ < span class ="k "> agent-mode:</ span > < span class ="s "> vision</ span >
3168+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span > </ pre >
3169+ </ div >
3170+
3171+ < h3 > Pin model for cost control</ h3 >
3172+ < div class ="code-block ">
3173+ < div class ="code-block-header ">
3174+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3175+ < span class ="code-block-label "> pin-model.yml</ span >
3176+ </ div >
3177+ < pre > - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3178+ < span class ="k "> with:</ span >
3179+ < span class ="k "> flow:</ span > < span class ="s "> flows/smoke.yaml</ span >
3180+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3181+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span >
3182+ < span class ="k "> model:</ span > < span class ="s "> 'gemini-2.0-flash'</ span > < span class ="c "> # cheaper/faster than pro</ span > </ pre >
3183+ </ div >
3184+
3185+ < h3 > Nightly regression on a schedule</ h3 >
3186+ < div class ="code-block ">
3187+ < div class ="code-block-header ">
3188+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3189+ < span class ="code-block-label "> nightly.yml</ span >
3190+ </ div >
3191+ < pre > < span class ="k "> on:</ span >
3192+ < span class ="k "> schedule:</ span >
3193+ - < span class ="k "> cron:</ span > < span class ="s "> '0 2 * * *'</ span > < span class ="c "> # 2 AM UTC every night</ span >
3194+
3195+ < span class ="k "> jobs:</ span >
3196+ < span class ="k "> nightly:</ span >
3197+ < span class ="k "> runs-on:</ span > < span class ="s "> ubuntu-latest</ span >
3198+ < span class ="k "> steps:</ span >
3199+ - < span class ="k "> uses:</ span > < span class ="s "> actions/checkout@v4</ span >
3200+ - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3201+ < span class ="k "> with:</ span >
3202+ < span class ="k "> flow:</ span > < span class ="s "> flows/full-regression.yaml</ span >
3203+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3204+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span >
3205+ < span class ="k "> report-name:</ span > < span class ="s "> nightly-report-${{ github.run_id }}</ span > </ pre >
3206+ </ div >
3207+ </ section >
3208+
3209+ < section id ="gha-reports " class ="reveal ">
3210+ < h2 > Reports</ h2 >
3211+ < p >
3212+ When < code > report: true</ code > (default), an HTML report is uploaded as a workflow artifact after each run.
3213+ Download it from the < strong > Actions run summary → Artifacts</ strong > . The report includes:
3214+ </ p >
3215+ < ul >
3216+ < li > Step-by-step screenshots with tap overlays</ li >
3217+ < li > Pass/fail status per step</ li >
3218+ < li > Execution timeline</ li >
3219+ < li > Screen recording (if < code > video: true</ code > is set in your flow)</ li >
3220+ </ ul >
3221+
3222+ < h3 > Use report path in a downstream step</ h3 >
3223+ < div class ="code-block ">
3224+ < div class ="code-block-header ">
3225+ < div class ="code-block-dots "> < span > </ span > < span > </ span > < span > </ span > </ div >
3226+ < span class ="code-block-label "> report-path.yml</ span >
3227+ </ div >
3228+ < pre > - < span class ="k "> uses:</ span > < span class ="s "> AppiumTestDistribution/AppClaw@v1</ span >
3229+ < span class ="k "> id:</ span > < span class ="s "> appclaw</ span >
3230+ < span class ="k "> with:</ span >
3231+ < span class ="k "> flow:</ span > < span class ="s "> flows/login.yaml</ span >
3232+ < span class ="k "> platform:</ span > < span class ="s "> android</ span >
3233+ < span class ="k "> api-key:</ span > < span class ="s "> ${{ secrets.LLM_API_KEY }}</ span >
3234+
3235+ - < span class ="k "> name:</ span > < span class ="s "> Print report location</ span >
3236+ < span class ="k "> run:</ span > < span class ="s "> echo "Report at ${{ steps.appclaw.outputs.report-path }}"</ span > </ pre >
3237+ </ div >
3238+ </ section >
3239+
3240+ < section id ="gha-runners " class ="reveal ">
3241+ < h2 > Runner Requirements</ h2 >
3242+ < table >
3243+ < thead >
3244+ < tr > < th > Platform</ th > < th > Runner</ th > < th > Notes</ th > </ tr >
3245+ </ thead >
3246+ < tbody >
3247+ < tr >
3248+ < td > < code > android</ code > </ td >
3249+ < td > < code > ubuntu-latest</ code > </ td >
3250+ < td > Free tier. KVM-enabled. Emulator boots in ~4-6 min.</ td >
3251+ </ tr >
3252+ < tr >
3253+ < td > < code > ios</ code > </ td >
3254+ < td > < code > macos-14</ code > </ td >
3255+ < td > Apple Silicon. macOS minutes cost ~10x Linux.</ td >
3256+ </ tr >
3257+ </ tbody >
3258+ </ table >
3259+ < p >
3260+ < strong > iOS tip:</ strong > For faster iOS CI, use LambdaTest cloud devices on < code > ubuntu-latest</ code >
3261+ instead of a macOS runner.
3262+ </ p >
3263+ </ section >
29323264 </ main >
29333265 </ div >
29343266
0 commit comments