Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DevOps sur Android : D'un git push à une releas...

DevOps sur Android : D'un git push à une release Play Store

- Devoxx France 2016
- Breizhcamp 2016
- Paris Android User Group Mars 2016

Jeremie Martinez

March 25, 2016
Tweet

More Decks by Jeremie Martinez

Other Decks in Technology

Transcript

  1. DevOps sur Android D'un git push à une release sur

    le Play Store @JeremMartinez www.jeremie-martinez.com
  2. DevOps n.pr [dɛvops] Fluidifier le processus de mise en production

    en rassemblant les équipes de développement et les équipes opérationnelles. Play Store
  3. Intégration continue Livraison continue Déploiement continu Tests unitaires Automatisation Release

    Packaging Build Tests d’intégration Test d’instrumentation Quality
  4. JUnit4 public final class LordOfTheRingsTest {
 
 @Before
 public void

    setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } } Tests unitaires
  5. Robolectric public final class LordOfTheRingsTest {
 
 @Before
 public void

    setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } } Tests unitaires
  6. @RunWith(RobolectricTestRunner.class)
 @Config(manifest = Config.NONE) Tests unitaires Robolectric public final class

    LordOfTheRingsTest {
 
 @Before
 public void setup() { … }
 
 @Test
 public void shouldBringThePrecious() { … } @After
 public void tearDown() { … } }
  7. @Test
 public void shouldRestoreFromParcelable() { // Given Character item =

    new Character("Frodo", "Baggins", "Hobbit"); 
 // When
 Parcel parcel = Parcel.obtain();
 item.writeToParcel(parcel, 0);
 parcel.setDataPosition(0); // Then Character fromParcel = Character.CREATOR.createFromParcel(parcel); assertThat(fromParcel.firstname).isEqualTo("Frodo"); assertThat(fromParcel.lastname).isEqualTo("Baggins");
 assertThat(fromParcel.race).isEqualTo("Hobbit"); } Tests unitaires
  8. 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) // );
  9. Quality // Only XML files
 @Override
 public boolean appliesTo(@NonNull Context

    context,
 @NonNull File file) {
 return LintUtils.isXmlFile(file);
 }
  10. 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`");
 }
 }
 }
  11. Quality public final class CaptainRegistry extends IssueRegistry {
 @Override
 public

    List<Issue> getIssues() {
 return Collections.singletonList(AttrPrefixDetector.ISSUE);
 }
 }
  12. 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
  13. @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
  14. 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
  15. 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
  16. 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
  17. Listings listings = edits.listings(); Listing listing = new Listing().
 setFullDescription(description).


    setShortDescription(shortDescription).
 setTitle(title);
 
 listings.update(PACKAGE, id, "en_US", listing).execute(); Publish
  18. 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"
  19. 
 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
  20. 
 // 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"
  21. 
 // Update APK listing
 Apklistings apklistings = edits.apklistings(); 


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