Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .github/workflows/phpunit-sqlite-s3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: PHPUnit SQLite - S3

on: pull_request

permissions:
contents: read

concurrency:
group: phpunit-sqlite-s3-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
matrix:
runs-on: ubuntu-latest-low
outputs:
php-version: ${{ steps.versions.outputs.php-available-list }}
server-max: ${{ steps.versions.outputs.branches-max-list }}
steps:
- name: Checkout app
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false

- name: Get version matrix
id: versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1

changes:
runs-on: ubuntu-latest-low
permissions:
contents: read
pull-requests: read

outputs:
src: ${{ steps.changes.outputs.src}}

steps:
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: changes
continue-on-error: true
with:
filters: |
src:
- '.github/workflows/**'
- 'appinfo/**'
- 'lib/**'
- 'templates/**'
- 'tests/**'
- 'vendor/**'
- 'vendor-bin/**'
- '.php-cs-fixer.dist.php'
- 'composer.json'
- 'composer.lock'

phpunit-sqlite:
runs-on: ubuntu-latest

needs: [changes, matrix]
if: needs.changes.outputs.src != 'false'

strategy:
matrix:
php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }}
server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }}

name: SQLite PHP S3 ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }}

services:
minio:
image: bitnami/minio@sha256:50cec18ac4184af4671a78aedd5554942c8ae105d51a465fa82037949046da01 # v2025.4.22
env:
MINIO_ROOT_USER: nextcloud
MINIO_ROOT_PASSWORD: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ=
MINIO_DEFAULT_BUCKETS: nextcloud
ports:
- "9000:9000"

steps:
- name: Set app env
if: ${{ env.APP_NAME == '' }}
run: |
# Split and keep last
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV

- name: Checkout server
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
submodules: true
repository: nextcloud/server
ref: ${{ matrix.server-versions }}

- name: Checkout Circles
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: master
path: apps/circles

- name: Checkout app
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
path: apps/${{ env.APP_NAME }}

- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
coverage: none
ini-file: development
# Temporary workaround for missing pcntl_* in PHP 8.3
ini-values: disable_functions=
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check composer file existence
id: check_composer
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0
with:
files: apps/${{ env.APP_NAME }}/composer.json

- name: Set up Circles dependencies
working-directory: apps/circles
run: composer i

- name: Set up dependencies
# Only run if phpunit config file exists
if: steps.check_composer.outputs.files_exists == 'true'
working-directory: apps/${{ env.APP_NAME }}
run: |
composer remove nextcloud/ocp --dev --no-scripts
composer i

- name: Set up Nextcloud
env:
DB_PORT: 4444
run: |
mkdir data
echo '<?php $CONFIG=["objectstore" => ["class" => "OC\Files\ObjectStore\S3", "arguments" => ["bucket" => "nextcloud", "autocreate" => true, "key" => "nextcloud", "secret" => "bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ=", "hostname" => "localhost", "port" => 9000, "use_ssl" => false, "use_path_style" => true, "uploadPartSize" => 52428800]]];' > config/config.php
./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
./occ app:enable --force circles
./occ app:enable --force ${{ env.APP_NAME }}

- name: Check PHPUnit script is defined
id: check_phpunit
continue-on-error: true
working-directory: apps/${{ env.APP_NAME }}
run: |
composer run --list | grep '^ test:unit ' | wc -l | grep 1

- name: PHPUnit
# Only run if phpunit config file exists
if: steps.check_phpunit.outcome == 'success'
working-directory: apps/${{ env.APP_NAME }}
run: composer run test:unit

- name: Check PHPUnit integration script is defined
id: check_integration
continue-on-error: true
working-directory: apps/${{ env.APP_NAME }}
run: |
composer run --list | grep '^ test:integration ' | wc -l | grep 1

- name: Run Nextcloud
# Only run if phpunit integration config file exists
if: steps.check_integration.outcome == 'success'
run: php -S localhost:8080 &

- name: PHPUnit integration
# Only run if phpunit integration config file exists
if: steps.check_integration.outcome == 'success'
working-directory: apps/${{ env.APP_NAME }}
run: composer run test:integration

- name: Print logs
if: always()
run: |
cat data/nextcloud.log

- name: Skipped
# Fail the action when neither unit nor integration tests ran
if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure'
run: |
echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts'
exit 1

summary:
permissions:
contents: none
runs-on: ubuntu-latest-low
needs: [changes, phpunit-sqlite]

