Seth McKnight Copilot commited on
Commit
29c3655
·
1 Parent(s): 5e32900

Update CI/CD workflow and enhance contributing guidelines (#51)

Browse files

* chore: Update CI/CD workflow to support multiple Python versions and add contributing guidelines

- Modify CI/CD workflow to enforce Python 3.x and support versions 3.10, 3.11, and 3.12.
- Add a new yamllint configuration file for consistent YAML formatting.
- Create a contributing guide with setup instructions and CI expectations.
- Enhance README with instructions for creating a reproducible Python environment using pyenv and venv.
- Introduce a dev-setup script to automate environment setup.
- Ensure project root and source paths are included in test configurations.

* Update dev-setup.sh

Co-authored-by: Copilot <[email protected]>

* fix: Update Python version in CI configuration to 3.10

* fix: Disable ChromaDB anonymized telemetry for local development

* ci: quote python versions in workflow matrix to avoid YAML float parsing

---------

Co-authored-by: Copilot <[email protected]>

Files changed (8) hide show
  1. .github/workflows/main.yml +20 -7
  2. .yamllint +10 -0
  3. CONTRIBUTING.md +30 -0
  4. README.md +38 -0
  5. app.py +12 -0
  6. dev-setup.sh +31 -0
  7. pyproject.toml +13 -1
  8. tests/conftest.py +12 -0
.github/workflows/main.yml CHANGED
@@ -26,6 +26,7 @@ jobs:
26
  - name: Set up Python
27
  uses: actions/setup-python@v5
28
  with:
 
29
  python-version: "3.10"
30
  - name: Install dev dependencies
31
  run: |
@@ -43,6 +44,12 @@ jobs:
43
  build-and-test:
44
  name: Build and test
45
  runs-on: ubuntu-latest
 
 
 
 
 
 
46
  env:
47
  PYTHONPATH: ${{ github.workspace }}
48
  steps:
@@ -53,7 +60,7 @@ jobs:
53
  - name: Set up Python
54
  uses: actions/setup-python@v5
55
  with:
56
- python-version: "3.10"
57
  - name: Install dependencies
58
  run: |
59
  python -m pip install --upgrade pip
@@ -95,7 +102,8 @@ jobs:
95
  run: |
96
  set -e
97
  echo "Triggering deploy for Render service $RENDER_SERVICE_ID"
98
- response=$(curl -s -X POST "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys" \
 
99
  -H "Authorization: Bearer ${RENDER_API_KEY}" \
100
  -H "Content-Type: application/json" \
101
  -d "{}")
@@ -122,8 +130,10 @@ jobs:
122
  retries=0
123
  max_retries=$MAX_RETRIES
124
  delay=$INITIAL_DELAY
125
- while [ $retries -lt $max_retries ]; do
126
- resp=$(curl -s -H "Authorization: Bearer ${RENDER_API_KEY}" "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys/${deploy_id}")
 
 
127
  status=$(echo "$resp" | jq -r '.status')
128
  echo "Deploy status: $status"
129
  # Treat common Render success-like statuses as success so we proceed.
@@ -195,6 +205,9 @@ jobs:
195
  # create PR using GitHub API
196
  PR_TITLE="chore: update deployed.md after deploy"
197
  PR_BODY="Automated update of deployed.md after successful deploy."
198
- curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" \
199
- https://api.github.com/repos/${{ github.repository }}/pulls \
200
- -d "{\"title\": \"${PR_TITLE}\", \"head\": \"${BRANCH_NAME}\", \"base\": \"main\", \"body\": \"${PR_BODY}\"}"
 
 
 
 
26
  - name: Set up Python
27
  uses: actions/setup-python@v5
28
  with:
29
+ # ensure CI enforces modern Python versions
30
  python-version: "3.10"
31
  - name: Install dev dependencies
32
  run: |
 
44
  build-and-test:
45
  name: Build and test
46
  runs-on: ubuntu-latest
47
+ strategy:
48
+ matrix:
49
+ # Quote versions so YAML treats them as strings. Unquoted 3.10 can be parsed as
50
+ # a float (3.1) which causes actions/setup-python to attempt to install the wrong
51
+ # runtime. Use '3.10', '3.11', etc.
52
+ python-version: ['3.10', '3.11', '3.12']
53
  env:
54
  PYTHONPATH: ${{ github.workspace }}
55
  steps:
 
60
  - name: Set up Python
61
  uses: actions/setup-python@v5
62
  with:
63
+ python-version: ${{ matrix.python-version }}
64
  - name: Install dependencies
65
  run: |
66
  python -m pip install --upgrade pip
 
102
  run: |
103
  set -e
104
  echo "Triggering deploy for Render service $RENDER_SERVICE_ID"
105
+ response=$(curl -s -X POST \
106
+ "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys" \
107
  -H "Authorization: Bearer ${RENDER_API_KEY}" \
108
  -H "Content-Type: application/json" \
109
  -d "{}")
 
130
  retries=0
131
  max_retries=$MAX_RETRIES
132
  delay=$INITIAL_DELAY
133
+ while [ $retries -lt $max_retries ]; do
134
+ resp=$(curl -s \
135
+ -H "Authorization: Bearer ${RENDER_API_KEY}" \
136
+ "https://api.render.com/v1/services/${RENDER_SERVICE_ID}/deploys/${deploy_id}")
137
  status=$(echo "$resp" | jq -r '.status')
138
  echo "Deploy status: $status"
139
  # Treat common Render success-like statuses as success so we proceed.
 
205
  # create PR using GitHub API
206
  PR_TITLE="chore: update deployed.md after deploy"
207
  PR_BODY="Automated update of deployed.md after successful deploy."
208
+ PR_PAYLOAD=$(printf '{"title":"%s","head":"%s","base":"main","body":"%s"}' "$PR_TITLE" "$BRANCH_NAME" "$PR_BODY")
209
+ curl -s -X POST \
210
+ -H "Authorization: token $GITHUB_TOKEN" \
211
+ -H "Accept: application/vnd.github.v3+json" \
212
+ "https://api.github.com/repos/${{ github.repository }}/pulls" \
213
+ -d "$PR_PAYLOAD"
.yamllint ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ # Repository yamllint configuration for msse-ai-engineering
3
+ # Relax rules that commonly conflict with GitHub Actions workflow formatting
4
+ extends: default
5
+ rules:
6
+ document-start: disable
7
+ truthy: disable
8
+ line-length:
9
+ max: 140
10
+ level: error
CONTRIBUTING.md ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing
2
+
3
+ Thanks for wanting to contribute! This repository uses a strict CI and formatting policy to keep code consistent.
4
+
5
+ ## Recommended local setup
6
+
7
+ We recommend using `pyenv` + `venv` to create a reproducible development environment. A helper script `dev-setup.sh` is included to automate the steps:
8
+
9
+ ```bash
10
+ # Run the helper script (default Python version can be overridden)
11
+ ./dev-setup.sh 3.11.4
12
+ source venv/bin/activate
13
+
14
+ # Install pre-commit hooks
15
+ pip install -r dev-requirements.txt
16
+ pre-commit install
17
+ ```
18
+
19
+ ## Before opening a PR
20
+
21
+ - Run formatting and linting: `make format` and `make ci-check`
22
+ - Run tests: `pytest`
23
+ - Ensure pre-commit hooks pass: `pre-commit run --all-files`
24
+
25
+ ## CI expectations
26
+
27
+ - CI runs pre-commit checks and the full test suite on PRs
28
+ - The project enforces Python >=3.10 in CI
29
+
30
+ Please open issues or PRs against `main` and follow the branch naming conventions described in the README.
README.md CHANGED
@@ -242,6 +242,32 @@ The application uses a comprehensive synthetic corpus of corporate policy docume
242
  - Git
243
  - OpenRouter API key (free tier available)
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  ### 1. Repository Setup
246
 
247
  ```bash
@@ -251,6 +277,10 @@ cd msse-ai-engineering
251
 
252
  ### 2. Environment Setup
253
 
 
 
 
 
254
  ```bash
255
  # Create and activate virtual environment
256
  python3 -m venv venv
@@ -263,6 +293,14 @@ pip install -r requirements.txt
263
  pip install -r dev-requirements.txt
264
  ```
265
 
 
 
 
 
 
 
 
 
266
  ### 3. Configuration
267
 
268
  ```bash
 
242
  - Git
243
  - OpenRouter API key (free tier available)
244
 
245
+ #### Recommended: Create a reproducible Python environment with pyenv + venv
246
+
247
+ If you used an older Python (for example 3.8) you'll hit build errors when installing modern ML packages like `tokenizers` and `sentence-transformers`. The steps below create a clean Python 3.11 environment and install project dependencies.
248
+
249
+ ```bash
250
+ # Install pyenv (Homebrew) if you don't have it:
251
+ # brew update && brew install pyenv
252
+
253
+ # Install a modern Python (example: 3.11.4)
254
+ pyenv install 3.11.4
255
+
256
+ # Use the newly installed version for this project (creates .python-version)
257
+ pyenv local 3.11.4
258
+
259
+ # Create a virtual environment and activate it
260
+ python -m venv venv
261
+ source venv/bin/activate
262
+
263
+ # Upgrade packaging tools and install dependencies
264
+ python -m pip install --upgrade pip setuptools wheel
265
+ pip install -r requirements.txt
266
+ pip install -r dev-requirements.txt || true
267
+ ```
268
+
269
+ If you prefer not to use `pyenv`, install Python 3.10+ from python.org or Homebrew and create the `venv` with the system `python3`.
270
+
271
  ### 1. Repository Setup
272
 
273
  ```bash
 
277
 
278
  ### 2. Environment Setup
279
 
280
+ Two supported flows are provided: a minimal venv-only flow and a reproducible pyenv+venv flow.
281
+
282
+ Minimal (system Python 3.10+):
283
+
284
  ```bash
285
  # Create and activate virtual environment
286
  python3 -m venv venv
 
293
  pip install -r dev-requirements.txt
294
  ```
295
 
296
+ Reproducible (recommended — uses pyenv to install a pinned Python and create a clean venv):
297
+
298
+ ```bash
299
+ # Use the helper script to install pyenv Python and create a venv
300
+ ./dev-setup.sh 3.11.4
301
+ source venv/bin/activate
302
+ ```
303
+
304
  ### 3. Configuration
305
 
306
  ```bash
app.py CHANGED
@@ -1,5 +1,17 @@
1
  from flask import Flask, jsonify, render_template, request
2
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  app = Flask(__name__)
4
 
5
 
 
1
  from flask import Flask, jsonify, render_template, request
2
 
3
+ # Disable ChromaDB anonymized telemetry for local development so the
4
+ # library doesn't attempt to call external PostHog telemetry endpoints.
5
+ # This avoids noisy errors in server logs and respects developer privacy.
6
+ try:
7
+ import chromadb
8
+
9
+ # Turn off anonymized telemetry (the chromadb package defaults this to True)
10
+ chromadb.configure(anonymized_telemetry=False)
11
+ except Exception:
12
+ # If chromadb isn't installed in this environment yet, ignore silently.
13
+ pass
14
+
15
  app = Flask(__name__)
16
 
17
 
dev-setup.sh ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # dev-setup.sh - create a reproducible development environment (pyenv + venv)
3
+ # Usage: ./dev-setup.sh [python-version]
4
+
5
+ set -euo pipefail
6
+ PYTHON_VERSION=${1:-3.11.4}
7
+
8
+ echo "Using python version: ${PYTHON_VERSION}"
9
+
10
+ if ! command -v pyenv >/dev/null 2>&1; then
11
+ echo "pyenv not found. Install via Homebrew: brew install pyenv"
12
+ exit 1
13
+ fi
14
+
15
+ pyenv install -s "${PYTHON_VERSION}"
16
+ pyenv local "${PYTHON_VERSION}"
17
+
18
+ # Recreate venv
19
+ rm -rf venv
20
+ pyenv exec python -m venv venv
21
+
22
+ # Activate and install
23
+ # shellcheck source=/dev/null
24
+ source venv/bin/activate
25
+ python -m pip install --upgrade pip setuptools wheel
26
+ python -m pip install -r requirements.txt
27
+ if [ -f dev-requirements.txt ]; then
28
+ python -m pip install -r dev-requirements.txt
29
+ fi
30
+
31
+ echo "Development environment ready. Activate with: source venv/bin/activate"
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
  [tool.black]
2
  line-length = 88
3
- target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
4
  include = '\.pyi?$'
5
  extend-exclude = '''
6
  /(
@@ -39,3 +39,15 @@ filterwarnings = [
39
  "ignore::DeprecationWarning",
40
  "ignore::PendingDeprecationWarning",
41
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  [tool.black]
2
  line-length = 88
3
+ target-version = ['py310', 'py311', 'py312']
4
  include = '\.pyi?$'
5
  extend-exclude = '''
6
  /(
 
39
  "ignore::DeprecationWarning",
40
  "ignore::PendingDeprecationWarning",
41
  ]
42
+
43
+ [build-system]
44
+ requires = ["setuptools>=61.0", "wheel"]
45
+ build-backend = "setuptools.build_meta"
46
+
47
+ [project]
48
+ name = "msse-ai-engineering"
49
+ version = "0.0.0"
50
+ description = "MSSE AI Engineering - RAG application"
51
+ readme = "README.md"
52
+ requires-python = ">=3.10"
53
+ authors = [ { name = "msse-ai-engineering" } ]
tests/conftest.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Ensure project root and src are on sys.path for tests
5
+ PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
6
+ SRC_PATH = os.path.join(PROJECT_ROOT, "src")
7
+
8
+ if PROJECT_ROOT not in sys.path:
9
+ sys.path.insert(0, PROJECT_ROOT)
10
+
11
+ if SRC_PATH not in sys.path:
12
+ sys.path.insert(0, SRC_PATH)