[{"data":1,"prerenderedAt":657},["ShallowReactive",2],{"docs-page:en:\u002Fdocs\u002Fintegration\u002Fapp-center-codepush":3,"docs-navigation:en":557},{"id":4,"title":5,"body":6,"description":549,"extension":550,"meta":551,"navigation":552,"path":553,"seo":554,"stem":555,"__hash__":556},"docs_en\u002Fen\u002Fdocs\u002Fintegration\u002Fapp-center-codepush.md","Migrate from App Center CodePush",{"type":7,"value":8,"toc":541},"minimark",[9,13,16,36,39,44,141,156,160,222,225,229,232,258,261,302,306,309,458,465,469,472,492,497,501,534,537],[10,11,12],"p",{},"Microsoft announced Visual Studio App Center retirement for March 31, 2025. Microsoft later extended Analytics and Diagnostics support, but states that the remaining App Center features retired after March 31, 2025. For CodePush, Microsoft points teams to a standalone CodePush server that can run independently from App Center.",[10,14,15],{},"Sources:",[17,18,19,29],"ul",{},[20,21,22],"li",{},[23,24,28],"a",{"href":25,"rel":26},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fappcenter\u002Fretirement",[27],"nofollow","Visual Studio App Center Retirement",[20,30,31],{},[23,32,35],{"href":33,"rel":34},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcode-push-server",[27],"Microsoft standalone CodePush server",[10,37,38],{},"This page explains how to think about a migration from App Center CodePush to Otalan. It is not an automatic import tool.",[40,41,43],"h2",{"id":42},"mental-model-mapping","Mental model mapping",[45,46,47,60],"table",{},[48,49,50],"thead",{},[51,52,53,57],"tr",{},[54,55,56],"th",{},"App Center CodePush concept",[54,58,59],{},"Otalan concept",[61,62,63,76,87,95,103,114,122,130],"tbody",{},[51,64,65,69],{},[66,67,68],"td",{},"App Center app",[66,70,71,72],{},"Otalan project app, usually the same mobile ",[73,74,75],"code",{},"appId",[51,77,78,81],{},[66,79,80],{},"Deployment, such as Staging or Production",[66,82,83,84],{},"Otalan ",[73,85,86],{},"channel",[51,88,89,92],{},[66,90,91],{},"Deployment key in the installed app",[66,93,94],{},"OTA App Key plus exact app\u002Fchannel\u002Fruntime settings",[51,96,97,100],{},[66,98,99],{},"CodePush release package",[66,101,102],{},"Otalan bundle",[51,104,105,108],{},[66,106,107],{},"Target binary version",[66,109,110,113],{},[73,111,112],{},"runtimeVersion"," compatibility line",[51,115,116,119],{},[66,117,118],{},"Promote release",[66,120,121],{},"Publish or roll out the same tested tuple\u002Fchannel intentionally",[51,123,124,127],{},[66,125,126],{},"Rollback",[66,128,129],{},"Reactivate an older Otalan bundle in the same tuple",[51,131,132,135],{},[66,133,134],{},"CLI release automation",[66,136,137,140],{},[73,138,139],{},"@otalan\u002Fcli"," bundle and publish flow",[10,142,143,144,146,147,146,150,152,153,155],{},"The biggest difference is tuple strictness. Otalan always matches ",[73,145,75],{},", ",[73,148,149],{},"platform",[73,151,86],{},", and ",[73,154,112],{}," exactly before serving an update.",[40,157,159],{"id":158},"migration-plan","Migration plan",[161,162,163,166,178,181,196,199,202,213,216,219],"ol",{},[20,164,165],{},"Inventory your CodePush apps, deployments, deployment keys, rollout rules, and target binary versions.",[20,167,168,169,146,171,146,173,152,175,177],{},"Decide the Otalan ",[73,170,75],{},[73,172,149],{},[73,174,86],{},[73,176,112],{}," mapping for each active release lane.",[20,179,180],{},"Create the Otalan organization, project, app registration, OTA App Key, and OTA Publish Key.",[20,182,183,184,187,188,191,192,195],{},"Add the Otalan runtime integration for the app stack: ",[73,185,186],{},"@otalan\u002Fcapacitor"," for Capacitor or ",[73,189,190],{},"@otalan\u002Fexpo"," with ",[73,193,194],{},"expo-updates"," for Expo.",[20,197,198],{},"Ship a new native binary that points to Otalan. Existing binaries that still point to CodePush will keep asking CodePush until users update the app.",[20,200,201],{},"Configure CI with the OTA Publish Key and the same tuple values used by the runtime.",[20,203,204,205,208,209,212],{},"Publish a canary bundle with ",[73,206,207],{},"otalan bundle"," and ",[73,210,211],{},"otalan publish",".",[20,214,215],{},"Verify the release path: publish one update, publish a second update, roll back, then confirm startup on the target devices.",[20,217,218],{},"Freeze new CodePush publishes only after the Otalan-backed binary is available and verified.",[20,220,221],{},"Keep old CodePush infrastructure or a compatibility plan for users who have not yet upgraded from old binaries.",[10,223,224],{},"Do not assume historical CodePush releases are imported into Otalan. Publish the known-good current bundle into Otalan and use that as the first rollback anchor.",[40,226,228],{"id":227},"runtime-integration-notes","Runtime integration notes",[10,230,231],{},"For Capacitor apps:",[17,233,234,239,252,255],{},[20,235,236,237],{},"install ",[73,238,186],{},[20,240,241,242,146,245,152,248,251],{},"keep ",[73,243,244],{},"@capawesome\u002Fcapacitor-live-update",[73,246,247],{},"@capacitor\u002Fapp",[73,249,250],{},"@capacitor\u002Fcore"," aligned with the supported Capacitor range",[20,253,254],{},"put only the OTA App Key in the app",[20,256,257],{},"keep OTA Publish Keys in local release tooling or CI",[10,259,260],{},"For Expo apps:",[17,262,263,267,272,275,278,296],{},[20,264,236,265],{},[73,266,190],{},[20,268,241,269,271],{},[73,270,194],{}," enabled",[20,273,274],{},"point the Expo update URL to the Otalan manifest endpoint",[20,276,277],{},"include the OTA App Key in update request headers",[20,279,280,281,283,284,287,288,291,292,295],{},"initialize ",[73,282,190],{}," once and call ",[73,285,286],{},"updater.check()"," for ",[73,289,290],{},"updateAvailable"," or ",[73,293,294],{},"updater.sync()"," for check-and-apply work",[20,297,241,298,301],{},[73,299,300],{},"checkAutomatically"," controlled so Expo does not check before the Otalan helper is initialized",[40,303,305],{"id":304},"ci-publishing-pattern","CI publishing pattern",[10,307,308],{},"A typical first Otalan CI job looks like this:",[310,311,316],"pre",{"className":312,"code":313,"language":314,"meta":315,"style":315},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","bun add -g @otalan\u002Fcli\notalan login --api-url \"$OTALAN_API_URL\" --api-key \"$OTALAN_API_KEY\"\notalan init --app-id \"$OTALAN_APP_ID\"\nbun run build\notalan bundle --target capacitor --platform ios --runtime-version \"$OTALAN_RUNTIME_VERSION\"\notalan publish --channel \"$OTALAN_CHANNEL\" --release-notes \"$GIT_COMMIT\"\n","bash","",[73,317,318,337,371,389,400,430],{"__ignoreMap":315},[319,320,323,327,331,334],"span",{"class":321,"line":322},"line",1,[319,324,326],{"class":325},"sBMFI","bun",[319,328,330],{"class":329},"sfazB"," add",[319,332,333],{"class":329}," -g",[319,335,336],{"class":329}," @otalan\u002Fcli\n",[319,338,340,343,346,349,353,357,360,363,365,368],{"class":321,"line":339},2,[319,341,342],{"class":325},"otalan",[319,344,345],{"class":329}," login",[319,347,348],{"class":329}," --api-url",[319,350,352],{"class":351},"sMK4o"," \"",[319,354,356],{"class":355},"sTEyZ","$OTALAN_API_URL",[319,358,359],{"class":351},"\"",[319,361,362],{"class":329}," --api-key",[319,364,352],{"class":351},[319,366,367],{"class":355},"$OTALAN_API_KEY",[319,369,370],{"class":351},"\"\n",[319,372,374,376,379,382,384,387],{"class":321,"line":373},3,[319,375,342],{"class":325},[319,377,378],{"class":329}," init",[319,380,381],{"class":329}," --app-id",[319,383,352],{"class":351},[319,385,386],{"class":355},"$OTALAN_APP_ID",[319,388,370],{"class":351},[319,390,392,394,397],{"class":321,"line":391},4,[319,393,326],{"class":325},[319,395,396],{"class":329}," run",[319,398,399],{"class":329}," build\n",[319,401,403,405,408,411,414,417,420,423,425,428],{"class":321,"line":402},5,[319,404,342],{"class":325},[319,406,407],{"class":329}," bundle",[319,409,410],{"class":329}," --target",[319,412,413],{"class":329}," capacitor",[319,415,416],{"class":329}," --platform",[319,418,419],{"class":329}," ios",[319,421,422],{"class":329}," --runtime-version",[319,424,352],{"class":351},[319,426,427],{"class":355},"$OTALAN_RUNTIME_VERSION",[319,429,370],{"class":351},[319,431,433,435,438,441,443,446,448,451,453,456],{"class":321,"line":432},6,[319,434,342],{"class":325},[319,436,437],{"class":329}," publish",[319,439,440],{"class":329}," --channel",[319,442,352],{"class":351},[319,444,445],{"class":355},"$OTALAN_CHANNEL",[319,447,359],{"class":351},[319,449,450],{"class":329}," --release-notes",[319,452,352],{"class":351},[319,454,455],{"class":355},"$GIT_COMMIT",[319,457,370],{"class":351},[10,459,460,461,464],{},"Use ",[73,462,463],{},"--target expo"," for Expo lanes, and publish iOS and Android separately when their generated assets differ.",[40,466,468],{"id":467},"rollback-flow","Rollback flow",[10,470,471],{},"Rollback is server-side state change inside the same tuple:",[161,473,474,480,483,489],{},[20,475,476,477,212],{},"List bundles for the tuple with the dashboard or ",[73,478,479],{},"otalan bundles",[20,481,482],{},"Pick the last known-good bundle.",[20,484,485,486,212],{},"Roll back with the dashboard or ",[73,487,488],{},"otalan rollback",[20,490,491],{},"Restart or re-check the app and confirm it receives the rolled-back bundle.",[10,493,494,495,212],{},"Rollback cannot switch native binary compatibility. If the broken release needs a different native plugin, permission, or runtime version, ship a store update and use a new ",[73,496,112],{},[40,498,500],{"id":499},"common-migration-failure-modes","Common migration failure modes",[17,502,503,506,509,514,517,520,528],{},[20,504,505],{},"old installed binaries still point to CodePush because no new store binary was shipped",[20,507,508],{},"CodePush deployment names were mapped to the wrong Otalan channels",[20,510,511,513],{},[73,512,112],{}," was treated like a label instead of a native compatibility boundary",[20,515,516],{},"iOS and Android were published into the same lane even though their exported assets differ",[20,518,519],{},"the app shipped an OTA Publish Key instead of an OTA App Key",[20,521,522,523,291,525,527],{},"Expo update requests bypassed ",[73,524,286],{},[73,526,294],{}," and ran before the Otalan helper initialized",[20,529,530,531],{},"dashboard manual Expo upload omitted ",[73,532,533],{},".otalan\u002Fbundle\u002Fmanifest.json",[10,535,536],{},"Treat the migration as a release-platform change, not just a package rename.",[538,539,540],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":315,"searchDepth":373,"depth":373,"links":542},[543,544,545,546,547,548],{"id":42,"depth":339,"text":43},{"id":158,"depth":339,"text":159},{"id":227,"depth":339,"text":228},{"id":304,"depth":339,"text":305},{"id":467,"depth":339,"text":468},{"id":499,"depth":339,"text":500},"Plan a migration from Microsoft App Center CodePush to Otalan, with mapping notes, rollout guidance, and common failure modes.","md",{},true,"\u002Fen\u002Fdocs\u002Fintegration\u002Fapp-center-codepush",{"title":5,"description":549},"en\u002Fdocs\u002Fintegration\u002Fapp-center-codepush","qHA5qCQimjYYiOwlDSTrxQl7AoCbN-ec0SXr2eEM9Qo",[558],{"title":559,"path":560,"stem":561,"children":562,"page":580},"En","\u002Fen","en",[563],{"title":564,"path":565,"stem":566,"children":567,"page":-1,"description":569},"Introduction","\u002Fen\u002Fdocs","en\u002Fdocs\u002Findex",[568,570,581,591,601,607,627,647],{"title":564,"path":565,"stem":566,"description":569},"Understand what Otalan is, when to use it, and how the first safe OTA release flow works for Capacitor and Expo apps.",{"title":571,"path":572,"stem":573,"children":574,"page":580},"About","\u002Fen\u002Fdocs\u002Fabout","en\u002Fdocs\u002Fabout",[575],{"title":576,"path":577,"stem":578,"description":579},"Security and trust","\u002Fen\u002Fdocs\u002Fabout\u002Fsecurity-trust","en\u002Fdocs\u002Fabout\u002Fsecurity-trust","How Otalan keeps OTA releases controlled: scoped keys, bundle validation, exact matching, rollout controls, rollback, deletion behavior, and support contact.",false,{"title":582,"path":583,"stem":584,"children":585,"page":580},"Build","\u002Fen\u002Fdocs\u002Fbuild","en\u002Fdocs\u002Fbuild",[586],{"title":587,"path":588,"stem":589,"description":590},"Generate a bundle","\u002Fen\u002Fdocs\u002Fbuild\u002Fgenerate-bundle","en\u002Fdocs\u002Fbuild\u002Fgenerate-bundle","Prepare a release artifact that will pass Otalan's validation pipeline, including the extra considerations required for Expo-based publish flows.",{"title":592,"path":593,"stem":594,"children":595,"page":580},"Deploy","\u002Fen\u002Fdocs\u002Fdeploy","en\u002Fdocs\u002Fdeploy",[596],{"title":597,"path":598,"stem":599,"description":600},"Publish a release","\u002Fen\u002Fdocs\u002Fdeploy\u002Fpublish-release","en\u002Fdocs\u002Fdeploy\u002Fpublish-release","Submit a release through the dashboard, understand each field in the publish form, and operate the validation lifecycle with confidence.",{"title":602,"path":603,"stem":604,"children":605,"page":580},"Integration","\u002Fen\u002Fdocs\u002Fintegration","en\u002Fdocs\u002Fintegration",[606],{"title":5,"path":553,"stem":555,"description":549},{"title":608,"path":609,"stem":610,"children":611,"page":580},"Quick Start","\u002Fen\u002Fdocs\u002Fquick-start","en\u002Fdocs\u002Fquick-start",[612,617,622],{"title":613,"path":614,"stem":615,"description":616},"Capacitor quick start","\u002Fen\u002Fdocs\u002Fquick-start\u002Fcapacitor","en\u002Fdocs\u002Fquick-start\u002Fcapacitor","Wire the Otalan Capacitor runtime into an installed app so it can check for OTA updates, install compatible bundles, and confirm successful launches.",{"title":618,"path":619,"stem":620,"description":621},"Expo quick start","\u002Fen\u002Fdocs\u002Fquick-start\u002Fexpo","en\u002Fdocs\u002Fquick-start\u002Fexpo","Configure expo-updates with Otalan, add the Otalan Expo helper, and make the installed app ready to receive compatible OTA bundles.",{"title":623,"path":624,"stem":625,"description":626},"Publish in 5 minutes","\u002Fen\u002Fdocs\u002Fquick-start\u002Fquick-start","en\u002Fdocs\u002Fquick-start\u002Fquick-start","Create the first Otalan release path, publish a baseline bundle, verify one update, and prove rollback before widening rollout.",{"title":628,"path":629,"stem":630,"children":631,"page":580},"Tooling","\u002Fen\u002Fdocs\u002Ftooling","en\u002Fdocs\u002Ftooling",[632,637,642],{"title":633,"path":634,"stem":635,"description":636},"AI skill","\u002Fen\u002Fdocs\u002Ftooling\u002Fai-skill","en\u002Fdocs\u002Ftooling\u002Fai-skill","Copy a compact assistant skill for Otalan SDK setup, CLI publishing, and safe credential boundaries.",{"title":638,"path":639,"stem":640,"description":641},"CLI","\u002Fen\u002Fdocs\u002Ftooling\u002Fcli","en\u002Fdocs\u002Ftooling\u002Fcli","Learn when to use the Otalan CLI, what workflows it covers, and how it complements the dashboard instead of replacing the platform model.",{"title":643,"path":644,"stem":645,"description":646},"SDKs","\u002Fen\u002Fdocs\u002Ftooling\u002Fsdk","en\u002Fdocs\u002Ftooling\u002Fsdk","Understand the difference between the Capacitor and Expo integrations, and choose the runtime package that matches the way your mobile app actually updates.",{"title":648,"path":649,"stem":650,"children":651,"page":580},"Versions","\u002Fen\u002Fdocs\u002Fversions","en\u002Fdocs\u002Fversions",[652],{"title":653,"path":654,"stem":655,"description":656},"v1","\u002Fen\u002Fdocs\u002Fversions\u002Fv1","en\u002Fdocs\u002Fversions\u002Fv1","Supported runtimes, public limits, and post-v1 candidates for Otalan v1.",1780287525123]