๐Ÿ” GitHub Actions Workflow Comparison: Two Valid Approaches

Options Trading System
name: Deploy Options Trading Systems to AWS Lambda
on:
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.12'
      - name: Install AWS SAM CLI
        run: |
          pip install aws-sam-cli
      # Deploy live trading system
      - name: Build and deploy live trading
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: options-trading-system
          IMAGE_TAG: ${{ github.sha }}
        run: |
          cd Enter_Mon_Hold_Till_Fri
          docker buildx build \
            --cache-from type=registry,ref=$ECR_REGISTRY/$ECR_REPOSITORY:cache-live \
            --cache-to type=registry,ref=$ECR_REGISTRY/$ECR_REPOSITORY:cache-live,mode=max \
            -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
            --push \
            --provenance=false .
          sam deploy \
            --no-confirm-changeset \
            --no-fail-on-empty-changeset \
            --stack-name options-trading-system \
            --capabilities CAPABILITY_IAM \
            --region us-east-1 \
            --resolve-s3 \
            --resolve-image-repos \
            --parameter-overrides ImageUri=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      - name: Clean up old ECR images
        env:
          ECR_REPOSITORY: options-trading-system
        run: |
          echo "๐Ÿงน Cleaning up old Docker images from $ECR_REPOSITORY..."
          OLD_IMAGES=$(aws ecr describe-images \
            --repository-name $ECR_REPOSITORY \
            --region us-east-1 \
            --query 'sort_by(imageDetails,&imagePushedAt)[:-3].imageDigest' \
            --output text)
          if [ -n "$OLD_IMAGES" ]; then
            echo "$OLD_IMAGES" | tr '\t' '\n' | while read digest; do
              if [ -n "$digest" ]; then
                echo "  โŒ Deleting image: $digest"
                aws ecr batch-delete-image \
                  --repository-name $ECR_REPOSITORY \
                  --image-ids imageDigest=$digest \
                  --region us-east-1 \
                  --no-cli-pager || true
              fi
            done
          else
            echo "  โœ… No old images to delete (keeping 3 most recent)"
          fi
          REMAINING=$(aws ecr describe-images \
            --repository-name $ECR_REPOSITORY \
            --region us-east-1 \
            --query 'length(imageDetails)' \
            --output text)
          echo "๐ŸŽ‰ Cleanup complete! $ECR_REPOSITORY now has $REMAINING images"
Sector Rotation System
name: Deploy Sector Rotation System to AWS Lambda
on:
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Build Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: etf-trading-system
          IMAGE_TAG: ${{ github.sha }}
        run: |
          cd live_trading
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.12'
      - name: Install AWS SAM CLI
        run: |
          pip install aws-sam-cli
      - name: Deploy with SAM
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: etf-trading-system
          IMAGE_TAG: ${{ github.sha }}
        run: |
          sam deploy \
            --no-confirm-changeset \
            --no-fail-on-empty-changeset \
            --stack-name etf-trading-system \
            --capabilities CAPABILITY_IAM \
            --region us-east-1 \
            --resolve-s3 \
            --resolve-image-repos \
            --parameter-overrides ImageUri=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      - name: Clean up old ECR images
        env:
          ECR_REPOSITORY: etf-trading-system
        run: |
          echo "๐Ÿงน Cleaning up old Docker images from $ECR_REPOSITORY..."
          OLD_IMAGES=$(aws ecr describe-images \
            --repository-name $ECR_REPOSITORY \
            --region us-east-1 \
            --query 'sort_by(imageDetails,&imagePushedAt)[:-3].imageDigest' \
            --output text)
          if [ -n "$OLD_IMAGES" ]; then
            echo "$OLD_IMAGES" | tr '\t' '\n' | while read digest; do
              if [ -n "$digest" ]; then
                echo "  โŒ Deleting image: $digest"
                aws ecr batch-delete-image \
                  --repository-name $ECR_REPOSITORY \
                  --image-ids imageDigest=$digest \
                  --region us-east-1 \
                  --no-cli-pager || true
              fi
            done
          else
            echo "  โœ… No old images to delete (keeping 3 most recent)"
          fi
          REMAINING=$(aws ecr describe-images \
            --repository-name $ECR_REPOSITORY \
            --region us-east-1 \
            --query 'length(imageDetails)' \
            --output text)
          echo "๐ŸŽ‰ Cleanup complete! $ECR_REPOSITORY now has $REMAINING images"

