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: BICA Backup CI/CD | ||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| pull_request: | ||
| branches: [ main ] | ||
| env: | ||
| IMAGE_NAME: bica-backup | ||
| TAG: test | ||
| DB_USER: myuser | ||
| DB_NAME: mydatabase | ||
| POSTGRES_IMAGE: postgres:15 | ||
| BACKUP_DIR: /mnt/backups | ||
| RETENTION_DAYS: 7 | ||
| jobs: | ||
| build: | ||
| name: Build Docker Image | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Setup Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
| - name: Build image | ||
| run: docker build -t $IMAGE_NAME:$TAG . | ||
| - name: Save image as artifact | ||
| run: docker save $IMAGE_NAME:$TAG -o image.tar | ||
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: docker-image | ||
| path: image.tar | ||
| setup-postgres: | ||
| name: Setup PostgreSQL with Sample Data | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| DB_PASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| outputs: | ||
| network: bica-net | ||
| steps: | ||
| - name: Create Docker network | ||
| run: docker network create bica-net || true | ||
| - name: Start PostgreSQL container | ||
| run: | | ||
| docker run -d --name postgres-db --network bica-net \ | ||
| -e POSTGRES_USER=$DB_USER \ | ||
| -e POSTGRES_PASSWORD=$DB_PASSWORD \ | ||
| -e POSTGRES_DB=$DB_NAME \ | ||
| ${{ env.POSTGRES_IMAGE }} | ||
| - name: Wait for Postgres to be ready | ||
| run: | | ||
| for i in $(seq 1 30); do | ||
| docker exec postgres-db pg_isready -U $DB_USER -d $DB_NAME && exit 0 | ||
| echo "Waiting for Postgres... attempt $i" | ||
| sleep 2 | ||
| done | ||
| echo "Postgres did not become ready in time, showing logs:" | ||
| docker logs postgres-db | ||
| exit 1 | ||
| - name: Populate database with sample data | ||
| run: | | ||
| docker exec -i postgres-db psql -U $DB_USER -d $DB_NAME <<EOF | ||
| CREATE TABLE IF NOT EXISTS users ( | ||
| id SERIAL PRIMARY KEY, | ||
| username TEXT NOT NULL UNIQUE, | ||
| email TEXT NOT NULL UNIQUE, | ||
| created_at TIMESTAMP DEFAULT NOW() | ||
| ); | ||
| CREATE TABLE IF NOT EXISTS posts ( | ||
| id SERIAL PRIMARY KEY, | ||
| user_id INTEGER NOT NULL REFERENCES users(id), | ||
| title TEXT NOT NULL, | ||
| content TEXT, | ||
| published_at TIMESTAMP | ||
| ); | ||
| CREATE TABLE IF NOT EXISTS comments ( | ||
| id SERIAL PRIMARY KEY, | ||
| post_id INTEGER NOT NULL REFERENCES posts(id), | ||
| author_name TEXT NOT NULL, | ||
| comment TEXT NOT NULL, | ||
| created_at TIMESTAMP DEFAULT NOW() | ||
| ); | ||
| INSERT INTO users (username, email) VALUES | ||
| ('alice', 'alice@example.com'), | ||
| ('bob', 'bob@example.com'), | ||
| ('carol', 'carol@example.com') | ||
| ON CONFLICT DO NOTHING; | ||
| INSERT INTO posts (user_id, title, content, published_at) VALUES | ||
| (1, 'First post', 'This is the content of the first post.', NOW() - INTERVAL '5 days'), | ||
| (1, 'Second post', 'More content here.', NOW() - INTERVAL '2 days'), | ||
| (2, 'Bob''s post', 'Bob writes something interesting.', NOW() - INTERVAL '3 days') | ||
| ON CONFLICT DO NOTHING; | ||
| INSERT INTO comments (post_id, author_name, comment) VALUES | ||
| (1, 'Eve', 'Great post, thanks!'), | ||
| (1, 'Mallory', 'I disagree with your point.'), | ||
| (3, 'Trent', 'Nice one, Bob!') | ||
| ON CONFLICT DO NOTHING; | ||
| EOF | ||
| backup-unencrypted: | ||
| name: Backup Unencrypted & Show pg_dump | ||
| runs-on: ubuntu-latest | ||
| needs: [build, setup-postgres] | ||
| env: | ||
| DB_USER: ${{ env.DB_USER }} | ||
| DB_PASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| DB_NAME: ${{ env.DB_NAME }} | ||
| BACKUP_DIR: ${{ env.BACKUP_DIR }} | ||
| RETENTION_DAYS: ${{ env.RETENTION_DAYS }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Download image artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: docker-image | ||
| path: . | ||
| - name: Load docker image | ||
| run: docker load -i image.tar | ||
| - name: Prepare backup folder | ||
| run: mkdir -p ./backups | ||
| - name: Run unencrypted backup | ||
| run: | | ||
| docker run --rm --network bica-net \ | ||
| --entrypoint /backup.sh \ | ||
| -e DB_HOST=postgres-db \ | ||
| -e DB_PORT=5432 \ | ||
| -e DB_USER=$DB_USER \ | ||
| -e DB_PASSWORD=$DB_PASSWORD \ | ||
| -e DB_NAME=$DB_NAME \ | ||
| -e BACKUP_DIR=$BACKUP_DIR \ | ||
| -e RETENTION_DAYS=$RETENTION_DAYS \ | ||
| -e ENCRYPT=false \ | ||
| -v ${{ github.workspace }}/backups:$BACKUP_DIR \ | ||
| $IMAGE_NAME:$TAG | ||
| - name: Show pg_dump first 40 lines | ||
| run: | | ||
| ls -lh ./backups | ||
| tar -xzf ./backups/*.tar.gz -C ./backups | ||
| head -40 ./backups/db_backup.sql | ||
| - name: Cleanup Docker containers and network | ||
| run: | | ||
| docker rm -f postgres-db || true | ||
| docker network rm bica-net || true | ||
| backup-encrypted: | ||
| name: Backup Encrypted | ||
| runs-on: ubuntu-latest | ||
| needs: [build, setup-postgres] | ||
| env: | ||
| DB_USER: ${{ env.DB_USER }} | ||
| DB_PASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| DB_NAME: ${{ env.DB_NAME }} | ||
| BACKUP_DIR: ${{ env.BACKUP_DIR }} | ||
| RETENTION_DAYS: ${{ env.RETENTION_DAYS }} | ||
| ENCRYPT_PASS: ${{ secrets.ENCRYPT_PASS }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Download image artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: docker-image | ||
| path: . | ||
| - name: Load docker image | ||
| run: docker load -i image.tar | ||
| - name: Prepare backup folder | ||
| run: mkdir -p ./backups | ||
| - name: Run encrypted backup | ||
| run: | | ||
| docker run --rm --network bica-net \ | ||
| --entrypoint /backup.sh \ | ||
| -e DB_HOST=postgres-db \ | ||
| -e DB_PORT=5432 \ | ||
| -e DB_USER=$DB_USER \ | ||
| -e DB_PASSWORD=$DB_PASSWORD \ | ||
| -e DB_NAME=$DB_NAME \ | ||
| -e BACKUP_DIR=$BACKUP_DIR \ | ||
| -e RETENTION_DAYS=$RETENTION_DAYS \ | ||
| -e ENCRYPT=true \ | ||
| -e ENCRYPT_PASS=$ENCRYPT_PASS \ | ||
| -v ${{ github.workspace }}/backups:$BACKUP_DIR \ | ||
| $IMAGE_NAME:$TAG | ||
| - name: List backups | ||
| run: ls -lh ./backups | ||
| - name: Upload encrypted backup artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: encrypted-backup | ||
| path: ./backups/*.enc | ||
| - name: Cleanup Docker containers and network | ||
| run: | | ||
| docker rm -f postgres-db || true | ||
| docker network rm bica-net || true | ||
| decrypt-and-show: | ||
| name: Decrypt backup and show pg_dump | ||
| runs-on: ubuntu-latest | ||
| needs: backup-encrypted | ||
| env: | ||
| ENCRYPT_PASS: ${{ secrets.ENCRYPT_PASS }} | ||
| steps: | ||
| - name: Prepare folder | ||
| run: mkdir -p ./backups | ||
| - name: Download encrypted backup artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: encrypted-backup | ||
| path: ./backups | ||
| - name: Decrypt backup | ||
| run: | | ||
| for f in ./backups/*.enc; do | ||
| openssl enc -aes-256-cbc -d -pbkdf2 -salt -in "$f" -out "${f%.enc}.tar.gz" -k "$ENCRYPT_PASS" | ||
| done | ||
| - name: Extract decrypted tarball and show pg_dump | ||
| run: | | ||
| tar -xzf ./backups/*.tar.gz -C ./backups | ||
| head -40 ./backups/db_backup.sql | ||
| docker-publish: | ||
| name: Push to Docker Hub | ||
| runs-on: ubuntu-latest | ||
| needs: [decrypt-and-show, backup-unencrypted] | ||
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | ||
| env: | ||
| IMAGE_NAME: ${{ env.IMAGE_NAME }} | ||
| TAG: ${{ env.TAG }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Download image artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: docker-image | ||
| path: . | ||
| - name: Load docker image | ||
| run: docker load -i image.tar | ||
| - name: Docker Login | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKER_USERNAME }} | ||
| password: ${{ secrets.DOCKER_PASSWORD }} | ||
| - name: Push image to Docker Hub | ||
| run: | | ||
| docker tag $IMAGE_NAME:$TAG ${{ secrets.DOCKER_USERNAME }}/$IMAGE_NAME:latest | ||
| docker push ${{ secrets.DOCKER_USERNAME }}/$IMAGE_NAME:latest | ||