Browse Source

Merge pull request #1067 from nextcloud/refactor/dev-container

pull/1071/head
Kate 1 year ago committed by GitHub
parent
commit
02a1ec681a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CONTRIBUTING.md
  2. 2
      cspell.json
  3. 5
      packages/nextcloud/test/core_test.dart
  4. 5
      packages/nextcloud/test/dashboard_test.dart
  5. 56
      packages/nextcloud/test/helper.dart
  6. 46
      packages/nextcloud/test/news_test.dart
  7. 5
      packages/nextcloud/test/notes_test.dart
  8. 10
      packages/nextcloud/test/notifications_test.dart
  9. 5
      packages/nextcloud/test/provisioning_api_test.dart
  10. 47
      packages/nextcloud/test/settings_test.dart
  11. 8
      packages/nextcloud/test/spreed_test.dart
  12. 5
      packages/nextcloud/test/uppush_test.dart
  13. 5
      packages/nextcloud/test/user_status_test.dart
  14. 5
      packages/nextcloud/test/webdav_test.dart
  15. 15
      tool/Dockerfile.dev
  16. 11
      tool/dev.sh
  17. 4
      tool/dev/install_app_version
  18. 0
      tool/dev/static/nasa.xml
  19. 0
      tool/dev/static/wikipedia.xml
  20. 0
      tool/overlay/.gitkeep

1
CONTRIBUTING.md

@ -38,7 +38,6 @@ You can read the full documentation at https://www.conventionalcommits.org.
## Tools ## Tools
We maintain a collection of scripts in `./tool/`. We maintain a collection of scripts in `./tool/`.
They range from setting up a local Nextcloud server (`./tool/dev.sh`) to generating assets. They range from setting up a local Nextcloud server (`./tool/dev.sh`) to generating assets.
Be aware that due to limitations in Apache the dev server will crash when you resize the terminal window.
## Monorepo ## Monorepo
For easier development we use a monorepo structure. For easier development we use a monorepo structure.

2
cspell.json

