DEV Community

Valter Zanchetti Filho
Valter Zanchetti Filho

Posted on

Começando com CI/CD no Oracle Database/APEX usando SQLcl

Depois de muito ler, tentar compreender, testar e apanhar da ferramenta, consegui fazer funcionar um processo simples de CI/CD entre desenvolvimento e produção. Consigo gerar releases da base de desenvolvimento que podem ser aplicados da mesma forma em outras bases de produção. Neste artigo, pretendo mostrar um pouco da minha abordagem.

Antes de iniciar, vamos alinhar as expectativas:

A. Nem tudo é perfeito

Se você espera um script perfeito, que vai fazer absolutamente tudo por você, já pode deixar essa expectativa de lado. SQLcl não é perfeito nem tão bem documentado. Você ainda vai precisar fazer alguns ajustes manuais.

Percebo que em cada versão existe um bug diferente, mas sempre é possível contornar de alguma forma;

B. Objetos java são ignorados

Objetos JAVA no banco de dados são esquecidos pela ferramenta. Você vai precisar encontrar uma forma de contornar isso, caso utilize.

C. Atenção se as duas bases rodam no mesmo PDB

Se você tem as bases de produção e desenvolvimento no mesmo PDB, sugiro fazer o CI/CD do APEX/ORDS num projeto separado, porque tem algumas particularidades que podem te trazer problemas.

D. Essa abordagem sugere que você está gerando os artefatos pela primeira vez

Estamos considerando aqui neste artigo que você está gerando os artefatos pela primeira vez para rodar numa base limpa. Se quiser rodar numa base que já está com os objetos atualizados, vai precisar rodar um comando para que elas se entendam. Vou dar a dica deste comando no final do artigo.

Requisitos:

Para seguir o passo a passo desse artigo você vai precisar de Oracle Database, é claro. Além disso, para replicar o tutorial você precisará de:

  1. Windows (pode utilizar Linux, mas vai precisar adaptar alguns comandos/abordagens)

  2. SQLcl (neste artigo utilizei a versão 25.2.1.195)

  3. Java (neste artigo jdk-21.0.8)

  4. Oracle Instant Client (neste caso 23.8)

  5. GIT (qualquer versão recente. Precisa ser instalado, não vale o portátil.)

Depois de tudo isso instalado, vamos iniciar abrindo o CMD no windows e setar algumas variáveis manualmente para não ter risco de ele se perder. Abaixo temos apenas um exemplo, mas você vai precisar definer os paths conforme a sua instalação. Recomendo digitar linha por linha para ter certeza que o Windows entendeu o que foi passado. Abaixo, o meu exemplo:

rem O Comando CHCP 650001 vai fazer com que o seu CMD trate melhor caracteres como acentos, entre outros.
chcp 65001 
rem Se houver estouro de memória pode setar manualmente a memória do Java.
rem set JAVA_TOOL_OPTIONS=-Xms512m -Xmx6G 
rem Aqui iremos definir o diretório do seu Java Home, assim teremos certeza que ele irá buscar no local correto.
set "JAVA_HOME=C:\Oracle\jdk-21.0.8"
rem Vamos também definir o diretório do seu SQLcl
set "SQLCL_HOME=C:\Oracle\sqlcl-25.2.1.195.1751\sqlcl"
rem Caso necessário, temos aqui também o caminho do instant client
rem SET INSTANT_CLIENT_HOME=C:\Util\instantclient\instantclient_23_4 
rem Com este comando, incluímos tudo isso junto com o PATH padrão.
set PATH=%SQLCL_HOME%\bin;%JAVA_HOME%\bin;%INSTANT_CLIENT_HOME%;%PATH%
rem Se quiser, pode chamar diretamente os dados do DB via TNSADMIN. O comando abaixo so se torna necessário se você fizer questão de usar o TNSADMIN
set TNS_ADMIN=C:\Oracle\instantclient_23_4\network\admin
Enter fullscreen mode Exit fullscreen mode

