Skip to content

Commit 90c0cc9

Browse files
committed
Resolve merge conflicts in ContentService.cs and Web.csproj
- Merged distributed caching and Redis integration from main branch - Kept image processing functionality from feature branch - Combined constructor parameters to include both IDistributedCache and IImageService - Added InvalidateTipsCacheAsync method to satisfy interface requirements - Updated package references to include both Azure Storage and Redis packages
2 parents 82880fe + 5a52263 commit 90c0cc9

68 files changed

Lines changed: 3251 additions & 23257 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,74 @@ This is "Copilot That Jawn" - a comprehensive ASP.NET Core web application that
1010

1111
## Technology Stack
1212
- **Framework**: ASP.NET Core 9.0+ with Razor Pages
13-
- **Architecture**: Razor Pages application with MVC controllers for complex operations
13+
- **Architecture**: .NET Aspire cloud-native application stack with Razor Pages application and MVC controllers for complex operations
14+
- **Orchestration**: .NET Aspire AppHost for service orchestration and configuration
1415
- **Content Storage**: Markdown files in Content/ directory for tips, tutorials, and guides
1516
- **Database**: Entity Framework Core (SQL Server or SQLite for development) for dynamic data
1617
- **Frontend**: Bootstrap 5, modern responsive design with CSS Grid/Flexbox
1718
- **JavaScript**: Vanilla JS with modern ES6+ features, minimal dependencies
19+
- **Cloud Services**: Azure integration through .NET Aspire service defaults and components
20+
21+
## .NET Aspire Architecture
22+
23+
This application is built using .NET Aspire, Microsoft's cloud-native application stack that provides a consistent, opinionated set of tools and patterns for building observable, production-ready apps.
24+
25+
### Aspire Project Structure
26+
```
27+
├── AppHost/ # .NET Aspire orchestration project
28+
│ ├── AppHost.cs # Application composition and service configuration
29+
│ ├── appsettings.json # Aspire host configuration
30+
│ └── infra/ # Infrastructure templates for deployment
31+
├── Web/ # Main web application project
32+
├── ServiceDefaults/ # Shared service configuration and extensions
33+
├── ContentLoader/ # Background service for content processing
34+
└── Shared/ # Shared models and utilities
35+
```
36+
37+
### Key Aspire Components Used
38+
- **Service Discovery**: Automatic service discovery and configuration
39+
- **Service Defaults**: Shared telemetry, health checks, and service configuration
40+
- **Resource Management**: Simplified resource provisioning and management
41+
- **Observability**: Built-in logging, metrics, and distributed tracing
42+
- **Development Dashboard**: Real-time monitoring and debugging during development
43+
44+
### Aspire Development Best Practices
45+
46+
#### AppHost Configuration
47+
- Define all services and their dependencies in `AppHost.cs`
48+
- Use environment-specific configuration for different deployment targets
49+
- Leverage Aspire's built-in resource templates for databases, caching, messaging
50+
- Configure service-to-service communication through Aspire's service discovery
51+
52+
#### Service Defaults Integration
53+
- All projects should reference the `ServiceDefaults` project
54+
- Use `builder.AddServiceDefaults()` in each service's `Program.cs`
55+
- Implement consistent logging, health checks, and telemetry across services
56+
- Follow Aspire's conventions for configuration and dependency injection
57+
58+
#### Running the Application
59+
- **Primary Development Command**: `dotnet run --project AppHost`
60+
- **Alternative**: Use `dotnet watch run --project AppHost` for development with hot reload
61+
- **Aspire Dashboard**: Access the development dashboard at the URL shown in console output
62+
- **Service Monitoring**: Use the Aspire dashboard to monitor service health, logs, and metrics
63+
64+
#### Resource Management
65+
- Define infrastructure requirements in the AppHost project
66+
- Use Aspire's resource provisioning for local development dependencies
67+
- Leverage infrastructure templates in `infra/` directory for cloud deployment
68+
- Configure connection strings and service endpoints through Aspire's configuration system
69+
70+
#### Testing with Aspire
71+
- Use Aspire's testing utilities for integration tests
72+
- Test service communication through Aspire's service discovery
73+
- Validate configuration and resource dependencies
74+
- Implement health checks for all services
75+
76+
### Deployment Considerations
77+
- Aspire applications can be deployed to various cloud platforms
78+
- Infrastructure templates generate appropriate deployment artifacts
79+
- Service configuration is managed through Aspire's configuration system
80+
- Observability and monitoring are built-in through Aspire's telemetry
1881

