Github Actions: Deploy Angular App por FTP em uma estrutura On-Premise
Tags: devops, github, angular, onpremise
Published: 2021-10-04 19:14:07

Se você chegou até aqui, provavelmente já sabe o que é o Github Actions (GhA) e como usá-lo, mas caso não seja esse o caso, pode começar lendo essa [introdução ao Github Actions](https://docs.github.com/pt/actions/learn-github-actions/introduction-to-github-actions).

### Objetivo

O objetivo desse texto é mostrar de forma direta como criar um arquivo yml para gerar o bundle de um projeto feito em angular e fazer a entrega desse bundle via FTP em uma estrutura On-Premise não exposta para a internet.

Nesse exemplo teremos dois *jobs*, em que o primeiro fará o build do projeto, gerará o bundle e preparará os artefatos para a publicação no FTP e o segundo vai pegar esses artefatos e enviá-los para o FTP.

### Por que separar as atividades em dois *jobs*?

Porque o primeiro será executado na estrutura do Github (Gh) e o segundo usará um [executor auto-hospedado](https://docs.github.com/pt/actions/hosting-your-own-runners/about-self-hosted-runners) que será instalado na sua estrutura On-Premise.

> A inclusão de um executor auto-hospedado não faz parte desse texto, veja [aqui](https://docs.github.com/pt/actions/hosting-your-own-runners/adding-self-hosted-runners) como fazer a instalação.

> NOTA: Se você não está acostumado com arquivos YML, lembre-se que a identação é importante.

### Job 1: Build

Vamos direto para o código:

``` yml
  build:
  
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: 10.16
    - name: Install packages with NPM
      run: npm install
    - name: Build for production
      run: npm run build -- --prod
    - name: Upload artifacts to be used in next job
      if: success()
      uses: actions/upload-artifact@v1
      with:
        name: deploy_dist
        path: dist
```
Vejamos o script acima. Logo no início temos o comando `runs-on: ubuntu-latest`, onde é feita a escolha do executor no qual o *job* será executado, nesse caso será a última versão do Ubuntu. Esse executor é fornecido pelo GhA e significa que o job será executado dentro da estrutura do Github.

Em seguida inicia-se o passo a passo (*steps*) do job, sendo o 1º o checkout do projeto e o 2º a preparação do NodeJs, no exemplo ainda escolho a versão do node que deve ser usada (10.16 nesse exemplo).

Ao executar o `npm install` no 3º passo, temos a instalação de todas as depêndencias do projeto. 

No 4º passo é feito o build do projeto. Aqui o comando pode sofrer variações de acordo com o projeto, você pode usar o comando que está acostumado a executar no seu ambiente de desenvolvimento quando quer fazer o build do projeto.

Já o 5º passo faz o upload dos artefatos, a linha `name: deploy_dist` define o nome do artefato como `deploy_dist` e a linha `path: dist` define qual caminho deve ser incluído no artefato, no exemplo usamos `dist` porque no meu projeto é o nome da pasta que contém os arquivos gerados no 4º passo, no arquivo `angular.json` você pode encontrar o nome que está sendo usado no seu projeto.

Com o primeiro *job* concluído, vamos para o segundo.

### Job 2: Deploy FTP

Novamente vamos direto para o código:

``` yml
  ftp-deploy:
    needs: build
    runs-on: self-hosted

    steps:

    - name: Download artifacts created at previous job
      if: success()
      uses: actions/download-artifact@v1
      with:
        name: deploy_dist
        path: dist
    - name: Deploy to FTP
      uses: SamKirkland/FTP-Deploy-Actions@master
      with:
        server: ${{ secrets.FTP_SERVER }}
        username: ${{ secrets.FTP_USER }}
        password: ${{ secrets.FTP_PASSWORD }}
        local-dir: dist/
        server-dir: ${{ secrets.FTP_APP_DIR }}
        dangerous-clean-slate: true # optional
```
Nessa 2º parte do script, temos algumas particularidades logo no início. Logo na 2ª linha usamos o `needs: build` para dizer ao GhA que esse *job* precisa esperar o *job* `build` ser executado com sucesso para poder iniciar sua execução. Outra particularidade é o comando `runs-on: self-hosted` que diz ao GA que esse *job* deve ser executado por um executor instalado na sua estrutura On-Premise.

Já em relação ao passos desse *job*, são bem simples. No 1º fazemos o download do artefato de nome `deploy_dist` e colocamos o conteúdo desse artefato na pasta `dist`. Esses nomes precisam obrigatoriamente ser idênticos aos nomes escolhidos na etapa de upload do *job* anterior.

No 2º passo, usamos a action `SamKirkland/FTP-Deploy-Actions@master` para fazer o upload para o FTP. Os parâmetros `server`, `username`, `password` e `server-dir` são preenchidos por variáveis armazenadas no Secrets do repositório, você pode ver como funciona o Secrets [aqui](https://docs.github.com/pt/actions/security-guides/encrypted-secrets).

> IMPORTANTE: tanto o parâmetro `local-dir` quanto o parâmetro `server-dir` precisam terminar com a barra `/` para dizer que queremos subir tudo que está na pasta `dist`. Assim como foi usado no exemplo: `dist/`.

Por último, o parâmetro `dangerous-clean-slate: true`, significa que todo o conteúdo que existe no `server-dir` será excluído antes que o upload dos arquivos inicie. Use essa opção apenas se for necessário para o seu caso.

### Finalizando o script

Com os dois *jobs* criados, a última parte é juntar tudo em um único arquivo e incluir o nome e o evento que irá acionar a execução desse script.

No nosso caso o arquivo final fica assim:

``` yml
name: Deploy to FTP On-Premise - Project ABC

on:
  push:
    branches:
      - main

  # the follow line allow this script to be started manually.
  workflow_dispatch:

jobs:
  build:
  
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v1
      with:
        node-version: 10.16
    - name: Install packages with NPM
      run: npm install
    - name: Build for production
      run: npm run build -- --prod
    - name: Upload artifacts to be used in next job
      if: success()
      uses: actions/upload-artifact@v1
      with:
        name: deploy_dist
        path: dist

  ftp-deploy:
    needs: build
    runs-on: self-hosted

    steps:

    - name: Download artifacts created at previous job
      if: success()
      uses: actions/download-artifact@v1
      with:
        name: deploy_dist
        path: dist
    - name: Deploy to FTP
      uses: SamKirkland/FTP-Deploy-Actions@master
      with:
        server: ${{ secrets.FTP_SERVER }}
        username: ${{ secrets.FTP_USER }}
        password: ${{ secrets.FTP_PASSWORD }}
        local-dir: dist/
        server-dir: ${{ secrets.FTP_APP_DIR }}
        dangerous-clean-slate: true # optional
```

### Conclusão

Configurar um script de deploy automático de um projeto pode ser mais simples do que parece, pelo menos em alguns casos.

O GhA pode ser usado para executar diversas outras tarefas, como a execução de testes no projeto, o envio de mensagens pelo telegram e muitas outras tarefas.

Fique à vontade para usar esse script como ponto de partida para a criação de um workflow mais adequado para a sua realidade.

Espero ter te ajudado de alguma forma com esse script.