mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-23 12:51:16 +00:00
Merge branch 'automatic-release'
This commit is contained in:
commit
1e9c9b4cea
10 changed files with 192 additions and 152 deletions
|
@ -29,6 +29,8 @@ Set the `ANDROID_NDK` environment variable to the Android NDK folder (e.g. `expo
|
||||||
Build Go and Syncthing using `./make-all.bash`.
|
Build Go and Syncthing using `./make-all.bash`.
|
||||||
Use `./gradlew assembleDebug` in the project directory to compile the APK.
|
Use `./gradlew assembleDebug` in the project directory to compile the APK.
|
||||||
|
|
||||||
|
To prepare a new release, execute `./prepare-release.bash`, and follow the instructions.
|
||||||
|
|
||||||
To check for updated gradle dependencies, run `gradle dependencyUpdates`. Additionally, the git submodule in `ext/syncthing/src/github.com/syncthing/syncthing` may need to be updated.
|
To check for updated gradle dependencies, run `gradle dependencyUpdates`. Additionally, the git submodule in `ext/syncthing/src/github.com/syncthing/syncthing` may need to be updated.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ dependencies {
|
||||||
compile 'com.google.zxing:android-integration:3.2.1'
|
compile 'com.google.zxing:android-integration:3.2.1'
|
||||||
compile 'com.google.code.gson:gson:2.7'
|
compile 'com.google.code.gson:gson:2.7'
|
||||||
compile 'org.mindrot:jbcrypt:0.3m'
|
compile 'org.mindrot:jbcrypt:0.3m'
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
testCompile 'org.robolectric:robolectric:3.1.2'
|
||||||
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.4.0'
|
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.4.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
79
prepare-release.bash
Executable file
79
prepare-release.bash
Executable file
|
@ -0,0 +1,79 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
NEW_VERSION_NAME=$1
|
||||||
|
OLD_VERSION_NAME=$(grep "versionName" "build.gradle" | awk '{print $2}')
|
||||||
|
if [[ -z $NEW_VERSION_NAME ]]
|
||||||
|
then
|
||||||
|
echo "New version name is empty. Please set a new version. Current version: $OLD_VERSION_NAME"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Checking for Syncthing Update
|
||||||
|
-----------------------------
|
||||||
|
"
|
||||||
|
PROJECT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
cd "ext/syncthing/src/github.com/syncthing/syncthing/"
|
||||||
|
git fetch
|
||||||
|
CURRENT_TAG=$(git describe)
|
||||||
|
LATEST_TAG=$(git describe $(git rev-list --tags --max-count=1))
|
||||||
|
if [ $CURRENT_TAG != $LATEST_TAG ]
|
||||||
|
then
|
||||||
|
git checkout -f $LATEST_TAG
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
git add "ext/syncthing/src/github.com/syncthing/syncthing"
|
||||||
|
git commit -m "Updated Syncthing to $LATEST_TAG"
|
||||||
|
./gradlew cleanNative buildNative
|
||||||
|
fi
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
|
||||||
|
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Updating Translations
|
||||||
|
-----------------------------
|
||||||
|
"
|
||||||
|
tx push -s
|
||||||
|
tx pull -a
|
||||||
|
./gradlew deleteUnsupportedPlayTranslations publishListingFatRelease
|
||||||
|
git add -A "src/fat/play/"
|
||||||
|
git add -A "src/main/res/values-*/strings.xml"
|
||||||
|
if ! git diff --cached --exit-code;
|
||||||
|
then
|
||||||
|
git commit -m "Imported translations"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Running Tests
|
||||||
|
-----------------------------
|
||||||
|
"
|
||||||
|
./gradlew lint
|
||||||
|
./gradlew connectedFatDebugAndroidTest
|
||||||
|
./gradlew testFatDebugUnitTest
|
||||||
|
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Updating Version
|
||||||
|
-----------------------------
|
||||||
|
"
|
||||||
|
OLD_VERSION_CODE=$(grep "versionCode" "build.gradle" -m 1 | awk '{print $2}')
|
||||||
|
NEW_VERSION_CODE=$(($OLD_VERSION_CODE + 1))
|
||||||
|
sed -i "s/versionCode $OLD_VERSION_CODE/versionCode $NEW_VERSION_CODE/" build.gradle
|
||||||
|
|
||||||
|
OLD_VERSION_NAME=$(grep "versionName" "build.gradle" | awk '{print $2}')
|
||||||
|
sed -i "s/$OLD_VERSION_NAME/\"$1\"/" build.gradle
|
||||||
|
git add "build.gradle"
|
||||||
|
git commit -m "Bumped version to $NEW_VERSION_NAME"
|
||||||
|
|
||||||
|
echo "
|
||||||
|
Update ready.
|
||||||
|
1. Run \`git push --follow-tags\`
|
||||||
|
2. Enter release notes at https://github.com/syncthing/syncthing-android/releases
|
||||||
|
3. Enter release notes at https://android.syncthing.net/job/Syncthing-Android-Release/configure
|
||||||
|
4. Start build at https://android.syncthing.net/job/Syncthing-Android-Release
|
||||||
|
"
|
|
@ -11,9 +11,8 @@ import java.util.List;
|
||||||
public class MockRestApi extends RestApi {
|
public class MockRestApi extends RestApi {
|
||||||
|
|
||||||
public MockRestApi(Context context, String url, String apiKey,
|
public MockRestApi(Context context, String url, String apiKey,
|
||||||
String guiUser, String guiPassword,
|
|
||||||
OnApiAvailableListener listener) {
|
OnApiAvailableListener listener) {
|
||||||
super(context, url, apiKey, guiUser, guiPassword, listener, null);
|
super(context, url, apiKey, listener, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class MockSyncthingService extends SyncthingService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RestApi getApi() {
|
public RestApi getApi() {
|
||||||
return new MockRestApi(this, null, null, null, null, null);
|
return new MockRestApi(this, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,7 +26,6 @@ public class RestApiTest extends AndroidTestCase {
|
||||||
new SyncthingRunnable(new MockContext(getContext()), SyncthingRunnable.Command.main);
|
new SyncthingRunnable(new MockContext(getContext()), SyncthingRunnable.Command.main);
|
||||||
|
|
||||||
ConfigXml config = new ConfigXml(new MockContext(getContext()));
|
ConfigXml config = new ConfigXml(new MockContext(getContext()));
|
||||||
config.changeDefaultFolder();
|
|
||||||
|
|
||||||
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
||||||
|
|
||||||
|
@ -39,7 +38,6 @@ public class RestApiTest extends AndroidTestCase {
|
||||||
}
|
}
|
||||||
}.execute(config.getWebGuiUrl());
|
}.execute(config.getWebGuiUrl());
|
||||||
mApi = new RestApi(getContext(), config.getWebGuiUrl(), config.getApiKey(),
|
mApi = new RestApi(getContext(), config.getWebGuiUrl(), config.getApiKey(),
|
||||||
null, null,
|
|
||||||
new RestApi.OnApiAvailableListener() {
|
new RestApi.OnApiAvailableListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onApiAvailable() {
|
public void onApiAvailable() {
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
package com.nutomic.syncthingandroid.test.syncthing;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.test.ServiceTestCase;
|
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
|
|
||||||
import com.nutomic.syncthingandroid.test.MockContext;
|
|
||||||
import com.nutomic.syncthingandroid.test.Util;
|
|
||||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These tests assume that syncthing keys have already been generated. If not, tests may fail
|
|
||||||
* because startup takes too long.
|
|
||||||
*/
|
|
||||||
public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
public SyncthingServiceTest() {
|
|
||||||
super(SyncthingService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
mContext = new MockContext(getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tearDown() throws Exception {
|
|
||||||
Util.deleteRecursive(mContext.getFilesDir());
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit();
|
|
||||||
super.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFirstStart() {
|
|
||||||
setContext(mContext);
|
|
||||||
startService(new Intent(mContext, SyncthingService.class));
|
|
||||||
assertTrue(getService().isFirstStart());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNotFirstStart() throws IOException {
|
|
||||||
setContext(mContext);
|
|
||||||
startService(new Intent(mContext, SyncthingService.class));
|
|
||||||
new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile();
|
|
||||||
assertFalse(getService().isFirstStart());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBindService() throws InterruptedException {
|
|
||||||
SyncthingServiceBinder binder = (SyncthingServiceBinder)
|
|
||||||
bindService(new Intent(getContext(), SyncthingService.class));
|
|
||||||
SyncthingService service = binder.getService();
|
|
||||||
final CountDownLatch latch = new CountDownLatch(2);
|
|
||||||
getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
|
||||||
@Override
|
|
||||||
public void onWebGuiAvailable() {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
getService().registerOnApiChangeListener(new SyncthingService.OnApiChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onApiChange(SyncthingService.State currentState) {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
latch.await(1, TimeUnit.SECONDS);
|
|
||||||
assertNotNull(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportExportConfig() {
|
|
||||||
setContext(mContext);
|
|
||||||
SyncthingServiceBinder binder = (SyncthingServiceBinder)
|
|
||||||
bindService(new Intent(getContext(), SyncthingService.class));
|
|
||||||
SyncthingService service = binder.getService();
|
|
||||||
File config = new File(mContext.getFilesDir(), ConfigXml.CONFIG_FILE);
|
|
||||||
File privateKey = new File(mContext.getFilesDir(), SyncthingService.PRIVATE_KEY_FILE);
|
|
||||||
File publicKey = new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE);
|
|
||||||
|
|
||||||
try {
|
|
||||||
config.createNewFile();
|
|
||||||
privateKey.createNewFile();
|
|
||||||
publicKey.createNewFile();
|
|
||||||
} catch (IOException e) {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
service.exportConfig();
|
|
||||||
|
|
||||||
config.delete();
|
|
||||||
privateKey.delete();
|
|
||||||
publicKey.delete();
|
|
||||||
|
|
||||||
service.importConfig();
|
|
||||||
assertTrue(config.exists());
|
|
||||||
assertTrue(privateKey.exists());
|
|
||||||
assertTrue(publicKey.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPassword() throws InterruptedException {
|
|
||||||
startService(new Intent(getContext(), SyncthingService.class));
|
|
||||||
new Handler().postDelayed(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
||||||
assertNotNull(sp.getString("gui_user", null));
|
|
||||||
assertEquals(20, sp.getString("gui_password", null).length());
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -38,17 +38,4 @@ public class ConfigXmlTest extends AndroidTestCase {
|
||||||
assertTrue(mConfig.getWebGuiUrl().startsWith("https://127.0.0.1:"));
|
assertTrue(mConfig.getWebGuiUrl().startsWith("https://127.0.0.1:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Just make sure the file is actually changed.
|
|
||||||
*
|
|
||||||
* This is not ideal, but way less complicated than starting up syncthing and accessing the API.
|
|
||||||
*/
|
|
||||||
public void testCreateCameraFolder() {
|
|
||||||
long oldTime = ConfigXml.getConfigFile(mContext).lastModified();
|
|
||||||
long oldSize = ConfigXml.getConfigFile(mContext).length();
|
|
||||||
mConfig.changeDefaultFolder();
|
|
||||||
assertNotSame(oldTime, ConfigXml.getConfigFile(mContext).lastModified());
|
|
||||||
assertNotSame(oldSize, ConfigXml.getConfigFile(mContext).lastModified());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,6 @@ public class ConfigXml {
|
||||||
*/
|
*/
|
||||||
public static final String CONFIG_FILE = "config.xml";
|
public static final String CONFIG_FILE = "config.xml";
|
||||||
|
|
||||||
private static final String INVALID_CONFIG_FILE = "config.xml.invalid";
|
|
||||||
|
|
||||||
private static final int OPEN_CONFIG_MAX_TRIES = 10;
|
private static final int OPEN_CONFIG_MAX_TRIES = 10;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
@ -83,19 +81,9 @@ public class ConfigXml {
|
||||||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||||
mConfig = db.parse(mConfigFile);
|
mConfig = db.parse(mConfigFile);
|
||||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||||
Log.w(TAG, "Failed to open config, moving to " + INVALID_CONFIG_FILE +
|
throw new OpenConfigException();
|
||||||
" and creating blank config");
|
|
||||||
File dest = new File(mConfigFile.getParent(), INVALID_CONFIG_FILE);
|
|
||||||
if (dest.exists())
|
|
||||||
dest.delete();
|
|
||||||
mConfigFile.renameTo(dest);
|
|
||||||
generateKeysConfig(context);
|
|
||||||
isFirstStart = true;
|
|
||||||
mConfigFile = getConfigFile(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mConfig == null)
|
|
||||||
throw new OpenConfigException();
|
|
||||||
|
|
||||||
if (isFirstStart) {
|
if (isFirstStart) {
|
||||||
changeDefaultFolder();
|
changeDefaultFolder();
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package com.nutomic.syncthingandroid.syncthing;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import com.nutomic.syncthingandroid.BuildConfig;
|
||||||
|
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These tests assume that syncthing keys have already been generated. If not, tests may fail
|
||||||
|
* because startup takes too long.
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(constants = BuildConfig.class)
|
||||||
|
public class SyncthingServiceTest {
|
||||||
|
|
||||||
|
private SyncthingService mService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mService = Robolectric.buildService(SyncthingService.class).get();
|
||||||
|
mService.onCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstStart() {
|
||||||
|
assertTrue(mService.isFirstStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotFirstStart() throws IOException {
|
||||||
|
new File(mService.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile();
|
||||||
|
assertFalse(mService.isFirstStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindService() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
mService.registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
||||||
|
@Override
|
||||||
|
public void onWebGuiAvailable() {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mService.registerOnApiChangeListener(new SyncthingService.OnApiChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
latch.await(1, TimeUnit.SECONDS);
|
||||||
|
assertNotNull(mService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImportExportConfig() {
|
||||||
|
File config = new File(mService.getFilesDir(), ConfigXml.CONFIG_FILE);
|
||||||
|
File privateKey = new File(mService.getFilesDir(), SyncthingService.PRIVATE_KEY_FILE);
|
||||||
|
File publicKey = new File(mService.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.createNewFile();
|
||||||
|
privateKey.createNewFile();
|
||||||
|
publicKey.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
mService.exportConfig();
|
||||||
|
|
||||||
|
config.delete();
|
||||||
|
privateKey.delete();
|
||||||
|
publicKey.delete();
|
||||||
|
|
||||||
|
assertTrue(mService.importConfig());
|
||||||
|
assertTrue(config.exists());
|
||||||
|
assertTrue(privateKey.exists());
|
||||||
|
assertTrue(publicKey.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPassword() throws InterruptedException {
|
||||||
|
new Handler().postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mService);
|
||||||
|
assertNotNull(sp.getString("gui_user", null));
|
||||||
|
assertEquals(20, sp.getString("gui_password", null).length());
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue