mirror of
				https://github.com/Burnett01/rsync-deployments.git
				synced 2025-11-04 09:29:04 +01:00 
			
		
		
		
	Add bats tests (#76)
* Add Bats tests and workflow for entrypoint.sh * Add Bats tests and workflow * Add comprehensive GitHub Actions CI workflow (#77) * Initial plan * Add comprehensive GitHub Actions CI workflow Co-authored-by: Burnett01 <1208707+Burnett01@users.noreply.github.com> * Enhance CI workflow with job dependencies and documentation Co-authored-by: Burnett01 <1208707+Burnett01@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Burnett01 <1208707+Burnett01@users.noreply.github.com> * Update CI workflow to only include master branch --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									33214bd98b
								
							
						
					
					
						commit
						53581dff6d
					
				
					 2 changed files with 228 additions and 0 deletions
				
			
		
							
								
								
									
										163
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,163 @@
 | 
			
		|||
# GitHub Actions CI workflow for rsync-deployments
 | 
			
		||||
# This workflow validates the action on every push and pull request by:
 | 
			
		||||
# - Running BATS tests for the entrypoint script
 | 
			
		||||
# - Validating the action.yml definition
 | 
			
		||||
# - Building and testing the Docker image
 | 
			
		||||
# - Checking file structure and permissions
 | 
			
		||||
# - Linting shell scripts
 | 
			
		||||
# - Running a final integration check
 | 
			
		||||
 | 
			
		||||
name: CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  test:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Test BATS Suite
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Install BATS
 | 
			
		||||
      run: |
 | 
			
		||||
        sudo apt-get update
 | 
			
		||||
        sudo apt-get install -y bats
 | 
			
		||||
 | 
			
		||||
    - name: Run BATS tests
 | 
			
		||||
      run: bats test/entrypoint.bats
 | 
			
		||||
 | 
			
		||||
  validate-action:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Validate Action Definition
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Validate action.yml
 | 
			
		||||
      run: |
 | 
			
		||||
        # Check if action.yml exists and has required fields
 | 
			
		||||
        if [ ! -f "action.yml" ]; then
 | 
			
		||||
          echo "Error: action.yml not found"
 | 
			
		||||
          exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        
 | 
			
		||||
        # Basic validation that action.yml contains required fields
 | 
			
		||||
        python3 -c "
 | 
			
		||||
        import yaml
 | 
			
		||||
        import sys
 | 
			
		||||
        
 | 
			
		||||
        with open('action.yml', 'r') as f:
 | 
			
		||||
            action = yaml.safe_load(f)
 | 
			
		||||
        
 | 
			
		||||
        required_fields = ['name', 'description', 'inputs', 'runs']
 | 
			
		||||
        for field in required_fields:
 | 
			
		||||
            if field not in action:
 | 
			
		||||
                print(f'Missing required field: {field}')
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
        
 | 
			
		||||
        # Check required inputs exist
 | 
			
		||||
        required_inputs = ['switches', 'remote_path', 'remote_host', 'remote_user', 'remote_key']
 | 
			
		||||
        for input_name in required_inputs:
 | 
			
		||||
            if input_name not in action['inputs']:
 | 
			
		||||
                print(f'Missing required input: {input_name}')
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
            if not action['inputs'][input_name].get('required', False):
 | 
			
		||||
                print(f'Input {input_name} should be marked as required')
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
        
 | 
			
		||||
        print('Action definition is valid')
 | 
			
		||||
        "
 | 
			
		||||
 | 
			
		||||
  docker-build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Build Docker Image
 | 
			
		||||
    needs: [validate-action, action-structure]
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Build Docker image
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "Building Docker image..."
 | 
			
		||||
        docker build -t rsync-deployments . --no-cache
 | 
			
		||||
        echo "Docker image built successfully"
 | 
			
		||||
 | 
			
		||||
  action-structure:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Validate Action Structure
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Check required files
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "Checking required files exist..."
 | 
			
		||||
        
 | 
			
		||||
        # Check all required files exist
 | 
			
		||||
        required_files=("action.yml" "Dockerfile" "entrypoint.sh")
 | 
			
		||||
        for file in "${required_files[@]}"; do
 | 
			
		||||
          if [ ! -f "$file" ]; then
 | 
			
		||||
            echo "Error: Required file $file not found"
 | 
			
		||||
            exit 1
 | 
			
		||||
          fi
 | 
			
		||||
          echo "✓ $file exists"
 | 
			
		||||
        done
 | 
			
		||||
        
 | 
			
		||||
        # Check entrypoint is executable
 | 
			
		||||
        if [ ! -x "entrypoint.sh" ]; then
 | 
			
		||||
          echo "Error: entrypoint.sh is not executable"
 | 
			
		||||
          exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        echo "✓ entrypoint.sh is executable"
 | 
			
		||||
        
 | 
			
		||||
        # Check basic script syntax
 | 
			
		||||
        bash -n entrypoint.sh
 | 
			
		||||
        echo "✓ entrypoint.sh has valid syntax"
 | 
			
		||||
        
 | 
			
		||||
        echo "All structure checks passed!"
 | 
			
		||||
 | 
			
		||||
  lint-shell:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Lint Shell Scripts
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Install ShellCheck
 | 
			
		||||
      run: |
 | 
			
		||||
        sudo apt-get update
 | 
			
		||||
        sudo apt-get install -y shellcheck
 | 
			
		||||
 | 
			
		||||
    - name: Lint entrypoint.sh
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "Linting shell scripts..."
 | 
			
		||||
        # Run shellcheck with exclusions for Docker-specific dependencies
 | 
			
		||||
        shellcheck -e SC1091 -e SC3046 entrypoint.sh || {
 | 
			
		||||
          echo "ShellCheck found issues, but running with Docker-specific exclusions..."
 | 
			
		||||
          shellcheck -e SC1091 -e SC3046 entrypoint.sh
 | 
			
		||||
        }
 | 
			
		||||
        echo "Shell script linting completed"
 | 
			
		||||
 | 
			
		||||
  integration-check:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Integration Check
 | 
			
		||||
    needs: [test, validate-action, docker-build, action-structure, lint-shell]
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout code
 | 
			
		||||
      uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
    - name: Final integration check
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "All CI jobs completed successfully!"
 | 
			
		||||
        echo "✅ BATS tests passed"
 | 
			
		||||
        echo "✅ Action definition validated"
 | 
			
		||||
        echo "✅ Docker image built and tested"
 | 
			
		||||
        echo "✅ File structure validated"
 | 
			
		||||
        echo "✅ Shell scripts linted"
 | 
			
		||||
        echo ""
 | 
			
		||||
        echo "🎉 rsync-deployments action is ready for use!"
 | 
			
		||||
							
								
								
									
										65
									
								
								test/entrypoint.bats
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								test/entrypoint.bats
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
#!/usr/bin/env bats
 | 
			
		||||
 | 
			
		||||
setup() {
 | 
			
		||||
    # Create a dummy ssh agent and agent-add for sourcing
 | 
			
		||||
    echo 'echo "agent started"' > agent-start
 | 
			
		||||
    echo 'echo "key added"' > agent-add
 | 
			
		||||
    chmod +x agent-start agent-add
 | 
			
		||||
 | 
			
		||||
    # Create a dummy rsync to capture its arguments
 | 
			
		||||
    echo 'echo "rsync $@"' > rsync
 | 
			
		||||
    chmod +x rsync
 | 
			
		||||
 | 
			
		||||
    PATH="$PWD:$PATH"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
teardown() {
 | 
			
		||||
    rm -f agent-start agent-add rsync
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@test "fails if INPUT_REMOTE_PATH is empty" {
 | 
			
		||||
    export INPUT_REMOTE_PATH=" "
 | 
			
		||||
    run ./entrypoint.sh
 | 
			
		||||
    [ "$status" -eq 1 ]
 | 
			
		||||
    [[ "${output}" == *"can not be empty"* ]]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@test "includes legacy RSA switches when allowed" {
 | 
			
		||||
    export INPUT_LEGACY_ALLOW_RSA_HOSTKEYS="true"
 | 
			
		||||
    export INPUT_REMOTE_PATH="remote/"
 | 
			
		||||
    export INPUT_REMOTE_KEY="dummy"
 | 
			
		||||
    export INPUT_REMOTE_KEY_PASS="dummy"
 | 
			
		||||
    export GITHUB_ACTION="dummy"
 | 
			
		||||
    export INPUT_SWITCHES="-avz"
 | 
			
		||||
    export INPUT_REMOTE_PORT="22"
 | 
			
		||||
    export INPUT_RSH=""
 | 
			
		||||
    export INPUT_PATH=""
 | 
			
		||||
    export INPUT_REMOTE_USER="user"
 | 
			
		||||
    export INPUT_REMOTE_HOST="host"
 | 
			
		||||
    export GITHUB_WORKSPACE="/tmp"
 | 
			
		||||
    export DSN="user@host"
 | 
			
		||||
    export LOCAL_PATH="/tmp/"
 | 
			
		||||
 | 
			
		||||
    run ./entrypoint.sh
 | 
			
		||||
    [[ "${output}" == *"HostKeyAlgorithms=+ssh-rsa"* ]]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@test "does not include legacy RSA switches when not allowed" {
 | 
			
		||||
    export INPUT_LEGACY_ALLOW_RSA_HOSTKEYS="false"
 | 
			
		||||
    export INPUT_REMOTE_PATH="remote/"
 | 
			
		||||
    export INPUT_REMOTE_KEY="dummy"
 | 
			
		||||
    export INPUT_REMOTE_KEY_PASS="dummy"
 | 
			
		||||
    export GITHUB_ACTION="dummy"
 | 
			
		||||
    export INPUT_SWITCHES="-avz"
 | 
			
		||||
    export INPUT_REMOTE_PORT="22"
 | 
			
		||||
    export INPUT_RSH=""
 | 
			
		||||
    export INPUT_PATH=""
 | 
			
		||||
    export INPUT_REMOTE_USER="user"
 | 
			
		||||
    export INPUT_REMOTE_HOST="host"
 | 
			
		||||
    export GITHUB_WORKSPACE="/tmp"
 | 
			
		||||
    export DSN="user@host"
 | 
			
		||||
    export LOCAL_PATH="/tmp/"
 | 
			
		||||
 | 
			
		||||
    run ./entrypoint.sh
 | 
			
		||||
    [[ "${output}" != *"HostKeyAlgorithms=+ssh-rsa"* ]]
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue