-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Add CI/CD scripts for Google Cloud deployment #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add CI/CD scripts for Google Cloud deployment #42
Conversation
WalkthroughAdds a new GitHub Actions CI/CD workflow and supporting script for automating .NET Core Web API deployment to Google Cloud Run. The workflow handles Docker image building, registry tagging, and separate production/staging deployments triggered by push and manual events, with generated version tags and deployment reporting. Changes
Sequence DiagramsequenceDiagram
participant GitHub as GitHub<br/>(Push/Dispatch)
participant Workflow as Workflow<br/>Runner
participant GCloud as Google Cloud<br/>SDK
participant Registry as Artifact<br/>Registry
participant CloudRun as Cloud<br/>Run
GitHub->>Workflow: Trigger on push/dispatch
activate Workflow
Note over Workflow: Generate version tag<br/>(dispatch input or git SHA)
Workflow->>GCloud: Authenticate & configure
activate GCloud
GCloud-->>Workflow: Auth complete
deactivate GCloud
Workflow->>Workflow: Build Docker image<br/>(webapi:latest)
Workflow->>Registry: Tag & push<br/>(versioned + latest)
activate Registry
Registry-->>Workflow: Push complete
deactivate Registry
alt Branch = main
Workflow->>CloudRun: Deploy production<br/>(versioned image)
activate CloudRun
CloudRun-->>Workflow: Prod deployed
deactivate CloudRun
else Branch = develop
Workflow->>CloudRun: Deploy staging<br/>(versioned image)
activate CloudRun
CloudRun-->>Workflow: Staging deployed
deactivate CloudRun
end
Workflow->>GitHub: Post deployment<br/>summary
deactivate Workflow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
scripts/ci-cd-deploy-gcloud.sh (1)
4-4: Replace Vietnamese comment with English.Maintain consistency with the English codebase by translating the comment:
# Tạo key cho service account→# Create key for service account..github/workflows/deploy-gcloud.yml (5)
80-88: Fix shell variable quoting in docker tag step (shellcheck SC2086).Variables in the docker tag command should be quoted to prevent word splitting:
- name: Tag Docker image run: | - REMOTE_IMAGE="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}" - REMOTE_IMAGE_LATEST="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:latest" + REMOTE_IMAGE="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}" + REMOTE_IMAGE_LATEST="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}:latest" - docker tag ${{ env.IMAGE_NAME }}:latest ${REMOTE_IMAGE} - docker tag ${{ env.IMAGE_NAME }}:latest ${REMOTE_IMAGE_LATEST} + docker tag "${{ env.IMAGE_NAME }}:latest" "${REMOTE_IMAGE}" + docker tag "${{ env.IMAGE_NAME }}:latest" "${REMOTE_IMAGE_LATEST}" echo "REMOTE_IMAGE=${REMOTE_IMAGE}" >> $GITHUB_ENV echo "REMOTE_IMAGE_LATEST=${REMOTE_IMAGE_LATEST}" >> $GITHUB_ENV
96-109: Remove--quietflag to reveal deployment errors.The
--quietflag suppresses output, which can hide deployment failures. This makes troubleshooting difficult. Remove it to see deployment status and errors:gcloud run deploy ${{ env.SERVICE_NAME }} \ --image ${{ env.REMOTE_IMAGE }} \ --platform managed \ --region ${{ env.REGION }} \ --allow-unauthenticated \ --memory 512Mi \ --cpu 1 \ --max-instances 10 \ --min-instances 0 \ --port 8080 \ --set-env-vars "ASPNETCORE_ENVIRONMENT=Production" \ - --quiet + # Removed --quiet to surface deployment status
111-125: Remove--quietflag from staging deployment as well.Apply the same change to maintain consistency:
gcloud run deploy ${{ env.SERVICE_NAME }}-staging \ --image ${{ env.REMOTE_IMAGE }} \ --platform managed \ --region ${{ env.REGION }} \ --allow-unauthenticated \ --memory 512Mi \ --cpu 1 \ --max-instances 5 \ --min-instances 0 \ --port 8080 \ --set-env-vars "ASPNETCORE_ENVIRONMENT=Staging" \ - --quiet + # Removed --quiet to surface deployment status
142-156: Optimize shell redirections and fix quoting in deployment summary (shellcheck SC2129, SC2086).Use a single redirection block for all summary writes and add proper quoting around variables:
- name: Create deployment summary run: | + { echo "## Deployment Summary 🚀" echo "" - echo "- **Version**: \`${{ steps.version.outputs.VERSION }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Image**: \`${{ env.REMOTE_IMAGE }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GITHUB_SHA::7}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: \`${{ steps.version.outputs.VERSION }}\`" + echo "- **Image**: \`${{ env.REMOTE_IMAGE }}\`" + echo "- **Branch**: \`${{ github.ref_name }}\`" + echo "- **Commit**: \`${GITHUB_SHA:0:7}\`" echo "" - if [ "${{ github.ref }}" == "refs/heads/main" ]; then - echo "- **Environment**: Production" >> $GITHUB_STEP_SUMMARY - elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then - echo "- **Environment**: Staging" >> $GITHUB_STEP_SUMMARY + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "- **Environment**: Production" else - echo "- **Environment**: Image built only (no deployment)" >> $GITHUB_STEP_SUMMARY + echo "- **Environment**: Staging or Image only" fi + } >> "$GITHUB_STEP_SUMMARY"
14-21: Clarify workflow behavior on pull requests.The workflow triggers on
pull_requestbut only builds the image without deploying. This should be documented or the trigger removed if PR builds aren't needed. Consider adding a comment or output to clarify this behavior:pull_request: branches: - main paths: - 'src/**' - 'appsettings*.json' - 'Dockerfile' - 'docker-compose.yml' + # Note: PRs trigger a build only; deployment occurs on merge to main/develop
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/deploy-gcloud.yml(1 hunks)scripts/ci-cd-deploy-gcloud.sh(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.8)
.github/workflows/deploy-gcloud.yml
51-51: shellcheck reported issue in this script: SC2086:info:9:30: Double quote to prevent globbing and word splitting
(shellcheck)
80-80: shellcheck reported issue in this script: SC2086:info:7:40: Double quote to prevent globbing and word splitting
(shellcheck)
80-80: shellcheck reported issue in this script: SC2086:info:8:54: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2129:style:1:1: Consider using { cmd1; cmd2; } >> file instead of individual redirects
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:1:35: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:2:12: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:3:67: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:4:52: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:5:52: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:6:46: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:7:12: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:9:43: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:11:40: Double quote to prevent globbing and word splitting
(shellcheck)
142-142: shellcheck reported issue in this script: SC2086:info:13:65: Double quote to prevent globbing and word splitting
(shellcheck)
🔇 Additional comments (3)
scripts/ci-cd-deploy-gcloud.sh (1)
2-2: Verify service account name: possible typo "fucntions" vs. "functions".The service account identifier
ci-cd-fucntions-hosting@...containsfucntions, which appears to be a typo (should befunctions). Please confirm this is intentional or correct it.Also applies to: 5-5
.github/workflows/deploy-gcloud.yml (2)
102-102: Review the--allow-unauthenticatedsetting for security.Both production and staging deployments use
--allow-unauthenticated, making the backend API publicly accessible without authentication. This is appropriate only for public APIs. For internal or protected services, configure proper authentication.Confirm this is intentional. If the API requires authentication, update both deployments:
- --allow-unauthenticated \ + --no-allow-unauthenticated \ + --set-env-vars "GCP_SA_KEY=${{ secrets.GCP_SA_KEY }}" \Also applies to: 118-118
50-61: Add quoting around variables to prevent word splitting (shellcheck SC2086).The version generation logic uses unquoted variables that could be split on whitespace. Apply proper quoting:
- if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.version_tag }}" ]; then + if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.version_tag }}" ]; then VERSION="${{ inputs.version_tag }}" elif [ "${{ github.ref_type }}" == "tag" ]; then VERSION="${{ github.ref_name }}" else # Use date + short SHA for immutable versioning - VERSION="$(date +%Y%m%d)-${GITHUB_SHA::7}" + VERSION="$(date +%Y%m%d)-${GITHUB_SHA:0:7}" fi
| #!/bin/bash | ||
| gcloud projects add-iam-policy-binding lucky-union-472503-c7 --member="serviceAccount:ci-cd-fucntions-hosting@lucky-union-472503-c7.iam.gserviceaccount.com" --role="roles/run.admin" | ||
|
|
||
| # Tạo key cho service account | ||
| gcloud iam service-accounts keys create github-key.json --iam-account=ci-cd-fucntions-hosting@lucky-union-472503-c7.iam.gserviceaccount.com No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Critical: Service account key will be exposed if not properly excluded from version control.
Creating github-key.json directly in the repository root is a severe security risk. If this file is not strictly excluded from version control, it will expose GCP credentials in the repository history.
- Verify that
github-key.jsonis listed in.gitignoreand add it if missing. - Consider whether this script should run as a one-time setup (outside CI/CD) rather than in every workflow execution.
- If this must run in CI/CD, use a temporary directory and ensure the key is only stored as a GitHub secret (e.g., base64-encoded in
secrets.GCP_SA_KEY), never written to disk.
+#!/bin/bash
+set -euo pipefail
+
gcloud projects add-iam-policy-binding lucky-union-472503-c7 --member="serviceAccount:ci-cd-fucntions-hosting@lucky-union-472503-c7.iam.gserviceaccount.com" --role="roles/run.admin"
-# Tạo key cho service account
+# Create key for service account
-gcloud iam service-accounts keys create github-key.json --iam-account=ci-cd-fucntions-hosting@lucky-union-472503-c7.iam.gserviceaccount.com
+gcloud iam service-accounts keys create /tmp/github-key.json --iam-account=ci-cd-fucntions-hosting@lucky-union-472503-c7.iam.gserviceaccount.com
+echo "Key created at /tmp/github-key.json"🏁 Script executed:
#!/bin/bash
# Check for .gitignore and verify github-key.json is excluded
if [ -f ".gitignore" ]; then
echo "=== .gitignore contents ==="
cat .gitignore
else
echo ".gitignore does not exist"
fiLength of output: 6924
🏁 Script executed:
#!/bin/bash
# Search for service account references with both spellings
echo "=== Searching for 'ci-cd-fucntions-hosting' (typo version) ==="
rg -n "ci-cd-fucntions-hosting" --type-list | head -20
echo ""
echo "=== Searching for 'ci-cd-functions-hosting' (correct spelling) ==="
rg -n "ci-cd-functions-hosting" --type-list | head -20Length of output: 910
🏁 Script executed:
#!/bin/bash
# Search for github-key.json references throughout codebase
echo "=== Searching for github-key.json references ==="
rg -n "github-key\.json" -C2
echo ""
echo "=== Searching for /tmp/github-key references ==="
rg -n "tmp.*github-key" -C2Length of output: 368
🏁 Script executed:
#!/bin/bash
# Examine the full script and look for CI/CD workflow files
echo "=== Full content of scripts/ci-cd-deploy-gcloud.sh ==="
cat scripts/ci-cd-deploy-gcloud.sh
echo ""
echo "=== Searching for workflow files that might call this script ==="
fd -e yaml -e yml . .github/workflows 2>/dev/null || echo "No .github/workflows directory found"Length of output: 548
🏁 Script executed:
#!/bin/bash
# Check for any error handling patterns in other scripts
echo "=== Checking for set -e or set -o pipefail in shell scripts ==="
rg -n "set -[eo]" --type bash -C1 | head -30Length of output: 92
🏁 Script executed:
#!/bin/bash
# Check workflow files for calls to the deploy script
echo "=== deploy-gcloud.yml content ==="
cat .github/workflows/deploy-gcloud.yml
echo ""
echo "=== build.yml content ==="
cat .github/workflows/build.ymlLength of output: 7577
🏁 Script executed:
#!/bin/bash
# Search for references to the ci-cd-deploy-gcloud.sh script
echo "=== Searching for ci-cd-deploy-gcloud.sh references ==="
rg -n "ci-cd-deploy-gcloud\.sh" -C3
echo ""
echo "=== Searching for github-key usage in workflows or scripts ==="
rg -n "github-key" -C2Length of output: 389
🏁 Script executed:
#!/bin/bash
# Check if scripts contain error handling directly
echo "=== Checking scripts directory for error handling ==="
fd -e sh scripts/ -exec grep -l "set -e\|set -o pipefail\|trap.*ERR" {} \;
echo ""
echo "=== Full listing of scripts directory ==="
find scripts/ -type f -exec file {} \;Length of output: 486
🏁 Script executed:
#!/bin/bash
# Check README or documentation for setup instructions
echo "=== Searching for README or setup documentation ==="
fd -i "readme|setup|install" . -type f | head -10
echo ""
echo "=== Searching for mentions of one-time setup or manual steps ==="
rg -i "setup|manual|one-time|initialize" README* 2>/dev/null || echo "No README found with these terms"Length of output: 343
🏁 Script executed:
#!/bin/bash
# Final verification: search for any documentation, comments, or issues mentioning this script
echo "=== Searching for documentation or comments about manual setup ==="
rg -i "deploy-gcloud|manual setup|one-time setup|initialize gcp" -C2 --max-count=10
echo ""
echo "=== Check git history for when script was added ==="
git log --follow --oneline scripts/ci-cd-deploy-gcloud.sh 2>/dev/null | head -5 || echo "Git history unavailable"
echo ""
echo "=== Check if README mentions this script ==="
cat README.md 2>/dev/null | grep -i "deploy-gcloud\|manual\|setup" | head -10 || echo "No matches in README"Length of output: 280
Add github-key.json to .gitignore to prevent credential exposure.
The critical security issue is that github-key.json is not listed in .gitignore. If this script creates a credentials file in the repository root, it could be accidentally committed, exposing GCP service account credentials in version control history.
Add the following line to .gitignore:
github-key.json
Note: The service account name ci-cd-fucntions-hosting is the actual account in use (not a typo). However, the script also lacks error handling—add set -euo pipefail after the shebang to ensure the script fails gracefully if any command encounters an error, rather than silently continuing.
🤖 Prompt for AI Agents
In scripts/ci-cd-deploy-gcloud.sh around lines 1 to 5, the script writes a
credentials file (github-key.json) into the repo and lacks strict error
handling; add github-key.json to the repository .gitignore (repo root) to
prevent accidental commits of credentials, and modify this script to enable
strict failure mode by inserting set -euo pipefail immediately after the shebang
so the script exits on errors/unset variables and fails safely; do not change
the service account name.
Summary by CodeRabbit