Você configurou o ambiente para iniciar os próximos passos. Não feche esta janela pois nela foram passados todos os parâmetros que precisamos. O nome do meu projeto exemplo será PMT.

Pode deixar o CMD minimizado e criar uma pasta vazia para o seu projeto. No meu caso, a pasta será:

C:\Oracle\projetos\PMT. No CMD vamos navegar até a pasta criada

cd C:\Oracle\projetos\PMT
Enter fullscreen mode Exit fullscreen mode

É importante garantir que você está na pasta certa. Agora podemos conectar o SQLcl:

CONECTANDO NO SQLCL

Para conectar no SQLcl, você tem mais de uma opção. Escolha apenas uma delas

  1. Conectar diretamente sem passar inicialmente a conexão com o banco, deixando para passar os dados da conexão após abrir o SQLcl.
sql -thin /nolog

conn PMT/pass@dbpessoal
Enter fullscreen mode Exit fullscreen mode
  1. Conexão passando o caminho diretamente
sql -thin PMT/pass@//databasehostnameorip:1521/servicename
Enter fullscreen mode Exit fullscreen mode
  1. Conexão via TNSNAMES.
sql -thin PMT/pass@dbpessoal
Enter fullscreen mode Exit fullscreen mode

PREPARANDO SEU PRIMEIRO RELEASE

Depois de conectado no banco de dados via SQLcl, já na pasta do seu projeto, você vai precisar iniciar e definir algumas configurações do projeto. Isso só precisará ser feito uma única vez.

project init -name PMT -schemas PMT
Enter fullscreen mode Exit fullscreen mode

Configurando o projeto

Assim que o projeto é iniciado, você pode simplesmente ignorar configurações adicionais ou personalizar de acordo com a sua preferência. Para verificar as configurações disponíveis, pode usar o comando abaixo.

project config -list -verbose
Enter fullscreen mode Exit fullscreen mode

Abaixo, vou listar os comandos de minha preferência, que podem ser utilizados como exemplo ou não. Para entender o que cada um dos comandos faz, você pode rodar o comando acima, que está tudo bem explicado.

project config set -name export.format.enable -value false -verbose 
project config set -name export.setTransform.emitSchema -value false -verbose
project config set -name export.apex.expComments -value true -verbose 
project config set -name export.apex.expOriginalIds -value true -verbose 
project config set -name export.apex.expSavedReports -value true -verbose 
project config set -name export.apex.expPubReports -value true -verbose
Enter fullscreen mode Exit fullscreen mode

Essa configurações ficam gravadas num arquivo dentro da pasta .dbtools.

Configurando filtros

Dentro da mesma pasta .dbtools, existe um subpasta de nome filters com um único arquivo project.filters. Se abrir o arquivo, verá outras configurações adicionais, como por exemplo, opções de ignorar objetos no projeto, exportar somente aplicações APEX específicas. Neste caso, não iremos fazer nenhuma mudança no arquivo. Vamos direto aos comandos GIT. De qualquer forma, recomendo que você analise os objetos APEX, ORDS e grants.

Considerando que você já tem o GIT instalado, iremos criar o main branch através deste comando. Como você ainda está dentro do SQLcl, teremos que adicionar um ! na frente dos comandos git. Isso vale para qualquer outro comando de sistema operacional dentro do SQLcl.

--com este comando você irá criar seu branch inicial e se posicionar nele. É importante colocar mesmo aqui que vem depois dos dois traços: -- nesse primeiro comando:
!git init --initial-branch=main

--com este comando iremos adicionar os arquivos no git
!git add .

--após adicionar precisamos fazer o commit e podemos cadastrar uma mensagem para identificar esse commit.
!git commit -m "Inicialização do projeto"
Enter fullscreen mode Exit fullscreen mode

Agora vamos fazer nosso primeiro release, chamaremos de 1.0.

--esse comando vai criar o branch e nos colocar dentro dele
!git checkout -b release-1.0
Enter fullscreen mode Exit fullscreen mode