if: always()

name: phpunit-sqlite-summary

steps:
- name: Summary status
run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-sqlite.result != 'success' }}; then exit 1; fi
5 changes: 5 additions & 0 deletions lib/Folder/FolderDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public function __construct(
public readonly bool $acl,
public readonly int $storageId,
public readonly int $rootId,
public readonly array $options,
) {
}

public function useSeparateStorage(): bool {
return $this->options['separate-storage'] ?? false;
}
}
4 changes: 3 additions & 1 deletion lib/Folder/FolderDefinitionWithMappings.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ public function __construct(
bool $acl,
int $storageId,
int $rootId,
array $options,
public readonly array $groups,
public readonly array $manage,
) {
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId);
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId, $options);
}

/**
Expand All @@ -44,6 +45,7 @@ public static function fromFolder(FolderDefinition $folder, array $groups, array
$folder->acl,
$folder->storageId,
$folder->rootId,
$folder->options,
$groups,
$manage,
);
Expand Down
5 changes: 4 additions & 1 deletion lib/Folder/FolderDefinitionWithPermissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ public function __construct(
bool $acl,
int $storageId,
int $rootId,
array $options,
public readonly ICacheEntry $rootCacheEntry,
public readonly int $permissions,
) {
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId);
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId, $options);
}

/**
Expand All @@ -45,6 +46,7 @@ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCac
$folder->acl,
$folder->storageId,
$folder->rootId,
$folder->options,
$rootCacheEntry,
$permissions,
);
Expand All @@ -71,6 +73,7 @@ public function withAddedPermissions(int $permissions): self {
$this->acl,
$this->storageId,
$this->rootId,
$this->options,
$this->rootCacheEntry,
$this->permissions | $permissions,
);
Expand Down
10 changes: 8 additions & 2 deletions lib/Folder/FolderManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function getAllFolders(): array {

$query = $this->connection->getQueryBuilder();

$query->select('folder_id', 'mount_point', 'quota', 'acl', 'storage_id', 'root_id')
$query->select('folder_id', 'mount_point', 'quota', 'acl', 'storage_id', 'root_id', 'options')
->from('group_folders', 'f');

$rows = $query->executeQuery()->fetchAll();
Expand Down Expand Up @@ -108,6 +108,7 @@ private function selectWithFileCache(?IQueryBuilder $query = null): IQueryBuilde
'acl',
'storage_id',
'root_id',
'options',
'c.fileid',
'c.storage',
'c.path',
Expand Down Expand Up @@ -549,13 +550,15 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $
}

private function rowToFolder(array $row): FolderDefinition {
$options = json_decode($row['options'], true);
return new FolderDefinition(
(int)$row['folder_id'],
(string)$row['mount_point'],
$this->getRealQuota((int)$row['quota']),
(bool)$row['acl'],
(int)$row['storage_id'],
(int)$row['root_id'],
is_array($options) ? $options : [],
);
}

Expand Down Expand Up @@ -660,11 +663,14 @@ public function createFolder(string $mountPoint): int {
->values([
'mount_point' => $query->createNamedParameter($mountPoint),
'quota' => self::SPACE_DEFAULT,
'options' => $query->createNamedParameter(json_encode([
'separate-storage' => true,
]))
]);
$query->executeStatement();
$id = $query->getLastInsertId();

['storage_id' => $storageId, 'root_id' => $rootId] = $this->folderStorageManager->getRootAndStorageIdForFolder($id);
['storage_id' => $storageId, 'root_id' => $rootId] = $this->folderStorageManager->initRootAndStorageForFolder($id, true);
$query->update('group_folders')
->set('root_id', $query->createNamedParameter($rootId))
->set('storage_id', $query->createNamedParameter($storageId))
Expand Down
4 changes: 3 additions & 1 deletion lib/Folder/FolderWithMappingsAndCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ public function __construct(
bool $acl,
int $storageId,
int $rootId,
array $options,
array $groups,
array $manage,
public readonly ICacheEntry $rootCacheEntry,
) {
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId, $groups, $manage);
parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId, $options, $groups, $manage);
}

/**
Expand All @@ -46,6 +47,7 @@ public static function fromFolderWithMapping(FolderDefinitionWithMappings $folde
$folder->acl,
$folder->storageId,
$folder->rootId,
$folder->options,
$folder->groups,
$folder->manage,
$rootCacheEntry,
Expand Down
Loading
Loading