Sem FTP: Subindo sites WordPress com GitHub Actions

Sem FTP: Subindo sites WordPress com GitHub Actions

  • Last modified: 5 de agosto de 2024
  • Reading time: 80 mins
  • Read in English

Você ainda abre o FileZilla para enviar arquivo por arquivo para o servidor? Chegou a hora de mudar! Depois deste post bastará subir os arquivos para um repositório no GitHub e eles serão enviados para o servidor automaticamente.

Se você está com pressa… tudo bem!

Não tem problema se você está com pressa. De forma bem resumida isso é o que vamos fazer:

  1. Criar um par de chaves SSH que se conectem ao seu servidor;
  2. Criar os segredos DEPLOY_SSH_HOST, DEPLOY_SSH_USER e DEPLOY_SSH_KEY no GitHub;
  3. Criar o arquivo bin/rsync-excludes.txt no seu repositório;
  4. Criar o arquivo .github/workflows/deploy.yml no seu repositório;
  5. Fazer qualquer modificação na branch trunk.

Se este post ajudar, não se esqueça de compartilhá-lo nas suas redes sociais. Assine a newsletter para receber os novos posts!

O que você precisa

A lista do que vamos precisar para esse post é bem simples:

  • Acesso SSH ao seu servidor: Disponível em quase todas as hospedagens atualmente;
  • Um repositório no GitHub: Grátis e ilimitados, inclusive os privados.

Não se esqueça de fazer um backup do conteúdo do seu servidor antes de continuar. Assim, se algo der errado, será mais fácil reverter.

O que a GitHub Action vai fazer

Basicamente, o que vai acontecer é que ao atualizar os arquivos em uma determinada branch do repositório, uma GitHub Action se conectará via SSH com o servidor e enviará os arquivos usando o comando rsync.

O ✓ verde significa que uma Action foi disparada e executada com sucesso.

Acesso SSH

A sua GitHub Action precisará se conectar através do SSH (Secure SHell) com o seu servidor para enviar os arquivos. Para que isso seja possível, vamos criar um par de chaves SSH e configurar o servidor para aceitá-lo.

1. Criar um par de chaves SSH

A criptografia usada pelo protocolo SSH usa duas chaves: uma pública e uma privada. Para que a sua GitHub Action consiga enviar os arquivos para o seu servidor, você precisará de um novo par de chaves. Embora possível, não recomendo usar a mesma chave que você usa normalmente.

O comando para criar uma nova chave é o seguinte:

ssh-keygen -t ed25519 -C "usuario@dominio"
  • ssh-keygen: O comando que gera uma nova chave;
  • -t ed25519: O algoritmo usado na criação da chave;
  • -C "usuario@dominio": Não precisa ser um e-mail de verdade. Só é usado para diferenciar as chaves, como um comentário. Algo como [email protected] já é o suficiente.

O comando pedirá:

  • Uma senha: deixe em branco para facilitar o processo;
  • Um nome de arquivo: não use o padrão, coloque um novo como deploy ou o nome do projeto. Não use nenhuma extensão.
Chamada do comando ssh-keygen e seu retorno

2. Autorizar a chave a conectar com o seu servidor

Cada serviço de hospedagem cuida disso de uma forma diferente. Em hospedagens com algum tipo de painel de controle isso pode estar em Minha conta > SSH ou similar.

Na Cloudways, por exemplo, as chaves SSH são gerenciadas por aplicação. Na seção Access Details, é possível gerenciar as chaves SSH em Application Credentials.

Painel de controle de uma aplicação na Cloudways

Na imagem vemos setas para três elementos:

  • Public IP: Será usado como Host na nossa configuração. Em muitos casos é a URL do próprio site.
  • Username: A Cloudways tem um username diferente por aplicação. Algumas hospedagens usam o mesmo usuário para acessar tanto o painel quanto o servidor via SSH.
  • SSH Keys: Este é o lugar onde é possível configurar novas chaves para essa aplicação. Lá você deve fornecer a chave pública (arquivo.pub) do par gerado.

3. Testar a chave SSH (Opcional)

Do seu computador é possível testar o acesso com a nova chave. Para isso basta executar o seguinte comando:

ssh -i <caminho-chave-privada> <usuario>@<servidor>
  • <caminho-chave-privada>: O caminho do arquivo da chave privada (sem .pub) no seu computador
  • <usuario>@<servidor>: Varia de serviço para serviço. No caso da Cloudways teríamos [email protected].

Se usássemos a chave gerada no exemplo, a chamada ficaria assim:

ssh -i /home/elia/.ssh/exemplopost [email protected]

Se tudo deu certo você vai conseguir acessar o terminal do seu servidor.

GitHub Actions

GitHub Actions é a ferramenta de automação do GitHub. Basicamente, o GitHub vai analisar qualquer arquivo .yml na pasta .github/workflows do seu repositório e tentar executá-lo cada vez que alguma coisa acontecer no seu repositório.

No nosso caso vamos criar um arquivo chamado .github/workflows/deploy.yml. Você pode dar o nome que quiser, desde que mantenha a extensão .yml e a localização do arquivo.

ATENÇÃO: Arquivos .yml não permitem tabs, só espaços (2 ou 4).

O conteúdo do nosso arquivo .github/workflows/deploy.yml será o seguinte:

.github/workflows/deploy.yml
name: Deploy

env:
  SSH_USER: ${{ secrets.DEPLOY_SSH_USER }}
  SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}

on:
  push:
    branches:
      - trunk

jobs:
  deploy_cloudways:
    name: Deploy to Cloudways
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Configure SSH
      run: |
        mkdir -p ~/.ssh/
        echo "$SSH_KEY" > ~/.ssh/deploy.key
        chmod 600 ~/.ssh/deploy.key
        cat >>~/.ssh/config <<END
        Host cloudways
          HostName $SSH_HOST
          User $SSH_USER
          IdentityFile ~/.ssh/deploy.key
          StrictHostKeyChecking no
        END
      env:
        SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}

    - name: Send files
      run: "rsync --delete -avO ${{ env.RSYNC_FLAGS }} --exclude-from=${{ env.EXCLUDES }} ./ ${{ env.SSH_USER }}@${{ env.SSH_HOST }}:${{ env.DESTINATION }}"
      env:
        RSYNC_FLAGS: '' #--dry-run
        EXCLUDES: bin/rsync-excludes.txt
        SSH_HOST: cloudways
        DESTINATION: "~/public_html/wp-content/"

Nome do fluxo de trabalho

A primeira linha do nosso arquivo simplesmente dá nome ao nosso workflow. O nome é usado simplesmente para visualizações e não interfere no funcionamento da automação.

name: Deploy

Segredos no GitHub Action

A segunda seção do nosso arquivo configura algumas variáveis de ambiente compartilhadas por alguns “passos” mais adiante.

env:
  SSH_USER: ${{ secrets.DEPLOY_SSH_USER }}
  SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}

Como a descrição do fluxo de trabalho é feita em um arquivo visível para qualquer um que tenha acesso ao repositório, o GitHub fornece um jeito de criar segredos, editáveis apenas por administradores do repositório ou organização.

Como criar um segredo no GitHub Actions

Vamos relembrar o comando de acesso SSH que usamos para testar anteriormente:

ssh -i <caminho-chave-privada> <usuario>@<servidor>

Com esse comando em mente, acesse o repositório e vá até Settings > Secrets and Variables > Actions. Vamos criar três segredos:

  • DEPLOY_SSH_KEY: O conteúdo da chave privada que você criou.
  • DEPLOY_SSH_USER: O que você usou como <usuario>;
  • DEPLOY_SSH_HOST: O que você usou como <servidor>;

Repare que DEPLOY_SSH_KEY é diferente das outras: passamos o caminho do arquivo no comando, mas o segredo tem o conteúdo do arquivo. Isso acontece porque criaremos o arquivo durante a execução da action.

Formulário para a criação de um segredo no GitHub

O segredo DEPLOY_SSH_KEY não está nessa seção env de propósito. Ele será usado mais adiante.

Gatilhos e eventos

on:
  push:
    branches:
      - trunk

O conteúdo dessa seção é bem auto-explicativo: os passos neste arquivo só devem ser executados quando o conteúdo da branch trunk for modificado.

Ao invés de push, poderia ser pull_request ou schedule, por exemplo.

Jobs

Um conjunto de passos que serão executados. Nosso arquivo de workflow só tem um job, mas é possível ter mais de um. Diferentes jobs são executados em paralelo.

jobs:
  deploy_cloudways:
    name: Deploy to Cloudways
    runs-on: ubuntu-latest

No nosso caso deploy_cloudways poderia ser qualquer slug, assim como Deploy to Cloudways. Esses são só nomes.

A string ubuntu-latest especifica em que sistema operacional o job deve ser executado. É possível especificar versões de Linux, Windows e MacOS. Veja a lista completa.

Etapas ou steps

Steps são subdivisões do nosso job, executadas em ordem.

    steps:
    - name: Checkout
      uses: actions/checkout@v2

O nosso primeiro passo é o checkout do repositório. Essa é, normalmente, a primeira etapa de todos os workflows que você verá por aí. Simplesmente traz o conteúdo do repositório para dentro do ambiente da action.

Configuração do SSH

Esta seção configura o SSH da máquina executando a nossa ação no GitHub.

    - name: Configure SSH
      run: |
        mkdir -p ~/.ssh/
        echo "$SSH_KEY" > ~/.ssh/deploy.key
        chmod 600 ~/.ssh/deploy.key
        cat >>~/.ssh/config <<END
        Host cloudways
          HostName $SSH_HOST
          User $SSH_USER
          IdentityFile ~/.ssh/deploy.key
          StrictHostKeyChecking no
        END
      env:
        SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
  • mkdir -p ~/.ssh/: Cria o diretório .ssh dentro da pasta $HOME do servidor.
  • echo "$SSH_KEY" > ~/.ssh/deploy.key: Coloca o conteúdo da variável SSH_KEY dentro do arquivo deploy.key.
  • chmod 600 ~/.ssh/deploy.key: Configura as permissões do arquivo deploy.key. Nessa caso, 600 significa que o dono do arquivo tem permissão de leitura e escrita. Nenhum outro usuário pode acessá-lo.
  • cat >>~/.ssh/config <<END: O que estiver entre <<END e END será inserido no arquivo config da pasta .ssh.
    • Host cloudways: Cria um nome para a configuração a seguir. Ao chamar o SSH passando cloudways, os dados seguintes serão usados;
    • HostName $SSH_HOST: O endereço do servidor. Se você lembrar do que falamos na seção env, essa variável receberá o valor do segredo DEPLOY_SSH_HOST;
    • User $SSH_USER: Similar ao HostName, mas aplicado ao usuário;
    • IdentityFile ~/.ssh/deploy.key: Especifica o caminho da chave a ser usada para se conectar com o Host;
    • StrictHostKeyChecking no: Pula a verificação da identificação do servidor com o conteúdo do arquivo known_hosts.

Logo abaixo vemos uma seção env, similar àquela global, declarada no começo do nosso arquivo. Repare que nesse caso a variável é criada apenas para esse passo. No caso, estamos passando o conteúdo do segredo DEPLOY_SSH_KEY para uma variável de ambiente chamada SSH_KEY. O conteúdo dessa variável será adicionado ao arquivo deploy.key como vimos acima.

Enviar os arquivos com rsync

No último passo vamos finalmente alterar o conteúdo do servidor. Para isso vamos usar o comando rsync, que sincroniza os arquivos de um lugar com outro. Nesse caso, os arquivos da máquina do GitHub (origem) com os arquivos no seu servidor (destino).

    - name: Send files
      run: "rsync --delete -avO ${{ env.RSYNC_FLAGS }} --exclude-from=${{ env.EXCLUDES }} ./ ${{ env.SSH_USER }}@${{ env.SSH_HOST }}:${{ env.DESTINATION }}"
      env:
        RSYNC_FLAGS: '' #--dry-run
        EXCLUDES: bin/rsync-excludes.txt
        SSH_HOST: cloudways
        DESTINATION: "~/public_html/wp-content/"

Se o comando rsync é novidade para você, vamos ver cada um dos parâmetros passados:

  • --delete: Se um arquivo não está na origem, ele deve ser excluído do destino.
  • -avO: -a significa sincronizar todos os arquivos, pastas e links simbólicos preservando suas permissões. O v é para verbose, ou seja, imprime no terminal a lista de modificações sendo feitas. O (um “o” maiúsculo) configura rsync para não se preocupar com datas de modificações de pastas.
  • ${{ env.RSYNC_FLAGS }}: Como você já deve imaginar, será substituído pelo conteúdo da variável RSYNC_FLAGS configurada mais abaixo. Normalmente, não recebe nada, mas você pode, por exemplo, passar --dry-run que só simula o chamado ao comando, sem fazer nenhuma mudança.
  • --exclude-from=${{ env.EXCLUDES }}: Você pode passar o endereço de um arquivo com a lista de tudo o que deve ser ignorado por rsync. Veja a próxima seção para saber mais sobre isso.
  • ./: o endereço da pasta local que deve ser sincronizada.
  • ${{ env.SSH_USER }}@${{ env.SSH_HOST }}:${{ env.DESTINATION }}: O endereço de destino. A última parte será substituída pela pasta no endereço de destino. No caso aqui o repositório contém os arquivos da pasta wp-content e o site está localizado na pasta $HOME/public_html do servidor. No seu caso a pasta pode estar em outro lugar, como /var/www/html. Acesse seu servidor ou procure nas configurações da sua hospedagem para saber a pasta em que seu site está localizado.

Sobre exclusões no rsync

No exemplo acima estamos usando um arquivo chamado rsync-excludes.txt localizado na pasta bin do repositório. Esse arquivo tem uma lista de arquivos e pastas que devem ser ignorados pelo comando rsync. Por exemplo:

rsync-excludes.txt
*.gitignore
*.gitmodules
*.git
*.gitkeep
*.github

/bin
*rsync-excludes.txt

/uploads
/upgrade
/themes/index.php
/plugins/index.php

Ao invés de usar --exclude-from=<arquivo> você também pode alterar a chamada a rsync e usar --exclude .git --exclude README.md, por exemplo.

Fazendo mais com GitHub Actions

Se você quiser, também é possível chamar comandos como composer install ou npm install durante o processo, evitando que você tenha que versionar arquivos de terceiros no seu repositório. Além disso, o processo também pode ser iniciado pela criação de um Pull Request ou por períodos (uma vez a cada dia, semana, etc.).

Para exemplos reais, veja o repositório do ElasticPress. Lá tem ações executadas uma vez por dia, testes automatizados, deploy para o repositório do WordPress.org e mais.

Por que abandonar o SFTP

Para começar, você deve usar um controle de versão como o git, mesmo que trabalhe sozinho. Ter seus projetos em um repositório git serve tanto para o controle das mudanças quanto como backup, além de ser requisito mínimo para a maioria das vagas atualmente.

O processo descrito aqui tem várias vantagens sobre o método antigo de subir arquivo por arquivo em um programa de SFTP como FileZilla, por exemplo. O meu favorito é que com esse método somente os arquivos alterados serão enviados, tornando o processo mais rápido e simples. Também evitamos que pessoas diferentes sobrescrevam as mudanças umas das outras.

Usei GitHub e GitHub Actions neste post, mas existem outras alternativas disponíveis se achar necessário. O processo de enviar arquivos para um servidor através do repositório é chamado de Continuous Delivery e faz parte do conjunto Continuous Integration, Delivery e Deployment, comumente abreviados como CI/CD.

Conclusão

Nesse post vimos um método para enviar arquivos para um servidor a partir de um repositório git.

Para acessar o servidor é preciso um par de chaves SSH. A chave pública é armazenada no servidor, a privada é guardada em um segredo no GitHub.

Também vimos como é fácil criar um workflow no GitHub: basta criar um arquivo .yml na pasta .github/workflows. Para restringir o acesso a informações sensíveis usamos os segredos do GitHub.

A sincronização dos arquivos é feita pelo comando rsync, que aceita vários parâmetros. Também é possível excluir arquivos da sincronização de formas diferentes: uma lista em um arquivo com o parâmetro --exclude-from ou várias chamadas para --exclude.

Este post é só o começo do que é possível com GitHub Actions. Também é possível usar ferramentas como o Composer ou NPM durante o processo.

Por fim, vimos porque essa forma de deployment é melhor que enviar arquivos individuais via SFTP.


Não se esqueça de compartilhar e comentar o post!

Este post tem 4 comentários

  1. Marcos Mendes

    Boa dica.. mas tenho uma dúvia.
    Usando actions para enviar arquivos via ftp direto do github serão enviados todos os arquivos toda vez que acionado ou é enviado apenas as alterações, inclusões e inclusões?

    1. Felipe Elia

      Oi Marcos! Se você enviar os arquivos via FTP através do GitHub, ele vai testar arquivo por arquivo. Se você seguir o post e enviar por SSH com rsync, só as modificações, inclusões e exclusões serão enviadas.

  2. Fernando

    Show de bola!!! Alguma chance de fazer esse tuto em vídeo? Curto demais seus videos pois vão direto ao ponto, são rápidos, dão uma noção geral e são super bem feitos… Semm falar claro, dos conteúdos excelentes. Obrigado Felipe.

    1. Felipe Elia

      Oi Fernando! Puxa, antes de mais nada deixa eu agradecer pelo elogio! Eu gostaria de voltar com os vídeos, mas a trabalheira envolvida é demais pra minha rotina por enquanto. Quem sabe um dia? 🙂 Valeu demais, abraços!

Os comentários estão encerrados.