Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1162dcf
IONOS(build): update build artifact pipeline to restore apps from cac…
printminion-co Feb 9, 2026
ba52282
IONOS(build): update upload condition to include manual workflow_disp…
printminion-co Feb 9, 2026
f2fb683
Update .github/workflows/build-artifact.yml
printminion-co Feb 10, 2026
265cdac
IONOS(config): update submodule bacda90 (feat(update-user-email): add…
github-actions[bot] Feb 12, 2026
43e439b
IONOS(activity): Switch activity app to IONOS fork
bromiesTM Feb 5, 2026
2604d51
IONOS(copilot): eductate copilot on recurring workflow docs
bromiesTM Feb 6, 2026
915737a
IONOS(activity): update submodule (delegate settings)
bromiesTM Feb 6, 2026
ae62a61
IONOS(config): update submodule (add delegated activity settings)
bromiesTM Feb 6, 2026
f93f0ad
IONOS(files_pdfviewer): add submodule v31.0.13 (21125af)
Arsalanulhaq Feb 11, 2026
b83bec1
IONOS(config): update submodule bdfd74f (build: enable files_pdfviewe…
github-actions[bot] Feb 12, 2026
f392cc9
IONOS(config): update submodule (add flag to hide dev notice)
bromiesTM Feb 11, 2026
c8cb748
IONOS(Settings): hide footer depending on config flag
bromiesTM Feb 11, 2026
637cd74
IONOS(bruteforcesettings): add new submodule
bromiesTM Feb 11, 2026
30b0f94
IONOS(bruteforcesettings): update submodule (delegate settings)
bromiesTM Feb 11, 2026
1cbfcff
IONOS(config): update submodule (delegate bruteforcesettings)
bromiesTM Feb 11, 2026
b7b0d26
IONOS(user_oidc): change submodule repo
bromiesTM Feb 6, 2026
24a82be
IONOS(user_oidc): update submodule (delegate settings)
bromiesTM Feb 6, 2026
2cc4e2a
IONOS(config): update submodule (add user oidc setting delegation)
bromiesTM Feb 6, 2026
2a3b407
Revert "IONOS(appsettings): conditionally display developer documenta…
Arsalanulhaq Feb 17, 2026
5eb3445
Revert "IONOS(tests): add tests for developer documentation link gene…
Arsalanulhaq Feb 17, 2026
6063639
docs: remove developer documentation link
Arsalanulhaq Dec 2, 2025
01f08b8
Revert "IONOS(config): add developer_documentation_url boolean and bu…
Arsalanulhaq Feb 17, 2026
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
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ The development container provides several convenience features for development:
* Pay attention to the IONOS-specific scripts and configurations when working on related features.
* Use the `IONOS/configure.sh` script for most configuration tasks rather than manual OCC commands.
* Before committing any changes, make sure to run the relevant tests and linters.
* **Recurring Tasks Documentation:** For common development tasks (e.g., delegating settings to admin, adding apps, etc.), check `../<tools-dir>/docs/recurring-tasks/` for step-by-step guides.

## Code Organization & Structure

Expand Down
204 changes: 108 additions & 96 deletions .github/workflows/build-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,6 @@ jobs:
fetch-depth: 1

- name: Setup JFrog CLI
if: needs.prepare-matrix.outputs.has_apps_to_restore == 'true'
uses: jfrog/setup-jfrog-cli@7c95feb32008765e1b4e626b078dfd897c4340ad # v4.4.1
env:
JF_URL: ${{ secrets.JF_ARTIFACTORY_URL }}
Expand All @@ -490,29 +489,111 @@ jobs:
# Ping the server
jf rt ping

- name: Restore cached apps
if: needs.prepare-matrix.outputs.has_apps_to_restore == 'true'
- name: Restore all apps from cache and JFrog
run: |
set -e
set -euo pipefail

# Restore apps that are in cache (from apps_to_restore)
if [ "${{ needs.prepare-matrix.outputs.has_apps_to_restore }}" == "true" ]; then
echo "📦 Restoring cached apps..."
APPS_TO_RESTORE='${{ needs.prepare-matrix.outputs.apps_to_restore }}'

# Process each app in the restore list
echo "$APPS_TO_RESTORE" | jq -c '.[]' | while read -r app_json; do
APP_NAME=$(echo "$app_json" | jq -r '.name')
APP_SHA=$(echo "$app_json" | jq -r '.sha')
APP_PATH=$(echo "$app_json" | jq -r '.path')
SOURCE=$(echo "$app_json" | jq -r '.source')

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Restoring: $APP_NAME (source: $SOURCE)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

if [ "$SOURCE" == "jfrog" ]; then
# Restore from JFrog
JFROG_PATH=$(echo "$app_json" | jq -r '.jfrog_path')
ARCHIVE_NAME="${APP_NAME}-${APP_SHA}.tar.gz"

echo "📥 Downloading from JFrog: $JFROG_PATH"

if jf rt download "$JFROG_PATH" "$ARCHIVE_NAME" --flat=true; then
echo "✅ Downloaded successfully"
echo "Extracting to $APP_PATH..."
mkdir -p "$(dirname "$APP_PATH")"
tar -xzf "$ARCHIVE_NAME" -C "$(dirname "$APP_PATH")"

if [ -d "$APP_PATH" ] && [ -f "$APP_PATH/appinfo/info.xml" ]; then
echo "✅ Restored $APP_NAME from JFrog"
else
echo "❌ Failed to extract or validate $APP_NAME"
exit 1
fi

rm -f "$ARCHIVE_NAME"
else
echo "❌ Failed to download from JFrog"
exit 1
fi

echo "📦 Restoring cached apps..."
APPS_TO_RESTORE='${{ needs.prepare-matrix.outputs.apps_to_restore }}'
elif [ "$SOURCE" == "github-cache" ]; then
# Restore from GitHub cache
CACHE_KEY=$(echo "$app_json" | jq -r '.cache_key')

# Process each app in the restore list
echo "$APPS_TO_RESTORE" | jq -c '.[]' | while read -r app_json; do
APP_NAME=$(echo "$app_json" | jq -r '.name')
APP_SHA=$(echo "$app_json" | jq -r '.sha')
APP_PATH=$(echo "$app_json" | jq -r '.path')
SOURCE=$(echo "$app_json" | jq -r '.source')
echo "💾 Restoring from GitHub cache: $CACHE_KEY"

# Use actions/cache/restore in a way that works in a script context
# We need to use gh CLI to restore the cache
if gh cache restore "$CACHE_KEY" --key "$CACHE_KEY"; then
echo "✅ Restored $APP_NAME from GitHub cache"

# Validate restoration
if [ ! -d "$APP_PATH" ] || [ ! -f "$APP_PATH/appinfo/info.xml" ]; then
echo "❌ Validation failed for $APP_NAME"
exit 1
fi
else
echo "❌ Failed to restore from GitHub cache"
exit 1
fi
else
echo "❌ Unknown source: $SOURCE"
exit 1
fi
done

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Restoring: $APP_NAME (source: $SOURCE)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ All cached apps restored successfully"
fi

# Also restore newly built apps from JFrog (from apps_to_build)
if [ "${{ needs.prepare-matrix.outputs.has_apps_to_build }}" == "true" ]; then
echo ""
echo "📦 Restoring newly built apps from JFrog..."
APPS_TO_BUILD='${{ needs.prepare-matrix.outputs.apps_to_build }}'
FULL_MATRIX='${{ needs.prepare-matrix.outputs.external_apps_matrix }}'
EFFECTIVE_CACHE_VERSION="${{ needs.prepare-matrix.outputs.effective_cache_version }}"

# Process each app in the build list
echo "$APPS_TO_BUILD" | jq -c '.[]' | while read -r app_json; do
APP_NAME=$(echo "$app_json" | jq -r '.name')
APP_SHA=$(echo "$app_json" | jq -r '.sha')

# Get app path from full matrix
APP_PATH=$(echo "$FULL_MATRIX" | jq -r --arg name "$APP_NAME" '.[] | select(.name == $name) | .path')

if [ "$SOURCE" == "jfrog" ]; then
# Restore from JFrog
JFROG_PATH=$(echo "$app_json" | jq -r '.jfrog_path')
if [ -z "$APP_PATH" ] || [ "$APP_PATH" == "null" ]; then
echo "❌ Could not find path for $APP_NAME in matrix"
exit 1
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Restoring newly built: $APP_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# Construct JFrog path matching what build-external-apps uploaded
JFROG_PATH="${{ env.ARTIFACTORY_REPOSITORY_SNAPSHOT }}/apps/${EFFECTIVE_CACHE_VERSION}/${APP_NAME}/${APP_NAME}-${APP_SHA}.tar.gz"
ARCHIVE_NAME="${APP_NAME}-${APP_SHA}.tar.gz"

echo "📥 Downloading from JFrog: $JFROG_PATH"
Expand All @@ -535,88 +616,19 @@ jobs:
echo "❌ Failed to download from JFrog"
exit 1
fi
done

elif [ "$SOURCE" == "github-cache" ]; then
# Restore from GitHub cache
CACHE_KEY=$(echo "$app_json" | jq -r '.cache_key')

echo "💾 Restoring from GitHub cache: $CACHE_KEY"

# Use actions/cache/restore in a way that works in a script context
# We need to use gh CLI to restore the cache
if gh cache restore "$CACHE_KEY" --key "$CACHE_KEY"; then
echo "✅ Restored $APP_NAME from GitHub cache"

# Validate restoration
if [ ! -d "$APP_PATH" ] || [ ! -f "$APP_PATH/appinfo/info.xml" ]; then
echo "❌ Validation failed for $APP_NAME"
exit 1
fi
else
echo "❌ Failed to restore from GitHub cache"
exit 1
fi
else
echo "❌ Unknown source: $SOURCE"
exit 1
fi
done
echo ""
echo "✅ All newly built apps restored from JFrog successfully"
fi

echo ""
echo "✅ All cached apps restored successfully"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ All apps restored successfully"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
env:
GH_TOKEN: ${{ github.token }}

- name: Download build external apps
uses: actions/download-artifact@v5
with:
pattern: external-app-build-*
path: apps-external/

- name: Reorganize downloaded apps-external artifacts
run: |
cd apps-external/

echo "Initial structure:"
ls -la

# Move contents from external-app-build-* directories to their target directories
for artifact_dir in external-app-build-*; do
if [ -d "$artifact_dir" ]; then
# Extract app name from artifact directory name
app_name=${artifact_dir#external-app-build-}

echo "Processing artifact: $artifact_dir -> $app_name"

# If target directory exists, merge the contents from the artifact directory containing build artifacts
if [ -d "$app_name" ]; then
echo "Target directory $app_name exists, merging contents from $artifact_dir"
# Copy contents from artifact directory to target directory
cp -r "$artifact_dir"/* "$app_name"/
# Remove the now-empty artifact directory
rm -rf "$artifact_dir"
else
# Move the artifact directory to the proper app name
echo "Moving $artifact_dir to $app_name"
mv "$artifact_dir" "$app_name"
fi
fi
done

echo "Reorganization complete. Final structure:"
ls -la

- name: Verify downloaded artifacts structure
run: |
echo "External apps structure:"
ls -la apps-external/
for app_dir in apps-external/*/; do
if [ -d "$app_dir" ]; then
echo "Contents of $app_dir:"
ls -la "$app_dir"
fi
done

- name: Set up node with version from package.json's engines
uses: actions/setup-node@v6
with:
Expand Down Expand Up @@ -698,10 +710,10 @@ jobs:

upload-to-artifactory:
runs-on: self-hosted
# Upload the artifact to the Artifactory repository on PR *OR* on "ionos-dev|ionos-stable|rc/*" branch push defined in the on:push:branches
# Upload the artifact to the Artifactory repository on PR *OR* on "ionos-dev|ionos-stable|rc/*" branch push defined in the on:push:branches *OR* on manual workflow_dispatch
if: |
always() &&
(github.event_name == 'pull_request' || github.ref_name == 'ionos-dev' || github.ref_name == 'ionos-stable' || startsWith(github.ref_name, 'rc/')) &&
(github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.ref_name == 'ionos-dev' || github.ref_name == 'ionos-stable' || startsWith(github.ref_name, 'rc/')) &&
needs.prepare-matrix.result == 'success' &&
(needs.build-external-apps.result == 'success' || needs.build-external-apps.result == 'skipped') &&
needs.build-artifact.result == 'success'
Expand Down
10 changes: 8 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
url = git@github.com:nextcloud/notify_push.git
[submodule "apps-external/activity"]
path = apps-external/activity
url = git@github.com:nextcloud/activity.git
url = git@github.com:IONOS-Productivity/nc-activity.git
[submodule "apps-external/tasks"]
path = apps-external/tasks
url = git@github.com:nextcloud/tasks.git
Expand Down Expand Up @@ -81,7 +81,7 @@
url = git@github.com:IONOS-Productivity/nc-notifications.git
[submodule "apps-external/user_oidc"]
path = apps-external/user_oidc
url = git@github.com:nextcloud/user_oidc.git
url = git@github.com:IONOS-Productivity/nc-user_oidc.git
[submodule "apps-external/end_to_end_encryption"]
path = apps-external/end_to_end_encryption
url = git@github.com:nextcloud/end_to_end_encryption.git
Expand All @@ -91,3 +91,9 @@
[submodule "apps-external/ncw_tools"]
path = apps-external/ncw_tools
url = git@github.com:IONOS-Productivity/ncw-tools.git
[submodule "apps-external/files_pdfviewer"]
path = apps-external/files_pdfviewer
url = git@github.com:nextcloud/files_pdfviewer.git
[submodule "apps-external/bruteforcesettings"]
path = apps-external/bruteforcesettings
url = git@github.com:IONOS-Productivity/nc-bruteforcesettings.git
2 changes: 1 addition & 1 deletion IONOS
2 changes: 1 addition & 1 deletion apps-external/activity
1 change: 1 addition & 0 deletions apps-external/bruteforcesettings
Submodule bruteforcesettings added at 423a10
1 change: 1 addition & 0 deletions apps-external/files_pdfviewer
Submodule files_pdfviewer added at 21125a
2 changes: 1 addition & 1 deletion apps-external/user_oidc
8 changes: 0 additions & 8 deletions apps/settings/lib/Controller/AppSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\Http\Client\IClientService;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IL10N;
Expand Down Expand Up @@ -75,7 +74,6 @@ public function __construct(
private IInitialState $initialState,
private AppDiscoverFetcher $discoverFetcher,
private IClientService $clientService,
private IAppConfig $appConfig,
) {
parent::__construct($appName, $request);
$this->appData = $appDataFactory->get('appstore');
Expand All @@ -92,12 +90,6 @@ public function viewApps(): TemplateResponse {

$this->initialState->provideInitialState('appstoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true));
$this->initialState->provideInitialState('appstoreBundles', $this->getBundles());

// Conditionally set developer docs link based on configuration
$displayDocumentationLink = $this->appConfig->getValueBool('settings', 'display_documentation_link', true);
$developerDocsUrl = $displayDocumentationLink ? $this->urlGenerator->linkToDocs('developer-manual') : '';
$this->initialState->provideInitialState('appstoreDeveloperDocs', $developerDocsUrl);

$this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates()));

if ($this->appManager->isInstalled('app_api')) {
Expand Down
6 changes: 6 additions & 0 deletions apps/settings/lib/Settings/Personal/ServerDevNotice.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCP\Settings\ISettings;
use OCP\Support\Subscription\IRegistry;
use OCP\Util;
use OCP\IConfig;

class ServerDevNotice implements ISettings {

Expand All @@ -25,6 +26,7 @@ public function __construct(
private IUserSession $userSession,
private IInitialState $initialState,
private IURLGenerator $urlGenerator,
private IConfig $config,
) {
}

Expand Down Expand Up @@ -62,6 +64,10 @@ public function getSection(): ?string {
return null;
}

if ($this->config->getSystemValueBool('settings.hide-dev-notice')) {
return null;
}

return 'personal-info';
}

Expand Down
5 changes: 0 additions & 5 deletions apps/settings/src/views/AppStoreNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@
</NcAppNavigationItem>
</template>

<NcAppNavigationItem v-if="developerDocsUrl"
id="app-developer-docs"
:name="t('settings', 'Developer documentation ↗')"
:href="developerDocsUrl" />
</template>
</NcAppNavigation>
</template>
Expand All @@ -116,7 +112,6 @@ import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import APPSTORE_CATEGORY_ICONS from '../constants/AppstoreCategoryIcons.ts'

const appstoreEnabled = loadState<boolean>('settings', 'appstoreEnabled', true)
const developerDocsUrl = loadState<string>('settings', 'appstoreDeveloperDocs', '')

const store = useAppsStore()
const categories = computed(() => store.categories)
Expand Down
Loading
Loading