Fixing issue with referencing non-existent names in tfstate. #10
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: Cache Terraform state | |
| uses: actions/cache@v4 | |
| with: | |
| path: ./terraform/terraform.tfstate | |
| key: terraform-state-${{ github.ref }} | |
| restore-keys: | | |
| terraform-state- | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: latest | |
| - name: Terraform Init | |
| working-directory: ./terraform | |
| run: terraform init | |
| - name: Terraform Validate | |
| working-directory: ./terraform | |
| run: terraform validate | |
| - name: Remove container from terraform.tfstate | |
| working-directory: ./terraform | |
| run: | | |
| # Check if state file exists and if resources exist in state | |
| if [ -f "terraform.tfstate" ]; then | |
| echo "State file found, checking for existing resources..." | |
| # Remove namespace if it exists in state | |
| if terraform state list | grep -q "nexaa_namespace.simple-ci"; then | |
| echo "Namespace resource found in state, removing..." | |
| terraform state rm nexaa_namespace.simple-ci | |
| else | |
| echo "Namespace resource not found in state, skipping removal" | |
| fi | |
| # Remove container if it exists in state | |
| if terraform state list | grep -q "nexaa_container.simple-ci"; then | |
| echo "Container resource found in state, removing..." | |
| terraform state rm nexaa_container.simple-ci | |
| else | |
| echo "Container resource not found in state, skipping removal" | |
| fi | |
| else | |
| echo "No state file found, skipping removal" | |
| fi | |
| env: | |
| TF_VAR_nexaa_username: ${{ secrets.NEXAA_USERNAME }} | |
| TF_VAR_nexaa_password: ${{ secrets.NEXAA_PASSWORD }} | |
| - name: Terraform Import | |
| working-directory: ./terraform | |
| run: | | |
| # Try to import the namespace resource first | |
| echo "Attempting to import namespace resource..." | |
| if terraform import nexaa_namespace.simple-ci simple-ci; then | |
| echo "✅ Namespace resource imported successfully" | |
| else | |
| echo "⚠️ Namespace resource import failed (resource may not exist remotely)" | |
| echo "This is normal for first deployments or if the namespace was deleted" | |
| echo "Terraform will create the resource during apply" | |
| fi | |
| # Try to import the container resource | |
| echo "Attempting to import container resource..." | |
| if terraform import nexaa_container.simple-ci simple-ci/simple-ci; then | |
| echo "✅ Container resource imported successfully" | |
| else | |
| echo "⚠️ Container resource import failed (resource may not exist remotely)" | |
| echo "This is normal for first deployments or if the container was deleted" | |
| echo "Terraform will create the resource during apply" | |
| fi | |
| env: | |
| TF_VAR_nexaa_username: ${{ secrets.NEXAA_USERNAME }} | |
| TF_VAR_nexaa_password: ${{ secrets.NEXAA_PASSWORD }} | |
| - name: Terraform Plan | |
| working-directory: ./terraform | |
| run: terraform plan | |
| env: | |
| TF_VAR_nexaa_username: ${{ secrets.NEXAA_USERNAME }} | |
| TF_VAR_nexaa_password: ${{ secrets.NEXAA_PASSWORD }} | |
| - 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_container_image: ${{ needs.build.outputs.image }} |