{"slug":"github-actions-cicd","title":"CI/CD Pipeline with GitHub Actions: Complete Guide","description":"Build a robust CI/CD pipeline using GitHub Actions for automated testing, building, and deployment","date":"2024-03-05","author":"Elaph Hilful","thumbnail":"/blog-images/github-actions.webp","tags":["CI/CD","GitHub Actions","DevOps","Automation"],"category":"DevOps","featured":false,"readTime":4,"htmlContent":"<p>Continuous Integration and Continuous Deployment (CI/CD) are essential practices in modern software development. GitHub Actions provides a powerful, integrated way to automate your workflows directly from your repository.</p>\n<h2>Why GitHub Actions?</h2>\n<p>GitHub Actions offers several advantages:</p>\n<ul>\n<li><strong>Integrated with GitHub</strong>: No need for external CI/CD tools</li>\n<li><strong>Matrix builds</strong>: Test across multiple OS and language versions</li>\n<li><strong>Marketplace</strong>: Thousands of pre-built actions</li>\n<li><strong>Free tier</strong>: 2,000 minutes/month for private repositories</li>\n<li><strong>Flexible</strong>: YAML-based configuration</li>\n</ul>\n<h2>Basic Workflow Structure</h2>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">CI</span> <span class=\"hljs-string\">Pipeline</span>\n\n<span class=\"hljs-attr\">on:</span>\n  <span class=\"hljs-attr\">push:</span>\n    <span class=\"hljs-attr\">branches:</span> [<span class=\"hljs-string\">main</span>, <span class=\"hljs-string\">develop</span>]\n  <span class=\"hljs-attr\">pull_request:</span>\n    <span class=\"hljs-attr\">branches:</span> [<span class=\"hljs-string\">main</span>]\n\n<span class=\"hljs-attr\">jobs:</span>\n  <span class=\"hljs-attr\">build:</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/checkout@v3</span>\n\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Setup</span> <span class=\"hljs-string\">Node.js</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/setup-node@v3</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">node-version:</span> <span class=\"hljs-string\">\"18\"</span>\n\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Install</span> <span class=\"hljs-string\">dependencies</span>\n        <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">ci</span>\n\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Run</span> <span class=\"hljs-string\">tests</span>\n        <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">test</span>\n\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span>\n        <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">run</span> <span class=\"hljs-string\">build</span>\n</code></pre>\n<h2>Advanced Features</h2>\n<h3>Matrix Strategy</h3>\n<p>Test across multiple versions and platforms:</p>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-attr\">jobs:</span>\n  <span class=\"hljs-attr\">test:</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">matrix.os</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">strategy:</span>\n      <span class=\"hljs-attr\">matrix:</span>\n        <span class=\"hljs-attr\">os:</span> [<span class=\"hljs-string\">ubuntu-latest</span>, <span class=\"hljs-string\">windows-latest</span>, <span class=\"hljs-string\">macos-latest</span>]\n        <span class=\"hljs-attr\">node-version:</span> [<span class=\"hljs-number\">16</span>, <span class=\"hljs-number\">18</span>, <span class=\"hljs-number\">20</span>]\n\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/checkout@v3</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Setup</span> <span class=\"hljs-string\">Node.js</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">matrix.node-version</span> <span class=\"hljs-string\">}}</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/setup-node@v3</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">node-version:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">matrix.node-version</span> <span class=\"hljs-string\">}}</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">ci</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">test</span>\n</code></pre>\n<h3>Caching Dependencies</h3>\n<p>Speed up workflows by caching dependencies:</p>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Cache</span> <span class=\"hljs-string\">node</span> <span class=\"hljs-string\">modules</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/cache@v3</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">~/.npm</span>\n    <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">runner.os</span> <span class=\"hljs-string\">}}-node-${{</span> <span class=\"hljs-string\">hashFiles('**/package-lock.json')</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">restore-keys:</span> <span class=\"hljs-string\">|\n      ${{ runner.os }}-node-\n</span></code></pre>\n<h3>Docker Integration</h3>\n<p>Build and push Docker images:</p>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Set</span> <span class=\"hljs-string\">up</span> <span class=\"hljs-string\">Docker</span> <span class=\"hljs-string\">Buildx</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">docker/setup-buildx-action@v2</span>\n\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Login</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">DockerHub</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">docker/login-action@v2</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">username:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.DOCKERHUB_USERNAME</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">password:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.DOCKERHUB_TOKEN</span> <span class=\"hljs-string\">}}</span>\n\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span> <span class=\"hljs-string\">and</span> <span class=\"hljs-string\">push</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">docker/build-push-action@v4</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">context:</span> <span class=\"hljs-string\">.</span>\n    <span class=\"hljs-attr\">push:</span> <span class=\"hljs-literal\">true</span>\n    <span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">user/app:latest</span>\n    <span class=\"hljs-attr\">cache-from:</span> <span class=\"hljs-string\">type=gha</span>\n    <span class=\"hljs-attr\">cache-to:</span> <span class=\"hljs-string\">type=gha,mode=max</span>\n</code></pre>\n<h2>Deployment Workflows</h2>\n<h3>Deploy to AWS</h3>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Configure</span> <span class=\"hljs-string\">AWS</span> <span class=\"hljs-string\">credentials</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">aws-actions/configure-aws-credentials@v2</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">aws-access-key-id:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.AWS_ACCESS_KEY_ID</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">aws-secret-access-key:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.AWS_SECRET_ACCESS_KEY</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">aws-region:</span> <span class=\"hljs-string\">us-east-1</span>\n\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Deploy</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">ECS</span>\n  <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">|\n    aws ecs update-service --cluster my-cluster \\\n      --service my-service --force-new-deployment\n</span></code></pre>\n<h3>Deploy to Kubernetes</h3>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Setup</span> <span class=\"hljs-string\">kubectl</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">azure/setup-kubectl@v3</span>\n\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Deploy</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">Kubernetes</span>\n  <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">|\n    kubectl config set-context --current --namespace=production\n    kubectl apply -f k8s/\n    kubectl rollout status deployment/myapp\n</span></code></pre>\n<h2>Security Best Practices</h2>\n<h3>Use Secrets</h3>\n<p>Never hardcode sensitive information:</p>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Run</span> <span class=\"hljs-string\">deployment</span>\n  <span class=\"hljs-attr\">env:</span>\n    <span class=\"hljs-attr\">API_KEY:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.API_KEY</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">DB_PASSWORD:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.DB_PASSWORD</span> <span class=\"hljs-string\">}}</span>\n  <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">./deploy.sh</span>\n</code></pre>\n<h3>Dependency Scanning</h3>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Run</span> <span class=\"hljs-string\">Snyk</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">check</span> <span class=\"hljs-string\">for</span> <span class=\"hljs-string\">vulnerabilities</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">snyk/actions/node@master</span>\n  <span class=\"hljs-attr\">env:</span>\n    <span class=\"hljs-attr\">SNYK_TOKEN:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.SNYK_TOKEN</span> <span class=\"hljs-string\">}}</span>\n</code></pre>\n<h3>Code Scanning</h3>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Initialize</span> <span class=\"hljs-string\">CodeQL</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">github/codeql-action/init@v2</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">languages:</span> <span class=\"hljs-string\">javascript</span>\n\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Perform</span> <span class=\"hljs-string\">CodeQL</span> <span class=\"hljs-string\">Analysis</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">github/codeql-action/analyze@v2</span>\n</code></pre>\n<h2>Complete Production Pipeline</h2>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Production</span> <span class=\"hljs-string\">Pipeline</span>\n\n<span class=\"hljs-attr\">on:</span>\n  <span class=\"hljs-attr\">push:</span>\n    <span class=\"hljs-attr\">branches:</span> [<span class=\"hljs-string\">main</span>]\n\n<span class=\"hljs-attr\">jobs:</span>\n  <span class=\"hljs-attr\">test:</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/checkout@v3</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Setup</span> <span class=\"hljs-string\">Node.js</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/setup-node@v3</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">node-version:</span> <span class=\"hljs-string\">\"18\"</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">ci</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">test</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">run:</span> <span class=\"hljs-string\">npm</span> <span class=\"hljs-string\">run</span> <span class=\"hljs-string\">lint</span>\n\n  <span class=\"hljs-attr\">build:</span>\n    <span class=\"hljs-attr\">needs:</span> <span class=\"hljs-string\">test</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/checkout@v3</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span> <span class=\"hljs-string\">Docker</span> <span class=\"hljs-string\">image</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">docker/build-push-action@v4</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">context:</span> <span class=\"hljs-string\">.</span>\n          <span class=\"hljs-attr\">push:</span> <span class=\"hljs-literal\">true</span>\n          <span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">|\n            myregistry/app:latest\n            myregistry/app:${{ github.sha }}\n</span>\n  <span class=\"hljs-attr\">deploy:</span>\n    <span class=\"hljs-attr\">needs:</span> <span class=\"hljs-string\">build</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Deploy</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">production</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">appleboy/ssh-action@master</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">host:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.PROD_HOST</span> <span class=\"hljs-string\">}}</span>\n          <span class=\"hljs-attr\">username:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.PROD_USER</span> <span class=\"hljs-string\">}}</span>\n          <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.SSH_KEY</span> <span class=\"hljs-string\">}}</span>\n          <span class=\"hljs-attr\">script:</span> <span class=\"hljs-string\">|\n            cd /app\n            docker-compose pull\n            docker-compose up -d\n</span></code></pre>\n<h2>Monitoring and Notifications</h2>\n<h3>Slack Notifications</h3>\n<pre role=\"region\" aria-label=\"yaml code block\"><code class=\"hljs language-yaml\" tabindex=\"0\"><span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Notify</span> <span class=\"hljs-string\">Slack</span>\n  <span class=\"hljs-attr\">if:</span> <span class=\"hljs-string\">always()</span>\n  <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">8398a7/action-slack@v3</span>\n  <span class=\"hljs-attr\">with:</span>\n    <span class=\"hljs-attr\">status:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">job.status</span> <span class=\"hljs-string\">}}</span>\n    <span class=\"hljs-attr\">text:</span> <span class=\"hljs-string\">\"Deployment to production completed\"</span>\n    <span class=\"hljs-attr\">webhook_url:</span> <span class=\"hljs-string\">${{</span> <span class=\"hljs-string\">secrets.SLACK_WEBHOOK</span> <span class=\"hljs-string\">}}</span>\n</code></pre>\n<h2>Best Practices Checklist</h2>\n<ul>\n<li>✅ Use specific action versions (e.g., <code>@v3</code> not <code>@latest</code>)</li>\n<li>✅ Cache dependencies to speed up builds</li>\n<li>✅ Use matrix builds for comprehensive testing</li>\n<li>✅ Implement proper secret management</li>\n<li>✅ Add status checks for pull requests</li>\n<li>✅ Use environments for deployment approvals</li>\n<li>✅ Monitor workflow execution times</li>\n<li>✅ Set up notifications for failures</li>\n</ul>\n<h2>Conclusion</h2>\n<p>GitHub Actions provides a powerful, flexible platform for building CI/CD pipelines. Start with simple workflows and gradually add complexity as your needs grow.</p>\n<p>The integration with GitHub's ecosystem makes it an excellent choice for teams already using GitHub for version control.</p>\n<h2>Resources</h2>\n<ul>\n<li><a href=\"https://docs.github.com/en/actions\">GitHub Actions Documentation</a></li>\n<li><a href=\"https://github.com/sdras/awesome-actions\">Awesome Actions</a></li>\n<li><a href=\"https://github.com/marketplace?type=actions\">Actions Marketplace</a></li>\n</ul>"}