๐Ÿ“Š Key Structural Differences

โœ“ Both Workflows Work Correctly

These are two valid organizational patterns with different tradeoffs:

  • Options Trading: Self-contained subdirectory with template + code, combined build/deploy step
  • Sector Rotation: Template at repo root, code in subdirectory, separate build/deploy steps
Aspect Options Trading Sector Rotation
Directory Structure Self-contained approach:
Enter_Mon_Hold_Till_Fri/
โ”œโ”€โ”€ template.yaml
โ”œโ”€โ”€ Dockerfile
โ””โ”€โ”€ lambda_function.py
Separated approach:
โ”œโ”€โ”€ template.yaml
โ””โ”€โ”€ live_trading/
    โ”œโ”€โ”€ Dockerfile
    โ””โ”€โ”€ lambda_function.py
Step Organization 1. Checkout
2. AWS Creds
3. ECR Login
4. Docker Buildx Setup
5. Python Setup
6. SAM Install
7. Build + Deploy (COMBINED)
8. Cleanup
1. Checkout
2. AWS Creds
3. ECR Login
4. Docker Build (SEPARATE)
5. Python Setup
6. SAM Install
7. SAM Deploy (SEPARATE)
8. Cleanup
Docker Tool docker buildx (advanced) docker build (standard)
Build Directory Enter_Mon_Hold_Till_Fri/ live_trading/
SAM Deploy Directory โœ“ Enter_Mon_Hold_Till_Fri/ (finds template.yaml there) โœ“ Repo root (finds template.yaml there)
Docker Push Integrated with --push flag Separate docker push command
Registry Caching โœ“ Uses --cache-from/--cache-to โœ— No caching

๐ŸŽฏ Pattern 1: Self-Contained Subdirectory (Options Trading)

โœ“ Self-Contained Approach

Philosophy: Everything for this system lives in one subdirectory

cd Enter_Mon_Hold_Till_Fri
โ†’
docker buildx build
โ†’
sam deploy (finds template.yaml here)
Enter_Mon_Hold_Till_Fri/ โ”œโ”€โ”€ template.yaml # Template with the code โ”œโ”€โ”€ Dockerfile โ”œโ”€โ”€ lambda_function.py โ”œโ”€โ”€ requirements.txt โ””โ”€โ”€ (data files, models, etc) Workflow step: - name: Build and deploy live trading run: | cd Enter_Mon_Hold_Till_Fri # Enter the self-contained directory docker buildx build ... # Build sam deploy ... # Deploy (finds template.yaml here)

Benefits:

  • Everything in one place - easy to move/copy the entire system
  • Clear module boundary - this directory IS the trading system
  • Single step deployment - build and deploy together
  • Uses advanced Docker buildx with layer caching

Tradeoffs:

  • Template and code tightly coupled in same directory
  • If you have multiple systems, each needs its own subdirectory with template

๐ŸŽฏ Pattern 2: Root Template with Code Subdirectory (Sector Rotation)

โœ“ Separated Approach

Philosophy: Infrastructure config (template) at root, application code in subdirectory

Step 1: cd live_trading
โ†’
docker build
โฌ‡ Step ends - directory resets to repo root โฌ‡
Step 2: sam deploy (finds template.yaml at root)
repo-root/ โ”œโ”€โ”€ template.yaml # Infrastructure at top level โ””โ”€โ”€ live_trading/ โ”œโ”€โ”€ Dockerfile โ”œโ”€โ”€ lambda_function.py โ”œโ”€โ”€ requirements.txt โ””โ”€โ”€ (trading logic) Workflow steps: - name: Build Docker image run: | cd live_trading # Build code in subdirectory docker build ... # Step ends โ†’ back to root - name: Deploy with SAM run: | sam deploy ... # Deploy from root (finds template.yaml)

