Skip to content
Open
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
14 changes: 14 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copy this file to .env.local before running the scripts.

# scripts
MARMAT_APP_NAME=MaRMAT
MARMAT_PYTHON=python3
MARMAT_VENV_DIR=.venv
MARMAT_SRC_SUBDIR=src
MARMAT_RUNTIME_REQUIREMENTS=requirements.txt
MARMAT_BUILD_REQUIREMENTS=requirements-build.txt
MARMAT_BUILD_DIR=build-local
MARMAT_RELEASE_DIR=release
MARMAT_ZIP_NAME=MaRMAT-local-macOS.zip
MARMAT_QT_PLATFORM=offscreen
MARMAT_SMOKE_TIMEOUT_SECONDS=5
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
__pycache__/
.venv/
.env.local
build-local/
release/
MaRMAT.spec
build/MaRMAT/Analysis-00.toc
build/MaRMAT/base_library.zip
Expand Down Expand Up @@ -31,3 +35,11 @@ build/MaRMAT-min/localpycs/pyimod03_ctypes.pyc
build/MaRMAT-min/localpycs/pyimod04_pywin32.pyc
build/MaRMAT-min/localpycs/struct.pyc
dist/MaRMAT-min.exe

# Local developer notes
SETUP_NOTES.md
local-docs/
local-notes/
private-docs/
notes-local/
*.local.md
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ The Marriott Reparative Metadata Assessment Tool (MaRMAT) is an open-source appl

3.2 [Windows Users](#32-windows-users)

3.3 [Developer Builds](#33-developer-builds)

4. [Tips](#4-tips)

5. [Credits and Acknowledgments](#5-credits-and-acknowledgments)
Expand Down Expand Up @@ -129,6 +131,37 @@ Option 3:
4. If the necesssary python libraries haven't been installed yet, double-click to `install-dependencies.bat` file.
5. Open MaRMAT by double-clicking the `run-marmat.bat` file.

### 3.3 Developer Builds

Developers who want to run or package MaRMAT from source can use the scripts in `scripts/`.

First, create local script settings and install the development dependencies:

```bash
cp .env.local.example .env.local
./scripts/setup-dev.sh
```

To run MaRMAT directly from source:

```bash
./scripts/run-source.sh
```

To build a local macOS app bundle and release zip:

```bash
./scripts/build-macos.sh
```

The macOS build output is written to `build-local/dist/MaRMAT.app`, and the zip archive is written to `release/MaRMAT-local-macOS.zip`. To smoke test the packaged app:

```bash
./scripts/smoke-test-macos.sh
```

These scripts are intended for local developer builds. Public release distribution may still require project-specific assets, signing, notarization, and platform-specific packaging checks.

## 4. Tips
- Ensure metadata files can be in a TSV or CSV format.
- Ensure the lexicon files are in a CSV format.
Expand Down
2 changes: 2 additions & 0 deletions requirements-build.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-r requirements.txt
pyinstaller
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pandas
PyQt6
matplotlib
wordcloud
beautifulsoup4
lxml
81 changes: 81 additions & 0 deletions scripts/_common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail

MARMAT_PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MARMAT_ENV_FILE="$MARMAT_PROJECT_ROOT/.env.local"

if [ ! -f "$MARMAT_ENV_FILE" ]; then
echo "Missing .env.local. Copy .env.local.example to .env.local before running this script." >&2
exit 1
fi

set -a
source "$MARMAT_ENV_FILE"
set +a

require_env() {
for name in "$@"; do
if [ "${!name+x}" != "x" ]; then
echo "Missing required setting: $name" >&2
exit 1
fi

if [ -z "${!name}" ]; then
echo "Empty required setting: $name" >&2
exit 1
fi
done
}

project_path() {
case "$1" in
/*)
printf '%s\n' "$1"
;;
*)
printf '%s/%s\n' "$MARMAT_PROJECT_ROOT" "$1"
;;
esac
}

require_file() {
for path in "$@"; do
if [ ! -f "$path" ]; then
echo "Missing required file: $path" >&2
exit 1
fi
done
}

require_dir() {
for path in "$@"; do
if [ ! -d "$path" ]; then
echo "Missing required directory: $path" >&2
exit 1
fi
done
}

require_env \
MARMAT_APP_NAME \
MARMAT_PYTHON \
MARMAT_VENV_DIR \
MARMAT_SRC_SUBDIR \
MARMAT_RUNTIME_REQUIREMENTS \
MARMAT_BUILD_REQUIREMENTS \
MARMAT_BUILD_DIR \
MARMAT_RELEASE_DIR \
MARMAT_ZIP_NAME \
MARMAT_QT_PLATFORM \
MARMAT_SMOKE_TIMEOUT_SECONDS

MARMAT_VENV_PATH="$(project_path "$MARMAT_VENV_DIR")"
MARMAT_SRC_PATH="$(project_path "$MARMAT_SRC_SUBDIR")"
MARMAT_RUNTIME_REQUIREMENTS_PATH="$(project_path "$MARMAT_RUNTIME_REQUIREMENTS")"
MARMAT_BUILD_REQUIREMENTS_PATH="$(project_path "$MARMAT_BUILD_REQUIREMENTS")"
MARMAT_BUILD_PATH="$(project_path "$MARMAT_BUILD_DIR")"
MARMAT_RELEASE_PATH="$(project_path "$MARMAT_RELEASE_DIR")"
MARMAT_PYTHON_BIN="$MARMAT_VENV_PATH/bin/python"
MARMAT_PYINSTALLER_BIN="$MARMAT_VENV_PATH/bin/pyinstaller"
MARMAT_APP_BUNDLE_PATH="$MARMAT_BUILD_PATH/dist/$MARMAT_APP_NAME.app"
MARMAT_RELEASE_ZIP_PATH="$MARMAT_RELEASE_PATH/$MARMAT_ZIP_NAME"
42 changes: 42 additions & 0 deletions scripts/build-macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail

source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"

if [ "$(uname -s)" != "Darwin" ]; then
echo "This script builds the macOS app bundle and must run on macOS." >&2
exit 1
fi

require_file "$MARMAT_PYTHON_BIN" "$MARMAT_PYINSTALLER_BIN" "$MARMAT_SRC_PATH/main.py"
require_dir "$MARMAT_SRC_PATH/data"

rm -rf "$MARMAT_BUILD_PATH"
mkdir -p "$MARMAT_BUILD_PATH" "$MARMAT_RELEASE_PATH"

"$MARMAT_PYTHON_BIN" -m compileall \
"$MARMAT_SRC_PATH/main.py" \
"$MARMAT_SRC_PATH/controllers" \
"$MARMAT_SRC_PATH/models" \
"$MARMAT_SRC_PATH/views"

cd "$MARMAT_SRC_PATH"
"$MARMAT_PYINSTALLER_BIN" \
--noconfirm \
--clean \
--onedir \
--windowed \
--name "$MARMAT_APP_NAME" \
--distpath "$MARMAT_BUILD_PATH/dist" \
--workpath "$MARMAT_BUILD_PATH/work" \
--specpath "$MARMAT_BUILD_PATH/spec" \
--add-data "$MARMAT_SRC_PATH/data:data" \
main.py

require_dir "$MARMAT_APP_BUNDLE_PATH"

rm -f "$MARMAT_RELEASE_ZIP_PATH"
ditto -c -k --sequesterRsrc --keepParent "$MARMAT_APP_BUNDLE_PATH" "$MARMAT_RELEASE_ZIP_PATH"

echo "Built app bundle: $MARMAT_APP_BUNDLE_PATH"
echo "Built release zip: $MARMAT_RELEASE_ZIP_PATH"
9 changes: 9 additions & 0 deletions scripts/run-source.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail

source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"

require_file "$MARMAT_PYTHON_BIN" "$MARMAT_SRC_PATH/main.py"

cd "$MARMAT_SRC_PATH"
exec "$MARMAT_PYTHON_BIN" main.py
21 changes: 21 additions & 0 deletions scripts/setup-dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"

require_dir "$MARMAT_SRC_PATH"
require_file "$MARMAT_BUILD_REQUIREMENTS_PATH" "$MARMAT_SRC_PATH/main.py"

if [ ! -d "$MARMAT_VENV_PATH" ]; then
"$MARMAT_PYTHON" -m venv "$MARMAT_VENV_PATH"
fi

"$MARMAT_PYTHON_BIN" -m pip install --upgrade pip
"$MARMAT_PYTHON_BIN" -m pip install -r "$MARMAT_BUILD_REQUIREMENTS_PATH"
"$MARMAT_PYTHON_BIN" -m compileall \
"$MARMAT_SRC_PATH/main.py" \
"$MARMAT_SRC_PATH/controllers" \
"$MARMAT_SRC_PATH/models" \
"$MARMAT_SRC_PATH/views"

echo "MaRMAT developer environment is ready."
38 changes: 38 additions & 0 deletions scripts/smoke-test-macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail

source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"

require_file "$MARMAT_APP_BUNDLE_PATH/Contents/MacOS/$MARMAT_APP_NAME"

QT_QPA_PLATFORM="$MARMAT_QT_PLATFORM" "$MARMAT_PYTHON_BIN" - "$MARMAT_APP_BUNDLE_PATH/Contents/MacOS/$MARMAT_APP_NAME" "$MARMAT_SMOKE_TIMEOUT_SECONDS" <<'PY'
import os
import subprocess
import sys

app_path = sys.argv[1]
timeout_seconds = int(sys.argv[2])

try:
result = subprocess.run(
[app_path],
env=os.environ.copy(),
text=True,
capture_output=True,
timeout=timeout_seconds,
)
except subprocess.TimeoutExpired:
print(f"Packaged app started and stayed running for {timeout_seconds} seconds.")
raise SystemExit(0)

if result.returncode == 0:
print("Packaged app exited cleanly before the smoke-test timeout.")
raise SystemExit(0)

if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
print(f"Packaged app smoke test failed with exit code {result.returncode}.", file=sys.stderr)
raise SystemExit(result.returncode)
PY