Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
aisbf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
aisbf
Commits
bef802ee
Commit
bef802ee
authored
Apr 24, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: verbose first-run debug output, root uses system dirs, non-root uses ~/.local
parent
23a9f726
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
120 additions
and
44 deletions
+120
-44
cli.py
cli.py
+120
-44
No files found.
cli.py
View file @
bef802ee
...
@@ -29,12 +29,22 @@ import shutil
...
@@ -29,12 +29,22 @@ import shutil
from
pathlib
import
Path
from
pathlib
import
Path
# Files and directories that must be present in the share directory for the
# Files and directories the share directory must contain for the server to start.
# server to start correctly.
_REQUIRED_FILES
=
[
'aisbf.sh'
,
'main.py'
,
'requirements.txt'
]
_REQUIRED_FILES
=
[
'aisbf.sh'
,
'main.py'
,
'requirements.txt'
]
_REQUIRED_DIRS
=
[
'templates'
,
'static'
,
'config'
]
_REQUIRED_DIRS
=
[
'templates'
,
'static'
,
'config'
]
def
_is_root
():
return
os
.
getuid
()
==
0
def
_default_share_dest
():
"""System dir for root, user dir for everyone else."""
if
_is_root
():
return
Path
(
'/usr/local/share/aisbf'
)
return
Path
.
home
()
/
'.local'
/
'share'
/
'aisbf'
def
_share_dir_candidates
():
def
_share_dir_candidates
():
"""Return candidate share directories in priority order, deduplicated."""
"""Return candidate share directories in priority order, deduplicated."""
seen
=
set
()
seen
=
set
()
...
@@ -46,9 +56,12 @@ def _share_dir_candidates():
...
@@ -46,9 +56,12 @@ def _share_dir_candidates():
seen
.
add
(
key
)
seen
.
add
(
key
)
result
.
append
(
Path
(
p
))
result
.
append
(
Path
(
p
))
# sysconfig paths for the running Python interpreter — covers venvs,
# sysconfig paths for the running interpreter — covers venvs, system
# system installs, and user installs regardless of pip's install scheme.
# installs, and all user-install schemes regardless of how pip was invoked.
add
(
Path
(
sysconfig
.
get_path
(
'data'
))
/
'share'
/
'aisbf'
)
try
:
add
(
Path
(
sysconfig
.
get_path
(
'data'
))
/
'share'
/
'aisbf'
)
except
Exception
:
pass
for
scheme
in
(
'posix_user'
,
'posix_prefix'
,
'posix_home'
):
for
scheme
in
(
'posix_user'
,
'posix_prefix'
,
'posix_home'
):
try
:
try
:
...
@@ -56,7 +69,7 @@ def _share_dir_candidates():
...
@@ -56,7 +69,7 @@ def _share_dir_candidates():
except
Exception
:
except
Exception
:
pass
pass
#
Legacy hardcoded
fallbacks
#
Hardcoded legacy
fallbacks
add
(
Path
(
'/usr/local/share/aisbf'
))
add
(
Path
(
'/usr/local/share/aisbf'
))
add
(
Path
(
'/usr/share/aisbf'
))
add
(
Path
(
'/usr/share/aisbf'
))
add
(
Path
.
home
()
/
'.local'
/
'share'
/
'aisbf'
)
add
(
Path
.
home
()
/
'.local'
/
'share'
/
'aisbf'
)
...
@@ -65,12 +78,10 @@ def _share_dir_candidates():
...
@@ -65,12 +78,10 @@ def _share_dir_candidates():
def
_share_dir_is_complete
(
d
):
def
_share_dir_is_complete
(
d
):
"""Return True only if d contains every file/dir the server needs."""
return
all
((
d
/
f
)
.
exists
()
for
f
in
_REQUIRED_FILES
+
_REQUIRED_DIRS
)
return
all
((
d
/
f
)
.
exists
()
for
f
in
_REQUIRED_FILES
+
_REQUIRED_DIRS
)
def
_find_share_dir
():
def
_find_share_dir
():
"""Return the first complete share directory found, or None."""
for
candidate
in
_share_dir_candidates
():
for
candidate
in
_share_dir_candidates
():
if
_share_dir_is_complete
(
candidate
):
if
_share_dir_is_complete
(
candidate
):
return
candidate
return
candidate
...
@@ -79,20 +90,21 @@ def _find_share_dir():
...
@@ -79,20 +90,21 @@ def _find_share_dir():
def
_pkg_bundle_dir
():
def
_pkg_bundle_dir
():
"""
"""
Return the aisbf/_share/ directory bundled inside the installed package,
Locate the bundle of runtime files shipped inside the aisbf package.
or None if it doesn't exist (e.g. editable source install before build).
Falls back to the project root for editable installs.
Two sources, tried in order:
1. aisbf/_share/ — populated by the build_py hook at wheel-build time
2. project root — for editable (pip install -e) installs
"""
"""
try
:
try
:
import
aisbf
as
_pkg
import
aisbf
as
_pkg
pkg_dir
=
Path
(
_pkg
.
__file__
)
.
parent
pkg_dir
=
Path
(
_pkg
.
__file__
)
.
parent
# Normal wheel install: _share/ populated by build_py hook
share
=
pkg_dir
/
'_share'
share
=
pkg_dir
/
'_share'
if
share
.
exists
()
and
(
share
/
'main.py'
)
.
exists
():
if
share
.
is_dir
()
and
(
share
/
'main.py'
)
.
exists
():
return
share
return
share
# Editable install:
files live in the project root (one level up)
# Editable install:
package lives inside the source tree
project_root
=
pkg_dir
.
parent
project_root
=
pkg_dir
.
parent
if
(
project_root
/
'main.py'
)
.
exists
():
if
(
project_root
/
'main.py'
)
.
exists
():
return
project_root
return
project_root
...
@@ -104,16 +116,73 @@ def _pkg_bundle_dir():
...
@@ -104,16 +116,73 @@ def _pkg_bundle_dir():
def
_bootstrap_share_dir
():
def
_bootstrap_share_dir
():
"""
"""
Copy all runtime files from the bundled package data to
Copy all runtime files from the package bundle to the share directory.
~/.local/share/aisbf/ and return the destination path, or None on failure.
Prints step-by-step progress so the first run is fully traceable.
Returns the destination Path on success, None on failure.
"""
"""
bundle
=
_pkg_bundle_dir
()
_log
=
lambda
msg
:
print
(
f
"[aisbf bootstrap] {msg}"
,
file
=
sys
.
stderr
)
_log
(
"First run — setting up share directory …"
)
_log
(
f
" Python : {sys.executable} ({sys.version.split()[0]})"
)
_log
(
f
" Running as : {'root (uid=0)' if _is_root() else f'uid={os.getuid()}'}"
)
_log
(
""
)
# ── sysconfig paths ──────────────────────────────────────────────────────
_log
(
"sysconfig data paths:"
)
for
scheme
in
(
None
,
'posix_user'
,
'posix_prefix'
,
'posix_home'
):
try
:
p
=
sysconfig
.
get_path
(
'data'
,
scheme
)
if
scheme
else
sysconfig
.
get_path
(
'data'
)
_log
(
f
" {scheme or 'default':15s}: {p}"
)
except
Exception
as
e
:
_log
(
f
" {scheme or 'default':15s}: ERROR — {e}"
)
_log
(
""
)
# ── candidate share dirs ──────────────────────────────────────────────────
_log
(
"Candidate share directories:"
)
for
c
in
_share_dir_candidates
():
has_sh
=
(
c
/
'aisbf.sh'
)
.
exists
()
has_main
=
(
c
/
'main.py'
)
.
exists
()
status
=
"COMPLETE"
if
_share_dir_is_complete
(
c
)
else
\
f
"partial (aisbf.sh={has_sh}, main.py={has_main})"
_log
(
f
" {c}: {status}"
)
_log
(
""
)
# ── package bundle ────────────────────────────────────────────────────────
_log
(
"Package bundle location:"
)
try
:
import
aisbf
as
_pkg
pkg_dir
=
Path
(
_pkg
.
__file__
)
.
parent
_log
(
f
" Package dir : {pkg_dir}"
)
share
=
pkg_dir
/
'_share'
_log
(
f
" _share exists: {share.exists()}"
)
if
share
.
is_dir
():
contents
=
sorted
(
p
.
name
for
p
in
share
.
iterdir
())
_log
(
f
" _share contents: {contents}"
)
bundle
=
_pkg_bundle_dir
()
_log
(
f
" Bundle source: {bundle}"
)
except
Exception
as
e
:
_log
(
f
" ERROR inspecting package: {e}"
)
bundle
=
None
_log
(
""
)
if
bundle
is
None
:
if
bundle
is
None
:
_log
(
"ERROR: No bundle source found."
)
_log
(
" The wheel was probably built before the build_py hook was added."
)
_log
(
" Rebuild the wheel from the current source and reinstall:"
)
_log
(
" python -m build"
)
_log
(
" pip install dist/aisbf-*.whl --force-reinstall"
)
return
None
return
None
dest
=
Path
.
home
()
/
'.local'
/
'share'
/
'aisbf'
# ── copy files ────────────────────────────────────────────────────────────
dest
=
_default_share_dest
()
_log
(
f
"Destination : {dest}"
)
try
:
try
:
dest
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
dest
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
_log
(
f
" Created directory: {dest}"
)
for
fname
in
_REQUIRED_FILES
:
for
fname
in
_REQUIRED_FILES
:
src
=
bundle
/
fname
src
=
bundle
/
fname
...
@@ -122,19 +191,35 @@ def _bootstrap_share_dir():
...
@@ -122,19 +191,35 @@ def _bootstrap_share_dir():
if
fname
.
endswith
(
'.sh'
):
if
fname
.
endswith
(
'.sh'
):
p
=
dest
/
fname
p
=
dest
/
fname
p
.
chmod
(
p
.
stat
()
.
st_mode
|
0o755
)
p
.
chmod
(
p
.
stat
()
.
st_mode
|
0o755
)
_log
(
f
" Copied : {fname}"
)
else
:
_log
(
f
" MISSING: {fname} (not in bundle — bundle may be incomplete)"
)
for
dname
in
_REQUIRED_DIRS
:
for
dname
in
_REQUIRED_DIRS
:
src
=
bundle
/
dname
src
=
bundle
/
dname
dst
=
dest
/
dname
dst
=
dest
/
dname
if
src
.
exists
():
if
src
.
is_dir
():
if
dst
.
exists
():
if
dst
.
exists
():
shutil
.
rmtree
(
dst
)
shutil
.
rmtree
(
dst
)
shutil
.
copytree
(
src
,
dst
)
shutil
.
copytree
(
src
,
dst
)
n
=
sum
(
1
for
_
in
dst
.
rglob
(
'*'
)
if
_
.
is_file
())
return
dest
if
_share_dir_is_complete
(
dest
)
else
None
_log
(
f
" Copied : {dname}/ ({n} files)"
)
else
:
_log
(
f
" MISSING: {dname}/ (not in bundle)"
)
if
_share_dir_is_complete
(
dest
):
_log
(
f
"SUCCESS: Share directory is ready at {dest}"
)
return
dest
missing
=
[
f
for
f
in
_REQUIRED_FILES
+
_REQUIRED_DIRS
if
not
(
dest
/
f
)
.
exists
()]
_log
(
f
"INCOMPLETE after copy — still missing: {missing}"
)
return
None
except
Exception
as
e
:
except
Exception
as
e
:
print
(
f
"Warning: could not bootstrap share directory: {e}"
,
file
=
sys
.
stderr
)
_log
(
f
"ERROR during copy: {e}"
)
import
traceback
traceback
.
print_exc
(
file
=
sys
.
stderr
)
return
None
return
None
...
@@ -142,31 +227,22 @@ def main():
...
@@ -142,31 +227,22 @@ def main():
share_dir
=
_find_share_dir
()
share_dir
=
_find_share_dir
()
if
share_dir
is
None
:
if
share_dir
is
None
:
share_dir
=
_bootstrap_share_dir
()
if
share_dir
is
None
:
checked
=
'
\n
'
.
join
(
f
' - {p}'
for
p
in
_share_dir_candidates
())
dest
=
_default_share_dest
()
print
(
print
(
"AISBF share directory not found or incomplete — bootstrapping from package data …"
,
f
"
\n
Error: could not set up the AISBF share directory at {dest}.
\n\n
"
"Locations checked:
\n
"
f
"{checked}
\n\n
"
"The wheel was likely built before the build_py hook was added.
\n
"
"Rebuild from the current source and reinstall:
\n
"
" python -m build
\n
"
" pip install dist/aisbf-*.whl --force-reinstall"
,
file
=
sys
.
stderr
,
file
=
sys
.
stderr
,
)
)
share_dir
=
_bootstrap_share_dir
()
sys
.
exit
(
1
)
if
share_dir
:
print
(
f
"Bootstrapped to {share_dir}. Future runs will use this location."
,
file
=
sys
.
stderr
,
)
else
:
checked
=
'
\n
'
.
join
(
f
' - {p}'
for
p
in
_share_dir_candidates
())
print
(
"Error: could not set up the AISBF share directory.
\n\n
"
"Checked locations:
\n
"
f
"{checked}
\n\n
"
"The wheel may have been built without runtime files.
\n
"
"Re-install from a source distribution to fix this:
\n
"
" pip install aisbf --no-binary aisbf
\n
"
"Or install system-wide as root:
\n
"
" sudo pip install aisbf"
,
file
=
sys
.
stderr
,
)
sys
.
exit
(
1
)
script_path
=
share_dir
/
'aisbf.sh'
script_path
=
share_dir
/
'aisbf.sh'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment