diff --git a/.github/workflows/build-artifact.yml b/.github/workflows/build-artifact.yml
index fe513c501edae..a2cbfdb849649 100644
--- a/.github/workflows/build-artifact.yml
+++ b/.github/workflows/build-artifact.yml
@@ -50,24 +50,33 @@ jobs:
outputs:
external-apps-matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+ with:
+ submodules: true
+ fetch-depth: '1'
+
+ - name: Install dependencies
+ run: sudo apt-get update && sudo apt-get install -y make jq
+
- name: Set matrix
id: set-matrix
run: |
# Create matrix configuration as a compact JSON string
matrix='[
{
- "name": "richdocuments",
- "path": "apps-external/richdocuments",
+ "name": "activity",
+ "path": "apps-external/activity",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_richdocuments_app"
+ "makefile_target": "build_activity_app"
},
{
- "name": "viewer",
- "path": "apps-external/viewer",
+ "name": "assistant",
+ "path": "apps-external/assistant",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_viewer_app"
+ "makefile_target": "build_assistant_app"
},
{
"name": "calendar",
@@ -77,11 +86,18 @@ jobs:
"makefile_target": "build_calendar_app"
},
{
- "name": "activity",
- "path": "apps-external/activity",
+ "name": "circles",
+ "path": "apps-external/circles",
+ "has_npm": false,
+ "has_composer": true,
+ "makefile_target": "build_circles_app"
+ },
+ {
+ "name": "collectives",
+ "path": "apps-external/collectives",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_activity_app"
+ "makefile_target": "build_collectives_app"
},
{
"name": "contacts",
@@ -91,46 +107,39 @@ jobs:
"makefile_target": "build_contacts_app"
},
{
- "name": "collectives",
- "path": "apps-external/collectives",
+ "name": "deck",
+ "path": "apps-external/deck",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_collectives_app"
- },
- {
- "name": "circles",
- "path": "apps-external/circles",
- "has_npm": false,
- "has_composer": true,
- "makefile_target": "build_circles_app"
+ "makefile_target": "build_deck_app"
},
{
- "name": "notifications",
- "path": "apps-external/notifications",
+ "name": "end_to_end_encryption",
+ "path": "apps-external/end_to_end_encryption",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_notifications_app"
+ "makefile_target": "build_end_to_end_encryption_app"
},
{
- "name": "notify_push",
- "path": "apps-external/notify_push",
- "has_npm": false,
+ "name": "forms",
+ "path": "apps-external/forms",
+ "has_npm": true,
"has_composer": true,
- "makefile_target": "build_notify_push_app"
+ "makefile_target": "build_forms_app"
},
{
- "name": "tasks",
- "path": "apps-external/tasks",
+ "name": "groupfolders",
+ "path": "apps-external/groupfolders",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_tasks_app"
+ "makefile_target": "build_groupfolders_app"
},
{
- "name": "spreed",
- "path": "apps-external/spreed",
+ "name": "integration_openai",
+ "path": "apps-external/integration_openai",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_spreed_app"
+ "makefile_target": "build_integration_openai_app"
},
{
"name": "mail",
@@ -149,7 +158,7 @@ jobs:
{
"name": "ncw_mailtemplate",
"path": "apps-external/ncw_mailtemplate",
- "has_npm": false,
+ "has_npm": true,
"has_composer": true,
"makefile_target": "build_ncw_mailtemplate_app"
},
@@ -161,32 +170,39 @@ jobs:
"makefile_target": "build_notes_app"
},
{
- "name": "groupfolders",
- "path": "apps-external/groupfolders",
+ "name": "notifications",
+ "path": "apps-external/notifications",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_groupfolders_app"
+ "makefile_target": "build_notifications_app"
},
{
- "name": "deck",
- "path": "apps-external/deck",
+ "name": "notify_push",
+ "path": "apps-external/notify_push",
+ "has_npm": false,
+ "has_composer": true,
+ "makefile_target": "build_notify_push_app"
+ },
+ {
+ "name": "password_policy",
+ "path": "apps-external/password_policy",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_deck_app"
+ "makefile_target": "build_password_policy_app"
},
{
- "name": "end_to_end_encryption",
- "path": "apps-external/end_to_end_encryption",
+ "name": "richdocuments",
+ "path": "apps-external/richdocuments",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_end_to_end_encryption_app"
+ "makefile_target": "build_richdocuments_app"
},
{
- "name": "forms",
- "path": "apps-external/forms",
+ "name": "spreed",
+ "path": "apps-external/spreed",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_forms_app"
+ "makefile_target": "build_spreed_app"
},
{
"name": "tables",
@@ -195,6 +211,13 @@ jobs:
"has_composer": true,
"makefile_target": "build_tables_app"
},
+ {
+ "name": "tasks",
+ "path": "apps-external/tasks",
+ "has_npm": true,
+ "has_composer": true,
+ "makefile_target": "build_tasks_app"
+ },
{
"name": "text",
"path": "apps-external/text",
@@ -210,25 +233,25 @@ jobs:
"makefile_target": "build_twofactor_totp_app"
},
{
- "name": "whiteboard",
- "path": "apps-external/whiteboard",
+ "name": "user_oidc",
+ "path": "apps-external/user_oidc",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_whiteboard_app"
+ "makefile_target": "build_user_oidc_app"
},
{
- "name": "assistant",
- "path": "apps-external/assistant",
+ "name": "viewer",
+ "path": "apps-external/viewer",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_assistant_app"
+ "makefile_target": "build_viewer_app"
},
{
- "name": "integration_openai",
- "path": "apps-external/integration_openai",
+ "name": "whiteboard",
+ "path": "apps-external/whiteboard",
"has_npm": true,
"has_composer": true,
- "makefile_target": "build_integration_openai_app"
+ "makefile_target": "build_whiteboard_app"
}
]'
@@ -241,6 +264,254 @@ jobs:
exit 1
fi
+ - name: Validate matrix against Makefile
+ run: |
+ set +e # Intentionally allow script to continue on error for custom error handling and reporting to GITHUB_STEP_SUMMARY
+ set -u # Exit on undefined variable
+
+ echo "### 🔍 Matrix Validation" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Debug: Check if apps-external exists
+ echo "Checking apps-external directory..."
+ if [ ! -d "apps-external" ]; then
+ echo "❌ **Error:** apps-external directory does not exist!" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Directory listing:" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ ls -la >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ exit 1
+ fi
+
+ echo "Apps-external directory exists. Listing contents:"
+ ls -la apps-external/ | head -10
+
+ # Check if jq is available
+ echo "Checking if jq is installed..."
+ if ! command -v jq &> /dev/null; then
+ echo "❌ **Error:** jq is not installed!" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "jq is required for matrix generation but was not found in PATH." >> $GITHUB_STEP_SUMMARY
+ exit 1
+ fi
+ echo "jq version: $(jq --version)"
+
+ echo "Generating matrix from Makefile..."
+ # Capture both stdout and stderr separately to better diagnose issues
+ makefile_output=$(make -f IONOS/Makefile generate_external_apps_matrix_json 2>&1)
+ makefile_exit_code=$?
+
+ echo "Makefile exit code: ${makefile_exit_code}"
+ echo "Makefile output length: ${#makefile_output}"
+
+ # Debug: Check if GITHUB_STEP_SUMMARY is set
+ echo "GITHUB_STEP_SUMMARY: ${GITHUB_STEP_SUMMARY:-NOT SET}"
+
+ # If the Makefile command failed, show the error
+ if [ ${makefile_exit_code} -ne 0 ]; then
+ echo ""
+ echo "=== MAKEFILE ERROR ==="
+ echo "Exit code: ${makefile_exit_code}"
+ echo "Output:"
+ echo "$makefile_output"
+ echo "====================="
+ echo ""
+
+ # Write to summary
+ echo "❌ **Error:** Makefile command failed with exit code ${makefile_exit_code}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Makefile error output
" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "$makefile_output" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo " " >> $GITHUB_STEP_SUMMARY
+
+ echo "Error written to summary file: ${GITHUB_STEP_SUMMARY}"
+ exit 1
+ fi
+
+ # Filter out the info message to get just the JSON
+ # The Makefile outputs "[i] Generating..." to stderr, but we captured everything with 2>&1
+ # So we need to extract just the JSON part
+ generated_matrix=$(echo "$makefile_output" | grep -v '^\[i\]' || echo "$makefile_output")
+
+ workflow_matrix='${{ steps.set-matrix.outputs.matrix }}'
+
+ # Debug output
+ echo "Generated matrix length: ${#generated_matrix}"
+ echo "Workflow matrix length: ${#workflow_matrix}"
+
+ # Show first 200 chars of generated matrix for debugging
+ if [ -n "$generated_matrix" ]; then
+ echo "Generated matrix preview: ${generated_matrix:0:200}..."
+ fi
+
+ # Validate that we got valid JSON
+ if ! echo "$generated_matrix" | jq empty 2>/dev/null; then
+ echo "❌ **Error:** Generated matrix is not valid JSON" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Invalid JSON output
" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "$generated_matrix" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo " " >> $GITHUB_STEP_SUMMARY
+ exit 1
+ fi
+
+ # Validate that we got data
+ if [ -z "$generated_matrix" ] || [ -z "$workflow_matrix" ]; then
+ echo "❌ **Error:** Failed to load matrices" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- Generated matrix empty: $([ -z "$generated_matrix" ] && echo "yes" || echo "no")" >> $GITHUB_STEP_SUMMARY
+ echo "- Workflow matrix empty: $([ -z "$workflow_matrix" ] && echo "yes" || echo "no")" >> $GITHUB_STEP_SUMMARY
+ echo "- Makefile exit code: ${makefile_exit_code}" >> $GITHUB_STEP_SUMMARY
+
+ exit 1
+ fi
+
+ # Sort both matrices for comparison
+ generated_sorted=$(echo "$generated_matrix" | jq -S '.' 2>&1 || echo "ERROR")
+ workflow_sorted=$(echo "$workflow_matrix" | jq -S '.' 2>&1 || echo "ERROR")
+
+ echo "Sorted matrix lengths - generated: ${#generated_sorted}, workflow: ${#workflow_sorted}"
+
+ # Compare the two matrices
+ if [ "$generated_sorted" = "$workflow_sorted" ]; then
+ echo "✅ **Validation passed!** The workflow matrix matches the Makefile configuration." >> $GITHUB_STEP_SUMMARY
+ echo ""
+ echo "✅ Matrix validation passed!"
+ else
+ echo "❌ **Validation failed!** The workflow matrix does not match the Makefile configuration." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ echo "Starting detailed comparison..."
+
+ # Extract app names from both matrices
+ generated_apps=$(echo "$generated_matrix" | jq -r '.[].name' 2>/dev/null | sort || echo "")
+ workflow_apps=$(echo "$workflow_matrix" | jq -r '.[].name' 2>/dev/null | sort || echo "")
+
+ echo "Generated apps count: $(echo "$generated_apps" | wc -l)"
+ echo "Workflow apps count: $(echo "$workflow_apps" | wc -l)"
+
+ # Find missing apps (in Makefile but not in workflow)
+ missing_apps=$(comm -23 <(echo "$generated_apps") <(echo "$workflow_apps"))
+ if [ $? -ne 0 ]; then
+ echo "Error: comm command failed when finding missing apps." >&2
+ exit 1
+ fi
+ # Find extra apps (in workflow but not in Makefile)
+ extra_apps=$(comm -13 <(echo "$generated_apps") <(echo "$workflow_apps"))
+ if [ $? -ne 0 ]; then
+ echo "Error: comm command failed when finding extra apps." >&2
+ exit 1
+ fi
+
+ echo "Missing apps: ${missing_apps:-none}"
+ echo "Extra apps: ${extra_apps:-none}"
+
+ if [ -n "$missing_apps" ]; then
+ echo "#### ⚠️ Missing Apps" >> $GITHUB_STEP_SUMMARY
+ echo "The following apps are configured in the Makefile but missing from the workflow:" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "$missing_apps" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ if [ -n "$extra_apps" ]; then
+ echo "#### ⚠️ Extra Apps" >> $GITHUB_STEP_SUMMARY
+ echo "The following apps are in the workflow but not configured in the Makefile:" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "$extra_apps" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ # Check for configuration mismatches in common apps
+ common_apps=$(comm -12 <(echo "$generated_apps") <(echo "$workflow_apps") 2>/dev/null || echo "")
+
+ echo "Common apps count: $(echo "$common_apps" | wc -l)"
+
+ if [ -n "$common_apps" ]; then
+ mismatched_apps=""
+
+ while IFS= read -r app; do
+ [ -z "$app" ] && continue
+ gen_config=$(echo "$generated_matrix" | jq -c --arg app "$app" '.[] | select(.name == $app)' 2>/dev/null || echo "")
+ wf_config=$(echo "$workflow_matrix" | jq -c --arg app "$app" '.[] | select(.name == $app)' 2>/dev/null || echo "")
+
+ if [ -n "$gen_config" ] && [ -n "$wf_config" ] && [ "$gen_config" != "$wf_config" ]; then
+ mismatched_apps="${mismatched_apps}${app}"$'\n'
+ fi
+ done <<< "$common_apps"
+
+ echo "Mismatched apps: ${mismatched_apps:-none}"
+
+ if [ -n "$mismatched_apps" ]; then
+ echo "#### ⚠️ Configuration Mismatches" >> $GITHUB_STEP_SUMMARY
+ echo "The following apps have different configurations (has_npm, has_composer, etc.):" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "$mismatched_apps" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "📋 Detailed differences
" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo '```diff' >> $GITHUB_STEP_SUMMARY
+
+ while IFS= read -r app; do
+ [ -z "$app" ] && continue
+ echo "=== $app ===" >> $GITHUB_STEP_SUMMARY
+ diff -u --label "Workflow" --label "Makefile" \
+ <(echo "$workflow_matrix" | jq --arg app "$app" '.[] | select(.name == $app)' 2>/dev/null || echo "{}") \
+ <(echo "$generated_matrix" | jq --arg app "$app" '.[] | select(.name == $app)' 2>/dev/null || echo "{}") \
+ >> $GITHUB_STEP_SUMMARY 2>&1 || true
+ done <<< "$mismatched_apps"
+
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo " " >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ fi
+
+ # Provide fix instructions
+ echo "#### 🔧 How to Fix" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Run this command locally to generate the correct matrix:" >> $GITHUB_STEP_SUMMARY
+ echo '```bash' >> $GITHUB_STEP_SUMMARY
+ echo "make -f IONOS/Makefile generate_external_apps_matrix_json" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Then update the \`matrix\` variable in \`.github/workflows/build-artifact.yml\` in the set-matrix step with the generated output." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Show full diff in expandable section
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "📄 Full matrix comparison
" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "**Workflow Matrix:**" >> $GITHUB_STEP_SUMMARY
+ echo '```json' >> $GITHUB_STEP_SUMMARY
+ echo "$workflow_matrix" | jq '.' 2>/dev/null >> $GITHUB_STEP_SUMMARY || echo "$workflow_matrix" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "**Makefile Matrix:**" >> $GITHUB_STEP_SUMMARY
+ echo '```json' >> $GITHUB_STEP_SUMMARY
+ echo "$generated_matrix" | jq '.' 2>/dev/null >> $GITHUB_STEP_SUMMARY || echo "$generated_matrix" >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
+ echo " " >> $GITHUB_STEP_SUMMARY
+
+ echo ""
+ echo "❌ ERROR: Matrix validation failed!"
+ echo "See the job summary for details on what's wrong and how to fix it."
+ echo "Summary file size: $(wc -c < $GITHUB_STEP_SUMMARY || echo 0) bytes"
+ exit 1
+ fi
+
build-external-apps:
runs-on: ubuntu-latest
needs: prepare-matrix
diff --git a/.gitmodules b/.gitmodules
index cb26b7c31205b..8011d8c0460ec 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -79,6 +79,9 @@
[submodule "apps-external/notifications"]
path = apps-external/notifications
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
[submodule "apps-external/end_to_end_encryption"]
path = apps-external/end_to_end_encryption
url = git@github.com:nextcloud/end_to_end_encryption.git
diff --git a/IONOS b/IONOS
index 82b2772197158..c18550dbc23c9 160000
--- a/IONOS
+++ b/IONOS
@@ -1 +1 @@
-Subproject commit 82b27721971588b98846717a87a9e64094c9dae3
+Subproject commit c18550dbc23c91aaf20f15c62027cc1c812c9525
diff --git a/apps-external/mail b/apps-external/mail
index 4534c95c95b9f..2addc199e43cf 160000
--- a/apps-external/mail
+++ b/apps-external/mail
@@ -1 +1 @@
-Subproject commit 4534c95c95b9fa0e919256bd88efdb9df656a607
+Subproject commit 2addc199e43cf95ccb20ffe39b379786be9c5ffe
diff --git a/apps-external/user_oidc b/apps-external/user_oidc
new file mode 160000
index 0000000000000..192826636cc8b
--- /dev/null
+++ b/apps-external/user_oidc
@@ -0,0 +1 @@
+Subproject commit 192826636cc8b25acfb43a56617db0758869f808
diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php
index f97396c5a9a32..05eff159e0ac1 100644
--- a/apps/user_ldap/lib/Access.php
+++ b/apps/user_ldap/lib/Access.php
@@ -598,7 +598,21 @@ public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped
)
) {
$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
- $newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
+ try {
+ $newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
+ } catch (HintException $e) {
+ // User limit reached - log and return false
+ $this->logger->warning(
+ 'Could not map {dn} as {name}: {message}.',
+ [
+ 'app' => 'user_ldap',
+ 'dn' => $fdn,
+ 'name' => $intName,
+ 'message' => $e->getMessage(),
+ ]
+ );
+ return false;
+ }
if ($newlyMapped) {
$this->logger->debug('Mapped {fdn} as {name}', ['fdn' => $fdn,'name' => $intName]);
return $intName;
diff --git a/core/Notification/CoreNotifier.php b/core/Notification/CoreNotifier.php
index 83a86513e03c1..3915ae5d6e1d3 100644
--- a/core/Notification/CoreNotifier.php
+++ b/core/Notification/CoreNotifier.php
@@ -59,16 +59,7 @@ public function prepare(INotification $notification, string $languageCode): INot
if ($notification->getSubject() === 'user_limit_reached') {
$notification->setParsedSubject($l->t('The account limit of this instance is reached.'));
- $notification->setParsedMessage($l->t('Enter your subscription key in the support app in order to increase the account limit. This does also grant you all additional benefits that Nextcloud Enterprise offers and is highly recommended for the operation in companies.'));
$notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'places/contacts.svg')));
- $action = $notification->createAction();
- $label = $l->t('Learn more ↗');
- $link = $this->config->getSystemValueString('one-click-instance.link', 'https://nextcloud.com/enterprise/');
- $action->setLabel($label)
- ->setParsedLabel($label)
- ->setLink($link, IAction::TYPE_WEB)
- ->setPrimary(true);
- $notification->addParsedAction($action);
return $notification;
}