Welcome to Module 6 of the tutorial series: Build an AI-Powered Documentation Assistant with DeepSeek and Flask. In this module, you’ll implement advanced features like version control, documentation drift detection, and automated deployment. You’ll also learn how to generate documentation for an entire GitHub repository, including subdirectories. By the end, your documentation assistant will be a fully automated, production-ready web service.
Prerequisites
Before starting Module 6, ensure you’ve completed the following steps:
- Module 5: You should have a working Flask app that generates and hosts API documentation on GitHub Pages.
- GitHub Repository: Your Flask app and documentation should be hosted on GitHub.
- DeepSeek API Key: Ensure your DeepSeek account has sufficient balance.
- Cloud Platform Account: Sign up for a cloud platform like Heroku, AWS, or Google Cloud for deployment.
- GitHub Access Token: Generate a GitHub access token with the
repo
scope and add it to your.env
file:GITHUB_ACCESS_TOKEN=your_github_access_token
Lesson 11: Version control & documentation drift detection
In this lesson, you’ll implement version control for your documentation and detect when documentation becomes outdated due to code changes.
Objective
- Compare old and new versions of documentation.
- Send update suggestions via GitHub PR comments.
- Add unit and integration tests for the Flask API.
Step 1: Implement Version Control for Documentation
You’ll add functionality to tag documentation versions and compare them.
- Open
docstring_generator.py
:Ensure that thetag_documentation_version
function exists and includes the following code:def tag_documentation_version(version): """ Tag the current documentation with a version number. """ import subprocess subprocess.run(["git", "tag", f"v{version}"]) subprocess.run(["git", "push", "origin", f"v{version}"])
The
tag_documentation_version
function tags the current documentation with a version number. It imports thesubprocess
module to run system commands. First, it uses thegit tag
command to create a version tag, prefixed with “v” and followed by the specified version number. Then, it pushes the newly created tag to the remote repository using thegit push
command. - Update
routes.py
:Add a new route to handle version tagging.@main_bp.route("/tag-version", methods=["POST"]) def tag_version_route(): """ Tag the current documentation with a version number. """ data = request.json version = data.get("version") try: tag_documentation_version(version) return jsonify({"message": f"Documentation tagged as v{version} successfully!"}) except Exception as e: return jsonify({"error": str(e)}), 500
The
tag_version_route
function handles the tagging of documentation with a version number. The@main_bp.route("/tag-version", methods=["POST"])
decorator maps the function to the/tag-version
endpoint and allows onlyPOST
requests.- The function retrieves JSON data from the request body and extracts the
version
value. - It calls the
tag_documentation_version
function, passing theversion
as an argument to create the tag. - If the tagging succeeds, the function responds with a JSON message confirming that the documentation was successfully tagged.
- If an error occurs during the process, the function catches the exception and returns a JSON response with the error message and a 500 (Internal Server Error) status code.
- The function retrieves JSON data from the request body and extracts the
- Test Version Tagging:Use the following curl command to test the new route:
curl -X POST http://127.0.0.1:5000/tag-version -H "Content-Type: application/json" -d '{"version": "1.0.0"}'
Step 2: Detect documentation drift
You’ll compare old and new versions of documentation to detect changes.
- Update
docstring_generator.py
:Ensure that thedetect_outdated_docs
function exists and includes the following code:def detect_outdated_docs(code, docs): """ Detect outdated documentation by comparing code and docs. """ current_docs = generate_markdown_docs(code) if current_docs != docs: return "Documentation is outdated. Please regenerate." return "Documentation is up-to-date."
The
detect_outdated_docs
function identifies outdated documentation by comparing the current code with the provided documentation.- The function generates the current documentation from the
code
by calling thegenerate_markdown_docs
function and assigns the result to thecurrent_docs
variable. - It compares
current_docs
with the provideddocs
. - If the two do not match, the function returns a message indicating that the documentation is outdated and needs regeneration.
- If the documentation matches the code, the function returns a message confirming that the documentation is up-to-date.
- The function generates the current documentation from the
- Update
routes.py
:Add a new route to check for outdated documentation.@main_bp.route("/check-docs", methods=["POST"]) def check_docs_route(): """ Check if documentation is outdated. """ data = request.json code = data.get("code") docs = data.get("docs") try: result = detect_outdated_docs(code, docs) return jsonify({"message": result}) except Exception as e: return jsonify({"error": str(e)}), 500
The
check_docs_route
function handles a request to check whether the documentation is outdated.- The function listens for a
POST
request at the/check-docs
endpoint. - It extracts the JSON data from the request and retrieves the values for
"code"
and"docs"
. - It calls the
detect_outdated_docs
function to compare the code with the provided documentation and assigns the result to theresult
variable. - If the function detects outdated documentation, it responds with a JSON object containing the message.
- If an error occurs during execution, the function catches the exception and responds with an error message and a status code of
500
.
- The function listens for a
- Test documentation drift detection:Use the following curl command to test the new route:
curl -X POST http://127.0.0.1:5000/check-docs -H "Content-Type: application/json" -d '{"code": "def add(a, b): return a + b", "docs": "Old documentation"}'
Step 3: Add unit and integration tests
You’ll write tests to ensure your Flask API works as expected.
- Create
test_routes.py
:Add unit and integration tests for your Flask API.import os import sys import pytest from app import create_app sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) @pytest.fixture def client(): app = create_app() app.config['TESTING'] = True with app.test_client() as client: yield client def test_home_route(client): response = client.get('/') assert response.status_code == 200 assert b"Welcome" in response.data def test_generate_docstring_route(client): response = client.post('/generate-docstring', json={"code": "def example(): pass"}) assert response.status_code in [200, 500] # 500 if DeepSeek API key is missing def test_check_docs_route(client): response = client.post("/check-docs", json={"code": "def add(a, b): return a + b", "docs": "Old documentation"}) assert response.status_code == 200 assert "Documentation is outdated" in response.json["message"]
The code defines a set of tests for a Flask application using the
pytest
framework.- The script imports the necessary modules:
os
,sys
,pytest
, and thecreate_app
function from theapp
module. - It adds the parent directory of the current script to the Python path. This step allows the script to access the
app
module when running tests. - The
client
fixture creates a Flask test client. It sets the app’s configuration toTESTING
mode and provides a client for making requests within awith
context. - The
test_home_route
function sends aGET
request to the root endpoint ('/'
). It verifies that the response status code is200
and that the response body contains the word"Welcome"
. - The
test_generate_docstring_route
function sends aPOST
request to the/generate-docstring
endpoint with a simple Python function as input. It allows either a200
status code for successful generation or a500
status code if the DeepSeek API key is missing. - The
test_check_docs_route
function sends aPOST
request to the/check-docs
endpoint with code and outdated documentation as input. It asserts that the response status code is200
and verifies that the message indicates outdated documentation.
- The script imports the necessary modules:
- Run Tests:
Installpytest
and run the tests:pip install pytest pytest tests/test_routes.py
Lesson 12: Deployment & CI/CD integration
In this lesson, you’ll deploy your Flask app and integrate it with a CI/CD pipeline.
Objective
- Deploy your Flask app as a web service.
- Integrate your documentation assistant with CI/CD pipelines for seamless updates.
Step 1: Deploy Your Flask App
You’ll deploy your Flask app to a cloud platform like Heroku.
- Install Heroku CLI:
Install the Heroku CLI if you haven’t already:brew install heroku/brew/heroku
- Create a
Procfile
:Add aProcfile
to your project root: - Deploy to Heroku:Follow these steps to deploy your app:
heroku login heroku create git push heroku main heroku open
Step 2: Set Up CI/CD with GitHub actions
You’ll automate testing and deployment using GitHub Actions.
- Create
.github/workflows/ci-cd.yml
:Add a GitHub Actions workflow file:name: CI/CD Pipeline on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.9' - name: Install dependencies run: pip install -r requirements.txt - name: Run tests run: pytest deploy: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 - name: Deploy to Heroku env: HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }} run: | git remote add heroku https://heroku:$HEROKU_API_KEY@git.heroku.com/your-app-name.git git push heroku main
The code defines a CI/CD pipeline using GitHub Actions. It automates testing and deployment when a developer pushes code or creates a pull request to the
main
branch.- Trigger Events: The pipeline activates when someone pushes code to the
main
branch or opens a pull request targeting themain
branch. - Job:
test
:- The job runs on an Ubuntu virtual machine.
- It checks out the code from the repository.
- It sets up Python version 3.9.
- It installs the project’s dependencies from the
requirements.txt
file. - It runs tests using the
pytest
testing framework.
- Job:
deploy
:- This job depends on the successful completion of the
test
job. - It runs on an Ubuntu virtual machine.
- It checks out the code from the repository.
- It deploys the code to Heroku using the Heroku API key stored as a secret in the repository.
- It adds Heroku as a remote Git repository and pushes the code to the
main
branch of the Heroku app.
- This job depends on the successful completion of the
This pipeline ensures that the code passes tests before deploying it to production.
- Trigger Events: The pipeline activates when someone pushes code to the
Step 3: Automate Documentation Updates
You’ll update your CI/CD pipeline to regenerate and push documentation when the codebase changes.
- Update
.github/workflows/ci-cd.yml
:Add a step to regenerate and push documentation:- name: Regenerate and push documentation run: | python -c "from app.utils.docstring_generator import generate_markdown_docs, save_docs, push_to_github; code = open('app/main.py').read(); docs = generate_markdown_docs(code); save_docs(docs, 'index.html'); push_to_github(docs, 'your-repo-name')"
The code defines a step in a CI/CD pipeline that regenerates and pushes documentation to a GitHub repository. Here’s a breakdown:
- Run the Python script inline: The code executes a Python script directly from the command line using the
-c
flag. - Import functions from the
docstring_generator
module: It imports thegenerate_markdown_docs
,save_docs
, andpush_to_github
functions from theapp.utils.docstring_generator
module. - Read the code from
app/main.py
: The script opens and reads the contents of theapp/main.py
file and stores it in thecode
variable. - Generate documentation: The script calls
generate_markdown_docs(code)
to generate documentation from the code’s docstrings and assigns the output to thedocs
variable. - Save the documentation to an HTML file: The script invokes
save_docs(docs, 'index.html')
, which saves the generated documentation to a file namedindex.html
. - Push the documentation to GitHub: The script calls
push_to_github(docs, 'your-repo-name')
, which pushes the documentation to the specified GitHub repository.
This step automates the documentation generation, saving, and deployment process.
- Run the Python script inline: The code executes a Python script directly from the command line using the
New feature: Generate documentation for an entire repository
In this section, you’ll learn how to generate documentation for an entire GitHub repository, including subdirectories.
Step 1: Update github_api.py
Modify the fetch_repo_contents
function to recursively fetch contents from directories.
def fetch_repo_contents(owner, repo, path=""): """ Fetch the contents of a GitHub repository recursively. """ url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}" headers = {"Authorization": f"token {GITHUB_ACCESS_TOKEN}"} response = requests.get(url, headers=headers) if response.status_code == 200: contents = response.json() all_contents = [] for item in contents: if item["type"] == "dir": # Recursively fetch contents of the directory all_contents.extend(fetch_repo_contents(owner, repo, item["path"])) else: all_contents.append(item) return all_contents else: raise Exception(f"Failed to fetch repository contents: {response.status_code}")
The fetch_repo_contents
function retrieves the contents of a GitHub repository, including its files and directories, by making requests to the GitHub API. Here’s how the code operates:
- Construct the URL: The function builds a URL that targets the contents of the specified repository. It dynamically inserts the
owner
,repo
, andpath
parameters into the URL. - Set the authorization header: The function includes an authorization header that uses a GitHub access token stored in the variable
GITHUB_ACCESS_TOKEN
to authenticate the request. - Send a GET request to the GitHub API: The function makes a request to the GitHub API using the
requests.get
method and stores the server’s response in theresponse
variable. - Check the response status code: If the response status code is
200
(indicating success), the function proceeds to process the response data. Otherwise, it raises an exception with an error message. - Parse the JSON response: The function converts the response data from JSON format into a Python object and stores it in the
contents
variable. - Initialize an empty list to store all contents: The function creates an empty list called
all_contents
to store the fetched files and directories. - Iterate through the contents: The function loops through each item in the
contents
list. If the item is a directory ("type": "dir"
), the function recursively calls itself to fetch the contents of that directory and adds them to theall_contents
list. If the item is a file, the function directly adds the item to the list. - Return the complete list of contents: After processing all items, the function returns the
all_contents
list, which contains both files and directories from the specified path. - Handle errors with an exception: If the response status code is not
200
, the function raises an exception with an error message indicating the failure and the status code.
Step 2: Update github_api.py
Modify the fetch_and_process_repo
function to process all .py
files, including those in subdirectories.
Facebook Comments