From 606401ab537a9adefd899bd627fad96171a7e1d0 Mon Sep 17 00:00:00 2001 From: Ronak P Patel Date: Wed, 23 Feb 2022 08:16:54 +0000 Subject: [PATCH 1/7] Resolve "Double slash in build url returned by OTA server" --- index.php | 2 +- src/Helpers/Build.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index 68d4339..0a159e1 100644 --- a/index.php +++ b/index.php @@ -60,5 +60,5 @@ $app = new CmOta($logger); $app - ->setConfig( 'basePath', $protocol.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME']) ) + ->setConfig( 'basePath', rtrim( $protocol.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME']) , "/" ) ) ->run(); diff --git a/src/Helpers/Build.php b/src/Helpers/Build.php index 307a76c..a2e8e70 100644 --- a/src/Helpers/Build.php +++ b/src/Helpers/Build.php @@ -97,7 +97,7 @@ $this->url = $this->_getUrl('', Flight::cfg()->get('buildsPath')); } else { $this->url = $this->_getUrl('', Flight::cfg()->get('basePath') . substr($physicalPath, $position)); - } + } $this->changelogUrl = $this->_getChangelogUrl(); if (!file_exists($this->filePath . '.prop')) { $prop = ""; -- GitLab From c5cdf16e8bef182302d0cc0a7c59ad688c6094ed Mon Sep 17 00:00:00 2001 From: Ronak P Patel Date: Wed, 23 Feb 2022 08:17:23 +0000 Subject: [PATCH 2/7] Resolve "Update README.md about builds structure" --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index d8d1bc4..4f69b13 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,21 @@ then finally visit http://localhost/ to see the REST Server up and running. - Full builds should be uploaded into `builds/full` directory. - Delta builds should be uploaded into `builds/delta` directory. +### Folder Structure for Builds + +All the builds , props, md5sum and other files of a device and channel needs to be in common folder. You must follow below folder structure. The same folder name must be used in API. + +```shell +builds/full// +``` + +For Example, All builds and other files of star2lte for stable channel will be in `/builds/full/stable/star2lte` + +### Device and Channel fields are now mandatory in API + +The fields `device` and `channel` are mandatory and `incremental` is an optional field in an API (which is used to get the builds). + + ## How to specify allowed migration paths - You can specify migration rules (whether an upgrade from a version to a version is allowed or not) by creating a migration_paths.json file in your build directory -- GitLab From a8ea24be168ed45c00f96c42eba8f25e207a696e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnau=20V=C3=A0zquez?= Date: Fri, 25 Feb 2022 17:37:58 +0000 Subject: [PATCH 3/7] Fix registry path --- .gitlab-ci.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c56a73e..082ccae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,6 @@ image: docker:19.03.1 # improved performance. variables: DOCKER_DRIVER: overlay2 - CONTAINER_IMAGE: registry.gitlab.e.foundation:5000/e/os/lineageota PUBLISH_USER: root PUBLISH_URL: ota.ecloud.global DOCKER_TLS_CERTDIR: "/certs" @@ -15,11 +14,11 @@ services: build: stage: build script: - - docker pull $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME || true - - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.e.foundation:5000 - - docker build --cache-from $CONTAINER_IMAGE:latest -t $CONTAINER_IMAGE -t $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME . - - docker push $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME - - if [ "${CI_COMMIT_REF_NAME}" = master ] ; then docker push $CONTAINER_IMAGE:latest ; fi + - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME || true + - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY + - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME . + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME + - if [ "${CI_COMMIT_REF_NAME}" = master ] ; then docker push $CI_REGISTRY_IMAGE:latest ; fi tags: - generic_privileged -- GitLab From d89b7f8d44c6a02d81b3322a3d15a4364d61513e Mon Sep 17 00:00:00 2001 From: Ronak P Patel Date: Mon, 7 Mar 2022 15:03:54 +0000 Subject: [PATCH 4/7] phpUnit test --- README.md | 17 +++++++++ composer.json | 3 ++ src/Helpers/Builds.php | 3 +- tests/ApiTest.php | 81 +++++++++++++++++++++++++++++++++++++++++ tests/BuildTest.php | 82 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100755 tests/ApiTest.php create mode 100755 tests/BuildTest.php diff --git a/README.md b/README.md index 4f69b13..beabd27 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,23 @@ I am not sure how much this may help anyway, but this must be used as an extreme Feel free to use this [simple script](https://github.com/julianxhokaxhiu/LineageOTAUnitTest) made with NodeJS. Instructions are included. +## LineageOTA Unit Testing + +You can cerate new `{FILENAME}Test.php` in `tests` folder. Use `vendor/bin/phpunit tests` command in CLI to run the tests. +In every test file, in `setUp()` function, you must set below details for valid test results : + +```properties +$this->romType = '{device}'; +$this->deviceType = '{type}'; +$this->incrementalVersion = '{incr}'; + +{device} - Device name +{type} - Build type +{incr} - Incremental version + +``` + + ## How to integrate within your ROM In order to integrate this REST Server within your ROM you have two possibilities: you can make use of the `build.prop` ( highly suggested ), or you can patch directly the `android_packages_apps_CMUpdater` package ( not suggested ). diff --git a/composer.json b/composer.json index 6d66bce..61e6358 100644 --- a/composer.json +++ b/composer.json @@ -57,5 +57,8 @@ }, "scripts": { "serve": "php -S 0.0.0.0:8000" + }, + "require-dev": { + "phpunit/phpunit": "^9" } } diff --git a/src/Helpers/Builds.php b/src/Helpers/Builds.php index b85773b..fb2ce2b 100644 --- a/src/Helpers/Builds.php +++ b/src/Helpers/Builds.php @@ -216,4 +216,5 @@ } $this->logger->debug('Total execution time of getBuilds in seconds'); } - } + +} \ No newline at end of file diff --git a/tests/ApiTest.php b/tests/ApiTest.php new file mode 100755 index 0000000..07990aa --- /dev/null +++ b/tests/ApiTest.php @@ -0,0 +1,81 @@ +router = new Router(); + $this->request = new Request(); + $this->dispatcher = new Dispatcher(); + + $this->romType = 'dev'; + $this->deviceType = 'Amber'; + $this->incrementalVersion = '0b971351f3'; + + $this->customData = array( + 'params' => array( + 'device' => $this->deviceType, + 'channels' => array( + $this->romType, + ), + 'source_incremental' => $this->incrementalVersion, + ), + ); + + $this->logger = new Logger('main'); + $this->getCmotaInstance(); + } + + public function getCmotaInstance(){ + Flight::register('cmota', '\JX\CmOta\CmOta',array($this->logger)); + return Flight::cmota(); + } + public function getBuildInstance(){ + Flight::register('builds', '\JX\CmOta\Helpers\Builds',array($this->logger)); + return Flight::builds(); + } + + public function testCheckIfBuildValid() { + $builds = $this->getBuildInstance(); + $builds->setPostData($this->customData); + $records = $builds->get(); + $this->assertGreaterThan( 0, sizeof($records),'Builds are not valid!'); + } + + public function testCheckIfCorrectAttribute() { + $attributes = array("timestamp" , "md5sum" , "url"); // You can change attributes here + + $builds = $this->getBuildInstance(); + $builds->setPostData($this->customData); + $records = $builds->get(); + if(sizeof($records)){ + foreach ($attributes as $attribute) { + $this->assertArrayHasKey($attribute, $records[0], "Builds doesn't contains '".$attribute."' as key"); + } + }else{ + $this->assertFalse('Builds'); + } + } + + +} \ No newline at end of file diff --git a/tests/BuildTest.php b/tests/BuildTest.php new file mode 100755 index 0000000..71a9b61 --- /dev/null +++ b/tests/BuildTest.php @@ -0,0 +1,82 @@ +router = new Router(); + $this->request = new Request(); + $this->dispatcher = new Dispatcher(); + + $this->romType = 'dev'; + $this->deviceType = 'Amber'; + $this->incrementalVersion = '0b971351f3'; + + $this->customData = array( + 'params' => array( + 'device' => $this->deviceType, + 'channels' => array( + $this->romType, + ), + 'source_incremental' => $this->incrementalVersion, + ), + ); + + $this->logger = new Logger('main'); + $this->getCmotaInstance(); + } + + public function getCmotaInstance(){ + Flight::register('cmota', '\JX\CmOta\CmOta',array($this->logger)); + return Flight::cmota(); + } + public function getBuildInstance(){ + Flight::register('builds', '\JX\CmOta\Helpers\Builds',array($this->logger)); + return Flight::builds(); + } + + // newer than (timestamp check) + public function testCheckTimestamp() { + $builds = $this->getBuildInstance(); + $builds->setPostData($this->customData); + $records = $builds->get(); + $currentTimestamp = strtotime(date('Y-m-d H:i:s')); + + foreach ($records as $record) { + $this->assertGreaterThan($record['timestamp'], $currentTimestamp, 'Build is not new!'); + } + + } + + // check Android version if it is same or not + public function testCheckAndroidVersion() { + $builds = $this->getBuildInstance(); + $builds->setPostData($this->customData); + $records = $builds->get(); + $expectedVersion = '0.16'; + foreach ($records as $record) { + $this->assertEquals($expectedVersion , $record['version'] ,$expectedVersion.' & '.$record['version'] .' Android Versions are different!'); + } + } + +} \ No newline at end of file -- GitLab From 322f59a667f413c409760f0c32a55cf9c3667af8 Mon Sep 17 00:00:00 2001 From: akhil Date: Wed, 9 Mar 2022 13:08:42 +0530 Subject: [PATCH 5/7] Added incremental rollout feature for builds --- README.md | 24 +++++++++++++++++++++ src/CmOta.php | 39 +++++++++++++++++----------------- src/Helpers/Build.php | 48 +++++++++++++++++++++++++++++++++++++++++- src/Helpers/Builds.php | 4 ++-- 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index beabd27..542c44d 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,30 @@ The fields `device` and `channel` are mandatory and `incremental` is an optional - The contents should be read as: Upgrades from major version 9 to major version 9 and to major version 10 are allowed. Upgrades from major version 10 to major version 10 and to major version 11 are allowed. - Current version is always available as an upgrade (if a more recent build is found), even if file is not available (for backwards compatibility too). +## How to set incremental rollout percentage in build config file + +- You can specify incremental rollout rules by adding values in `config.json` file. You can add the file only if you want percentage incremental rollout for a build. +- The format of file name will be : `.zip.config.json` + +```shell +$ cd builds/full/test/guacamoleb/ +$ tree +. +├── e.x.xx-xxyyzz-guacamoleb.zip # the full ROM zip file +└── e.x.xx-xxyyzz-guacamoleb.zip.config.json # the ROM build.config.json file +``` + +- E.g. : Suppose builds for "guacamoleb" devices are stored at "/mnt/rom/builds/full/test/guacamoleb". The file "/mnt/rom/builds/full/test/guacamoleb/e.x.xx-xxyyzz-guacamoleb.zip.config.json" should then have contents like: +```json + { + "rollout": { + "percentage": 20 + } + } +``` +- The `percentage` value must be between 0 to 100. Do not add `%` sign after the value. +- The `config.json` file is not mandatory. In case it does not exist, a build will consider a 100% rollout percentage. + ### ONLY for LineageOS 15.x and newer If you are willing to use this project on top of your LineageOS 15.x ( or newer ) ROM builds, you may have noticed that the file named `build.prop` have been removed inside your ZIP file, and has been instead integrated within your `system.new.dat` file, which is basically an ext4 image ( you can find out more here: https://source.android.com/devices/tech/ota/block ). diff --git a/src/CmOta.php b/src/CmOta.php index 8df8c54..d04591f 100644 --- a/src/CmOta.php +++ b/src/CmOta.php @@ -110,25 +110,26 @@ // LineageOS new API Flight::route('/api/v1/@deviceType/@romType(/@incrementalVersion)', function ( $deviceType, $romType, $incrementalVersion ){ - Flight::builds()->setPostData( - array( - 'params' => array( - 'device' => $deviceType, - 'channels' => array( - $romType, - ), - 'source_incremental' => $incrementalVersion, - ), - ) - ); - - $ret = array( - 'id' => null, - 'response' => Flight::builds()->get(), - 'error' => null - ); - - Flight::json($ret); + Flight::builds()->setPostData( + array( + 'params' => array( + 'device' => $deviceType, + 'channels' => array( + $romType, + ), + 'source_incremental' => $incrementalVersion, + ), + ) + ); + + $ret = array( + 'id' => null, + 'response' => Flight::builds()->get(), + 'error' => null + ); + + Flight::json($ret); + }); } diff --git a/src/Helpers/Build.php b/src/Helpers/Build.php index a2e8e70..f3061a5 100644 --- a/src/Helpers/Build.php +++ b/src/Helpers/Build.php @@ -38,6 +38,7 @@ private $incremental = ''; private $filePath = ''; private $buildProp = ''; + private $confProp = ''; private $uid = null; private $size = ''; private $logger = null; @@ -82,6 +83,7 @@ // - builds/CURRENT_ZIP_FILE.zip/system/build.prop // - builds/CURRENT_ZIP_FILE.zip.prop ( which must exist ) $this->buildProp = explode("\n", @file_get_contents($this->getPropFilePath())); + $this->confProp = $this->getConfProp(); // Try to fetch build.prop values. In some cases, we can provide a fallback, in other a null value will be given $this->timestamp = intval($this->getBuildPropValue('ro.build.date.utc') ?? filemtime($this->filePath)); $this->incremental = $this->getBuildPropValue('ro.build.version.incremental') ?? ''; @@ -510,4 +512,48 @@ { return is_callable($func) && false === stripos(ini_get('disable_functions'), $func); } - } + + /** + * Check if the current build is valid with multiple conditions + * @param type $params The params dictionary inside the current POST request + * @return boolean True if valid, False if not. + */ + public function includeInResults($params) + { + return $this->isValid($params) && $this->checkRollout(); + } + + /** + * Return the correct config file path (depending of version) + * @return string + */ + private function getConfigFilePath() + { + return file_exists($this->filePath . '.config.json') ? $this->filePath . '.config.json' : ''; + } + + /** + * Return the all values from config file path + * @return JSON if valid otherwise null + */ + private function getConfProp(){ + $configFilePath = $this->getConfigFilePath(); + return ($configFilePath) ? json_decode( file_get_contents($configFilePath) , true) : array(); + } + + /** + * Return if rollout successful or not for a build + * @return boolean + */ + public function checkRollout() + { + $rolloutpercentage = isset($this->confProp['rollout']['percentage']) ? $this->confProp['rollout']['percentage'] : 100; + if ($rolloutpercentage >= 0 && $rolloutpercentage < 100) { + $rand_number = rand(0, 100); + if ($rand_number > $rolloutpercentage) { + return false; + } + } + return true; + } + } \ No newline at end of file diff --git a/src/Helpers/Builds.php b/src/Helpers/Builds.php index fb2ce2b..4fa8780 100644 --- a/src/Helpers/Builds.php +++ b/src/Helpers/Builds.php @@ -203,8 +203,8 @@ } else { $build = new Build($file, $dir, $this->logger); } - - if ($build->isValid($this->postData['params'])) { + + if ($build->includeInResults($this->postData['params'])) { array_push($this->builds, $build); if (!empty($this->postData['params']) && strcmp($this->postData['params']['source_incremental'], $build->getIncremental()) == 0) { $this->currentBuild = $build; -- GitLab From b0ea21c8c6ec1fcad6826c441e9953ed99d9ed49 Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 9 Mar 2022 16:11:18 +0000 Subject: [PATCH 6/7] Check build size before adding to builds array in response --- src/Helpers/Build.php | 2 +- src/Helpers/Builds.php | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Helpers/Build.php b/src/Helpers/Build.php index f3061a5..fd97512 100644 --- a/src/Helpers/Build.php +++ b/src/Helpers/Build.php @@ -520,7 +520,7 @@ */ public function includeInResults($params) { - return $this->isValid($params) && $this->checkRollout(); + return $this->isValid($params) && $this->checkRollout() && $this->getSize() > 0; } /** diff --git a/src/Helpers/Builds.php b/src/Helpers/Builds.php index 4fa8780..f4cdbe6 100644 --- a/src/Helpers/Builds.php +++ b/src/Helpers/Builds.php @@ -206,10 +206,12 @@ if ($build->includeInResults($this->postData['params'])) { array_push($this->builds, $build); - if (!empty($this->postData['params']) && strcmp($this->postData['params']['source_incremental'], $build->getIncremental()) == 0) { - $this->currentBuild = $build; - $this->logger->info($build->getIncremental().' is the current build'); - } + } + + $sourceIncremental = isset($this->postData['params']['source_incremental']) ? $this->postData['params']['source_incremental'] : NULL; + if ($build->isValid($this->postData['params']) && $sourceIncremental && strcmp($sourceIncremental, $build->getIncremental()) === 0) { + $this->currentBuild = $build; + $this->logger->info($build->getIncremental().' is the current build'); } } } -- GitLab From a17dcd90b903cf6454162cc819bd25da76f27a71 Mon Sep 17 00:00:00 2001 From: Ronak P Patel Date: Fri, 11 Mar 2022 09:17:59 +0000 Subject: [PATCH 7/7] Bug-Fix: issue with 0 percentage rollout --- src/Helpers/Build.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Helpers/Build.php b/src/Helpers/Build.php index fd97512..e873d83 100644 --- a/src/Helpers/Build.php +++ b/src/Helpers/Build.php @@ -547,13 +547,14 @@ */ public function checkRollout() { - $rolloutpercentage = isset($this->confProp['rollout']['percentage']) ? $this->confProp['rollout']['percentage'] : 100; - if ($rolloutpercentage >= 0 && $rolloutpercentage < 100) { - $rand_number = rand(0, 100); - if ($rand_number > $rolloutpercentage) { - return false; - } + $rolloutpercentage = isset($this->confProp['rollout']['percentage']) ? (int) $this->confProp['rollout']['percentage'] : 100; + if ($rolloutpercentage <= 0 || $rolloutpercentage > 100) { + return TRUE; + } + $rand_number = rand(1, 100); + if ($rand_number > $rolloutpercentage) { + return FALSE; } - return true; + return TRUE; } } \ No newline at end of file -- GitLab