diff --git a/mailcow_plugin_runtime_v0_3_0_setup_v2.sh b/mailcow_plugin_runtime_v0_3_0_setup_v2.sh deleted file mode 100644 index da99019..0000000 --- a/mailcow_plugin_runtime_v0_3_0_setup_v2.sh +++ /dev/null @@ -1,226 +0,0 @@ -#!/bin/sh -# Re-exec under bash when invoked with sh. -if [ -z "${BASH_VERSION:-}" ]; then - if command -v bash >/dev/null 2>&1; then - exec bash "$0" "$@" - else - echo "This script requires bash." >&2 - exit 1 - fi -fi -set -Eeuo pipefail - -# mailcow-plugin-runtime v0.3.0 bootstrap helper -# -# Purpose: -# - clean and stabilize the git repo -# - prepare a v0.2.0 baseline commit/tag -# - create a v0.3.0 feature branch -# - scaffold Mailcow nginx integration, runtime bootstrap wiring, -# hook renderer placeholders, and ownCloud attach plugin placeholders -# -# Default mode is DRY RUN. Nothing changes unless --apply is passed. -# -# Usage: -# bash mailcow_plugin_runtime_v0_3_0_setup.sh -# bash mailcow_plugin_runtime_v0_3_0_setup.sh --apply -# bash mailcow_plugin_runtime_v0_3_0_setup.sh --apply --skip-git -# -# Notes: -# - This script is intentionally conservative. -# - It creates scaffolding and placeholders, not a fully working Mailcow patch. -# - Review every generated file before committing and pushing. - -APPLY=0 -SKIP_GIT=0 -SKIP_SCAFFOLD=0 -FEATURE_BRANCH="feature/mailcow-plugin-runtime-v0.3.0" -BASELINE_TAG="v0.2.0" -BASELINE_COMMIT_MSG="chore: clean repo and prepare plugin runtime baseline" -SCAFFOLD_COMMIT_MSG="feat: scaffold v0.3.0 mailcow integration and plugin hook structure" - -while [[ $# -gt 0 ]]; do - case "$1" in - --apply) APPLY=1 ;; - --skip-git) SKIP_GIT=1 ;; - --skip-scaffold) SKIP_SCAFFOLD=1 ;; - --feature-branch) FEATURE_BRANCH="${2:?missing value}"; shift ;; - --baseline-tag) BASELINE_TAG="${2:?missing value}"; shift ;; - *) - echo "Unknown argument: $1" >&2 - exit 1 - ;; - esac - shift -done - -say() { printf '\n[%s] %s\n' "INFO" "$*"; } -warn() { printf '\n[%s] %s\n' "WARN" "$*"; } -run() { - if [[ "$APPLY" -eq 1 ]]; then - printf '+ %s\n' "$*" - eval "$*" - else - printf '[dry-run] %s\n' "$*" - fi -} - -require_repo() { - git rev-parse --show-toplevel >/dev/null 2>&1 || { - echo "This script must be run inside a git repository." >&2 - exit 1 - } -} - -repo_root() { - git rev-parse --show-toplevel -} - -append_line_once() { - local file="$1" - local line="$2" - if [[ -f "$file" ]] && grep -Fqx "$line" "$file"; then - return 0 - fi - if [[ "$APPLY" -eq 1 ]]; then - printf '%s\n' "$line" >> "$file" - else - printf '[dry-run] append to %s: %s\n' "$file" "$line" - fi -} - -write_file_if_missing() { - local path="$1" - local content="$2" - if [[ -e "$path" ]]; then - say "Keeping existing file: $path" - return 0 - fi - if [[ "$APPLY" -eq 1 ]]; then - mkdir -p "$(dirname "$path")" - python3 - <<'PY' "$path" "$content" -import sys -path, content = sys.argv[1], sys.argv[2] -with open(path, 'w', encoding='utf-8') as f: - f.write(content) -PY - else - printf '[dry-run] create file: %s\n' "$path" - fi -} - -ensure_gitignore() { - local file=".gitignore" - if [[ ! -f "$file" && "$APPLY" -eq 1 ]]; then - : > "$file" - fi - append_line_once "$file" "node_modules/" - append_line_once "$file" ".DS_Store" - append_line_once "$file" "dist/" - append_line_once "$file" ".env" - append_line_once "$file" ".env.local" -} - -cleanup_index() { - if git ls-files --error-unmatch node_modules >/dev/null 2>&1; then - run "git rm -r --cached node_modules" - else - say "node_modules is not tracked." - fi - - local ds - while IFS= read -r ds; do - [[ -z "$ds" ]] && continue - run "git rm --cached '$ds'" - done < <(git ls-files | grep -E '(^|/)\.DS_Store$' || true) -} - -maybe_commit() { - local message="$1" - if git diff --cached --quiet && git diff --quiet; then - say "No changes detected for commit: $message" - return 0 - fi - run "git add -A" - run "git commit -m '$message'" -} - -maybe_tag() { - local tag="$1" - if git rev-parse "$tag" >/dev/null 2>&1; then - say "Tag already exists: $tag" - else - run "git tag -a '$tag' -m 'mailcow plugin runtime $tag baseline'" - fi -} - -ensure_branch() { - local branch="$1" - local current - current="$(git branch --show-current)" - if [[ "$current" == "$branch" ]]; then - say "Already on branch: $branch" - return 0 - fi - if git show-ref --verify --quiet "refs/heads/$branch"; then - run "git checkout '$branch'" - else - run "git checkout -b '$branch'" - fi -} - -scaffold_files() { - write_file_if_missing "deploy/nginx/mailcow-plugin-runtime.conf" "# Mailcow plugin runtime reverse proxy\n# Include this from the Mailcow nginx site or merge into the active server block.\n# Adjust upstream address if the runtime is not on localhost:4110.\n\nlocation /plugins-runtime/ {\n proxy_pass http://127.0.0.1:4110/;\n proxy_http_version 1.1;\n\n proxy_set_header Host \\$host;\n proxy_set_header X-Real-IP \\$remote_addr;\n proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto \\$scheme;\n\n proxy_request_buffering off;\n proxy_buffering off;\n client_max_body_size 100m;\n}\n" - - write_file_if_missing "docs/v0.3.0-mailcow-integration.md" "# mailcow-plugin-runtime v0.3.0 integration plan\n\n## Scope\n- Proxy runtime behind Mailcow at `/plugins-runtime/`\n- Load runtime bootstrap into Mailcow UI\n- Render compose toolbar and attachment hooks\n- Wire `owncloud-attach` plugin end to end\n\n## Deployment notes\n1. Start runtime on port `4110`.\n2. Add `deploy/nginx/mailcow-plugin-runtime.conf` to the Mailcow nginx server block.\n3. Ensure `/plugins-runtime/runtime/bootstrap.js` is reachable from Mailcow.\n4. Inject the bootstrap loader into the Mailcow compose page.\n\n## Expected follow-up\n- Replace placeholders in `src/integrations/mailcow` with actual selectors and injection logic.\n- Implement attachment token exchange and ownCloud file listing here.\n" - - write_file_if_missing "src/integrations/mailcow/bootstrap-loader.ts" 'export interface MailcowBootstrapOptions {\n runtimeBaseUrl?: string;\n}\n\nexport function loadPluginRuntimeBootstrap(options: MailcowBootstrapOptions = {}): HTMLScriptElement {\n const runtimeBaseUrl = options.runtimeBaseUrl ?? "/plugins-runtime";\n const script = document.createElement("script");\n script.src = `${runtimeBaseUrl}/runtime/bootstrap.js`;\n script.async = true;\n script.dataset.runtimeBaseUrl = runtimeBaseUrl;\n document.head.appendChild(script);\n return script;\n}\n' - - write_file_if_missing "src/integrations/mailcow/dom-targets.ts" 'export const MAILCOW_SELECTORS = {\n composeToolbar: "[data-mailcow-compose-toolbar], .compose-toolbar, .toolbar",\n composeAttachments: "[data-mailcow-compose-attachments], .compose-attachments, .attachments",\n};\n' - - write_file_if_missing "src/integrations/mailcow/install.ts" 'import { loadPluginRuntimeBootstrap } from "./bootstrap-loader";\nimport { mountComposeToolbarHook, mountComposeAttachmentsHook } from "../hooks/mailcow-hooks";\n\nexport function installMailcowPluginRuntime(): void {\n loadPluginRuntimeBootstrap();\n\n window.addEventListener("DOMContentLoaded", () => {\n mountComposeToolbarHook();\n mountComposeAttachmentsHook();\n });\n}\n' - - write_file_if_missing "src/hooks/mailcow-hooks.ts" 'import { MAILCOW_SELECTORS } from "../integrations/mailcow/dom-targets";\n\nfunction ensureHookContainer(selector: string, id: string): HTMLElement | null {\n const host = document.querySelector(selector);\n if (!host) return null;\n\n let node = document.getElementById(id);\n if (!node) {\n node = document.createElement("div");\n node.id = id;\n node.dataset.pluginHookContainer = id;\n host.appendChild(node);\n }\n\n return node;\n}\n\nexport function mountComposeToolbarHook(): void {\n ensureHookContainer(MAILCOW_SELECTORS.composeToolbar, "mailcow-plugin-hook-compose-toolbar");\n}\n\nexport function mountComposeAttachmentsHook(): void {\n ensureHookContainer(MAILCOW_SELECTORS.composeAttachments, "mailcow-plugin-hook-compose-attachments");\n}\n' - - write_file_if_missing "plugins/owncloud-attach/README.md" "# owncloud-attach plugin\n\n## Goal\nAdd an ownCloud file picker to the Mailcow compose flow.\n\n## Planned behavior\n- Toolbar button opens picker\n- Picker lists the authenticated user's ownCloud files\n- Selection returns attachment metadata to the runtime\n- Runtime injects attachment entries into Mailcow compose UI\n" - - write_file_if_missing "plugins/owncloud-attach/plugin.json" "{\n \"id\": \"owncloud-attach\",\n \"version\": \"0.3.0\",\n \"name\": \"owncloud-attach\",\n \"hooks\": [\n \"compose.toolbar\",\n \"compose.attachments\"\n ]\n}\n" - - write_file_if_missing "plugins/owncloud-attach/ui/index.html" '\n\n \n \n \n owncloud-attach\n \n \n \n
\n

owncloud-attach

\n

Placeholder UI for the ownCloud picker modal.

\n \n
\n \n\n' - - write_file_if_missing "plugins/owncloud-attach/api/README.md" "# owncloud-attach API placeholders\n\nExpected endpoints:\n- GET /plugins/owncloud-attach/api/files\n- POST /plugins/owncloud-attach/api/attach\n- GET /plugins/owncloud-attach/api/health\n\nImplement the runtime-side token exchange and ownCloud file listing here.\n" - - write_file_if_missing "scripts/next-steps.sh" "#!/usr/bin/env bash\nset -euo pipefail\n\necho 'Next manual steps:'\necho '1. Wire deploy/nginx/mailcow-plugin-runtime.conf into the Mailcow nginx server block.'\necho '2. Make Mailcow load src/integrations/mailcow/install.ts output on compose pages.'\necho '3. Replace placeholder selectors in src/integrations/mailcow/dom-targets.ts.'\necho '4. Build owncloud-attach picker API and file attachment flow.'\n" - - if [[ "$APPLY" -eq 1 ]]; then - chmod +x scripts/next-steps.sh || true - fi -} - -main() { - require_repo - local root - root="$(repo_root)" - say "Repository root: $root" - say "Mode: $([[ "$APPLY" -eq 1 ]] && echo APPLY || echo DRY RUN)" - - if [[ "$SKIP_GIT" -eq 0 ]]; then - say "Stabilizing git baseline" - ensure_gitignore - cleanup_index - maybe_commit "$BASELINE_COMMIT_MSG" - maybe_tag "$BASELINE_TAG" - ensure_branch "$FEATURE_BRANCH" - fi - - if [[ "$SKIP_SCAFFOLD" -eq 0 ]]; then - say "Scaffolding v0.3.0 integration files" - scaffold_files - maybe_commit "$SCAFFOLD_COMMIT_MSG" - fi - - say "Done. Review changes with: git status && git diff --stat" - warn "This script does not push changes. Push manually after review." -} - -main "$@" diff --git a/mailcow_plugin_runtime_v0_3_0_setup_v3.sh b/mailcow_plugin_runtime_v0_3_0_setup_v4.sh similarity index 97% rename from mailcow_plugin_runtime_v0_3_0_setup_v3.sh rename to mailcow_plugin_runtime_v0_3_0_setup_v4.sh index 5748f87..677134c 100644 --- a/mailcow_plugin_runtime_v0_3_0_setup_v3.sh +++ b/mailcow_plugin_runtime_v0_3_0_setup_v4.sh @@ -174,7 +174,7 @@ ensure_branch() { } scaffold_files() { - write_file_if_missing "deploy/nginx/mailcow-plugin-runtime.conf" "# Mailcow plugin runtime reverse proxy\n# Include this from the Mailcow nginx site or merge into the active server block.\n# Adjust upstream address if the runtime is not on localhost:4110.\n\nlocation /plugins-runtime/ {\n proxy_pass http://127.0.0.1:4110/;\n proxy_http_version 1.1;\n\n proxy_set_header Host \\$host;\n proxy_set_header X-Real-IP \\$remote_addr;\n proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto \\$scheme;\n\n proxy_request_buffering off;\n proxy_buffering off;\n client_max_body_size 100m;\n}\n" + write_file_if_missing "deploy/nginx/mailcow-plugin-runtime.conf" "# Mailcow plugin runtime reverse proxy\n# Include this from the Mailcow nginx site or merge into the active server block.\n# Adjust upstream address if the runtime is not on localhost:4110.\n\nlocation /plugins-runtime/ {\n proxy_pass http://127.0.0.1:4110/;\n proxy_http_version 1.1;\n\n proxy_set_header Host \$host;\n proxy_set_header X-Real-IP \$remote_addr;\n proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto \$scheme;\n\n proxy_request_buffering off;\n proxy_buffering off;\n client_max_body_size 100m;\n}\n" write_file_if_missing "docs/v0.3.0-mailcow-integration.md" "# mailcow-plugin-runtime v0.3.0 integration plan\n\n## Scope\n- Proxy runtime behind Mailcow at `/plugins-runtime/`\n- Load runtime bootstrap into Mailcow UI\n- Render compose toolbar and attachment hooks\n- Wire `owncloud-attach` plugin end to end\n\n## Deployment notes\n1. Start runtime on port `4110`.\n2. Add `deploy/nginx/mailcow-plugin-runtime.conf` to the Mailcow nginx server block.\n3. Ensure `/plugins-runtime/runtime/bootstrap.js` is reachable from Mailcow.\n4. Inject the bootstrap loader into the Mailcow compose page.\n\n## Expected follow-up\n- Replace placeholders in `src/integrations/mailcow` with actual selectors and injection logic.\n- Implement attachment token exchange and ownCloud file listing here.\n"