Change s3 backend #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| branches: [ main ] | |
| jobs: | |
| lint: | |
| name: Lint Code | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run ESLint | |
| run: npm run lint | |
| build: | |
| name: Build Docker Images | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| outputs: | |
| tag: ${{ steps.set-outputs.outputs.tag }} | |
| image: ${{ steps.set-outputs.outputs.image }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set outputs | |
| id: set-outputs | |
| run: | | |
| TAG="main-${{ github.sha }}" | |
| IMAGE="ghcr.io/${{ github.repository }}:${TAG}" | |
| echo "tag=${TAG}" >> $GITHUB_OUTPUT | |
| echo "image=${IMAGE}" >> $GITHUB_OUTPUT | |
| echo "Generated tag: ${TAG}" | |
| echo "Generated image: ${IMAGE}" | |
| - name: Build development image | |
| run: docker build --target development -t simple-ci:dev . | |
| - name: Build production image | |
| run: docker build --target production -t simple-ci:prod . | |
| - name: Save images | |
| run: | | |
| docker save simple-ci:dev | gzip > dev-image.tar.gz | |
| docker save simple-ci:prod | gzip > prod-image.tar.gz | |
| - name: Upload dev image artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dev-image | |
| path: dev-image.tar.gz | |
| retention-days: 1 | |
| - name: Upload prod image artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: prod-image | |
| path: prod-image.tar.gz | |
| retention-days: 1 | |
| test: | |
| name: Test Against Running Production Container | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js for testing | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Install test dependencies | |
| run: npm ci | |
| - name: Run unit tests (isolated) | |
| run: npm test | |
| - name: Download prod image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: prod-image | |
| - name: Load prod image | |
| run: docker load < prod-image.tar.gz | |
| - name: Start production container | |
| run: | | |
| docker run -d --name test-app -p 3000:3000 simple-ci:prod | |
| sleep 5 | |
| - name: Run E2E tests against running container | |
| env: | |
| TEST_BASE_URL: http://localhost:3000 | |
| run: npm run test:e2e | |
| - name: Generate test coverage | |
| run: npm run test:coverage | |
| - name: Stop container | |
| if: always() | |
| run: docker stop test-app && docker rm test-app | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v3 | |
| with: | |
| file: ./coverage/lcov.info | |
| flags: unittests | |
| name: codecov-umbrella | |
| publish: | |
| name: Publish to GitHub Container Registry | |
| runs-on: ubuntu-latest | |
| needs: [build, lint, test] | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download prod image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: prod-image | |
| - name: Load prod image | |
| run: docker load < prod-image.tar.gz | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Tag and push image | |
| run: | | |
| # Tag with commit-based tag and latest (if main branch) | |
| docker tag simple-ci:prod ${{ needs.build.outputs.image }} | |
| docker push ${{ needs.build.outputs.image }} | |
| # Also tag as latest if this is the main branch | |
| if [ "${{ github.ref }}" = "refs/heads/main" ]; then | |
| docker tag simple-ci:prod ghcr.io/${{ github.repository }}:latest | |
| docker push ghcr.io/${{ github.repository }}:latest | |
| fi | |
| - name: Output image info | |
| run: | | |
| echo "📦 Published images with commit-based tags:" | |
| echo "${{ needs.build.outputs.image }}" | |
| if [ "${{ github.ref }}" = "refs/heads/main" ]; then | |
| echo "ghcr.io/${{ github.repository }}:latest" | |
| fi | |
| echo "" | |
| echo "🔖 Primary tag: ${{ needs.build.outputs.tag }}" | |
| echo "📌 Commit: ${{ github.sha }}" | |
| echo "👤 Author: ${{ github.event.head_commit.author.name }}" | |
| echo "💬 Message: ${{ github.event.head_commit.message }}" | |
| terraform: | |
| runs-on: ubuntu-latest | |
| needs: [build, publish] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: latest | |
| - name: Extract branch name | |
| id: extract_branch | |
| run: | | |
| # Extract branch name from ref (remove refs/heads/ prefix) | |
| BRANCH_NAME="${GITHUB_REF#refs/heads/}" | |
| # For PRs, use the head branch | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BRANCH_NAME="${{ github.head_ref }}" | |
| fi | |
| # Sanitize branch name (replace / with -) | |
| BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/\//-/g') | |
| echo "branch=${BRANCH_NAME}" >> $GITHUB_OUTPUT | |
| echo "Deploying environment: ${BRANCH_NAME}" | |
| - name: Terraform Init | |
| working-directory: ./terraform | |
| run: | | |
| BRANCH_NAME="${{ steps.extract_branch.outputs.branch }}" | |
| echo "Using state file: simple-ci-${BRANCH_NAME}/terraform.tfstate" | |
| terraform init \ | |
| -backend-config="key=simple-ci-${BRANCH_NAME}/terraform.tfstate" | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }} | |
| - name: Terraform Validate | |
| working-directory: ./terraform | |
| run: terraform validate | |
| - name: Terraform Apply | |
| working-directory: ./terraform | |
| run: terraform apply -auto-approve | |
| env: | |
| TF_VAR_nexaa_username: ${{ secrets.NEXAA_USERNAME }} | |
| TF_VAR_nexaa_password: ${{ secrets.NEXAA_PASSWORD }} | |
| TF_VAR_environment: ${{ steps.extract_branch.outputs.branch }} | |
| TF_VAR_container_image: ${{ needs.build.outputs.image }} | |
| AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }} |