@ -13,7 +13,7 @@
"external", "external",
"packages/file_icons/lib/src/data.dart", "packages/file_icons/lib/src/data.dart",
"packages/neon_lints/lib", "packages/neon_lints/lib",
"packages/nextcloud/test/files" "tool/dev/static"
], ],
"dictionaries": [ "dictionaries": [
"bash", "bash",

5
packages/nextcloud/test/core_test.dart

@ -8,13 +8,10 @@ void main() {
group( group(
'core', 'core',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

5
packages/nextcloud/test/dashboard_test.dart

@ -7,13 +7,10 @@ void main() {
group( group(
'dashboard', 'dashboard',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

56
packages/nextcloud/test/helper.dart

@ -156,8 +156,24 @@ Future<TestNextcloudClient> getTestClient(
return client; return client;
} }
Future<DockerContainer> getDockerContainer(final DockerImage image, {final bool useApache = false}) async { Future<DockerContainer> getDockerContainer() async {
late ProcessResult result; const dockerImageName = 'ghcr.io/nextcloud/neon/dev';
var result = await runExecutableArguments(
'docker',
[
'images',
'-q',
dockerImageName,
],
);
if (result.exitCode != 0) {
throw Exception('Querying docker image failed: ${result.stderr}');
}
if (result.stdout.toString().isEmpty) {
throw Exception('Missing docker image $dockerImageName. Please build it using ./tool/build-dev-container.sh');
}
late int port; late int port;
while (true) { while (true) {
port = randomPort(); port = randomPort();
@ -171,12 +187,7 @@ Future<DockerContainer> getDockerContainer(final DockerImage image, {final bool
'host.docker.internal:host-gateway', 'host.docker.internal:host-gateway',
'-p', '-p',
'$port:80', '$port:80',
image, dockerImageName,
if (!useApache) ...[
'php',
'-S',
'0.0.0.0:80',
],
], ],
); );
// 125 means the docker run command itself has failed which indicated the port is already used // 125 means the docker run command itself has failed which indicated the port is already used
@ -195,35 +206,6 @@ Future<DockerContainer> getDockerContainer(final DockerImage image, {final bool
); );
} }
typedef DockerImage = String;
Future<DockerImage> getDockerImage() async {
const dockerImageName = 'nextcloud-neon-dev';
final inputStream = StreamController<List<int>>();
final process = runExecutableArguments(
'docker',
[
'build',
'-t',
dockerImageName,
'-f',
'-',
'../../tool',
],
stdin: inputStream.stream,
);
inputStream.add(utf8.encode(File('../../tool/Dockerfile.dev').readAsStringSync()));
await inputStream.close();
final result = await process;
if (result.exitCode != 0) {
throw Exception('Failed to build docker image');
}
return dockerImageName;
}
class TestNextcloudUser { class TestNextcloudUser {
TestNextcloudUser( TestNextcloudUser(
this.username, this.username,

46
packages/nextcloud/test/news_test.dart

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:nextcloud/news.dart' as news; import 'package:nextcloud/news.dart' as news;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:universal_io/io.dart';
import 'helper.dart'; import 'helper.dart';
@ -11,30 +10,22 @@ void main() {
group( group(
'news', 'news',
() { () {
late DockerImage image;
late HttpServer rssServer;
setUpAll(() async {
image = await getDockerImage();
rssServer = await getRssServer();
});
tearDownAll(() async => rssServer.close(force: true));
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
Future<DynamiteResponse<news.ListFeeds, void>> addWikipediaFeed([final int? folderID]) async => Future<DynamiteResponse<news.ListFeeds, void>> addWikipediaFeed([final int? folderID]) async =>
client.news.addFeed( client.news.addFeed(
url: 'http://host.docker.internal:${rssServer.port}/wikipedia.xml', url: 'http://localhost/static/wikipedia.xml',
folderId: folderID, folderId: folderID,
); );
Future<DynamiteResponse<news.ListFeeds, void>> addNasaFeed() async => client.news.addFeed( Future<DynamiteResponse<news.ListFeeds, void>> addNasaFeed() async => client.news.addFeed(
url: 'http://host.docker.internal:${rssServer.port}/nasa.xml', url: 'http://localhost/static/nasa.xml',
); );
test('Is supported', () async { test('Is supported', () async {
@ -58,7 +49,7 @@ void main() {
expect(response.body.starredCount, null); expect(response.body.starredCount, null);
expect(response.body.newestItemId, isNotNull); expect(response.body.newestItemId, isNotNull);
expect(response.body.feeds, hasLength(1)); expect(response.body.feeds, hasLength(1));
expect(response.body.feeds[0].url, 'http://host.docker.internal:${rssServer.port}/wikipedia.xml'); expect(response.body.feeds[0].url, 'http://localhost/static/wikipedia.xml');
response = await client.news.listFeeds(); response = await client.news.listFeeds();
expect(response.statusCode, 200); expect(response.statusCode, 200);
@ -67,7 +58,7 @@ void main() {
expect(response.body.starredCount, 0); expect(response.body.starredCount, 0);
expect(response.body.newestItemId, isNotNull); expect(response.body.newestItemId, isNotNull);
expect(response.body.feeds, hasLength(1)); expect(response.body.feeds, hasLength(1));
expect(response.body.feeds[0].url, 'http://host.docker.internal:${rssServer.port}/wikipedia.xml'); expect(response.body.feeds[0].url, 'http://localhost/static/wikipedia.xml');
}); });
test('Delete feed', () async { test('Delete feed', () async {
@ -401,7 +392,7 @@ void main() {
expect(response.body.newestItemId, isNotNull); expect(response.body.newestItemId, isNotNull);
expect(response.body.feeds, hasLength(1)); expect(response.body.feeds, hasLength(1));
expect(response.body.feeds[0].folderId, 1); expect(response.body.feeds[0].folderId, 1);
expect(response.body.feeds[0].url, 'http://host.docker.internal:${rssServer.port}/wikipedia.xml'); expect(response.body.feeds[0].url, 'http://localhost/static/wikipedia.xml');
}); });
test('Mark folder as read', () async { test('Mark folder as read', () async {
@ -430,28 +421,3 @@ void main() {
timeout: timeout, timeout: timeout,
); );
} }
Future<HttpServer> getRssServer() async {
final wikipediaRss = File('test/files/wikipedia.xml').readAsStringSync();
final nasaRss = File('test/files/nasa.xml').readAsStringSync();
while (true) {
try {
final port = randomPort();
final server = await HttpServer.bind(InternetAddress.anyIPv6, port);
unawaited(
server.forEach((final request) async {
switch (request.uri.path) {
case '/wikipedia.xml':
request.response.write(wikipediaRss);
case '/nasa.xml':
request.response.write(nasaRss);
default:
request.response.statusCode = HttpStatus.badRequest;
}
await request.response.close();
}),
);
return server;
} catch (_) {}
}
}

5
packages/nextcloud/test/notes_test.dart

@ -9,13 +9,10 @@ void main() {
group( group(
'notes', 'notes',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

10
packages/nextcloud/test/notifications_test.dart

@ -7,13 +7,10 @@ import 'helper.dart';
void main() { void main() {
group('notifications', () { group('notifications', () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient( client = await getTestClient(
container, container,
username: 'admin', username: 'admin',
@ -103,13 +100,10 @@ void main() {
}); });
group('Push', () { group('Push', () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient( client = await getTestClient(
container, container,
username: 'admin', username: 'admin',

5
packages/nextcloud/test/provisioning_api_test.dart

@ -7,13 +7,10 @@ void main() {
group( group(
'provisioning_api', 'provisioning_api',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient( client = await getTestClient(
container, container,
username: 'admin', username: 'admin',

47
packages/nextcloud/test/settings_test.dart

@ -5,29 +5,30 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future<void> main() async { void main() {
await run(await getDockerImage()); group(
} 'settings',
() {
Future<void> run(final DockerImage image) async { late DockerContainer container;
group('settings', () { late TestNextcloudClient client;
late DockerContainer container; setUp(() async {
late TestNextcloudClient client; container = await getDockerContainer();
setUp(() async { client = await getTestClient(
container = await getDockerContainer(image); container,
client = await getTestClient( username: 'admin',
container, );
username: 'admin', });
); tearDown(() => container.destroy());
});
tearDown(() => container.destroy());
group('Logs', () { group('Logs', () {
test('Download', () async { test('Download', () async {
final response = await client.settings.logSettings.download(); final response = await client.settings.logSettings.download();
final logs = utf8.decode(response.body); final logs = utf8.decode(response.body);
expect(logs, await container.nextcloudLogs()); expect(logs, await container.nextcloudLogs());
});
}); });
}); },
}); retry: retryCount,
timeout: timeout,
);
} }

8
packages/nextcloud/test/spreed_test.dart

@ -12,16 +12,10 @@ void main() {
group( group(
'spreed', 'spreed',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client1; late TestNextcloudClient client1;
setUp(() async { setUp(() async {
container = await getDockerContainer( container = await getDockerContainer();
image,
useApache: true,
);
client1 = await getTestClient(container); client1 = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

5
packages/nextcloud/test/uppush_test.dart

@ -7,13 +7,10 @@ void main() {
group( group(
'uppush', 'uppush',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient( client = await getTestClient(
container, container,
username: 'admin', username: 'admin',

5
packages/nextcloud/test/user_status_test.dart

@ -7,13 +7,10 @@ void main() {
group( group(
'user_status', 'user_status',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

5
packages/nextcloud/test/webdav_test.dart

@ -26,14 +26,11 @@ void main() {
group( group(
'webdav', 'webdav',
() { () {
late DockerImage image;
setUpAll(() async => image = await getDockerImage());
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(image); container = await getDockerContainer();
client = await getTestClient(container); client = await getTestClient(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

15
tool/Dockerfile.dev

@ -1,9 +1,7 @@
ARG SERVER_VERSION=27.1.3@sha256:4ab3159b25310ffdb699f532d45f706d5d949100ed7ac11820d83467f29678b9 ARG SERVER_VERSION=27.1.3-fpm-alpine@sha256:70ddd4abb804bc061a4f882a81cc62a219b262c0ed1784439e66b091ef26104b
FROM nextcloud:$SERVER_VERSION FROM nextcloud:$SERVER_VERSION
WORKDIR /usr/src/nextcloud WORKDIR /usr/src/nextcloud
RUN chown -R www-data:www-data .
USER www-data
RUN ./occ maintenance:install --admin-pass admin --admin-email admin@example.com RUN ./occ maintenance:install --admin-pass admin --admin-email admin@example.com
RUN ./occ config:system:set allow_local_remote_servers --value=true RUN ./occ config:system:set allow_local_remote_servers --value=true
@ -30,8 +28,11 @@ RUN install_app_version spreed https://github.com/nextcloud-releases/spreed/rele
RUN ./occ talk:turn:add turn,turns staticauth.openrelay.metered.ca:443 udp,tcp --secret openrelayprojectsecret RUN ./occ talk:turn:add turn,turns staticauth.openrelay.metered.ca:443 udp,tcp --secret openrelayprojectsecret
RUN ./occ app:enable password_policy RUN ./occ app:enable password_policy
RUN (sh /entrypoint.sh php -S 0.0.0.0:8080 &) && \ RUN (sh /entrypoint.sh php -S 0.0.0.0:80 &) && \
until curl -s -o /dev/null http://localhost:8080/status.php; do true; done && \ until curl -s -o /dev/null http://localhost/status.php; do true; done && \
# Do not setup the demo user here # Do not setup the demo user here
for user in admin user1 user2; do curl -u "$user:$user" -H "ocs-apirequest: true" -s -o /dev/null http://localhost:8080/ocs/v2.php/cloud/user; done for user in admin user1 user2; do curl -u "$user:$user" -H "ocs-apirequest: true" -s -o /dev/null http://localhost/ocs/v2.php/cloud/user; done
COPY --chown=www-data:www-data overlay /usr/src/nextcloud/ COPY --chown=www-data:www-data dev/static /usr/src/nextcloud/static
ENV PHP_CLI_SERVER_WORKERS=10
CMD ["php", "-S", "0.0.0.0:80"]

11
tool/dev.sh

@ -6,4 +6,13 @@ source tool/common.sh
./tool/build-dev-container.sh ./tool/build-dev-container.sh
echo "Running development instance on http://localhost. To access it in an Android Emulator use http://10.0.2.2" echo "Running development instance on http://localhost. To access it in an Android Emulator use http://10.0.2.2"
docker run --rm -v nextcloud-neon-dev:/usr/src/nextcloud -v nextcloud-neon-dev:/var/www/html -p "80:80" --add-host=host.docker.internal:host-gateway "$(image_tag "dev:latest")"
container="$(docker run -d --rm -v nextcloud-neon-dev:/usr/src/nextcloud -v nextcloud-neon-dev:/var/www/html -p "80:80" --add-host=host.docker.internal:host-gateway "$(image_tag "dev:latest")")"
function cleanup() {
docker kill "$container"
}
trap cleanup EXIT
docker logs -f "$container" &
sleep infinity

4
tool/dev/install_app_version

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/sh
set -euxo pipefail set -euxo
app="$1" app="$1"
url="$2" url="$2"

0
packages/nextcloud/test/files/nasa.xml → tool/dev/static/nasa.xml

0
packages/nextcloud/test/files/wikipedia.xml → tool/dev/static/wikipedia.xml

0
tool/overlay/.gitkeep

Loading…
Cancel
Save