Skip to main content

Command Palette

Search for a command to run...

Comprehensive Guide: GitHub Actions CI/CD for Solana Anchor Projects

A Detailed Report on Common Pitfalls and Their Solutions

Updated
10 min read
Comprehensive Guide: GitHub Actions CI/CD for Solana Anchor Projects
S
I'm a passionate developer diving headfirst into the Solana ecosystem. I'm currently on a mission to go from "What is a PDA?" to building production-grade DeFi backends.

1. Introduction to GitHub Actions

What is GitHub Actions?

GitHub Actions is a powerful continuous integration and continuous deployment (CI/CD) platform that automates your software development workflows. It enables you to build, test, and deploy your code directly from your GitHub repository.

Why Use GitHub Actions?

  • Automation: Automatically run tests on every push or pull request

  • Consistency: Ensure consistent build environments across all contributors

  • Early Bug Detection: Catch issues before they reach production

  • Parallel Execution: Run multiple jobs simultaneously for faster feedback

  • Reusable Workflows: Share and reuse workflow templates across projects

  • Cost-Effective: Free for public repositories and includes generous free minutes for private ones

Key Components of GitHub Actions

Component Description Example
Workflow An automated procedure defined in YAML .github/workflows/test.yml
Event What triggers the workflow push, pull_request, schedule
Job A set of steps executed on the same runner test, build, deploy
Step Individual tasks within a job checkout code, install dependencies
Action Reusable units of work actions/checkout@v4, actions/setup-node@v4
Runner The virtual machine that executes the job ubuntu-latest, windows-latest

Basic Workflow Structure

name: Workflow Name

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:        # Manual trigger

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run a command
        run: echo "Hello, World!"

2. Common GitHub Actions Errors and Solutions

Category 1: YAML Syntax Errors

Error: Workflow Not Showing in Actions Tab

Symptoms:

  • Workflow file exists but doesn't appear in the Actions tab

  • No error messages visible in the interface

Common Causes:

Error Type Example Fix
Indentation errors Wrong spacing (tabs vs spaces) Use 2 spaces, validate with linter
Missing keywords on or jobs omitted Include all required sections
Invalid structure Jobs nested incorrectly Follow the YAML schema
Trailing characters Extra spaces or characters Remove all trailing spaces

Example of Invalid YAML:

name: Test
on: [push]     # Missing colon after pull_request
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "test"    # Wrong indentation

Solution:

name: Test
on: 
  push:
  pull_request:      # ✅ Correct structure
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "test"

Prevention:

  1. Use YAML Lint to validate your workflow

  2. Use VS Code with YAML extension for syntax highlighting

  3. Commit and check the Actions tab immediately


Category 2: Path and Directory Issues

Error 1: No Such File or Directory

Symptoms:

Error: An error occurred trying to start process '/usr/bin/bash' with 
working directory '/home/runner/work/repo/repo/folder'. 
No such file or directory

Common Causes:

Mistake Correct Why
working-directory: hello-world working-directory: hello_world Folder name mismatch
paths: "hello-world/**" paths: "hello_world/**" Inconsistent naming
working-directory: programs/hello-world working-directory: hello_world Wrong project structure

Example of Incorrect Paths:

on:
  push:
    paths:
      - "hello-world/**"          # ❌ Wrong folder name
  pull_request:
    paths:
      - "hello_world/**"          # ❌ Inconsistent

jobs:
  test:
    defaults:
      run:
        working-directory: hello-world   # ❌ Folder doesn't exist

Solution:

on:
  push:
    paths:
      - "hello_world/**"          # ✅ Consistent with actual folder name
  pull_request:
    paths:
      - "hello_world/**"

jobs:
  test:
    defaults:
      run:
        working-directory: hello_world   # ✅ Exact folder name

Best Practices for Paths:

  1. Use ls -la locally to confirm folder names

  2. Be consistent: use underscore (_) OR hyphen (-) throughout

  3. Use ** for recursive matching (all subfolders)

  4. Test locally with act to simulate GitHub Actions

Error 2: Trigger Paths Not Matching

