DevOps sur Android : from one git push to a Play Store release

DevOps sur Android : from one git push to a Play Store release

Slides in English.

Talk given à Devoxx Belgium and Droidcon Vienna

05dd369062f7c4e450e1e08d1471da5b?s=128

Jeremie Martinez

November 10, 2016
Tweet

Transcript

  1. 1.

    DevOps on Android From one git push to a Play

    Store release @JeremMartinez www.jeremie-martinez.com
  2. 2.
  3. 3.
  4. 5.

    DevOps n.pr [dɛvops] Promotes a release process which bring together

    through communication and collaboration both development and operational teams. Play Store
  5. 12.

    Merge request mandatory At least one reviewer Merge always by

    the reviewer Branch must always be rebased Push
  6. 15.
  7. 19.

    JUnit4 public final class LordOfTheRingsTest {
 
 @Before
 public void

    setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } } Unit tests
  8. 20.

    Robolectric public final class LordOfTheRingsTest {
 
 @Before
 public void

    setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } } Unit tests
  9. 21.

    @RunWith(RobolectricTestRunner.class)
 @Config(manifest = Config.NONE) Unit tests Robolectric public final class

    LordOfTheRingsTest {
 
 @Before
 public void setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } }
  10. 26.
  11. 28.
  12. 31.

    Quality public class AttrPrefixDetector extends ResourceXmlDetector {
 
 public static

    final Issue ISSUE = Issue.create("AttrNotPrefixed", //
 "You must prefix your custom attr by `ct`", //
 "We prefix all our attrs to avoid clashes.", //
 Category.TYPOGRAPHY, //
 5, // Priority
 Severity.WARNING, //
 new Implementation(AttrPrefixDetector.class, // Scope.RESOURCE_FILE_SCOPE) // );
  13. 32.

    Quality // Only XML files
 @Override
 public boolean appliesTo(@NonNull Context

    context,
 @NonNull File file) {
 return LintUtils.isXmlFile(file);
 }
  14. 36.

    Quality @Override
 public void visitElement(XmlContext context, Element element) {
 final

    Attr attributeNode = element.getAttributeNode(ATTR_NAME);
 if (attributeNode != null) {
 final String val = attributeNode.getValue();
 if (!val.startsWith("android:") && !val.startsWith("ct")) {
 context.report(ISSUE,
 attributeNode,
 context.getLocation(attributeNode),
 "You must prefix your custom attr by `ct`");
 }
 }
 }
  15. 37.

    Quality public final class CaptainRegistry extends IssueRegistry {
 @Override
 public

    List<Issue> getIssues() {
 return Collections.singletonList(AttrPrefixDetector.ISSUE);
 }
 }
  16. 38.
  17. 45.

    Automate releases in order to deliver quickly, easily and reliably

    Continuous integration Continuous delivery
  18. 61.

    private static final String[] EXPECTED_PERMISSIONS = { … } @Test


    public void shouldMatchPermissions() throws Exception {
 final AndroidManifest androidManifest = new AndroidManifest( //
 Fs.fileFromPath("build/intermediates/manifests/full/debug/AndroidManifest.xml"), //
 null, //
 null //
 );
 final Set<String> requestedPermissions = new HashSet<>(androidManifest.getUsedPermissions());
 
 assertThat(requestedPermissions).containsOnly(EXPECTED_PERMISSIONS);
 } Publish
  19. 64.

    @Test
 public void upgradeShouldBeTheSameAsCreate() throws Exception {
 DbOpenHelper helper =

    new DbOpenHelper(RuntimeEnvironment.application);
 
 helper.onCreate(newDatabase());
 helper.onUpgrade(originDatabase(), 1, CURRENT_VERSION);
 
 Set<String> newSchema = extractSchema(newFile.getAbsolutePath());
 Set<String> upgradedSchema = extractSchema(upgradedFile.getAbsolutePath());
 
 assertThat(upgradedSchema).isEqualTo(newSchema);
 } Publish
  20. 65.

    connection = DriverManager.getConnection(JDBC_SQLITE + url);
 
 tables = connection.getMetaData().getTables(null, null,

    null, null);
 while (tables.next()) {
 
 final String tableType = tables.getString("TABLE_TYPE");
 final String tableName = tables.getString("TABLE_NAME");
 schema.add(tableType + " " + tableName);
 Publish
  21. 66.

    columns = connection.getMetaData().getColumns(null, null, tableName, null);
 while (columns.next()) {
 


    final String columnName = columns.getString("COLUMN_NAME");
 final String columnType = columns.getString("TYPE_NAME");
 final String columnNullable = columns.getString("IS_NULLABLE");
 final String columnDefault = columns.getString("COLUMN_DF");
 schema.add("TABLE " + tableName +
 " COLUMN " + columnName + " " + columnType +
 " NULLABLE=" + columnNullable +
 " DEFAULT=" + columnDefault);
 } Publish
  22. 68.
  23. 69.
  24. 70.
  25. 71.
  26. 72.
  27. 73.
  28. 76.

    http = GoogleNetHttpTransport.newTrustedTransport();
 json = JacksonFactory.getDefaultInstance();
 
 GoogleCredential credential =

    new GoogleCredential.Builder().
 setTransport(http).
 setJsonFactory(json).
 setServiceAccountPrivateKeyId(KEY_ID).
 setServiceAccountId(SERVICE_ACCOUNT_EMAIL).
 setServiceAccountScopes(AndroidPublisherScopes.ANDROIDPUBLISHER).
 setServiceAccountPrivateKeyFromPemFile(secretFile).
 build();
 
 publisher = new AndroidPublisher.Builder(http, json, credential).
 setApplicationName(PACKAGE).
 build(); Publish
  29. 78.

    Listings listings = edits.listings(); Listing listing = new Listing().
 setFullDescription(description).


    setShortDescription(shortDescription).
 setTitle(title);
 
 listings.update(PACKAGE, id, "en_US", listing).execute(); Publish
  30. 79.

    Images images = edits.images();
 
 FileContent content = new FileContent(PNG_MIME_TYPE,

    file);
 
 images.upload(PACKAGE, id, "en_US", "phone5", content).execute(); Publish "tablet7" "tablet9"
 "wear"
  31. 80.

    
 Apks apks = edits.apks(); 
 FileContent apkContent = new

    FileContent(APK_MIME_TYPE, apkFile);
 Apk apk = apks.upload(PACKAGE, id, apkContent).execute(); 
 int version = apk.getVersionCode();
 Publish
  32. 81.

    
 // Assign APK to Track
 Tracks tracks = edits.tracks();

    
 Track track = new Track().setVersionCodes(version); 
 tracks.update(PACKAGE, id, "production", track).execute(); Publish "rollout" "beta" "alpha"
  33. 82.

    
 // Update APK listing
 Apklistings apklistings = edits.apklistings(); 


    ApkListing whatsnew = new ApkListing().setRecentChanges(changes); 
 apklistings.update(PACKAGE, id, version, "en_US", whatsnew).execute(); Publish