Estamos dentro do branch 1.0, então é hora de exportar nosso banco de de dados. Para isso iremos rodar os comandos abaixo. Não deixe seus desenvolvedores fazerem modificações na sua base enquanto o export não termina.

-- Você pode escolher o número de threads utilizadas. Neste caso, escolhi 5 threads, que é o padrão.
project export -threads 5 -verbose

--adiciona e faz o commit no git.
!git add .
!git commit -m "release-1.0"
Enter fullscreen mode Exit fullscreen mode

Apenas para conhecimento, todos os objetos exportados ficam na pasta /scr.

Depois disso, colocaremos o projeto em stage, que irá comparar o export atual com os branches anteriores. Abaixo, a lista de comandos.

--a primeira coisa a fazer é rodar o project stage, que irá compar os objetos exportados na versão atual com as versões anteriores.
project stage

--se você quiser adicionar uma lista de comandos dml, deve acrescentar com este comando, nessa etapa.
--project stage add-custom -file-name dml_file_name

--caso queira verificar se está tudo certo com o projeto pode rodar o project verify, mas não é obrigatório.
--project verify -verbose 

--vamos informar a versão do release
project release -version 1.0 -verbose
--com o comando abaixo, iremos gerar o artefato. Um .zip com toda a instrução de deploy e todos os objetos, já listados na ordem em que devem ser executados.
project gen-artifact -version 1.0 -verbose

--adicionamos e commitamos.
!git add . 
!git commit -m "Commit Version 1.0"

--depois de resultado de sucesso em todas as etapas acima precisamos voltar para o branch main e fazer um merge com o release atual.
!git checkout main 
!git merge release-1.0
Enter fullscreen mode Exit fullscreen mode

Nesta etapa você concluiu a geração do seu primeiro release e está pronto para aplica-lo em outras bases.

FAZENDO O DEPLOY DO SEU PRIMEIRO RELEASE NA BASE DE DESTINO

Depois que o seu release estiver pronto, você verá que o objeto .zip estará na sua pasta artifacts, dentro da pasta principal do seu projeto. Agora iremos repetir alguns comandos e acessar a base de destino. Lembrando que estamos considerando uma base de destino limpa, onde os objetos ainda não existem. Se a outra base já contiver os objetos isso não vai funcionar, precisa rodar primeiro um outro comando para isso, que será abordado no final desse artigo.

  1. Caso ainda esteja com o cmd aberto, basta rodar o comando quit para sair do SQLcl e reconectar na base que irá receber os objetos. Caso tenha fechado o cmd, basta rodar novamente os comandos de setar as variáveis de ambiente, voltar para a pasta do projeto e iniciar o SQLcl (dessa vez você irá conectar na base de destino, aquela que vai receber os objetos. Isso é muito importante.)
project deploy -file artifact/PMT-1.0.zip -verbose
Enter fullscreen mode Exit fullscreen mode

Como escolhemos o modo verbose ele irá mostrar cada objeto que está sendo incluído no banco de dados de destino, até você receber a mensagem de sucesso. Além dos seus objetos, ele vai criar 3 tabelas na sua base de destino: DATABASECHANGELOG, DATABASECHANGELOG_ACTIONS e DATABASECHANGELOGLOCK, que são as tabelas de controle do liquibase.

PREPARANDO SEUS PRÓXIMOS RELEASES

Considerando que você já gerou seu primeiro release e agora precisa gerar outros, recomendo fazer um backup da pasta do projeto. Não tente usar o comando de compactar do windows, pq ele desconsidera tudo o que tem relação com o git naquela pasta. Você pode copiar a pasta ou gerar um .zip com o 7Zip.

Vamos ao trabalho. O primeiro passo é voltar para o SQLcl, na pasta do projeto e conectar na base de origem novamente. Para isso vamos executar:

