I took a project from github and developed a few functionalities that I needed for my own use.
I created a new apk after finishing those, the phone recognizes it as being a new version of the existing app, but it fails installation after a few seconds of "Installing". App not installed.
Links to printscreens:
https://gyazo.com/93b94183572f139d9a07b937c5a1eed2
https://gyazo.com/bdbee5e182f7e8d69ee00c7daac88cf3
I've displayed my current versionCode and it was something about 10230000. Tried to change it in gradle to 10230008, still fails. Changed my app versionName as well without any positive result.
The initial version of module gradle was:
...
versionName rootProject.ext.versionName
versionCode rootProject.ext.versionCode
buildConfigField 'String', 'REALM_ENCRYPTION_KEY', "\"${rootProject.ext.realmEncryptionKey}\""
...
Project gradle:
...
apply plugin: 'kotlin'
ext {
versionName = project['version.major'] + '.' +
project['version.minor'] + '.' +
project['version.patch']
versionCode = (project['version.major'].toInteger() * 1000 * 1000 * 1000) +
(project['version.minor'].toInteger() * 1000 * 1000) +
(project['version.patch'].toInteger() * 1000) +
project['version.build'].toInteger()
bugsnagAPIKey = System.getenv('BUGSNAG_API_KEY') ?: ''
realmEncryptionKey = System.getenv('REALM_ENCRYPTION_KEY') ?: 'ZX06poC7a96dL9,FR_9|Ww<2%]?4Ij(3wR3DmyNj0[{(,8g%jX2{03P45_p`N6|2'
}
...
Why can't the app be installed even though it is recognized as being an update?
Try in android studio:
Go to File -> Settings -> Build, Execution, Deployment -> Instant Run
and turn it of.
And try in you android phone:
Settings -> Apps & notifications -> See all x apps ->
Search your app and click on it and do FORCE STOP and UNINSTALL.
If you wants to update this application from the previous binary release (that is, you took this .apk from GitHub directly, and install it into your phone, compared to build the previous version from source code), then you have two choices:
Ask the original packager (author?) to pack your source for you.
Uninstall the previous APK, then install the new one.
Crack the signing key.
The easiest way is obviously the second one.
In my memory, the core of original question is the signing key of that .apk file.
If you are trying to update the app, the excited version on the device must have the same signature as the new version, otherweise it will not being installed. The ones you can try to do is uninstall the app and then reinstall it, then they will work well.
It may also be because you do not have enough expression for the new update.
Related
In order to improve our QA workflow, we want to automatically build an APK file for each pull-request on Github so we can test it BEFORE the branch is merged. We already figured out how to build the file, but we are now wondering how to integrate this in our workflow.
It seems like most available Beta programs (e.g. Crashlytics Beta, Google Play) mostly focus on creating one beta version shortly before the release, but don't allow hosting multiple APKs in parallel.
Here's an example for our ideal workflow:
Developer finishes coding and creates a pull-request
Tests run
If tests are successful, an APK is built automatically and uploaded somewhere (that's the part we're trying to figure out)
QA takes a look at the pull-request and should be able to easily download the correct APK on their testing device
If there are no issues during QA, the pull-request is merged
The APK file is automatically deleted
We specifically don't want to test the APK after the pull-request has been merged, but instead test before so less bugs pop up in our develop branch.
Actually Crashlytics allow to have several versions of APK.
Ech version can have each own Version string and of course release notes, to help QA to find correct APK.
Point 3 from question can be described in that way:
CI configured to upload build to Crashlytics.
It can be achieved by gradle task:
gradle assembleRelease crashlyticsUploadDistributionRelease
It is really useful to have special build type (pullrequest) for this case. You can specify special distribution rules via distribution groups, notifications about builds and release notes.
build.gradle:
//example function for change log
def getLastGitCommitMessage() {
try {
"git log -1 --pretty=%B".execute().text.trim()
} catch (e) {
'Undefined message.'
}
}
android {
buildTypes {
...
pullrequest {
//invitation
ext.betaDistributionGroupAliases = "QA, devs"
// notification
ext.betaDistributionNotifications = true
// last commit message as release notes
ext.betaDistributionReleaseNotes = getLastGitCommitMessage()
}
}
}
In this case build and upload command will be like that:
gradle assemblePullrequest crashlyticsUploadDistributionPullrequest
There is a lot of ways to achieve it. But in my opinion the best way is to create next stage that will produce apk as artefact and later your QA team will be able to download apk on device and test it. As anaxad wrote you can also send apk file using mail and distribution list. But such solution will be more difficult, because you need to create task (e.g. using docker) that will send mail with apk.
Make your system the server.
After that at the time of generate APK, Give your server path.
If you do like this then every time your apk will update,
So you need to use one variable? Which will decide your apk deploy on your local server or not.
After finish your development, make it true and then your apk will copy on your local server. Then it is easily accessible to QA team.
Follow this question.
Some demo code.
debug {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def apk = output.outputFile;
def newName;
newName = apk.name.replace("-" + variant.buildType.name, "")
.replace(project.name, name);
newName = newName.replace("-", "-" + version + "-" + milestone +
"-" + build + "-");
output.outputFile = new File(apk.parentFile, newName);
}
}
}
Currently I have a react native app and the issue that I have is that is very time consuming to update the version on every build or commit.
Also, I have Sentry enabled so every time I build, some builds get the same version so some crashes are hard to determine where they came from.
Lastly, updating the version manually is error prone.
How can I setup my builds to generate an automatic version every time I build and forget about all of this manual task?
While the currently accepted answer will work, there is a much simpler, and therefore more reliable way to do it.
You can actually read the value set in package.json right from build.gradle.
Modify your android/app/build.gradle:
// On top of your file import a JSON parser
import groovy.json.JsonSlurper
// Create an easy to use function
def getVersionFromNpm() {
// Read and parse package.json file from project root
def inputFile = new File("$rootDir/../package.json")
def packageJson = new JsonSlurper().parseText(inputFile.text)
// Return the version, you can get any value this way
return packageJson["version"]
}
android {
defaultConfig {
applicationId "your.app.id"
versionName getVersionFromNpm()
}
}
This way you won't need a pre-build script or anything, it will just work.
Since I was working with this for several days, I decided to share with everyone how I did it, because it could help others.
Tools used:
GitVersion: We will use GitVersion to generate a semantic version automatically depending on many factors like current branch, tags, commits, etc. The toold does an excellent job and you can forget about naming your versions. Of course, if you set a tag to a commit, it will use that tag as name.
PowerShell: This command line OS built by Microsoft has the ability to be run from Mac, Linux or Windows, and I chose it because the builds can be agnostic of the OS version. For example I develop on Windows but the build machine has MacOS.
Edit App build.gradle
The app gradle only needs one line added at the end of it. In my case I have the Google Play Services gradle and I added it after that.
apply from: 'version.gradle'
version.gradle
This file should be in the same folder as your app gradle and this is the content:
task updatePackage(type: Exec, description: 'Updating package.json') {
commandLine 'powershell', ' -command ' , '$semver=(gitversion /showvariable Semver); Set-Content -path version.properties -value semver=$semver; npm version --no-git-tag-version --allow-same-version $semver'
}
preBuild.dependsOn updatePackage
task setVariantVersion {
doLast {
if (plugins.hasPlugin('android') || plugins.hasPlugin('android-library')) {
def autoIncrementVariant = { variant ->
variant.mergedFlavor.versionName = calculateVersionName()
}
if (plugins.hasPlugin('android')){
//Fails without putting android. first
android.applicationVariants.all { variant -> autoIncrementVariant(variant) }
}
if (plugins.hasPlugin('android-library')) {
//Probably needs android-library before libraryVariants. Needs testing
libraryVariants.all { variant -> autoIncrementVariant(variant) }
}
}
}
}
preBuild.dependsOn setVariantVersion
setVariantVersion.mustRunAfter updatePackage
ext {
versionFile = new File('version.properties')
calculateVersionName = {
def version = readVersion()
def semver = "Unknown"
if (version != null){
semver = version.getProperty('semver')
}
return semver
}
}
Properties readVersion() {
//It gets called once for every variant but all get the same version
def version = new Properties()
try {
file(versionFile).withInputStream { version.load(it) }
} catch (Exception error) {
version = null
}
return version
}
Now, let's review what the script is actually doing:
updatePackage: This task runs at the very beginning of your build (actually before preBuild) and it executes gitversion to get the current version and then creates a version.properties file which later be read by gradle to take the version.
setVariantVersion: This is called afterEvaluate on every variant. Meaning that if you have multiple builds like debug, release, qa, staging, etc, all will get the same version. For my use case this is fine, but you might want to tweak this.
Task Order: One thing that bothered me was that the version was being run before the file was generated. This is fixed by using the mustRunAfter tag.
PowerShell Script Explained
This is the script that gets run first. Let's review what is doing:
$semver=(gitversion /showvariable Semver);
Set-Content -path props.properties -value semver=$semver;
npm version --no-git-tag-version --allow-same-version $semver
Line 1: gitversion has multiple type of versions. If you run it without any parameter you will get a json file with many variants. Here we are saying that we only want the SemVer. (See also FullSemVer)
Line 2: PowerShell way to create a file and save the contents to it. This can be also made with > but I had encoding issues and the properties file was not being read.
Line 3: This line updates your package.json version. By default it saves a commit to git with the new version. --no-git-tag-version makes sure you don't override it.
And that is it. Now every time you make a build, the version should be generated automatically, your package.json updated and your build should have that specific version name.
App Center
Since I am using App Center to make the builds, I will tell you how you can use this in a Build machine. You only need to use a custom script.
app-center-pre-build.sh
#!/usr/bin/env sh
#Installing GitVersion
OS=$(uname -s)
if [[ $OS == *"W64"* ]]; then
echo "Installing GitVersion with Choco"
choco install GitVersion.Portable -y
else
echo "Installing GitVersion with Homebrew"
brew install --ignore-dependencies gitversion
fi
This is needed because GitVersion is not currently a part of the build machines. Also, you need to ignore the mono dependency when installing, otherwise you get an error when brew tries to link the files.
The #MacRusher version was fine for me. Just for further readers, I had to add .toInteger() to make it work. Since I'm using yarn version --patch to automatically upgrade the version in package.json I also had to take only the two first characters.
Here is the new version:
// On top of your file import a JSON parser
import groovy.json.JsonSlurper
def getVersionFromPackageJson() {
// Read and parse package.json file from project root
def inputFile = new File("$rootDir/../package.json")
def packageJson = new JsonSlurper().parseText(inputFile.text)
// Return the version, you can get any value this way
return packageJson["version"].substring(0,2).toInteger()
}
android {
defaultConfig {
applicationId "your.app.id"
versionName getVersionFromPackageJson()
}
}
I have an Android that I deploy to a Google Play alpha track trough a CI server. For the Android versionCode I make use of the CI build number that I inject into the Gradle script through an environment variable.
This used to work fine; but currently Google Play is not accepting any builds. When I manually trigger a alpha upload build (using the gradle-play-publisher plugin) for Gradle I eventually end up with the following error:
APK has an invalid version code.
So when looking (using aapt dump badging apk-path) at the generated APK I see an empty value for the version code (versionCode='').
The relevant code from the build script:
def appVersionCode = System.getenv("BUILD_NUMBER") as Integer ?: 0
defaultConfig {
...
versionCode appVersionCode
...
}
It seems the variable is not read correctly; however it reads System.getenv("KEY_PASS") correctly to use for signing.
The variable is also set:
❯ echo $BUILD_NUMBER
1234
Does anyone have an idea why this specific variable doesn't seem to be read (anymore)?
Gradle version: 3.5 with Android Gradle plugin 2.3.1.
Try to change your code to this:
def appVersionCode = Integer.valueOf(System.env.BUILD_NUMBER ?: 0)
defaultConfig {
...
versionCode appVersionCode
...
}
Currently we have Bamboo listening to a Git repository on any changes. When a change occur the build process starts and increases the (Bamboo) build number by one.
I thought it would be nice to use this same build number for the Android project (versionCode) so that the user of the app can always refer to the actual build he/she received. This way the build number goes from 1 to 2 on the build server. Only the workstation doesn't know about this and still uses version 1.
Is there any way to sync this build number?
Tried:
One possible solution a friend of mine suggested was to use a git command to get the commit number: git rev-list HEAD --count which is awesome. The only downside of this is that you cannot properly change build number within Bamboo. So the build number of Bamboo should be leading.
Btw, I'm using Android Studio with Gradle
Bamboo can be configured to set the versionCode value in the manifest to the build number when building, so propagating this change back to where the development is happening should be unnecessary. To configure Bamboo to do this, add a Script task to the build plan (before the actual build task) with a body of:
sed -i 's/android:versionCode="[[:digit:]]*"/android:versionCode="${bamboo.buildNumber}"/' AndroidManifest.xml
Yes, it is quite easy with gradle. You have to retrieve the latest build result in the gradle script and put it in the versionCode.
Here it is what you need (remember to change the variables to refer your server):
def getBambooNumber(){
def url = "https://bambooServer:bambooPort/rest/api/latest/result/PROJECT-BAMBOO-ID/latest.json?os_authType=basic".toURL()
def authValue = "USER:PASSWORD".bytes.encodeBase64().toString()
def json = new JsonSlurper().parseText(url.getText(requestProperties : ["Authorization" : "Basic " + authValue]))
return json.buildNumber + 1 // +1 to Get the new build number
}
def bambooBuild = bambooBuildNumber()
android {
...
defaultConfig {
...
versionCode bambooBuild
//This allows you to access in BuildConfig to the bamboo build
buildConfigField "Integer", "BAMBOO_BUILD", "${bambooBuild}"
...
}
...
}
Let me know if this works for you.
I'm currently developing a custom library for Android.
My idea is to have a version for the app (I'm currently setting it in de build.gradle file and it works fine), and a different version for the library.
Is there a best practice in order to achieve this without having the two build.gradle files merged together?
I found a couple of similar questions on SO but they were 4 years old and not very helpful.
The solution for this is not Git as you probably already figured out. OP wants to externally version a library, potentially as a means to provide an API to a third-party without providing the source code.
This is not trivial with Gradle but it is doable.
You can do this at different levels, I'll explain the easiest and most straight-forward.
Setting the version number
First you need to add a version variable to your gradle.properties file. You can set this variable with a simple one-liner:
version=0.1.0-SNAPSHOT
Building a version string
In your project create a file called versioning.gradle and paste the following code inside (I found this online a long time ago, I don't have the source anymore):
ext {
/**
* Builds an Android version code from the version of the project.
* This is designed to handle the -SNAPSHOT and -RC format.
*
* I.e. during development the version ends with -SNAPSHOT. As the code stabilizes and release nears
* one or many Release Candidates are tagged. These all end with "-RC1", "-RC2" etc.
* And the final release is without any suffix.
* #return
*/
buildVersionCode = {
//The rules is as follows:
//-SNAPSHOT counts as 0
//-RC* counts as the RC number, i.e. 1 to 98
//final release counts as 99.
//Thus you can only have 98 Release Candidates, which ought to be enough for everyone
def candidate = "99"
def (major, minor, patch) = version.toLowerCase().replaceAll('-', '').tokenize('.')
if (patch.endsWith("snapshot")) {
candidate = "0"
patch = patch.replaceAll("[^0-9]","")
} else {
def rc
(patch, rc) = patch.tokenize("rc")
if (rc) {
candidate = rc
}
}
(major, minor, patch, candidate) = [major, minor, patch, candidate].collect{it.toInteger()}
(major * 1000000) + (minor * 10000) + (patch * 100) + candidate;
}
}
Once you've done this, be sure to include the versioning.gradle file from your main build.gradle file by adding apply from: '../versioning.gradle' near where the Android plugin is loaded.
Then, in your defaultConfig block, you want to change the default version variables to the following values:
versionCode buildVersionCode()
versionName version
Version number in the filename of the library
If you're truly building an Android library, then the expected output is an .aar file.
Therefore, add the following gradle code to your build.gradle file:
libraryVariants.all {
variant -> variant.outputs.each {
output -> output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(".aar", "-${defaultConfig.versionName}.aar")
)
}
}
And that's it! Your output file will have a name with the version number you specify in the gradle.proprties file.
Caveats
This will version your library and/or API in a very static manner. Meaning that there's no way for a developer to dynamically get the version number through a method call.
Be sure to also have a method available that supplies client developers with the version number AND be sure to keep the two in sync. Only then you have a properly versioned library.
Why would you want to do this?
If the problem is that you need to be able to take different actions in the programs using the library depending on the version, then simply create a public static final constant somewhere in the library.
If the reason is cosmetic (e.g., for presentation), then simply put it in your Readme, Changelog, or wherever you need it. Messing up the build files for this purpose is a bad idea.
Based on your explanation:
My recommendation is to put it in the library's AndroidManifest.xml in that case (android:versionCode and android:versionName). This is what Google does with many of their own libraries, and it's harmless wrt the build.