NewFiveFour | Blog | Portfolio


Git: Setting your username and email

Set your git email and username locally to the repo. I always forget this.

git config user.name bumblebee
git config user.email buzz@buzz.buzz

git


Android: A debug build signed with a constant keystore

Each debug build of your android application will be signed with the debug keystore.
 
This is a problem because in continous integration environments, like travis-ci or docker, the debug keystore is regenerated on each creation of the environment.
 
This means if someone has a debug version of your application, they won't be able to upgrade to a more recent version, because the keystores will be different.
 
To get around this, let's create and add a keystore to our repository for continuous integration environments. Create this in the app/ directory. And ensure the key password is the same as the keystore password.

keytool -genkey -v -keystore ci-key.keystore -alias ci-key-alias -keyalg RSA -keysize 2048 -validity 10000

Now in your app/build.gradle file add this to the android block:

signingConfigs {
  ci {
    keyAlias "ci-key-alias"
    keyPassword System.getEnv("CI_KEYSTORE_PASSWORD")
    storeFile file("ci-key.keystore")
    storePassword System.getEnv("CI_KEYSTORE_PASSWORD")
  }
}

We give it the alias and reference to the file we created. We'll get the keystore key from the environment, one injected into the contiuous integration environment, for example.
 
In the same android block, create a new build variant, initalised as the debug variable, called debug_with_ci_keystore:

buildTypes {
    debug_with_ci_keystore.initWith(buildTypes.debug)
    debug_with_ci_keystore {
        if(System.getenv("CI_KEYSTORE_PASSWORD")) signingConfig signingConfigs.ci
        minifyEnabled false
    }
...
}

We give the variant the ci signing config above if we have the correct environment variable, CI_KEYSTORE_PASSWORD. This means, if we don't, we only create app/build/outputs/apk/app-debug_with_ci_keystore-unsigned.apk, not the signed app-debug_with_ci_keystore.apk.
 
Now on ./gradlew build, if we have CI_KEYSTORE_PASSWORD set in our environment, we will create app/build/outputs/apk/app-debug_with_ci_keystore.apk, which will have a constant keystore.
 
The above obviously works for a release signing: just change the build type to release, although you may not want to keep your release keystore in your repository.

android android-keystore keytool


HockeyApp: Generating release notes from git

The problem with uploading to HockeyApp is the release notes are not automatically created.
 
And we can create them by looking at all the git commit logs since the last build.
 
For this to happen we need to know what the last commit was.
 
Although we can specify this in our upload, when we query the builds we can't get this information back. Instead, we'll add the commit SHA to the release notess.
 
Our plan will be
  1. Look at the app versions via HockeyApp's API
  2. Find the lastest version
  3. Look at the release notes that and retrieve our added commit SHA
  4. Make git output all our commit logs since then
  5. Make release notes out of that (including the current git commit)
Here's the entire commented shell script:

# We need an initial bullet point for our list of commit logs
echo -n "* "
# Get the latest app uploads
curl -H "X-HockeyAppToken: $HOCKEYAPP_TOKEN" \
"https://rink.hockeyapp.net/api/2/apps/YOUR_HOCKEYAPP_APP_ID/app_versions?page=1" | \
# Put every property on a separate line
sed 's/,/\n/g' | \
# Remove all the quotation marks
sed 's/"//g' | \
# Look at only the notes properties
grep notes | \
# Look at the first one, i.e. the latest app upload
head -n 1 | \
# Find the commit information at the bottom of the notes
sed -n 's/.*(commit:\([^)]*\)).*/\1/p' | \
# Let's find all the logs since that commit
xargs -I '{}' git log {}..HEAD --pretty=format:'%s' --no-merges | \
# Add a star to each newline to make the list
awk '$0="* "$0'
# The end of the revision log must have the latest commit
# This is so later we can do the above again
echo
echo -n "* (commit:" 
git rev-parse HEAD | xargs echo -n
echo -n ')'

And when we upload to HockeyApp, via travis-ci, we can include it like so:

- bash release_notes_for_hockeyapp.sh > release_notes
- >
  curl
  -F "status=2"
  -F "notify=1"
  -F "notes=<release_notes"
  -F "notes_type=0"
  -F "ipa=@app/build/outputs/apk/YOUR_APK.apk"
  -H "X-HockeyAppToken: $HOCKEYAPP_TOKEN"
  https://rink.hockeyapp.net/api/2/apps/upload


hockeyapp git travis-ci


Android: Continuous Deployment with HockeyApp

Continuous Deployment is automatically distributing new app to your testers, and automatically reporting crashes. This guide shows you how to use HockeyApp for the distribution aspect.
 
I'm going to assume you've got a basic Android App in development.
  1. First, sign up to https://HockeyApp.net
  2. Click add new app, and choose to add it manually
  3. Give it the same package name as appears in your AndroidManifest.xml
  4. Now it's created, click on integrate HockeyApp quick tutorial. It will tell you add these parts to your app:
 
   app/build.gradle:
    
      repositories {
        ...
        jcenter()
        ...
      }
      
      android {
        ...
        defaultConfig {
          ...
          manifestPlaceholders = [HOCKEYAPP_APP_ID: "IT_WILL_TELL_YOU_YOUR_APP_ID"]
          ...
        }
        ...
      dependencies {
        ...
        compile 'net.hockeyapp.android:HockeySDK:4.0.1'
        ....
      }      
 
 app/src/main/AndroidManifest.xml (within the application tag):
 
      <meta-data android:name="net.hockeyapp.android.appIdentifier" android:value="${HOCKEYAPP_APP_ID}" />
 
 In your Activity:
 
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Your own code to create the view
        // ...
    
        checkForUpdates();
      }
    
      @Override
      public void onResume() {
        super.onResume();
        // ... your own onResume implementation
        checkForCrashes();
      }
    
      @Override
      public void onPause() {
        super.onPause();
        unregisterManagers();
      }
    
      @Override
      public void onDestroy() {
        super.onDestroy();
        unregisterManagers();
      }
    
      private void checkForCrashes() {
        CrashManager.register(this);
      }
    
      private void checkForUpdates() {
        // Remove this for store builds!
        UpdateManager.register(this);
      }
    
      private void unregisterManagers() {
        UpdateManager.unregister();
      }  

0. Now build and install that on your phone, and upload the APK to HockeyApp via the upload version button, clicking through all the dialog boxes until you can see the version on the Overview.
0. Now change something in the app, like some text, and update the versionCode in app/build.grade. Build it, but do not install this to your device (so we can see automatic updates on our phone - you don't normally do this)
0. With this newly build version, upload it to HockeyApp as before.
 
Now when you open the app again, or do something to trigger onResume(), it will ask you if you want to update to the latest version.
 
Click update, and voila - you and your testers will see the newest app, and any crashes will be reported to you with a stacktrace and device information.
 
We don't yet automatically upload our APK to HockeyApp via a build server / continuous integration environment, or send up the release notes, but we can do that in a later tutorial.

android hockeyapp


Javascript: Selenium setup in NodeJS with Firefox and ES6 modules

First install npm install selenium-webdriver. I'm on 4.0.0.
 
Then import Selenium with ES6 modules. We'll rename them since they're not built with ES6 modules in mind.

import * as webdriver from 'selenium-webdriver'
import * as firefox from 'selenium-webdriver/firefox'
let ff = firefox.default; 
let wd = webdriver.default

Then tell Selenium where your Firefox binary is and create the selenium instance.

let options = new ff.Options().setBinary('/usr/bin/firefox')
let browser = new wd.Builder().forBrowser('firefox').setFirefoxOptions(options).build()

Selenium will respond with promises. This is a little annoying so let's use an async function and use await.

async function test() {
  await browser.get("http://localhost:8080/")
  let next = await browser.findElement(wd.By.linkText("next"))
  await next.click()
  let pageSource = await browser.getPageSource()
  let hasPage = pageSource.indexOf("Page 2 of 2") != -1
  assert.equal(hasPage, true, "Checking if we're on page 2")
})

Finally we use the findElement method, once we've loaded a page, and then use the By class to find the link text. Then we click on it.
 
We then get the page source and do some string searching on that to use a normal nodejs assert statement.

javascript selenium nodejs


Page 1 of 89
next

Portfolio