1982
## Project Structure Guidelines
2083

@@ -198,22 +261,30 @@ Content/
198261
### Development Workflow Best Practices
199262

200263
#### Running and Testing the Application
201-
- **Prefer `dotnet watch`**: Always use `dotnet watch run` for development instead of manually building, running, stopping, and restarting the application
202-
- **Hot Reload Benefits**: `dotnet watch` provides automatic rebuilding and browser refresh when files change, significantly improving development efficiency
203-
- **Recommended Commands**:
204-
- `dotnet watch run` - Start the application with hot reload
205-
- `dotnet watch test` - Run tests with automatic re-execution on file changes
206-
- `dotnet watch build` - Build with automatic rebuilding on changes
207-
208-
#### File Monitoring
209-
- `dotnet watch` automatically monitors C# files, Razor pages, CSS, JavaScript, and other static assets
210-
- Changes trigger automatic compilation and browser refresh
211-
- Reduces context switching and speeds up the development cycle
264+
- **Primary Development Command**: `dotnet run --project AppHost` - Starts the entire Aspire application stack
265+
- **With Hot Reload**: `dotnet watch run --project AppHost` - Starts with automatic rebuilding and browser refresh
266+
- **Aspire Dashboard**: Monitor services, logs, and metrics through the Aspire development dashboard
267+
- **Individual Service Testing**: Use `dotnet test` in specific project directories for unit tests
268+
- **Integration Testing**: Run full stack tests using Aspire's testing framework
269+
270+
#### Aspire-Specific Development Workflow
271+
- **Service Orchestration**: All services are managed through the AppHost project
272+
- **Configuration Management**: Use Aspire's configuration system for service settings and connection strings
273+
- **Service Discovery**: Services communicate through Aspire's built-in service discovery
274+
- **Observability**: Monitor application performance through the Aspire dashboard during development
275+
276+
#### File Monitoring and Hot Reload
277+
- Aspire's `dotnet watch` automatically monitors all projects in the application stack
278+
- Changes to any service trigger appropriate rebuilds and restarts
279+
- The Aspire dashboard provides real-time feedback on service status during development
280+
- Static assets (CSS, JS) are monitored and trigger browser refresh automatically
212281

213282
#### Development Environment Setup
214-
- Ensure the development environment is configured for optimal `dotnet watch` performance
215-
- Use the `--verbose` flag for debugging watch issues: `dotnet watch run --verbose`
216-
- Configure file exclusions in `.csproj` if needed to avoid unnecessary rebuilds
283+
- Ensure .NET 9.0+ SDK is installed for Aspire support
284+
- Use `dotnet run --project AppHost` as the primary development command
285+
- Configure the Aspire dashboard for optimal monitoring during development
286+
- Set up appropriate logging levels in `appsettings.Development.json` for each service
287+
- Use environment variables through Aspire's configuration system rather than direct file configuration
217288

218289
## Content Strategy
219290

.github/workflows/azure-dev.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ on:
1111
# Exclude Content folder changes to avoid unnecessary deployments
1212
paths-ignore:
1313
- 'Content/**'
14+
- '.github/**'
15+
- 'docs/*'
1416

1517
# Set up permissions for deploying with secretless Azure federated credentials
1618
# https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication
@@ -29,6 +31,7 @@ jobs:
2931
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
3032
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
3133
CACHE_REFRESH_API_KEY: ${{ secrets.CACHE_REFRESH_API_KEY }}
34+
AZURE_REDIS: ${{ secrets.AZURE_REDIS }}
3235

3336
steps:
3437
- name: Checkout
@@ -39,7 +42,6 @@ jobs:
3942
uses: actions/setup-dotnet@v4
4043
with:
4144
dotnet-version: |
42-
8.x.x
4345
9.x.x
4446
4547
- name: Log in with Azure (Federated Credentials)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Cleanup Container Images
2+
3+
# Run daily at 2 AM UTC to clean up old container images
4+
on:
5+
schedule:
6+
- cron: '0 2 * * *' # Daily at 2 AM UTC
7+
workflow_dispatch: # Allow manual triggering
8+
inputs:
9+
dry_run:
10+
description: 'Dry run mode (show what would be deleted without actually deleting)'
11+
required: false
12+
default: 'false'
13+
type: boolean
14+
15+
# Set up permissions for Azure authentication
16+
permissions:
17+
id-token: write
18+
contents: read
19+
packages: write
20+
21+
jobs:
22+
cleanup-images:
23+
runs-on: ubuntu-latest
24+
env:
25+
AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
26+
AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
27+
AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
28+
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
29+
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
30+
31+
steps:
32+
- name: Install Azure CLI
33+
run: |
34+
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
35+
36+
- name: Log in with Azure (Federated Credentials)
37+
uses: azure/login@v2
38+
with:
39+
client-id: ${{ env.AZURE_CLIENT_ID }}
40+
tenant-id: ${{ env.AZURE_TENANT_ID }}
41+
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
42+
43+
- name: Get Azure Container Registry Name
44+
id: get-acr
45+
run: |
46+
# Get the resource group name
47+
rg_name="rg-${{ env.AZURE_ENV_NAME }}"
48+
echo "Resource group: $rg_name"
49+
50+
# Verify the resource group exists
51+
if ! az group show --name "$rg_name" >/dev/null 2>&1; then
52+
echo "Resource group '$rg_name' not found. Skipping cleanup."
53+
echo "acr_name=" >> $GITHUB_OUTPUT
54+
exit 0
55+
fi
56+
57+
# Get the ACR name from the resource group
58+
acr_name=$(az acr list --resource-group "$rg_name" --query "[0].name" --output tsv 2>/dev/null)
59+
60+
if [ -z "$acr_name" ] || [ "$acr_name" = "null" ]; then
61+
echo "No Azure Container Registry found in resource group '$rg_name'."
62+
echo "acr_name=" >> $GITHUB_OUTPUT
63+
else
64+
echo "Found ACR: $acr_name"
65+
echo "acr_name=$acr_name" >> $GITHUB_OUTPUT
66+
fi
67+
68+
- name: Cleanup Old Container Images
69+
run: |
70+
acr_name="${{ steps.get-acr.outputs.acr_name }}"
71+
dry_run="${{ github.event.inputs.dry_run || 'false' }}"
72+
73+
if [ -z "$acr_name" ]; then
74+
echo "No Azure Container Registry found. Skipping cleanup."
75+
exit 0
76+
fi
77+
78+
echo "Starting container image cleanup for ACR: $acr_name"
79+
echo "Retention policy: Keep 5 most recent images per repository"
80+
81+
# Get all repositories in the ACR
82+
repositories=$(az acr repository list --name "$acr_name" --output tsv 2>/dev/null)
83+
84+
if [ -z "$repositories" ]; then
85+
echo "No repositories found in ACR. Nothing to clean up."
86+
exit 0
87+
fi
88+
89+
echo "Found repositories: $(echo "$repositories" | wc -l)"
90+
91+
# Build the purge command with filters for each repository
92+
PURGE_FILTERS=""
93+
for repo in $repositories; do
94+
PURGE_FILTERS="$PURGE_FILTERS --filter '$repo:.*'"
95+
done
96+
97+
# Construct the purge command
98+
PURGE_CMD="acr purge $PURGE_FILTERS --ago 0d --keep 5 --untagged"
99+
100+
if [ "$dry_run" = "true" ]; then
101+
PURGE_CMD="$PURGE_CMD --dry-run"
102+
echo "🔍 DRY RUN MODE: Will show what would be deleted without actually deleting"
103+
fi
104+
105+
echo "Running purge command..."
106+
# Set longer timeout (1 hour) for large registries
107+
az acr run \
108+
--cmd "$PURGE_CMD" \
109+
--registry "$acr_name" \
110+
--timeout 3600 \
111+
/dev/null
112+
113+
exit_code=$?
114+
115+
if [ $exit_code -eq 0 ]; then
116+
echo "✅ Cleanup process completed successfully!"
117+
else
118+
echo "❌ Cleanup process failed with exit code $exit_code"
119+
exit $exit_code
120+
fi

0 commit comments

Comments
 (0)