mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 22:31: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`.
|
||||
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.
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ dependencies {
|
|||
compile 'com.google.zxing:android-integration:3.2.1'
|
||||
compile 'com.google.code.gson:gson:2.7'
|
||||
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'
|
||||
}
|
||||
|
||||
|
|
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 MockRestApi(Context context, String url, String apiKey,
|
||||
String guiUser, String guiPassword,
|
||||
OnApiAvailableListener listener) {
|
||||
super(context, url, apiKey, guiUser, guiPassword, listener, null);
|
||||
super(context, url, apiKey, listener, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -49,7 +49,7 @@ public class MockSyncthingService extends SyncthingService {
|
|||
|
||||
@Override
|
||||
public RestApi getApi() {
|
||||
return new MockRestApi(this, null, null, null, null, null);
|
||||
return new MockRestApi(this, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,6 @@ public class RestApiTest extends AndroidTestCase {
|
|||
new SyncthingRunnable(new MockContext(getContext()), SyncthingRunnable.Command.main);
|
||||
|
||||
ConfigXml config = new ConfigXml(new MockContext(getContext()));
|
||||
config.changeDefaultFolder();
|
||||
|
||||
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;
|
||||
|
||||
|
@ -39,7 +38,6 @@ public class RestApiTest extends AndroidTestCase {
|
|||
}
|
||||
}.execute(config.getWebGuiUrl());
|
||||
mApi = new RestApi(getContext(), config.getWebGuiUrl(), config.getApiKey(),
|
||||
null, null,
|
||||
new RestApi.OnApiAvailableListener() {
|
||||
@Override
|
||||
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:"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
private static final String INVALID_CONFIG_FILE = "config.xml.invalid";
|
||||
|
||||
private static final int OPEN_CONFIG_MAX_TRIES = 10;
|
||||
|
||||
private final Context mContext;
|
||||
|
@ -83,19 +81,9 @@ public class ConfigXml {
|
|||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
mConfig = db.parse(mConfigFile);
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
Log.w(TAG, "Failed to open config, moving to " + INVALID_CONFIG_FILE +
|
||||
" 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);
|
||||
throw new OpenConfigException();
|
||||
}
|
||||
}
|
||||
if (mConfig == null)
|
||||
throw new OpenConfigException();
|
||||
|
||||
if (isFirstStart) {
|
||||
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