diff --git a/archivebox/templates/core/setup_wizard.html b/archivebox/templates/core/setup_wizard.html index dee493f1..3142ae23 100644 --- a/archivebox/templates/core/setup_wizard.html +++ b/archivebox/templates/core/setup_wizard.html @@ -10,9 +10,14 @@

Setup access to your ArchiveBox Server: {{ display_host|default:canonical_host }}

-
- Hosting Location: What machine are you hosting this ArchiveBox server on? -
+
+ + + +
@@ -20,9 +25,14 @@
Choose where this server is hosted.
-
- DNS mode: How will DNS point to this ArchiveBox server? -
+
+ + + +
@@ -30,9 +40,14 @@
Choose a DNS mode to begin testing.
-
- Ingress & TLS mode: How will HTTPS traffic reach this ArchiveBox server? -
+
+ + + +
@@ -118,4 +133,4 @@
- + diff --git a/archivebox/templates/static/setup_wizard.css b/archivebox/templates/static/setup_wizard.css index 78c369b3..9ee24381 100644 --- a/archivebox/templates/static/setup_wizard.css +++ b/archivebox/templates/static/setup_wizard.css @@ -6,6 +6,12 @@ #archivebox-setup-wizard .abx-setup-label { display:block; margin-bottom:7px; font-size:15px; font-weight:800; } #archivebox-setup-wizard .abx-question { margin:18px 0 0; padding:16px; border:1px solid #dbe3ee; border-radius:9px; } #archivebox-setup-wizard .abx-question legend { padding:0 6px; font-size:15px; font-weight:800; } +#archivebox-setup-wizard .abx-question-toggle { display:flex; width:100%; align-items:center; justify-content:space-between; gap:12px; padding:0; border:0; background:transparent; color:inherit; font:inherit; text-align:left; cursor:pointer; } +#archivebox-setup-wizard .abx-question-toggle-icon { color:#64748b; font-size:16px; transition:transform .15s ease; } +#archivebox-setup-wizard .abx-question.is-collapsed .abx-question-toggle-icon { transform:rotate(-90deg); } +#archivebox-setup-wizard .abx-question.is-collapsed .abx-question-options { display:none; } +#archivebox-setup-wizard .abx-question.is-valid { border-color:#15803d; background:#f0fdf4; box-shadow:0 0 0 1px #15803d; } +#archivebox-setup-wizard .abx-question.is-invalid { border-color:#dc2626; background:#fffafa; } #archivebox-setup-wizard .abx-question-options { display:grid; gap:9px; } #archivebox-setup-wizard .abx-question-option { display:flex; align-items:flex-start; gap:10px; padding:11px 12px; border:1px solid #dbe3ee; border-radius:7px; background:#f8fafc; color:#334155; cursor:pointer; } #archivebox-setup-wizard .abx-question-option:has(input:checked) { border-color:#15803d; background:#f0fdf4; box-shadow:0 0 0 1px #15803d; } @@ -17,6 +23,8 @@ #archivebox-setup-wizard .abx-url-comparison-status.is-match { color:#15803d; } #archivebox-setup-wizard .abx-url-comparison-status.is-warning { color:#b45309; } #archivebox-setup-wizard .abx-question-status { margin-top:9px; padding:9px 11px; border-radius:7px; background:#f8fafc; color:#475569; font-weight:700; } +#archivebox-setup-wizard .abx-question.is-valid .abx-question-status { background:#dcfce7; color:#166534; } +#archivebox-setup-wizard .abx-question.is-invalid .abx-question-status { background:#fef2f2; color:#991b1b; } #archivebox-setup-wizard .abx-setup-control { box-sizing:border-box; width:100%; height:44px !important; min-height:44px !important; margin:0; padding:0 12px !important; border:1px solid #b8c4d4 !important; border-radius:7px; background:#fff !important; color:#0f172a !important; font:14px/normal -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif !important; opacity:1 !important; } #archivebox-setup-wizard select.abx-setup-control { appearance:auto !important; -webkit-appearance:menulist !important; } #archivebox-setup-wizard select.abx-setup-control option { color:#0f172a !important; background:#fff !important; } diff --git a/archivebox/templates/static/setup_wizard.js b/archivebox/templates/static/setup_wizard.js index c13ed38f..14183dd6 100644 --- a/archivebox/templates/static/setup_wizard.js +++ b/archivebox/templates/static/setup_wizard.js @@ -33,6 +33,36 @@ Array.prototype.forEach.call(inputs, function(input) { input.checked = input.value === value; }); } + function initializeQuestionSections() { + document.querySelectorAll('.abx-question[data-status-id]').forEach(function(question) { + var status = document.getElementById(question.dataset.statusId); + var toggle = question.querySelector('.abx-question-toggle'); + + function setCollapsed(collapsed) { + question.classList.toggle('is-collapsed', collapsed); + toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + } + + function syncQuestionState() { + var statusText = status.textContent.trim(); + var isValid = statusText.indexOf('✅') === 0; + var isInvalid = statusText.indexOf('❌') === 0; + question.classList.toggle('is-valid', isValid); + question.classList.toggle('is-invalid', isInvalid); + if (isValid) setCollapsed(true); + if (isInvalid) setCollapsed(false); + } + + toggle.addEventListener('click', function() { + setCollapsed(!question.classList.contains('is-collapsed')); + }); + new MutationObserver(syncQuestionState).observe(status, {childList: true, characterData: true, subtree: true}); + syncQuestionState(); + }); + } + + initializeQuestionSections(); + var detectedUrl = new URL(baseUrlInput.value); var detectedLocalhost = detectedUrl.hostname === 'localhost' || detectedUrl.hostname.endsWith('.localhost'); if (detectedLocalhost) { diff --git a/archivebox/tests/test_ui_admin_config_widget.py b/archivebox/tests/test_ui_admin_config_widget.py index 1f28eaef..da2bcbe3 100644 --- a/archivebox/tests/test_ui_admin_config_widget.py +++ b/archivebox/tests/test_ui_admin_config_widget.py @@ -167,15 +167,30 @@ def test_unconfigured_superuser_banner_uses_browser_assisted_setup_wizard(): assert "PERMISSIONS" in html assert "/admin/machine/machine/current/change/" in html assert "setup_wizard.css" in html - assert "setup_wizard.js?v=20260723-2" in html + assert 'data-status-id="archivebox-setup-hosting-status"' in html + assert 'aria-controls="archivebox-setup-hosting-options"' in html + assert 'data-status-id="archivebox-setup-dns-status"' in html + assert 'aria-controls="archivebox-setup-dns-options"' in html + assert 'data-status-id="archivebox-setup-tls-status"' in html + assert 'aria-controls="archivebox-setup-tls-options"' in html + assert "setup_wizard.js?v=20260726-1" in html def test_setup_wizard_assets_enforce_selection_and_access_requirements(): assert "border-color:#15803d; background:#f0fdf4" in SETUP_WIZARD_CSS assert "accent-color:#15803d" in SETUP_WIZARD_CSS assert "#archivebox-setup-title code { font-size:inherit; line-height:inherit; }" in SETUP_WIZARD_CSS + assert ".abx-question.is-collapsed .abx-question-options { display:none; }" in SETUP_WIZARD_CSS + assert ".abx-question.is-valid { border-color:#15803d; background:#f0fdf4" in SETUP_WIZARD_CSS + assert ".abx-question.is-invalid { border-color:#dc2626;" in SETUP_WIZARD_CSS assert "updateQuestionDefaults" not in SETUP_WIZARD_JS + assert "function initializeQuestionSections()" in SETUP_WIZARD_JS + assert "var isValid = statusText.indexOf('✅') === 0" in SETUP_WIZARD_JS + assert "var isInvalid = statusText.indexOf('❌') === 0" in SETUP_WIZARD_JS + assert "if (isValid) setCollapsed(true)" in SETUP_WIZARD_JS + assert "if (isInvalid) setCollapsed(false)" in SETUP_WIZARD_JS + assert "toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true')" in SETUP_WIZARD_JS assert "canonicalHost" not in SETUP_WIZARD_JS assert "Browser URL matches BASE_URL." in SETUP_WIZARD_JS assert "Browser URL matches admin.BASE_URL as expected." in SETUP_WIZARD_JS diff --git a/bin/setup_monorepo.sh b/bin/setup_monorepo.sh index a65c85b2..906a941f 100755 --- a/bin/setup_monorepo.sh +++ b/bin/setup_monorepo.sh @@ -49,6 +49,17 @@ resolve_git_binary() { test -x "$GIT_BINARY" } +repo_target_branch() { + case "$1" in + archivebox) + printf 'dev\n' + ;; + *) + printf 'main\n' + ;; + esac +} + is_member_repo() { local repo_root="$1" local repo_name @@ -82,7 +93,7 @@ bootstrap_build_dependencies() { case "$OSTYPE" in linux*) - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -90,7 +101,7 @@ bootstrap_build_dependencies() { --binproviders=env,apt \ --overrides='{"apt":{"install_args":["build-essential"]}}' \ cc >/dev/null - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -99,14 +110,14 @@ bootstrap_build_dependencies() { --overrides='{"env":{"version":["ldapsearch","-VV"]},"apt":{"install_args":["ldap-utils","python3-dev","python3-setuptools","libssl-dev","libldap2-dev","libsasl2-dev","zlib1g-dev","libatomic1"],"version":["ldapsearch","-VV"]}}' \ ldapsearch >/dev/null - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ --lib="$ABXPKG_LIB_DIR" \ --binproviders=env \ cc >/dev/null - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -121,7 +132,7 @@ bootstrap_build_dependencies() { test -x "$ABXPKG_LIB_DIR/env/bin/ldapsearch" ;; darwin*) - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --json \ --lib="$ABXPKG_LIB_DIR" \ @@ -136,7 +147,7 @@ bootstrap_build_dependencies() { brew_root="$(dirname "$(dirname "$brew_target")")" export ABXPKG_BREW_ROOT="$brew_root" - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -144,7 +155,7 @@ bootstrap_build_dependencies() { --binproviders=env,brew \ --overrides='{"brew":{"install_args":["llvm"]}}' \ clang >/dev/null - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -154,7 +165,7 @@ bootstrap_build_dependencies() { ldapvc >/dev/null PATH="$PATH:$brew_root/opt/llvm/bin" \ - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -162,7 +173,7 @@ bootstrap_build_dependencies() { --binproviders=env \ clang >/dev/null PATH="$PATH:$brew_root/opt/openldap/bin" \ - uv run --no-sync abxpkg env \ + uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \ --install \ --no-cache \ --json \ @@ -203,7 +214,25 @@ bootstrap_build_dependencies() { } sync_workspace() { - uv sync --all-packages --all-extras --no-cache --active + uv sync --all-extras --no-cache --active + + for repo_name in "${REPO_NAMES[@]}"; do + printf 'Syncing %s into monorepo environment\n' "$repo_name" + case "$repo_name" in + archivebox) + ( + cd "$ROOT_DIR/$repo_name" + UV_PROJECT_ENVIRONMENT="$ROOT_DIR/.venv" uv sync --dev --all-extras --inexact --no-cache --active + ) + ;; + *) + ( + cd "$ROOT_DIR/$repo_name" + UV_PROJECT_ENVIRONMENT="$ROOT_DIR/.venv" uv sync --dev --inexact --no-cache --active + ) + ;; + esac + done } ensure_setup_link() { @@ -280,10 +309,20 @@ fi ensure_member_repo() { local repo_name="$1" local repo_dir="$ROOT_DIR/$repo_name" + local target_branch + local current_branch + target_branch="$(repo_target_branch "$repo_name")" if [[ -d "$repo_dir/.git" ]]; then printf 'Updating existing checkout: %s\n' "$repo_name" - if "$GIT_BINARY" -C "$repo_dir" -c pull.rebase=false pull --ff-only --quiet >/dev/null 2>&1; then + current_branch="$("$GIT_BINARY" -C "$repo_dir" branch --show-current)" + + if [[ "$current_branch" != "$target_branch" ]]; then + "$GIT_BINARY" -C "$repo_dir" fetch --depth=1 origin "$target_branch" --quiet + "$GIT_BINARY" -C "$repo_dir" checkout -B "$target_branch" --track "origin/$target_branch" >/dev/null + fi + + if "$GIT_BINARY" -C "$repo_dir" -c pull.rebase=false pull --ff-only --quiet origin "$target_branch" >/dev/null 2>&1; then printf 'Updated: %s\n' "$repo_name" else printf 'Skipping pull for %s (local changes, divergent branch, detached HEAD, or no upstream)\n' "$repo_name" >&2 @@ -297,7 +336,7 @@ ensure_member_repo() { fi printf 'Cloning %s/%s.git -> %s\n' "$GITHUB_BASE" "$repo_name" "$repo_name" - "$GIT_BINARY" clone "$GITHUB_BASE/$repo_name.git" "$repo_dir" + "$GIT_BINARY" clone --branch "$target_branch" "$GITHUB_BASE/$repo_name.git" "$repo_dir" } for repo_name in "${REPO_NAMES[@]}"; do @@ -315,7 +354,6 @@ rm -Rf ./*/.venv # delete all sub-repo venvs, the monorepo venv needs to take uv venv --allow-existing "$ROOT_DIR/.venv" # shellcheck disable=SC1091 source "$ROOT_DIR/.venv/bin/activate" -uv sync --package abxpkg --no-dev --no-cache --active bootstrap_build_dependencies sync_workspace echo @@ -326,5 +364,5 @@ echo " VIRTUAL_ENV=$VIRTUAL_ENV" echo " PYTHON_BIN=$VIRTUAL_ENV/bin/python" echo echo "TIPS:" -echo " - Always use 'uv run ...' within each subrepo, never in the root & never run 'python ...' directly" +echo " - Use 'uv run ...' inside subrepos for package work; their standalone uv.lock files remain authoritative for CI/release" echo " - Always read $ROOT_DIR/README.md into context before starting any work"