Symptoms:

  • Workflow appears but doesn't run when files change

  • Push doesn't trigger the expected workflow

Example of Wrong Trigger Configuration:

on:
  push:
    paths:
      - "src/**"          # ❌ Folder doesn't exist

Solution:

on:
  push:
    paths:
      - "hello_world/**"          # ✅ Correct path
      - "README.md"               # ✅ Specific file
      - "!hello_world/ignore/**"  # ✅ Exclude paths

Category 3: Environment and Toolchain Issues

Error: edition2024 Feature Required

Symptoms:

error: failed to parse manifest at `/path/to/cpufeatures-0.3.0/Cargo.toml`

Caused by:
  feature `edition2024` is required

  The package requires the Cargo feature called `edition2024`, 
  but that feature is not stabilized in this version of Cargo (1.79.0)

Root Cause:

  • The cpufeatures crate version 0.3.0 uses the experimental edition2024 feature

  • The GitHub runner's default Rust version (1.79.0) doesn't support it

  • The edition2024 feature requires Rust 1.85.0 or later

Solutions:

Solution 1: Install Specific Rust Version (Recommended)

steps:
  - name: Install Rust
    uses: actions-rs/toolchain@v1
    with:
      toolchain: 1.85.0
      override: true

Solution 2: Use Nightly Toolchain

steps:
  - name: Install Rust Nightly
    uses: actions-rs/toolchain@v1
    with:
      toolchain: nightly
      override: true

Solution 3: Pin Dependency Version

# In Cargo.toml
[patch.crates-io]
cpufeatures = { git = "https://github.com/RustCrypto/utils", rev = "a51fe1b" }

Best Practices:

  1. Always specify a Rust version in CI workflows

  2. Use actions-rs/toolchain for reliable version management

  3. Test with multiple Rust versions using matrix builds


Category 4: Git Branch Management

Error: Divergent Branches

Symptoms:

! [rejected]        main -> main (fetch first)
error: failed to push some refs
Updates were rejected because the remote contains work that you do not have locally

Visual Explanation:

Local repository (main)          Remote repository (origin/main)
─────────────────────────        ──────────────────────────────

   A - B - C - D - X                 A - B - C - D - Y
         (your commit)                     (someone else's commit)

   - Local is ahead by 1 commit (X)
   - Remote is ahead by 1 commit (Y)
   - They diverged from common commit D

Solutions:

Solution 1: Rebase (Recommended)

git pull --rebase origin main

Result After Rebase:

   A - B - C - D - Y - X'   (local main)
                       ^
                       your commit reapplied on top

Solution 2: Merge

git pull --no-rebase origin main

Result After Merge:

   A - B - C - D - X - M
                \     /
                 Y ---

Set Rebase as Default:

git config --global pull.rebase true

Best Practices:

  1. Use git pull --rebase for cleaner history

  2. Pull before pushing to avoid divergence

  3. Use feature branches for collaborative work

  4. Resolve conflicts locally before pushing


Category 5: Dependency Installation Issues

Error: Node.js or Package Manager Not Found

Symptoms:

Command 'yarn' not found
Command 'npm' not found

Solution:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'yarn'           # Caches dependencies for faster runs

- name: Install dependencies
  run: yarn install

Error: Solana CLI Version Issues

Symptoms:

No such file or directory: solana-keygen
anchor: command not found

Solution:

- name: Install Solana CLI
  run: |
    sh -c "$(curl -sSfL https://release.anza.xyz/v2.1.21/install)"
    echo "\(HOME/.local/share/solana/install/active_release/bin" >> \)GITHUB_PATH

- name: Verify Solana Installation
  run: solana --version

3. My Complete Journey: From Setup to Success

Project Structure

solana-anchor-smart-contract-examples/
├── .github/
│   └── workflows/
│       └── test-hello-world.yml
├── hello_world/                    # Anchor project
│   ├── Anchor.toml
│   ├── Cargo.toml
│   ├── package.json
│   ├── programs/
│   │   └── hello_world/
│   └── tests/
├── README.md
└── .gitignore

Attempt 1: Basic Workflow (Failed)

What I Did Wrong:

  • Inconsistent path naming (hello-world vs hello_world)

  • Wrong working directory

  • No Rust version specification

Result:

Error: No such file or directory
Workflow didn't appear in Actions tab

Attempt 2: Corrected Paths (Partially Worked)

What I Fixed:

  • Used consistent paths: hello_world/**

  • Set correct working directory: hello_world

  • Validated YAML syntax

Result:

  • Workflow appeared but failed with Rust toolchain error

Attempt 3: Full Working Solution

What I Added:

  • Rust 1.85.0 installation

  • Proper Solana and Anchor installation

  • Environment variables for Anchor test

Final Working Workflow:

name: Test Hello World Program

on:
  push:
    branches: [main]
    paths:
      - "hello_world/**"
  pull_request:
    branches: [main]
    paths:
      - "hello_world/**"
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-24.04

    defaults:
      run:
        working-directory: hello_world

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: 1.85.0
          override: true

      - name: Install Solana CLI
        run: |
          sh -c "$(curl -sSfL https://release.anza.xyz/v2.1.21/install)"
          echo "\(HOME/.local/share/solana/install/active_release/bin" >> \)GITHUB_PATH

      - name: Install Anchor
        run: cargo install --git https://github.com/coral-xyz/anchor --tag v0.31.1 anchor-cli --locked

      - name: Generate keypair
        run: solana-keygen new --outfile $HOME/.config/solana/id.json --no-bip39-passphrase --force

      - name: Install Node dependencies
        run: yarn install

      - name: Run Anchor tests
        run: anchor test
        env:
          ANCHOR_PROVIDER_URL: "http://127.0.0.1:8899"
          ANCHOR_WALLET: "$HOME/.config/solana/id.json"

4. Best Practices and Recommendations

1. Workflow Organization

# Use descriptive names
name: Test Hello World Program

# Be specific with triggers
on:
  push:
    branches: [main]
    paths:
      - "hello_world/**"
  workflow_dispatch:  # Manual trigger

# Use environment variables for configuration
env:
  ANCHOR_VERSION: "0.31.1"
  RUST_VERSION: "1.85.0"

2. Caching for Performance

- name: Cache Cargo registry
  uses: actions/cache@v3
  with:
    path: ~/.cargo/registry
    key: \({{ runner.os }}-cargo-registry-\){{ hashFiles('**/Cargo.lock') }}

- name: Cache Node modules
  uses: actions/cache@v3
  with:
    path: hello_world/node_modules
    key: \({{ runner.os }}-node-modules-\){{ hashFiles('hello_world/yarn.lock') }}

3. Matrix Builds for Multiple Programs

strategy:
  matrix:
    program: ["hello_world", "counter"]

steps:
  - name: Test program
    run: |
      cd ${{ matrix.program }}
      anchor test

4. Error Handling

- name: Run tests
  run: anchor test
  continue-on-error: false   # Fail workflow if tests fail

- name: Upload logs on failure
  if: failure()
  uses: actions/upload-artifact@v3
  with:
    name: test-logs
    path: |
      **/test-ledger/*.log
      **/*.txt

5. Troubleshooting Cheat Sheet

Symptom Likely Cause Solution
Workflow doesn't appear YAML syntax error Validate with linter
"No such file" error Wrong path Check exact folder names
Fails during build Missing toolchain Add Rust installation step
"edition2024" error Old Rust version Use Rust 1.85+
Push rejected Divergent branches git pull --rebase
Cache not working Wrong cache key Use hashFiles()
Test validator fails Port conflict Increase startup_wait

6. Conclusion

Setting up GitHub Actions for a Solana Anchor project requires attention to detail, especially regarding:

  1. YAML Syntax: Always validate your workflow files

  2. Path Precision: Match exact folder names (underscore vs. hyphen matters!)

  3. Toolchain Version: Specify compatible Rust versions

  4. Git Management: Use rebase to avoid divergent branches

  5. Dependencies: Properly install and cache dependencies

The journey from a failing workflow to a green checkmark is educational and rewarding. Each error teaches you something new about how CI/CD pipelines work and how to debug them effectively.


7. Additional Resources