Benefits:

  • Clean separation: infrastructure vs application code
  • Template visible at top level - easy to find and modify
  • Separate build/deploy steps - clearer logging, easier debugging
  • Common pattern in many repos

Tradeoffs:

  • Code and infrastructure split across directories
  • No Docker buildx caching (uses standard docker build)
  • Two steps instead of one

๐Ÿ”ง Docker Build Comparison

Feature Options Trading Sector Rotation
Build Command docker buildx build docker build
Cache Strategy --cache-from type=registry
--cache-to type=registry,mode=max
Benefit: Faster builds by reusing layers
None
Impact: Rebuilds from scratch each time
Push Method Integrated: --push flag
Benefit: Single command
Separate: docker push
Impact: Two commands needed
Provenance --provenance=false Default (not specified)
Requires Buildx Setup โœ“ Yes (separate step) โœ— No (uses standard Docker)

๐Ÿ’ก GitHub Actions Step Behavior

Important: Each Step Resets Working Directory

In GitHub Actions, every step starts with a fresh shell session at the repository root. Any cd commands only affect that specific step.

- name: Step 1 run: | cd some/subdirectory echo "I'm in $(pwd)" # Step ends โ†’ directory change is forgotten - name: Step 2 run: | echo "I'm in $(pwd)" # Back at repo root!

This is why Sector Rotation works: the Docker build step changes directory, but the SAM deploy step starts fresh at the repo root.

๐Ÿค” Which Pattern Should You Use?

Consideration Self-Contained (Options) Root Template (Sector)
Multiple systems in repo โœ“ Each system is independent directory โš ๏ธ Need multiple templates at root or complex template
Easy to relocate โœ“ Copy entire directory โš ๏ธ Need to move both root template and subdirectory
Infrastructure visibility โš ๏ธ Template buried in subdirectory โœ“ Template obvious at repo root
Build performance โœ“ Docker buildx with layer caching Standard build, no caching
Step clarity Single combined step โœ“ Separate steps, clearer logs
Common convention Less common (but valid) โœ“ More typical repo structure

๐Ÿ”„ Converting Between Patterns

From Self-Contained โ†’ Root Template

# Move template to root mv Enter_Mon_Hold_Till_Fri/template.yaml ./ # Split workflow steps - name: Build Docker image run: | cd Enter_Mon_Hold_Till_Fri docker build ... - name: Deploy with SAM run: | sam deploy ...

From Root Template โ†’ Self-Contained

# Move template into subdirectory mv template.yaml live_trading/ # Combine workflow steps - name: Build and deploy run: | cd live_trading docker build ... sam deploy ...

๐Ÿ“ Summary

Pattern Status Key Characteristic Best For
Options Trading
(Self-Contained)
โœ“ WORKS Everything in subdirectory, single combined step, buildx caching Multi-system repos where each system should be independent and portable
Sector Rotation
(Root Template)
โœ“ WORKS Template at root, code in subdirectory, separate build/deploy steps Single-system repos following conventional project structure

๐Ÿ’ก Key Takeaways

  • Both patterns are valid and work correctly - choose based on your repo structure needs
  • Options Trading uses a self-contained approach where template.yaml lives WITH the code
  • Sector Rotation uses a separated approach where template.yaml lives at repo root
  • The key difference is organizational philosophy, not correctness
  • GitHub Actions step behavior: Each step starts fresh at repo root, but you can cd anywhere within a step

๐Ÿ” Technical Deep Dive: Docker Build Differences

Why Options Trading Uses Docker Buildx

# Standard docker build (Sector Rotation) docker build -t image:tag . docker push image:tag # Advanced docker buildx (Options Trading) docker buildx build \ --cache-from type=registry,ref=image:cache \ --cache-to type=registry,ref=image:cache,mode=max \ -t image:tag \ --push \ --provenance=false .

Buildx advantages:

When is buildx overkill?