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
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
github-actions:
patterns:
- "*"
68 changes: 68 additions & 0 deletions .github/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Release Scripts

Standalone scripts to build a Nextcloud server release without GitHub Actions. These are the same scripts used by the `release-build.yml` workflow.

## Prerequisites

- `git`, `jq`, `php` (compatible version), `composer`
- `sudo` (for setting file ownership)
- `tar`, `zip`, `sha256sum`, `sha512sum`, `md5sum`
- Signing key (PEM) if signing is needed

## Quick start

Build a release for v34.0.1:

```bash
SCRIPTS=".github/scripts"
VERSION="34.0.1"
TAG="v${VERSION}"
CONFIG="stable34.json"

# 1. Fetch all components
bash "$SCRIPTS/fetch-all.sh" "$TAG" "$CONFIG" /tmp/build --docs

# 2. Clean server dev files (removes .git automatically after using it)
bash "$SCRIPTS/clean-server-dev-files.sh" /tmp/build/server

# 3. Assemble into nextcloud/ structure
bash "$SCRIPTS/assemble.sh" /tmp/build /tmp/nextcloud

# 4. Clean dev files from all apps, core, settings
for dir in /tmp/nextcloud/apps/*/ /tmp/nextcloud/core/ /tmp/nextcloud/settings/; do
bash "$SCRIPTS/clean-dev-files.sh" "$dir"
done

# 5. Update version.php
bash "$SCRIPTS/update-version-php.sh" /tmp/nextcloud stable stable34

# 6. Sign (optional, requires signing key)
bash "$SCRIPTS/sign-release.sh" /tmp/nextcloud /path/to/signing-key.pem

# 7. Generate metadata (NC30+, optional)
bash "$SCRIPTS/generate-metadata.sh" /tmp/nextcloud "$VERSION" ./releases

# 8. Package (creates tar.bz2, zip, checksums)
bash "$SCRIPTS/package.sh" /tmp/nextcloud "$VERSION" ./releases
```

## Scripts

| Script | Purpose |
|---|---|
| `fetch-all.sh` | Clone server, 3rdparty, all apps, updater, skeleton, docs |
| `assemble.sh` | Place all components into the `nextcloud/` structure |
| `clean-server-dev-files.sh` | Remove dev files from server (uses .nextcloudignore or hardcoded list) |
| `clean-dev-files.sh` | Remove dev files from an app directory |
| `update-version-php.sh` | Rewrite version.php with channel, build timestamp, edition |
| `sign-release.sh` | Sign core + all apps with occ integrity commands |
| `generate-metadata.sh` | Generate migration metadata (NC30+) |
| `package.sh` | Set permissions, create tar.bz2 + zip, generate checksums |

## Notes

- `fetch-all.sh` runs composer automatically on apps with runtime dependencies
- `clean-server-dev-files.sh` must run before removing `.git` from the server (it uses `git ls-files` for .nextcloudignore)
- `sign-release.sh` uses the repo's `resources/codesigning/core.crt` by default. Pass a custom cert as the third argument for testing
- `package.sh` requires `sudo` to set file ownership to `nobody:nogroup`
- The workflow adds parallelism (fetches all apps simultaneously) and a compare step. The scripts produce identical output
50 changes: 50 additions & 0 deletions .github/scripts/assemble.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
#
# Assemble a Nextcloud release from fetched components.
# Usage: assemble.sh <build-dir> <output-dir>
#
# Expects the build dir to contain: server/, 3rdparty/, apps/*/, updater/, example-files/

set -e

BUILD="${1:?Usage: assemble.sh <build-dir> <output-dir>}"
OUTPUT="${2:?Missing output directory}"

# Server is the base
cp -a "$BUILD/server" "$OUTPUT"

# Replace 3rdparty
rm -rf "$OUTPUT/3rdparty"
cp -a "$BUILD/3rdparty" "$OUTPUT/3rdparty"

# Place apps
for app_dir in "$BUILD/apps"/*/; do
id=$(basename "$app_dir")
cp -a "$app_dir" "$OUTPUT/apps/$id"
done

# Updater: only index.php + updater.phar
mkdir -p "$OUTPUT/updater"
cp "$BUILD/updater/index.php" "$OUTPUT/updater/index.php"
cp "$BUILD/updater/updater.phar" "$OUTPUT/updater/updater.phar"

# Example files replace skeleton
rm -rf "$OUTPUT/core/skeleton"
cp -a "$BUILD/example-files" "$OUTPUT/core/skeleton"

# Docs (if fetched)
if [ -d "$BUILD/docs" ] && [ "$(ls -A "$BUILD/docs")" ]; then
rm -f "$OUTPUT/core/doc/user/index.php" "$OUTPUT/core/doc/admin/index.php"
[ -d "$BUILD/docs/user_manual/en" ] && cp -a "$BUILD/docs/user_manual/en/"* "$OUTPUT/core/doc/user/"
[ -d "$BUILD/docs/admin_manual" ] && cp -a "$BUILD/docs/admin_manual/"* "$OUTPUT/core/doc/admin/"
[ -f "$BUILD/docs/Nextcloud_User_Manual.pdf" ] && cp -a "$BUILD/docs/Nextcloud_User_Manual.pdf" "$OUTPUT/core/skeleton/Nextcloud Manual.pdf"
fi

# Final cleanup
find "$OUTPUT" -name .git -exec rm -rf {} + 2>/dev/null || true
rm -f "$OUTPUT/config/config.php"
rm -rf "$OUTPUT/data"

echo "Release assembled at $OUTPUT"
90 changes: 90 additions & 0 deletions .github/scripts/clean-dev-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
#
# Clean development files from an app or component directory.
# Usage: clean-dev-files.sh <directory>

set -e

DIR="${1:-.}"
cd "$DIR"

# Files to preserve even if .nextcloudignore lists them
KEEP_FILES=(composer.json composer.lock package.json package-lock.json)

# Process .nextcloudignore if present
if [ -f ".nextcloudignore" ]; then
APP_DIR=$(pwd)
while IFS= read -r line || [[ -n "$line" ]]; do
pattern=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/\r$//')
[[ -z "$pattern" || "$pattern" == \#* ]] && continue

# Check if this pattern targets a preserved file
# e.g. /composer.* would match composer.json — skip the whole pattern
clean="${pattern#/}"
skip=false
for keep in "${KEEP_FILES[@]}"; do
# shellcheck disable=SC2254
if [[ "$keep" == $clean ]]; then
skip=true
break
fi
done
$skip && continue

if [[ "$pattern" == /* ]]; then
pattern="${pattern#/}"
# shellcheck disable=SC2086
for match in $APP_DIR/$pattern; do
[ -e "$match" ] && rm -rf "$match"
done
else
find "$APP_DIR" -name "$pattern" -exec rm -rf {} + 2>/dev/null || true
fi
done < .nextcloudignore
fi

# Remove dev file patterns (synced with release script's getDataToBeRemovedFromAppFolders)
DEV_PATTERNS=(
.babelrc .codecov.yml .devcontainer .drone.yml .editorconfig .eslintignore
.git/ .gitattributes .github .gitignore .git-blame-ignore-revs
.jshintrc .l10nignore .lgtm .nextcloudignore .npmignore .noopenapi
.php_cs.dist .php-cs-fixer.dist.php .prettierignore .scrutinizer.yml
.stylelintignore .travis.yml .tx/
build-js/ build.xml
check-handlebars-templates.sh codecov.yml compile-handlebars-templates.sh
CONTRIBUTING.md
cypress.json cypress/
issue_template.md jest-raw-loader.js jsconfig.json
krankerl.toml l10n/.gitkeep Makefile phpDocumentor.sh psalm.xml
README.md rector.php renovate.json screenshots/ src/ testConfiguration.json
tests/ vendor-bin/
webpack.common.js webpack.dev.js webpack.js webpack.prod.js
)

for pattern in "${DEV_PATTERNS[@]}"; do
rm -rf "$pattern"
if [[ "$pattern" != */ ]]; then
rm -rf "${pattern}.license"
fi
done

# JavaScript config files: cross-product of base names × extensions
# (matches release script's $javascriptConfigs × $suffix loop)
JS_CONFIG_BASES=(
.babelrc .eslintrc .prettierrc .stylelintrc
babel.config cypress.config eslint.config jest.config jsconfig
oxlintrc oxlint.config playwright.config postcss.config prettier.config
rspack.config stylelint.config tsconfig vite.config vitest.config
webpack webpack.config
)
JS_EXTENSIONS=(.json .js .mjs .cjs .ts .mts .cts)

for base in "${JS_CONFIG_BASES[@]}"; do
for ext in "${JS_EXTENSIONS[@]}"; do
rm -rf "${base}${ext}"
rm -rf "${base}${ext}.license"
done
done

43 changes: 43 additions & 0 deletions .github/scripts/clean-server-dev-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
#
# Clean development files from the server repository.
# Removes .git at the end (needs it first for .nextcloudignore).
# Usage: clean-server-dev-files.sh [server-dir]

set -e

cd "${1:-.}"

# NC25+ uses .nextcloudignore with gitignore-style patterns.
# git ls-files -ic finds tracked files matching the ignore patterns.
# Older versions fall back to a hardcoded removal list.
if [ -f ".nextcloudignore" ]; then
git ls-files -ic --exclude-from=.nextcloudignore -z | xargs -0 -r -n 10 -- rm -fr
find . -empty -type d -delete
else
rm -rf .babelrc .codecov.yml .devcontainer .drone.yml .editorconfig
rm -rf .envrc .eslintignore .eslintrc.js .git-blame-ignore-revs
rm -rf .gitattributes .github .gitignore .gitmodules .idea .jshintrc
rm -rf .mailmap .npmignore .php-cs-fixer.dist.php .php_cs.dist
rm -rf .pre-commit-config.yaml .scrutinizer.yml .tag .tx
rm -rf CHANGELOG.md CODE_OF_CONDUCT.md COPYING-README DESIGN.md
rm -rf Makefile README.md SECURITY.md __mocks__ __tests__
rm -rf apps/dav/bin apps/testing
rm -rf autotest-checkers.sh autotest-external.sh autotest-js.sh autotest.sh
rm -rf babel.config.js build codecov.yml contribute custom.d.ts
rm -rf cypress cypress.config.ts cypress.d.ts
rm -rf eslint.config.js eslint.config.mjs flake.lock flake.nix
rm -rf jest.config.js jest.config.ts openapi.json
rm -rf psalm-ncu.xml psalm-ocp.xml psalm.xml stylelint.config.js
rm -rf tests tsconfig.json vendor-bin vite.config.ts
rm -rf vitest.config.mts vitest.config.ts
rm -rf webpack.common.cjs webpack.common.js webpack.config.js
rm -rf webpack.dev.js webpack.modules.cjs webpack.modules.js webpack.prod.js
rm -rf window.d.ts
rm -rf .direnv .well-known config/config.php data
fi

# Remove .git last (needed above for git ls-files)
rm -rf .git
95 changes: 95 additions & 0 deletions .github/scripts/fetch-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
#
# Fetch all release components (server, 3rdparty, apps, updater, skeleton).
# Usage: fetch-all.sh <tag-or-branch> <config.json> <output-dir> [--docs]
#
# Examples:
# fetch-all.sh v34.0.1 stable34.json /tmp/build
# fetch-all.sh stable34 stable34.json /tmp/build --docs

set -e

REF="${1:?Usage: fetch-all.sh <ref> <config.json> <output-dir> [--docs]}"
CONFIG="${2:?Missing config.json path}"
OUTPUT="${3:?Missing output directory}"
FETCH_DOCS="${4:-}"

mkdir -p "$OUTPUT"

clone() {
local repo="$1" ref="$2" dest="$3"
echo "Fetching $repo@$ref → $dest"
git clone --depth 1 --branch "$ref" "https://github.com/$repo.git" "$dest" -q 2>/dev/null \
|| git clone --depth 1 "https://github.com/$repo.git" "$dest" -q
}

# Server
clone "nextcloud/server" "$REF" "$OUTPUT/server"
git -C "$OUTPUT/server" rev-parse HEAD > "$OUTPUT/server/release-commit-hash"

# 3rdparty: clone at the same ref as server (matches the release script)
rm -rf "$OUTPUT/server/3rdparty"
clone "nextcloud/3rdparty" "$REF" "$OUTPUT/3rdparty"
rm -rf "$OUTPUT/3rdparty/.git" "$OUTPUT/3rdparty/.github" "$OUTPUT/3rdparty/.gitignore" "$OUTPUT/3rdparty/README.md"

# Updater (only 2 files needed)
UPDATER_TMP=$(mktemp -d)
clone "nextcloud/updater" "$REF" "$UPDATER_TMP"
mkdir -p "$OUTPUT/updater"
cp "$UPDATER_TMP/index.php" "$OUTPUT/updater/index.php"
cp "$UPDATER_TMP/updater.phar" "$OUTPUT/updater/updater.phar"
rm -rf "$UPDATER_TMP"

# Example files (becomes core/skeleton)
clone "nextcloud/example-files" "$REF" "$OUTPUT/example-files"
rm -rf "$OUTPUT/example-files/.git"

# Apps from JSON config
jq -r '.[] | "\(.id) \(.repo) \(.composer_args // "")"' "$CONFIG" | while read -r id repo composer_args; do
clone "$repo" "$REF" "$OUTPUT/apps/$id"
rm -rf "$OUTPUT/apps/$id/.git"

# Run composer if there are real non-dev dependencies
if [ -f "$OUTPUT/apps/$id/composer.json" ] && jq -e '
.require // {} | to_entries
| map(select(
.key != "php"
and (.key | startswith("ext-") | not)
and (.key | startswith("bamarni/") | not)
))
| length > 0
' "$OUTPUT/apps/$id/composer.json" > /dev/null 2>&1; then
echo " Running composer install for $id"
ARGS="${composer_args:---no-dev -a --quiet}"
(cd "$OUTPUT/apps/$id" && COMPOSER_ALLOW_SUPERUSER=1 composer install $ARGS)
fi
done

# Docs (optional)
if [ "$FETCH_DOCS" = "--docs" ]; then
MAJOR=$(echo "$REF" | sed 's/^v//;s/\..*//')
DOCS_TMP=$(mktemp -d)
git clone --depth 1 --branch gh-pages "https://github.com/nextcloud/documentation.git" "$DOCS_TMP" -q 2>/dev/null || true

if [ -d "$DOCS_TMP" ]; then
mkdir -p "$OUTPUT/docs/user_manual/en" "$OUTPUT/docs/admin_manual"
for base in "server/${MAJOR}" "server/stable${MAJOR}" "${MAJOR}" "."; do
if [ -d "$DOCS_TMP/${base}/user_manual/en" ]; then
cp -a "$DOCS_TMP/${base}/user_manual/en/"* "$OUTPUT/docs/user_manual/en/"
break
fi
done
for base in "server/${MAJOR}" "server/stable${MAJOR}" "${MAJOR}" "."; do
if [ -d "$DOCS_TMP/${base}/admin_manual" ]; then
cp -a "$DOCS_TMP/${base}/admin_manual/"* "$OUTPUT/docs/admin_manual/"
break
fi
done
find "$DOCS_TMP" -name "Nextcloud_User_Manual.pdf" -exec cp -a {} "$OUTPUT/docs/" \; 2>/dev/null
rm -rf "$DOCS_TMP"
fi
fi

echo "All components fetched to $OUTPUT"
Loading
Loading