--exportando o projeto novamente
project export -verbose
--criando novo branch
!git checkout -b release-1.1
!git add . 
!git commit -m "Novos arquivos exportados da base de desenvolvimento na ver. 1.1"
-- colocando em stage para comparar nosso branch atual com o main
project stage -verbose
--criando o release
project release -version 1.1 -verbose
--gerando o novo artefato
project gen-artifact -version 1.1 -verbose
!git add . 
!git commit -m "Commit dos novos arquivos da versão 1.1"
--fazendo o checkout e voltando para o main branch
!git checkout main
--fazendo o merge do último release na main
!git merge release-1.1
Enter fullscreen mode Exit fullscreen mode

Tudo pronto. Você verá agora mais um artefato .zip incrementado com todos os objetos do release anterior + os objetos do release atual. Na hora de aplicar, ele irá confirmar no liquibase que todos os objetos do release anterior foram aplicados e logo irá começar a aplicar as mudanças do release atual. Caso você já tenha certeza que os anteriores estão aplicados com sucesso, pode descompactar o artefato e remover as referências dos releases anteriores do xml main.changelog que fica em /releases.

Após modificado precisará recompactar o arquivo com as alterações para poder fazer o deploy.

OUTRAS CONSIDERAÇÕES

A. Caso você esteja querendo iniciar quando sua base de dados de destino já contém os objetos:

Neste caso você irá criar os objetos normalmente, porém, antes de fazer o deploy do primeiro release na base de destino, precisará rodar o comando abaixo [na base de destino]:

liquibase changelog-sync -chf dist/releases/main.changelog.xml
Enter fullscreen mode Exit fullscreen mode

Esse comando irá criar as tabelas do liquibase na sua base de destino e marcar os objetos como já aplicados. A partir daqui os próximos releases irão funcionar, visto que ele entenderá que o primeiro já foi aplicado.

B. Se estiver tentando aplicar tudo isso no mesmo PDB, porém em schemas diferentes:

Faça com que o ORDS e APEX sejam um processo a parte, porque ele pode acabar tentando substituir o que já existe. Para isso em .dbtools/filters marque para não exportar APEX nem ORDS, você pode fazer isso depois de forma separada.

OBSERVAÇÕES FINAIS:

Algumas observações pessoais sobre este processo:

  • Cada artefato de release novo carrega todos os releases antigos junto com ele. Ele sempre vai se comportar assim, porém, na hora de aplicar, para tudo funcionar você não deve mais por a mão na base de destino, principalmente no que diz respeito a criação de objetos e alterações de tabelas. Todo o processo deve ser feito somente através da ferramenta.

  • No windows, percebi que as permissões ficam bem vinculadas com o usuário, então se você tem um servidor TS e mais de uma pessoa vai mexer, recomendo criar um usuário devops.

  • As vezes a tela congela e você precisa apenas apertar ENTER para continuar. Em alguns casos a tela congela e não tem enter que resolva, só esperar. É normal.

  • Recomendo gerar os releases com certa frequência, não deixar acumular muitas mudanças para gerar o release. Digo isso, pois facilita bastante na hora de investigar o artefato em busca de erros.

  • Teste por sua conta e risco. Recomendo fazer backups do banco de dados antes de fazer os deploys.

  • Se tiver interesse em ver o tutorial adaptado para linux, basta comentar aqui, porque já comecei a trabalhar nisso.

Bugs que eu percebi em algumas versões do SQLcl

25.2.1 - Percebo que a versão ainda tem problemas para lidar com chaves primários tipo IDENTIFIED BY. Precisei trocar várias tabelas do banco de dados para o modelo normal (sequence + trigger) para não apresentar erro. Ainda tenho problema para o liquibase reconhecer objetos que foram dropados ou renomeados. Também pode apresentar problemas para conectar via TNSNAMES.

24.1 - As sequences já existentes são geradas toda vez e isso traz conflito.

Referências:

https://rafal.hashnode.dev/part-51-oracle-sqlcl-project-the-only-cicd-tool-for-apex-you-will-ever-need

https://pretius.com/blog/oracle-sqlcl-project-command

Top comments (0)