Back to Blog
Lesson 35 of the CI/CD: Continuous Integration from Scratch course
DevOpsAugust 10, 20263 min read

Deploying to Staging: Automating Your CI/CD Release Pipeline

Learn to automate your deployment by chaining a staging release job to your CI pipeline. Ensure reliable delivery with verified, automated workflows.

CI/CDdeploymentautomationgithub-actionsdevops
Close-up of rusty industrial pipes and valves, showcasing aging machinery in a factory setting.

Previously in this course, we explored Introduction to Continuous Delivery: Moving Beyond Just CI and established the logic for Staging Environments: Mastering Deployment Targets in CI/CD. In this lesson, we move from theory to execution: we will add a dedicated deployment job to your pipeline, using the Automated Deployment Scripts: Mastering Remote Server Provisioning we previously developed, to achieve true automation.

The Deployment Job: Orchestrating the Release

In our current pipeline, we have jobs that lint our code, run tests, and potentially build Docker images. However, these steps end in "verification." To achieve continuous deployment, we must treat the "deployment" phase as a formal job in our YAML workflow.

By defining a new job, we isolate the deployment logic from the testing logic. This provides two major benefits:

  1. Clear Failure Isolation: If the deployment fails, the logs will explicitly show a failure in the "deploy" phase rather than the "test" phase.
  2. Dependency Management: We can use the needs keyword to ensure that deployment never triggers unless the preceding test suite passes perfectly.

Implementing the Deployment Job

We will now add a deploy-staging job to our existing workflow. This job will execute the shell script you created in our previous session to push code or images to your staging server.

Here is how to structure this in your .github/workflows/main.yml file:

YAML
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Tests
        run: npm test

  deploy-staging:
    needs: test  # This is the key: only run if 'test' succeeds
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Deployment Script
        env:
          SERVER_IP: ${{ secrets.STAGING_SERVER_IP }}
        run: |
          chmod +x ./scripts/deploy.sh
          ./scripts/deploy.sh

Understanding the Chain of Command

The needs: test directive is the cornerstone of safe automation. If the test job fails, GitHub Actions will automatically skip the deploy-staging job. This prevents broken code from ever reaching your staging environment.

When you push your next commit, you will see the pipeline visualize this dependency in the GitHub Actions tab. The deployment job will remain in a "waiting" state until the tests complete, at which point it automatically transitions to "running."

Hands-on Exercise: Connect the Pipeline

  1. Open your project's .github/workflows/main.yml file.
  2. Add the deploy-staging job block as shown above.
  3. Ensure your deploy.sh script is located in the /scripts directory of your repository.
  4. Commit and push your changes.
  5. Navigate to your repository's "Actions" tab and observe the sequence of jobs. Verify that the staging deployment triggers only after the test suite finishes with a green checkmark.

Common Pitfalls

  • Missing Execution Permissions: If your deployment script fails with "Permission Denied," it is usually because the file lost its executable status during git commit. Always ensure you run chmod +x inside the workflow step, as shown in the example.
  • Hardcoded Credentials: Never put your server IP or SSH keys directly in the YAML file. Use GitHub Secrets to pass these values as environment variables.
  • Assuming Environment State: Do not assume the server is ready. A robust deployment script should check for the existence of directories or services before attempting to deploy.

FAQ

Q: Can I run multiple deployments in parallel? A: Yes, if your architecture supports it. However, for staging environments, sequential execution (as shown) is safer to prevent race conditions.

Q: What if I only want to deploy on the main branch? A: Use the if conditional in your job: if: github.ref == 'refs/heads/main'. This prevents staging deployments from feature branches.

Recap

We successfully integrated our deployment script into the CI/CD pipeline, turning a manual process into an automated, dependency-aware workflow. By utilizing the needs keyword, we have ensured that our staging environment is only updated when our code passes all validation gates.

Up next: We will secure our pipeline further by implementing Environment-Specific Secrets, ensuring that sensitive data is scoped appropriately to prevent accidental exposure.

Similar Posts