From fdb204a2ad18959a1f2c22b404c2f42526d5761e Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Wed, 28 May 2025 01:34:39 +0200 Subject: [PATCH 01/12] assignment 1 improved to reach excellet in rubric --- Dockerfile | 10 +++++----- src/app.py | 16 +++++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index a6761be..cef35ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,10 @@ RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt COPY . . -ENV FLASK_APP=src/app.py -ENV PYTHONUNBUFFERED=1 -ENV FLASK_ENV=development +ENV PYTHONPATH=/app +ENV PORT=8080 +ENV HOST=0.0.0.0 -EXPOSE 8080 +EXPOSE ${PORT} -CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=8080"] +CMD ["python", "src/app.py"] diff --git a/src/app.py b/src/app.py index 23e8264..cf1c627 100644 --- a/src/app.py +++ b/src/app.py @@ -1,13 +1,18 @@ from flask import Flask -from flask import request, current_app, jsonify +from flask import request, jsonify from flasgger import Swagger, swag_from import requests -from libml.preprocessing import preprocess_train, preprocess_inference +from libml.preprocessing import preprocess_inference + import pickle import numpy as np import os import logging -from config import BASE_URL, MODEL_VERSION +from config import MODEL_VERSION, BASE_URL + +# Set up environment variables +HOST = os.environ.get("HOST", "0.0.0.0") +PORT = os.environ.get("PORT", 8080) if not os.path.exists("models"): @@ -64,6 +69,7 @@ def home(): @app.route("/predict", methods=["POST"]) @swag_from( { + "summary": "Make a sentiment prediction based on input review", "tags": ["Prediction"], "parameters": [ { @@ -122,5 +128,5 @@ def predict(): return jsonify({"error": "Prediction failed"}), 500 -# if __name__ == "__main__": -# app.run(debug=True) +if __name__ == "__main__": + app.run(host=HOST, port=PORT, debug=False) From cfe83a693b603be746301c18e7ba78763fb09fef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 May 2025 23:35:32 +0000 Subject: [PATCH 02/12] Update pylint score badge to 9.67 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b9ca8f..2a4ccda 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # model-service -![Pylint Score](https://img.shields.io/badge/pylint-10%2E00%2F10-brightgreen) +![Pylint Score](https://img.shields.io/badge/pylint-9%2E67%2F10-brightgreen) This repository acts as a wrapper service for the ML model which provides a REST API to expose the model via endpoints. From 09e2c3fd117fe14fc8f13efb502eff9c348bc884 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Wed, 28 May 2025 02:02:42 +0200 Subject: [PATCH 03/12] linting warnings/errors fixed --- src/app.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app.py b/src/app.py index cf1c627..067440d 100644 --- a/src/app.py +++ b/src/app.py @@ -10,9 +10,12 @@ import logging from config import MODEL_VERSION, BASE_URL +DEFAULT_PORT = 8080 +DEFAULT_HOST = "127.0.0.1" + # Set up environment variables -HOST = os.environ.get("HOST", "0.0.0.0") -PORT = os.environ.get("PORT", 8080) +HOST = os.environ.get("HOST", DEFAULT_HOST) +PORT = int(os.environ.get("PORT", DEFAULT_PORT)) if not os.path.exists("models"): @@ -129,4 +132,4 @@ def predict(): if __name__ == "__main__": - app.run(host=HOST, port=PORT, debug=False) + app.run(host=HOST, port=PORT) From 5e5b239224474c6b8f71de0fce4417a2b7ae2a97 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Wed, 28 May 2025 02:59:16 +0200 Subject: [PATCH 04/12] Automated Release Process part of rubric referenced to achieve execellent --- .github/workflows/workflow.yml | 75 ++++++++++++++++++++++++++++++++-- Dockerfile | 23 +++++++++-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 2b7df7f..08ca13e 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -17,28 +17,97 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - name: Extract version from tag run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV - - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . + platforms: linux/amd64,linux/arm64 push: true tags: | ghcr.io/remla25-team21/model-service:${{ env.VERSION }} ghcr.io/remla25-team21/model-service:latest - - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.ref_name }} generate_release_notes: true + bump_version_on_main: + name: Bump version on main to next pre-release + needs: release + runs-on: ubuntu-latest + permissions: + contents: write + if: success() + steps: + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + + - name: Get released version from trigger + id: get_released_version + run: echo "RELEASED_VERSION_TAG=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + + - name: Fetch all tags + run: git fetch --tags + - name: Get current pre-release counter + id: get_pre_release_counter + run: | + base_version="${{ env.RELEASED_VERSION_TAG }}" + # List tags matching pre-release pattern and sort them. + pre_tags=$(git tag --list "v${base_version}-pre.*" --sort=-v:refname) + + if [[ -z "$pre_tags" ]]; then + counter=0 + else + # Extract the last numeric counter from the highest tag + last_tag=$(echo "$pre_tags" | head -n 1) + echo "Last pre-release tag: $last_tag" + + # Extract counter after last dash, e.g. 1.2.3-pre.5 -> 5 + counter=$(echo "$last_tag" | grep -oE '[0-9]+$') + counter=${counter:-0} + fi + + next_counter=$((counter + 1)) + echo "Next counter: $next_counter" + + echo "PRE_RELEASE_COUNTER=$next_counter" >> $GITHUB_ENV + + - name: Calculate next pre-release version with counter + id: calculate_next_version + run: | + current_version="${{ env.RELEASED_VERSION_TAG }}" + pre_counter="${{ env.PRE_RELEASE_COUNTER }}" + IFS='.' read -r major minor patch <<< "$current_version" + + next_pre_release_version="${major}.${minor}.${patch}-pre.${pre_counter}" + + echo "Next pre-release version: $next_pre_release_version" + echo "NEXT_VERSION=$next_pre_release_version" >> $GITHUB_ENV + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Optionally create a tag instead of a version file + - name: Create pre-release tag + run: | + git tag -a "v${{ env.NEXT_VERSION }}" -m "Start next development cycle" + git push origin "v${{ env.NEXT_VERSION }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile index cef35ee..3e83936 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,10 @@ -FROM python:3.11-slim +# Using multi-stage build to minimize the final image size + +# Stage 1: Builder +FROM python:3.11-slim AS builder RUN apt-get update && \ - apt-get install -y git curl && \ + apt-get install -y git curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -12,10 +15,24 @@ RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt COPY . . +# Stage 2: Final image +FROM python:3.11-slim + + +RUN apt-get update && \ + apt-get install -y curl --no-install-recommends && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages +COPY --from=builder /app /app + ENV PYTHONPATH=/app ENV PORT=8080 ENV HOST=0.0.0.0 EXPOSE ${PORT} -CMD ["python", "src/app.py"] +CMD ["python", "src/app.py"] \ No newline at end of file From 1e11a6fc3d52efd24a7b759d0bead972a1984e29 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 May 2025 01:01:38 +0000 Subject: [PATCH 05/12] Update pylint score badge to 10.00 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a4ccda..9b9ca8f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # model-service -![Pylint Score](https://img.shields.io/badge/pylint-9%2E67%2F10-brightgreen) +![Pylint Score](https://img.shields.io/badge/pylint-10%2E00%2F10-brightgreen) This repository acts as a wrapper service for the ML model which provides a REST API to expose the model via endpoints. From 6951f52e604ed82d55a0f81bcfe172ba6d66629b Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Wed, 28 May 2025 14:25:04 +0200 Subject: [PATCH 06/12] updated github workflow --- .github/workflows/lint-n-test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint-n-test.yaml b/.github/workflows/lint-n-test.yaml index 0a9f0c5..0ada915 100644 --- a/.github/workflows/lint-n-test.yaml +++ b/.github/workflows/lint-n-test.yaml @@ -13,7 +13,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.11" - name: Install dependencies run: | @@ -54,6 +54,6 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git add README.md git commit -m "Update pylint score badge to ${{ steps.pylint.outputs.pylint_score }}" - git push + git push origin HEAD:${GITHUB_REF##*/} env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 00062cf9a25bbc4f76099b403b05cc2ae26909ed Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Wed, 28 May 2025 14:28:34 +0200 Subject: [PATCH 07/12] forgot to save the latest workflow changes --- .github/workflows/workflow.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 08ca13e..1c2a6fb 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -44,6 +44,8 @@ jobs: with: tag_name: ${{ github.ref_name }} generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} bump_version_on_main: name: Bump version on main to next pre-release needs: release From 6714c6de962f38d957b8e31120a5962066ee0871 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Sun, 8 Jun 2025 17:29:04 +0200 Subject: [PATCH 08/12] workflow updated --- .github/workflows/lint-n-test.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-n-test.yaml b/.github/workflows/lint-n-test.yaml index 0ada915..0a155c3 100644 --- a/.github/workflows/lint-n-test.yaml +++ b/.github/workflows/lint-n-test.yaml @@ -52,8 +52,9 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout ${{ github.head_ref || github.ref_name }} # Switch to the branch git add README.md git commit -m "Update pylint score badge to ${{ steps.pylint.outputs.pylint_score }}" - git push origin HEAD:${GITHUB_REF##*/} + git push env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 84a8a554f1eb67d9582720e5395f4e0e5430f317 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Sun, 8 Jun 2025 17:45:20 +0200 Subject: [PATCH 09/12] handle the case where there are no staged changes in the workflow --- .github/workflows/lint-n-test.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-n-test.yaml b/.github/workflows/lint-n-test.yaml index 0a155c3..9729d31 100644 --- a/.github/workflows/lint-n-test.yaml +++ b/.github/workflows/lint-n-test.yaml @@ -52,8 +52,17 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git checkout ${{ github.head_ref || github.ref_name }} # Switch to the branch + git add README.md + + # Check if there are any staged changes. If not, exit gracefully. + # `git diff --staged --quiet` exits with 1 if there are changes, 0 if not. + if git diff --staged --quiet; then + echo "No changes to commit. README is up-to-date." + exit 0 + fi + + # If there are changes, commit and push them git commit -m "Update pylint score badge to ${{ steps.pylint.outputs.pylint_score }}" git push env: From e2905016556930d9973a73d7a9f6562c62a7cf14 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Sun, 8 Jun 2025 17:57:51 +0200 Subject: [PATCH 10/12] workflow permission changed --- .github/workflows/workflow.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1c2a6fb..dc5449c 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -13,6 +13,8 @@ jobs: release: name: Build and Release runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout code From 409d6282a6310a9057ed41aa1b8742722541a347 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Sun, 8 Jun 2025 18:11:28 +0200 Subject: [PATCH 11/12] added permission to push to ghcr --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index dc5449c..e44bd6b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - + packages: write steps: - name: Checkout code uses: actions/checkout@v4 From d608b70c853b4a69a15162ae379fb4f2b71d8706 Mon Sep 17 00:00:00 2001 From: kanta8864 Date: Sun, 8 Jun 2025 18:25:33 +0200 Subject: [PATCH 12/12] bump version number and then create pre-release --- .github/workflows/workflow.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index e44bd6b..646980b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -98,7 +98,9 @@ jobs: pre_counter="${{ env.PRE_RELEASE_COUNTER }}" IFS='.' read -r major minor patch <<< "$current_version" - next_pre_release_version="${major}.${minor}.${patch}-pre.${pre_counter}" + next_patch=$((patch + 1)) + + next_pre_release_version="${major}.${minor}.${next_patch}-pre.${pre_counter}" echo "Next pre-release version: $next_pre_release_version" echo "NEXT_VERSION=$next_pre_release_version" >> $GITHUB_ENV