Odin Deployment
Version 3 · Add current launchers, SHA-256 deployment, shared assets, and scoped restart procedures.
Odin Deployment
Purpose
QWebHub and its sibling modules deploy to Odin through local Windows BAT launchers backed by the shared Python FTP helper. The FTP password is stored once in the Windows user environment and is never written into a repository, BAT file, wiki page, or deployment command.
One-time credential setup
Store the password for future processes with a placeholder command such as:
setx ODIN_FTP_PASSWORD "<password>"
Open a new terminal after running setx; it does not update terminals that are already open. Do not replace the placeholder in documentation or commit the real value anywhere.
The launchers map ODIN_FTP_PASSWORD to the generic helper environment, and the Python FTP helpers also read ODIN_FTP_PASSWORD directly. If it is unavailable, the helpers fall back to a secure interactive prompt.
Deployment launchers
| Scope | Launcher |
|---|---|
| QWebHub only | QWebHub\deploy_odin.bat |
| QProcess only | QProcess\deploy_odin.bat |
| QDBAdmin only | AdminManagement\QDBAdmin\deploy_odin.bat |
| QDG KB Viewer only | QDG KB Viewer\deploy_odin.bat |
| Gary Breeding Reports only | Gary Breeding Report\deploy_odin.bat |
| Hub, all supported modules, then shared assets | QWebHub\deploy_all_to_odin.bat |
For each deployment launcher:
- Double-clicking uploads changed files.
- Passing
--dry-runlists files without uploading. - Passing
--applyexplicitly uploads changed files. - Passing
--apply --forceuploads all selected files even when server file sizes match.
Examples:
Set-Location <QWebHub repository>
.\deploy_odin.bat --dry-run
.\deploy_odin.bat --apply --force
The QWebHub launcher includes scripts\reload_qwebhub.py. QProcess deploys its active application package and templates with local diagnostic/test exclusions. QDG KB Viewer deploys its application package, templates, static source, manage.py, and requirements.
SHA-256 deployment behaviour
QWebHub owns the deployment engine used by supported module launchers. Each
remote project root has a versioned .odin-deploy-manifest.json. Upload
decisions compare local SHA-256 values with that manifest rather than relying on
file size or timestamps.
Uploads use 1–8 workers per project (default 4), one FTP connection per worker,
and one bounded retry with a fresh connection after a transient disconnect. The
manifest is published only after every planned upload succeeds. A failed file
leaves the manifest unchanged, so a later run safely schedules the incomplete
deployment again. deploy_all_to_odin.bat remains project-sequential and stops
at the first failed project. It supplies shared non-secret FTP host and username
defaults; the password remains only in ODIN_FTP_PASSWORD.
Useful read-only modes:
deploy_odin --check
deploy_odin --dry-run
deploy_odin --verify-remote
--offline-dry-run inventories local files without connecting but cannot
accurately identify remote changes. --verify-remote downloads selected remote
files and verifies their hashes without writing.
Shared static assets
QWebHub collects shared CSS from Global Memory/CSS and optimized 200×200 web
icons from Global Assests/Icons. Larger Admin Images files are source
artwork and are deliberately excluded from shared static discovery.
Collect static files and restart QWebHub
After uploading application or CSS changes, use the QWebHub button Collect
static files and restart QWebHub. It sends a CSRF-protected POST to
/operations/reload/, schedules a detached helper, runs
manage.py collectstatic --noinput, stops if collection fails, then starts the
narrowly scoped Odin task \QWebHub\Restart. That task recycles the IIS
application pool currently hosting QWebHub; it does not run iisreset or give
the web process unrestricted IIS privileges.
The operation is temporarily available to everyone behind Odin's site-level authentication. Restrict it to the existing administrator permission/group once QDBAdmin administrator accounts are provisioned.
Verification
-
Open a new terminal and confirm the variable exists without printing its value:
if ($env:ODIN_FTP_PASSWORD) { 'ODIN_FTP_PASSWORD is available' } -
Run the intended launcher with
--dry-runand verify the destination and file list. -
Run the launcher normally or with
--apply. -
For QWebHub, confirm
/healthz/returns successfully after the server-side restart workflow. -
Confirm QDG KB Viewer cards and static styling render correctly after collection/restart.
Security rules
- Never put the password value in a BAT file, Python file, JSON configuration, Git history, prompt, log, or wiki page.
- Keep credential-bearing local launchers ignored with exact paths or narrow patterns.
- Before committing deployment changes, inspect every BAT and JSON configuration file for credentials and remove any tracked sensitive file from the Git index while preserving the local copy.
- Rotate the credential if it appears in source control, command output, or another exposed location.