Skip to content

Instantly share code, notes, and snippets.

@benchester
Created August 3, 2012 09:12
Show Gist options
  • Save benchester/3246142 to your computer and use it in GitHub Desktop.
Save benchester/3246142 to your computer and use it in GitHub Desktop.
script for uploading to Hockey from Jenkins
<?php
//==== USAGE =====
// -w "Workspace" - required
// -t "Title" - required
// -r "Release type" - optional (2:Alpha, 0:Beta, 1:Live)
// -n "Notes" - optional
// -v no arguments, enable for logging
//==== JENKINS SETUP ====
// 1) Install Jenkins Post build task plugin
// https://wiki.jenkins-ci.org/display/JENKINS/Post+build+task
// 2) Add post build task:
// cd ..
// php script.php -w ${WORKSPACE} -t ${JOB_NAME} -r "0" -n ${BUILD_ID} -v
// This should be your app token for all apps
$appToken = "INSERT_TOKEN_HERE";
//============================================
// Get cmd line args
//============================================
$opts = "w:"; // workspace - required
$opts .= "t:"; // title - required for creating a new app
$opts .= "r::"; // release type - optional
$opts .= "n::"; // notes - optional
$opts .= "v"; // verbose - for logging
$options = getopt($opts);
// get cmdline args
$workspace = $options[w];
// trim workspace path
$path_array = explode("/", $workspace);
$project_dir = $path_array[count($path_array) - 1];
$title = $options[t];
$release_type = $options[r];
$notes = $options[n];
// default to Beta
if (strlen($release_type) == 0)
{
$release_type = "0";
}
// default notes
if (strlen($notes) == 0)
{
$notes = "default note";
}
if ($options[v] == false)
{
$verbose = true;
}
else
{
$verbose = false;
}
//============================================
// Utility functions
//============================================
/*
* Recursive glob() (from php.net)
*/
function rglob($pattern='*', $flags = 0, $path='')
{
$paths = glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
$files = glob($path.$pattern, $flags);
foreach ($paths as $path) { $files = array_merge($files, rglob($pattern, $flags, $path)); }
return $files;
}
// Set up curl
function connectSetup($url)
{
global $appToken;
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($c, CURLOPT_HTTPHEADER, array("X-HockeyAppToken: ".$appToken));
return $c;
}
function getPlatform()
{
$platform = new stdClass();
foreach(rglob("*.ipa") as $ipa){}
foreach(rglob("*.apk") as $apk){}
if (!empty($ipa))
{
$platform = "iOS";
}
else if (!empty($apk))
{
$platform = "Android";
}
return $platform;
}
//============================================
// Pre-upload functions
//============================================
// returns target array for Android
// (Assuming there will never be multiple targets)
function getAndroidTarget()
{
$target = new stdClass();
foreach (rglob("*.apk") as $current)
{
// ignore unsigned and unaligned apks
if (!preg_match('/unaligned/', $current) && !preg_match('/unsigned/', $current))
{
$apk = $current;
$path_array = explode("/", $apk);
// get base folder containing apk
$basename = $path_array[0];
// get path to Android.manifest in base folder
foreach (rglob("AndroidManifest.xml", 0, $basename) as $manifest) {}
// read from .manifest
$handle = @fopen($manifest, "r");
if ($handle)
{
while (($buffer = fgets($handle, 4096)) !== false)
{
if (substr_count($buffer, "package") == 1)
{
$bundleID = str_replace("package=", "", $buffer);
$bundleID = str_replace("\"", "", $bundleID);
$bundleID = trim($bundleID);
$target = array(
'bundleID' =>$bundleID,
'apk' =>$apk,
'manifest' =>$manifest
);
}
}
}
}
}
return $target;
}
// returns array of target arrays for iOS
function getiOSTargets()
{
$targets = array();
foreach (rglob("*.plist") as $plist)
{
// use Info.plists in .app.dSYMs in build directory
if (preg_match('/build/', $plist) && preg_match('/Info/', $plist))
{
// read from .plist
$handle = @fopen($plist, "r");
if ($handle)
{
while (($buffer = fgets($handle, 4096)) !== false) {
// if we've found the bundle id, get the next line
if (substr_count($buffer, "CFBundleIdentifier") == 1)
{
if (($buffer = fgets($handle, 4096)) !== false)
{
// trim the bundleID
$bundleID = str_replace("<string>", "", $buffer);
$bundleID = str_replace("</string>", "", $bundleID);
$bundleID = trim($bundleID);
// if the bundleID is unique and is real (check for com)
if (!in_array($bundleID, $targets) && preg_match('/com/', $bundleID))
{
$bundleID = str_replace("com.apple.xcode.dsym.","",$bundleID);
// Trim plist path to just app name
$path_array = explode("/", $plist);
foreach ($path_array as $string)
{
if (substr_count($string, ".app.dSYM") == 1)
{
$baseString = str_replace(".app.dSYM", "", $string);
}
}
// find matching ipa and dSYM.zip files
// If there are multiple matches, this will use the last match
// Match assumes there will be a - directly after baseString
// e.g. MYO2Ref-Release-3.0-dSYM.zip, MyO2-Release-3.0.ipa
foreach (rglob($baseString."-*dSYM.zip") as $dsym) {}
foreach (rglob($baseString."-*.ipa") as $ipa) {}
array_push($targets,
array(
'bundleID' =>$bundleID,
'dsym' =>$dsym,
'ipa' => $ipa,
'plist' => $plist
));
}
}
}
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fclose($handle);
}
}
}
//var_dump($targets);
return $targets;
}
// Returns array of apps from HockeyApp
function getApps()
{
$url = "https://rink.hockeyapp.net/api/2/apps/";
$c = connectSetup($url);
$result = curl_exec($c);
$json = json_decode($result);
curl_close($c);
return $json->apps;
}
// Checks whether bundleID is in apps array (UNUSED)
// If bundleID exists, return app, otherwise return stdClass
// returned object contains bundle, platform, publicid, title [etc]
function appForBundleID($bundleID, $apps)
{
$return = new stdClass();
foreach ($apps as $app)
{
if (strcmp($app->bundle_identifier, $bundleID) == 0)
{
$return = $app;
}
}
return $return;
}
//============================================
// Upload functions
//============================================
// Creates a new app (UNUSED)
// platform = iOS, Android, Mac OS, Windows Phone, Custom
// release_type = 2:Alpha, 0:Beta, 1:Live
function newApp($title, $bundle_identifier, $platform, $release_type)
{
$url = "https://rink.hockeyapp.net/api/2/apps/new";
$c = connectSetup($url);
$data = array(
'title' => $title,
'bundle_identifier' => $bundle_identifier,
'platform' => $platform,
'release_type' => $release_type
);
curl_setopt($c, CURLOPT_POSTFIELDS, $data);
$result = curl_exec($c);
curl_close($c);
$json = json_decode($result);
return $json;
}
// Uploads with notes, ipa, and dsym
function uploadApp($ipa, $dsym, $notes)
{
$url = "https://rink.hockeyapp.net/api/2/apps/";
$c = connectSetup($url);
$data = array(
'status' =>'2',
'notify' =>'1',
'notes' => $notes,
'notes_type' => '0',
'ipa' => "@".$ipa
//'dsym' => "@".$dsym
);
$dsym_array = (array)$dsym;
if (!empty($dsym_array))
{
array_push($data, $data['dsym']="@".$dsym);
}
curl_setopt($c, CURLOPT_POSTFIELDS, $data);
$result = curl_exec($c);
curl_close($c);
$json = json_decode($result);
return $json;
}
//============================================
// Upload to Hockey
//============================================
// change current directory to project directory
chdir("../".$project_dir);
if ($verbose)
echo "Current working directory: ".getcwd()."\n";
// get apps on Hockey
$apps = getApps();
// get platform
$platform = getPlatform();
if ($verbose)
echo "Platform detected: ".$platform."\n";
// For iOS
if (strcmp($platform, "iOS")==0)
{
// get targets in project directory
$targets = getiOSTargets();
if ($verbose){
echo count($targets)." iOS targets found:\n";
print_r($targets);}
// iterate through targets
foreach ($targets as $currentTarget) {
$bundleID = $currentTarget['bundleID'];
$ipa = $currentTarget['ipa'];
$dsym = $currentTarget['dsym'];
// check bundleID against Hockey apps (UNUSED)
//$app = appForBundleID($bundleID, $apps);
echo "Uploading app with bundleID: ".$bundleID."\n";
echo "JSON response: \n";
print_r(uploadApp($ipa, $dsym, $notes));
echo "\n";
}
}
else if (strcmp($platform, "Android")==0)
{
// get target in project directory
$target = getAndroidTarget();
if ($verbose){
echo "Android target:\n";
print_r($target)."\n";}
$bundleID = $target['bundleID'];
$apk = $target['apk'];
// no dSYM on android
$dsym = new stdClass();
// check bundleID against Hockey apps (UNUSED)
//$app = appForBundleID($bundleID, $apps);
echo "Uploading app with bundleID: ".$bundleID."\n";
echo "JSON response:\n";
print_r(uploadApp($apk, $dsym, $notes));
echo "\n";
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment