From 77356c41919a2ba60cccbe97e20143c924778d6a Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Wed, 22 Oct 2025 13:46:14 -0500 Subject: [PATCH] Story 4.4 - simple check works --- STORY_4.4_IMPLEMENTATION_SUMMARY.md | 105 ++++++++++++++++++++++++++++ STORY_4.4_QUICKSTART.md | 55 +++++++++++++++ docs/prd/epic-4-deployment.md | 4 +- src/cli/commands.py | 84 ++++++++++++++++++++++ 4 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 STORY_4.4_IMPLEMENTATION_SUMMARY.md create mode 100644 STORY_4.4_QUICKSTART.md diff --git a/STORY_4.4_IMPLEMENTATION_SUMMARY.md b/STORY_4.4_IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..c618f32 --- /dev/null +++ b/STORY_4.4_IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,105 @@ +# Story 4.4: Post-Deployment Verification - Implementation Summary + +## Status: COMPLETE + +Story points: 5 + +## Overview +Implemented a simple CLI command to verify deployed URLs return 200 OK status. + +## Implementation + +### New CLI Command +Added `verify-deployment` command to `src/cli/commands.py`: + +```bash +uv run python main.py verify-deployment --batch-id [--sample N] [--timeout 10] +``` + +**Options:** +- `--batch-id, -b`: Project/batch ID to verify (required) +- `--sample, -s`: Number of random URLs to check (optional, default: check all) +- `--timeout, -t`: Request timeout in seconds (default: 10) + +### Core Functionality +1. Queries database for deployed articles in specified batch +2. Filters articles with `deployed_url` and status='deployed' +3. Makes HTTP GET requests to verify 200 OK status +4. Supports checking all URLs or random sample +5. Clear output showing success/failure for each URL +6. Summary report with total checked, successful, and failed counts + +### Code Changes +- **Modified:** `src/cli/commands.py` + - Added imports: `requests`, `random` + - Added `verify_deployment()` command function + +### Acceptance Criteria Verification +- ✅ CLI command available: `verify-deployment --batch_id ` +- ✅ Takes batch ID as input +- ✅ Retrieves URLs for all articles in batch from database +- ✅ Makes HTTP GET requests to sample or all URLs +- ✅ Reports which URLs return 200 OK and which do not +- ✅ Clear, easy-to-read output +- ✅ Can be run manually after deployment + +## Usage Examples + +### Verify all URLs in a batch: +```bash +uv run python main.py verify-deployment --batch-id 10 +``` + +### Verify random sample of 5 URLs: +```bash +uv run python main.py verify-deployment --batch-id 10 --sample 5 +``` + +### Custom timeout: +```bash +uv run python main.py verify-deployment --batch-id 10 --timeout 30 +``` + +## Sample Output +``` +Verifying deployment for batch: My Project (ID: 10) +Keyword: main keyword + +Found 25 deployed articles +Checking all 25 URLs + +✓ https://example.com/article-1.html +✓ https://example.com/article-2.html +✗ https://example.com/article-3.html (HTTP 404) +... + +====================================================================== +Verification Summary +====================================================================== +Total checked: 25 +Successful: 24 +Failed: 1 + +Failed URLs: + https://example.com/article-3.html + Title: Article Three Title + Error: 404 + +====================================================================== +``` + +## Dependencies +- `requests==2.32.5` (already in requirements.txt) + +## Testing +- Command help output verified +- Follows existing CLI command patterns +- Simple, focused implementation per user requirements + +## Notes +- No authentication required (read-only operation) +- Uses existing database repositories +- Minimal dependencies +- No freelance features added - strictly adheres to acceptance criteria +- Can be integrated into auto-deploy workflow in future if needed + diff --git a/STORY_4.4_QUICKSTART.md b/STORY_4.4_QUICKSTART.md new file mode 100644 index 0000000..4a80ce1 --- /dev/null +++ b/STORY_4.4_QUICKSTART.md @@ -0,0 +1,55 @@ +# Story 4.4: Post-Deployment Verification - Quick Start + +## Command Usage + +Verify that deployed URLs are live and returning 200 OK status: + +```bash +uv run python main.py verify-deployment --batch-id +``` + +## Options + +| Option | Short | Description | Default | +|--------|-------|-------------|---------| +| `--batch-id` | `-b` | Project/batch ID to verify | Required | +| `--sample` | `-s` | Number of random URLs to check | All | +| `--timeout` | `-t` | Request timeout in seconds | 10 | + +## Examples + +### Check all URLs in a batch +```bash +uv run python main.py verify-deployment --batch-id 10 +``` + +### Check random sample of 5 URLs +```bash +uv run python main.py verify-deployment --batch-id 10 --sample 5 +``` + +### Use custom timeout +```bash +uv run python main.py verify-deployment --batch-id 10 --timeout 30 +``` + +## Output + +The command will: +1. Show batch information +2. Display each URL check result with ✓ (success) or ✗ (failed) +3. Provide a summary of results +4. List failed URLs with error details + +## Exit Codes + +- `0`: All URLs returned 200 OK +- `1`: One or more URLs failed or error occurred + +## Notes + +- Only checks articles with `deployed_url` set and status='deployed' +- Follows redirects automatically +- No authentication required (read-only operation) +- Can be run multiple times safely + diff --git a/docs/prd/epic-4-deployment.md b/docs/prd/epic-4-deployment.md index 7c1134a..e352dbf 100644 --- a/docs/prd/epic-4-deployment.md +++ b/docs/prd/epic-4-deployment.md @@ -7,7 +7,7 @@ To deploy all finalized HTML content (articles and boilerplate pages) for a batc - **Story 4.1**: ✅ COMPLETE (22 story points, real-world validated) - **Story 4.2**: ✅ COMPLETE (implemented in Story 4.1) - **Story 4.3**: ✅ COMPLETE (implemented in Story 4.1) -- **Story 4.4**: Not started +- **Story 4.4**: ✅ COMPLETE (5 story points) - **Story 4.5**: Not started ## Stories @@ -64,7 +64,7 @@ To deploy all finalized HTML content (articles and boilerplate pages) for a batc **Note:** The `last_deployed_at` timestamp for `site_deployments` could be added as enhancement if needed. ### Story 4.4: Post-Deployment Verification -**Status:** Not Started +**Status:** ✅ COMPLETE (5 story points) **As a user**, I want a simple way to verify that a batch of articles has been deployed successfully, by checking a sample of URLs for a `200 OK` status, so I can have confidence the deployment worked. diff --git a/src/cli/commands.py b/src/cli/commands.py index 1259c99..12e5f13 100644 --- a/src/cli/commands.py +++ b/src/cli/commands.py @@ -24,6 +24,8 @@ from src.deployment.bunny_storage import BunnyStorageClient, BunnyStorageError from src.deployment.deployment_service import DeploymentService from src.deployment.url_logger import URLLogger import os +import requests +import random def authenticate_admin(username: str, password: str) -> Optional[User]: @@ -1098,5 +1100,87 @@ def deploy_batch( raise click.Abort() +@app.command("verify-deployment") +@click.option('--batch-id', '-b', required=True, type=int, help='Project/batch ID to verify') +@click.option('--sample', '-s', type=int, help='Number of random URLs to check (default: check all)') +@click.option('--timeout', '-t', type=int, default=10, help='Request timeout in seconds (default: 10)') +def verify_deployment(batch_id: int, sample: Optional[int], timeout: int): + """Verify deployed URLs return 200 OK status""" + try: + session = db_manager.get_session() + + try: + content_repo = GeneratedContentRepository(session) + project_repo = ProjectRepository(session) + + project = project_repo.get_by_id(batch_id) + if not project: + click.echo(f"Error: Project/batch {batch_id} not found", err=True) + raise click.Abort() + + click.echo(f"Verifying deployment for batch: {project.name} (ID: {batch_id})") + click.echo(f"Keyword: {project.main_keyword}\n") + + articles = content_repo.get_by_project_id(batch_id) + deployed_articles = [a for a in articles if a.deployed_url and a.status == 'deployed'] + + if not deployed_articles: + click.echo("No deployed articles found for this batch.") + return + + click.echo(f"Found {len(deployed_articles)} deployed articles") + + urls_to_check = deployed_articles + if sample and sample < len(deployed_articles): + urls_to_check = random.sample(deployed_articles, sample) + click.echo(f"Checking random sample of {sample} URLs\n") + else: + click.echo(f"Checking all {len(deployed_articles)} URLs\n") + + successful = [] + failed = [] + + for article in urls_to_check: + url = article.deployed_url + try: + response = requests.get(url, timeout=timeout, allow_redirects=True) + if response.status_code == 200: + successful.append((url, article.title)) + click.echo(f"✓ {url}") + else: + failed.append((url, article.title, response.status_code)) + click.echo(f"✗ {url} (HTTP {response.status_code})") + except requests.exceptions.RequestException as e: + failed.append((url, article.title, str(e))) + click.echo(f"✗ {url} (Error: {type(e).__name__})") + + click.echo("\n" + "=" * 70) + click.echo("Verification Summary") + click.echo("=" * 70) + click.echo(f"Total checked: {len(urls_to_check)}") + click.echo(f"Successful: {len(successful)}") + click.echo(f"Failed: {len(failed)}") + + if failed: + click.echo("\nFailed URLs:") + for url, title, error in failed: + title_preview = title[:50] + "..." if len(title) > 50 else title + click.echo(f" {url}") + click.echo(f" Title: {title_preview}") + click.echo(f" Error: {error}") + + click.echo("=" * 70) + + if failed: + raise click.Abort() + + finally: + session.close() + + except Exception as e: + click.echo(f"Error verifying deployment: {e}", err=True) + raise click.Abort() + + if __name__ == "__main__": app()