name: Push to SFTP on: workflow_dispatch: # push: # branches: # - main # Optional: Run automatically on pushes to the main branch jobs: deploy-to-sftp: runs-on: ubuntu-latest steps: # Was @v7, which does not exist (latest major is v5): the workflow # failed on its very first step. - name: Checkout repository uses: actions/checkout@v4 - name: Install lftp and ssh run: sudo apt-get update && sudo apt-get install -y lftp openssh-client - name: Set up SSH Private Key env: # Binds the secret to a secure environment variable SSH_PRIVATE_KEY: ${{ secrets.SSH }} run: | mkdir -p ~/.ssh # Uses the environment variable, so the raw key is never printed in the execution log echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa - name: Push files via SFTP with progress run: | # The mirror command below uses the -R (reverse) flag # to push from local './' to remote './' # Connection is made using 'open' inside the execution block to enforce SSH key usage # # --exclude-glob entries: the previous command mirrored the entire # working tree, so CI definitions, the test suite and clear_db.py -- # a script that DELETEs every table and recreates admin/password -- # were all shipped to the production node. # # --delete is deliberately NOT used. Uploaded contracts, logs and the # server's own .env live under the deployment root and are absent # from the repository; deleting anything not present locally would # destroy them. Stale files therefore accumulate: switching to an # atomic timestamped-directory deploy is tracked as OPS-011. lftp -e "set sftp:connect-program 'ssh -a -x -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o BatchMode=yes -o PasswordAuthentication=no'; \ set sftp:auto-confirm yes; \ set net:max-retries 5; \ set net:timeout 30; \ set cmd:fail-exit yes; \ open -u ${{ secrets.SSH_USER }}, sftp://sftp.node4.immortal.host:2022; \ mirror -R --verbose --parallel=4 \ --exclude-glob .git/ \ --exclude-glob .github/ \ --exclude-glob .gitea/ \ --exclude-glob .venv/ \ --exclude-glob venv/ \ --exclude-glob tests/ \ --exclude-glob audit/ \ --exclude-glob .ai/ \ --exclude-glob __pycache__/ \ --exclude-glob clear_db.py \ ./ ./; \ quit"