Last active
August 8, 2018 13:10
-
-
Save mariotaku/7a0c51955d14def2fa0e to your computer and use it in GitHub Desktop.
Sign and upload compiled apk to Github releases automatically using Travis CI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
language: android | |
android: | |
components: | |
# Uncomment the lines below if you want to | |
# use the latest revision of Android SDK Tools | |
- platform-tools | |
- tools | |
# The BuildTools version used by your project | |
- build-tools-22.0.0 | |
# The SDK version used to compile your project | |
- android-22 | |
# Additional components | |
- extra-google-google_play_services | |
- extra-google-m2repository | |
- extra-android-m2repository | |
# Specify at least one system image, | |
# if you need to run emulator(s) during your tests | |
# - sys-img-armeabi-v7a-android-19 | |
# - sys-img-x86-android-17 | |
script: ./gradlew build | |
before_install: | |
- sudo apt-get update -qq | |
- sudo apt-get install -y python2.7 | |
after_success: ./scripts/travis_upload_release_to_github.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Twidere - Twitter client for Android | |
* | |
* Copyright (C) 2012-2015 Mariotaku Lee <[email protected]> | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
apply plugin: 'com.android.application' | |
/** | |
* Gradle script for signing applications | |
* Just apply this script to your application build.gradle file. | |
* DON'T FORGET TO IGNORE signing.properties FROM YOUR VERSION CONTROL!!! | |
* | |
* @author Mariotaku Lee <[email protected]> | |
*/ | |
android { | |
signingConfigs { | |
debug { | |
File signingPropFile = rootProject.file('signing.properties') | |
if (signingPropFile.exists()) { | |
Properties signingProp = new Properties() | |
signingProp.load(signingPropFile.newDataInputStream()) | |
storeFile file(signingProp.get("debug.storeFile")) | |
storePassword signingProp.get("debug.storePassword") | |
keyAlias signingProp.get("debug.keyAlias") | |
keyPassword signingProp.get("debug.keyPassword") | |
} else if (System.getenv('DEBUG_KEYSTORE_BASE64') != null) { | |
storeFile decodeKeyStoreFileFromBase64Env('DEBUG_KEYSTORE_BASE64') | |
storePassword System.getenv('DEBUG_KEYSTORE_PASSWORD') | |
keyAlias System.getenv('DEBUG_KEYSTORE_KEY_ALIAS') | |
keyPassword System.getenv('DEBUG_KEYSTORE_KEY_PASSWORD') | |
} | |
} | |
release { | |
File signingPropFile = rootProject.file('signing.properties') | |
if (signingPropFile.exists()) { | |
Properties signingProp = new Properties() | |
signingProp.load(signingPropFile.newDataInputStream()) | |
storeFile file(signingProp.get("release.storeFile")) | |
storePassword signingProp.get("release.storePassword") | |
keyAlias signingProp.get("release.keyAlias") | |
keyPassword signingProp.get("release.keyPassword") | |
} else if (System.getenv('RELEASE_KEYSTORE_BASE64') != null) { | |
storeFile decodeKeyStoreFileFromBase64Env('RELEASE_KEYSTORE_BASE64') | |
storePassword System.getenv('RELEASE_KEYSTORE_PASSWORD') | |
keyAlias System.getenv('RELEASE_KEYSTORE_KEY_ALIAS') | |
keyPassword System.getenv('RELEASE_KEYSTORE_KEY_PASSWORD') | |
} | |
} | |
} | |
buildTypes { | |
debug { | |
if (rootProject.file('signing.properties').exists() | |
|| System.getenv('DEBUG_KEYSTORE_BASE64') != null) { | |
signingConfig signingConfigs.debug | |
} | |
} | |
release { | |
if (rootProject.file('signing.properties').exists() | |
|| System.getenv('RELEASE_KEYSTORE_BASE64') != null) { | |
signingConfig signingConfigs.release | |
} | |
} | |
} | |
} | |
def decodeKeyStoreFileFromBase64Env(String name) { | |
String keyStoreBase64 = System.getenv(name) | |
if (keyStoreBase64 == null) return null | |
File tempKeyStoreFile = File.createTempFile("tmp_ks_", ".jks", File.createTempDir()) | |
FileOutputStream fos = null | |
try { | |
fos = new FileOutputStream(tempKeyStoreFile) | |
fos.write(keyStoreBase64.decodeBase64()) | |
fos.flush() | |
} finally { | |
if (fos != null) { | |
fos.close() | |
} | |
} | |
return tempKeyStoreFile | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
release.storeFile=/path/to/your/keystore/file.jks | |
release.storePassword=p455w0rd | |
release.keyAlias=awesome | |
release.keyPassword=53cr3t | |
debug.storeFile=/path/to/your/keystore/file.jks | |
debug.storePassword=p455w0rd | |
debug.keyAlias=awesome | |
debug.keyPassword=53cr3t |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# -*- coding: utf-8 -*- | |
from __future__ import print_function | |
import os | |
import sys | |
import httplib | |
import urllib | |
import urlparse | |
import json | |
import fnmatch | |
import re | |
from os import getenv | |
from subprocess import check_output | |
from subprocess import CalledProcessError | |
__author__ = 'mariotaku' | |
git_https_url_prefix = 'https://github.com/' | |
git_ssh_url_prefix = '[email protected]:' | |
git_git_url_prefix = 'git://github.com/' | |
git_file_suffix = '.git' | |
github_header_accept = 'application/vnd.github.v3+json' | |
github_header_user_agent = 'TravisUploader/0.1' | |
DEVNULL = open(os.devnull, 'w') | |
repo_url = None | |
try: | |
repo_url = check_output(['git', 'config', '--get', 'remote.origin.url']).splitlines()[0] | |
except CalledProcessError: | |
print('No remote url for this project, abort') | |
exit(0) | |
user_repo_name = None | |
if repo_url.startswith(git_ssh_url_prefix): | |
user_repo_name = repo_url[len(git_ssh_url_prefix):] | |
elif repo_url.startswith(git_https_url_prefix): | |
user_repo_name = repo_url[len(git_https_url_prefix):] | |
elif repo_url.startswith(git_git_url_prefix): | |
user_repo_name = repo_url[len(git_git_url_prefix):] | |
if not user_repo_name: | |
print('Not a github repo (%s), abort' % repo_url, file=sys.stderr) | |
exit(0) | |
if user_repo_name.endswith(git_file_suffix): | |
user_repo_name = user_repo_name[:-len(git_file_suffix)] | |
current_tag = None | |
current_tag_body = None | |
try: | |
current_tag = check_output(['git', 'describe', '--tags', '--exact-match', '--abbrev=0'], | |
stderr=DEVNULL).splitlines()[0] | |
except CalledProcessError: | |
print('This commit doesn\'t have tag, abort', file=sys.stderr) | |
exit(0) | |
try: | |
current_tag_body = '\n'.join( | |
check_output(['git', 'show', '-s', '--format=%b', current_tag], stderr=DEVNULL).splitlines()[2:]) | |
except CalledProcessError: | |
current_tag_body = "Automatic upload for version %s" % current_tag | |
github_access_token = getenv('GITHUB_ACCESS_TOKEN') | |
if not github_access_token: | |
print('No access token given, abort', file=sys.stderr) | |
exit(0) | |
github_authorization_header = "token %s" % github_access_token | |
print('Creating release for tag %s' % current_tag) | |
req_headers = {'Accept': github_header_accept} | |
conn = httplib.HTTPSConnection('api.github.com') | |
conn.request('POST', '/repos/%s/releases' % user_repo_name, | |
body=json.dumps({ | |
'tag_name': current_tag, | |
'name': "Version %s" % current_tag, | |
'body': current_tag_body | |
}), | |
headers={ | |
'Accept': github_header_accept, | |
'Authorization': github_authorization_header, | |
'Content-Type': 'application/json', | |
'User-Agent': github_header_user_agent | |
}) | |
response = conn.getresponse() | |
if response.status == 422: | |
conn = httplib.HTTPSConnection('api.github.com') | |
conn.request('GET', '/repos/%s/releases/tags/%s' % (user_repo_name, current_tag), | |
headers={ | |
'Accept': github_header_accept, | |
'Authorization': github_authorization_header, | |
'User-Agent': github_header_user_agent | |
}) | |
response = conn.getresponse() | |
if response.status not in range(200, 204): | |
print('Unable to create or get release, abort', file=sys.stderr) | |
exit(0) | |
response_values = json.loads(response.read()) | |
upload_url = urlparse.urlparse(re.sub('\{\?([\w\d_\-]+)\}', '', response_values['upload_url'])) | |
for root, dirnames, filenames in os.walk(os.getcwd()): | |
for filename in fnmatch.filter(filenames, '*-release.apk'): | |
conn = httplib.HTTPSConnection(upload_url.hostname) | |
conn.request('POST', "%s?%s" % (upload_url.path, urllib.urlencode({'name': filename})), | |
body=open(os.path.join(root, filename), 'r'), | |
headers={ | |
'Accept': github_header_accept, | |
'Authorization': github_authorization_header, | |
'Content-Type': 'application/json', | |
'User-Agent': github_header_user_agent | |
}) | |
print("Upload %s returned %d" % (filename, conn.getresponse().status)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice gist.
But why you dont use the release feature of travis ?