package runci; import runci.Config.*; import runci.System.*; import sys.io.File; import sys.FileSystem; import haxe.*; using StringTools; class Deployment { static var S3_HXBUILDS_ADDR(default, null) = 's3://hxbuilds/builds/haxe'; static var gitInfo(get, null):{repo:String, branch:String, commit:String, timestamp:Float, date:String}; static function get_gitInfo() return if (gitInfo != null) gitInfo else gitInfo = { repo: commandResult("git", ["config", "--get", "remote.origin.url"]).stdout.trim(), branch: commandResult("git", ["rev-parse", "--abbrev-ref", "HEAD"]).stdout.trim(), commit: commandResult("git", ["rev-parse", "HEAD"]).stdout.trim(), timestamp: Std.parseFloat(commandResult("git", ["show", "-s", "--format=%ct", "HEAD"]).stdout), date: { var gitTime = commandResult("git", ["show", "-s", "--format=%ct", "HEAD"]).stdout; var tzd = { var z = Date.fromTime(0); z.getHours() * 60 * 60 * 1000 + z.getMinutes() * 60 * 1000; } // make time in the UTC time zone var time = Date.fromTime(Std.parseFloat(gitTime) * 1000 - tzd); DateTools.format(time, "%Y-%m-%dT%H:%M:%SZ"); } } static function isDeployNightlies() { return Sys.getEnv("DEPLOY_NIGHTLIES") != null && (gitInfo.branch == "development" || gitInfo.branch == "master" || gitInfo.branch == "deploy-test"); } static function deployBintray():Void { if ( Sys.getEnv("BINTRAY") != null && Sys.getEnv("BINTRAY_USERNAME") != null && Sys.getEnv("BINTRAY_API_KEY") != null ) { // generate bintray config var tpl = new Template(File.getContent("extra/bintray.tpl.json")); var compatDate = ~/[^0-9]/g.replace(gitInfo.date, ""); var json = tpl.execute({ packageSubject: { var sub = Sys.getEnv("BINTRAY_SUBJECT"); sub != null ? sub : Sys.getEnv("BINTRAY_USERNAME"); }, os: systemName.toLowerCase(), versionName: '${haxeVer}+${compatDate}.${gitInfo.commit.substr(0,7)}', versionDesc: "Automated CI build.", gitRepo: gitInfo.repo, gitBranch: gitInfo.branch, gitCommit: gitInfo.commit, gitDate: gitInfo.date, }); var path = "extra/bintray.json"; File.saveContent("extra/bintray.json", json); infoMsg("saved " + FileSystem.absolutePath(path) + " with content:"); Sys.println(json); } } static function isDeployApiDocsRequired() { return Sys.getEnv("DEPLOY_API_DOCS") != null && ( gitInfo.branch == "development" || switch(Sys.getEnv("TRAVIS_TAG")) { // TODO: there's no Travis anymore, we might want to change this for GH actions case null, _.trim() => "": false; case tag: true; } ); } /** Deploy doc to api.haxe.org. */ static function deployApiDoc():Void { changeDirectory(repoDir); runCommand("make", ["xmldoc"]); File.saveContent("extra/doc/info.json", Json.stringify({ "commit": gitInfo.commit, "branch": gitInfo.branch, })); switch (Sys.getEnv("GHP_REMOTE")) { // should be in the form of https://token@github.com/account/repo.git case null: infoMsg('Missing GHP_REMOTE, skip api doc deploy.'); case remoteRepo: var localRepo = "extra/api.haxe.org"; runCommand("git", ["clone", remoteRepo, localRepo]); runCommand("haxe", ["--cwd", localRepo, "--run", "ImportXml", FileSystem.absolutePath("extra/doc")]); } } static function cleanup32BitDlls() { infoMsg('Cleaning up the 32-bit DLLS'); cleanup32BitDll('zlib1.dll'); } static function cleanup32BitDll(name:String) { var cygRoot = Sys.getEnv("CYG_ROOT"); if (cygRoot != null) { while (true) { var proc = new sys.io.Process('$cygRoot/bin/bash', ['-lc', '/usr/bin/cygpath -w "`which $name`"']); var out = proc.stdout.readAll().toString().trim(); var err = proc.stderr.readAll().toString().trim(); if (proc.exitCode() == 0) { if (!is64BitDll(out)) { infoMsg('Deleting the file $out because it is a 32-bit DLL'); sys.FileSystem.deleteFile(out); } else { break; } } else { infoMsg('Error while getting the cygpath for $name: $out\n$err'); break; // no more dlls } } } else { var path = Sys.getEnv('PATH').split(';'); for (base in path) { var fullPath = '$base/$name'; if (sys.FileSystem.exists(fullPath) && !is64BitDll(fullPath)) { infoMsg('Deleting the file $fullPath because it is a 32-bit DLL'); sys.FileSystem.deleteFile(fullPath); } } } } static function is64BitDll(path:String) { if (!sys.FileSystem.exists(path)) { throw 'The DLL at path $path was not found'; } var file = sys.io.File.read(path); if (file.readByte() != 'M'.code || file.readByte() != 'Z'.code) { throw 'The DLL at path $path is invalid: Invalid MZ magic header'; } file.seek(0x3c, SeekBegin); var peSigOffset = file.readInt32(); file.seek(peSigOffset, SeekBegin); if (file.readByte() != 'P'.code || file.readByte() != 'E'.code || file.readByte() != 0 || file.readByte() != 0) { throw 'Invalid PE header signature: PE expected'; } // coff header file.readString(20); // pe header var peKind = file.readUInt16(); file.close(); switch(peKind) { case 0x20b: // 64 bit return true; case 0x10b: // 32 bit return false; case 0x107: // rom return false; case _: throw 'Unknown PE header kind $peKind'; } } static function fileExtension(file:String) { file = haxe.io.Path.withoutDirectory(file); var idx = file.indexOf('.'); if (idx < 0) { return ''; } else { return file.substr(idx); } } /** Deploy source package to ppa:haxe/snapshots. */ static function deployPPA():Void { if ( gitInfo.branch == "development" && Sys.getEnv("DEPLOY") != null && Sys.getEnv("haxeci_decrypt") != null ) { // setup deb info runCommand("git config --global user.name \"${DEBFULLNAME}\""); runCommand("git config --global user.email \"${DEBEMAIL}\""); // setup haxeci_ssh runCommand("openssl aes-256-cbc -k \"$haxeci_decrypt\" -in extra/haxeci_ssh.enc -out extra/haxeci_ssh -d"); runCommand("chmod 600 extra/haxeci_ssh"); runCommand("ssh-add extra/haxeci_ssh"); // setup haxeci_sec.gpg runCommand("openssl aes-256-cbc -k \"$haxeci_decrypt\" -in extra/haxeci_sec.gpg.enc -out extra/haxeci_sec.gpg -d"); runCommand("gpg --allow-secret-key-import --import extra/haxeci_sec.gpg"); runCommand("sudo apt-get install devscripts git-buildpackage ubuntu-dev-tools dh-make -y"); var compatDate = ~/[^0-9]/g.replace(gitInfo.date, ""); var SNAPSHOT_VERSION = '${haxeVerFull}+1SNAPSHOT${compatDate}+${gitInfo.commit.substr(0,7)}'; runCommand('cp out/haxe*_src.tar.gz "../haxe_${SNAPSHOT_VERSION}.orig.tar.gz"'); changeDirectory(".."); runCommand("git clone https://github.com/HaxeFoundation/haxe-debian.git"); changeDirectory("haxe-debian"); runCommand("git checkout upstream"); runCommand("git checkout next"); runCommand('gbp import-orig "../haxe_${SNAPSHOT_VERSION}.orig.tar.gz" -u "${SNAPSHOT_VERSION}" --debian-branch=next'); runCommand('dch -v "1:${SNAPSHOT_VERSION}-1" --urgency low "snapshot build"'); runCommand("debuild -S -sa"); runCommand("backportpackage -d yakkety --upload ${PPA} --yes ../haxe_*.dsc"); runCommand("backportpackage -d xenial --upload ${PPA} --yes ../haxe_*.dsc"); runCommand("backportpackage -d vivid --upload ${PPA} --yes ../haxe_*.dsc"); runCommand("backportpackage -d trusty --upload ${PPA} --yes ../haxe_*.dsc"); runCommand("git checkout debian/changelog"); runCommand("git merge -X ours --no-edit origin/next-precise"); runCommand('dch -v "1:${SNAPSHOT_VERSION}-1" --urgency low "snapshot build"'); runCommand("debuild -S -sa"); runCommand("backportpackage -d precise --upload ${PPA} --yes ../haxe_*.dsc"); } } static var haxeVer(default, never) = { var haxe_ver = haxe.macro.Compiler.getDefine("haxe_ver"); switch (haxe_ver.split(".")) { case [major]: major; case [major, minor] if (minor.length == 1): '${major}.${minor}'; case [major, minor] if (minor.length > 1): var patch = Std.parseInt(minor.substr(1)); var minor = minor.charAt(0); '${major}.${minor}.${patch}'; case _: throw haxe_ver; } } static var haxeVerFull(default, never) = { var ver = haxeVer.split("."); while (ver.length < 3) { ver.push("0"); } ver.join("."); } static public function deploy():Void { if (isDeployApiDocsRequired()) { deployApiDoc(); } else { infoMsg("Not deploying API doc"); } } }