Menggunakan Proprietary Jar dengan Gitlab CI dan Heroku
Pada artikel sebelumnya, kita sudah mengotomasi proses build dengan Gitlab CI dan deployment ke Heroku. Proses tersebut bisa dilakukan dengan lancar apabila semua library/dependensi yang kita gunakan dalam aplikasi kita adalah open source. Dependensi open source tersedia di repository Maven Central, sehingga dimanapun kita jalankan perintah build, maka Maven akan mengunduh dependensi yang dibutuhkan langsung dari internet.
Akan menjadi persoalan kalau project kita menggunakan library yang tidak open source atau proprietary. Contoh paling umum adalah database Oracle. Agar aplikasi kita bisa terhubung ke database Oracle, kita harus menggunakan JDBC Driver dari Oracle yang tidak open source, sehingga tidak tersedia di Maven Central.
Untuk mengatasi hal ini, kita perlu menyesuaikan proses build aplikasi kita agar tetap bisa berjalan otomatis dari commit hingga deployment.
Membuat Repo Maven Lokal
Agar project kita terpenuhi dependensinya, kita perlu membuat repository lokal di laptop/PC dan kemudian menginstal dependensi tersebut di repo lokal. Tentunya sebelumnya kita perlu mengunduh dulu JDBC Driver Oracle di websitenya. Kita asumsikan saja setelah diunduh, file ojdbc8.jar
tersebut kita letakkan di folder /tmp/
.
Selanjutnya, kita akan membuat repository Maven lokal di dalam folder project. Biasanya Maven sudah memiliki repo lokal yang berlokasi di HOME/.m2/repository
. Kita akan memasukkan file ojdbc8.jar
tersebut sesuai dengan struktur folder dan aturan penamaan file Maven. Berikut perintah untuk instalasinya
mvn install:install-file -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -Dfile=/tmp/ojdbc8.jar
Setelah file tersebut terinstal (bisa dipastikan dengan cara melihat ke folder HOME/.m2/repository/com/oracle/jdbc
) kita bisa menggunakannya di project dengan mendeklarasikan dependensi seperti ini
<dependency>
<groupid>com.oracle.jdbc</groupid>
<artifactid>ojdbc8</artifactid>
<version>12.2.0.1</version>
</dependency>
Kita bisa test build dan jalankan project kita seperti biasa, misalnya dengan perintah mvn clean spring-boot:run
untuk memastikan aplikasi kita bisa dijalankan dengan baik.
Bila sudah berjalan lancar, kita akan melakukan tindakan lebih lanjut supaya project kita bisa dibuild juga secara otomatis oleh Gitlab CI. Kira-kira seperti ini diagramnya:
Setup Gitlab CI
Agar skema di atas bisa berjalan dengan baik, kita perlu server yang bisa diakses dari internet, misalnya kita beri nama file.server.saya.com
. Kita upload file ojdbc8.jar
tersebut dari laptop ke server tersebut
scp /tmp/ojdbc8.jar root@file.server.saya.com:/var/lib/
File ini nantinya akan diunduh oleh proses build Gitlab CI dengan perintah sebagai berikut
scp root@file.server.saya.com:/var/lib/ojdbc8.jar /tmp/
Untuk selanjutnya diinstal di repo lokal Maven dengan perintah yang sama
mvn install:install-file -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -Dfile=/tmp/ojdbc8.jar
Agar perintah scp
dari Gitlab CI ke file.server.saya.com
berjalan dengan mulus, perlu dilakukan persiapan:
- membuat pasangan private-public key untuk SSH
- memasang isi private key menjadi secret variabel di Gitlab CI
- mendaftarkan public key ke
file.server.saya.com
agar bisa login SSH tanpa password - mendaftarkan digital signature hostname
file.server.saya.com
di Gitlab CI agar tidak diminta konfirmasi host karena baru pertama akses SSH
Rangkaian persiapan ini telah dijelaskan di artikel terdahulu. Silakan dibaca kembali bila lupa.
Berikut potongan file konfigurasi .gitlab-ci.yml
yang berkaitan dengan kegiatan unduh dan setup repo lokal maven di Gitlab CI.
image: maven:3-jdk-8
stages:
- build
- deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && ssh-keyscan -H "$SSH_HOSTNAME" > ~/.ssh/known_hosts'
- scp root@$SSH_HOSTNAME:/var/lib/ojdbc8.jar /tmp/
build:
stage: build
script:
- mvn install:install-file -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -Dfile=/tmp/ojdbc8.jar
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
Jangan lupa daftarkan variabel SSH_HOSTNAME
dengan isi file.server.saya.com
.
Deployment ke Heroku
Pada artikel terdahulu memang kita telah membahas tentang deployment ke Heroku. Akan tetapi pada artikel tersebut, kita menggunakan metode source deployment, yaitu mengunggah source code ke Heroku untuk kemudian menyuruh Heroku melakukan build sekali lagi.
Metode ini tidak bisa kita lakukan saat ini, karena nantinya akan ribet lagi untuk menyuruh Heroku mengunduh ojdbc8.jar
dari file.server.saya.com
. Oleh karena itu, kita ingin langsung saja mengunggah hasil build dari langkah sebelumnya. Hasil build dari proses sebelumnya bisa digunakan oleh proses berikut dengan konfigurasi artifact yang kita pasang pada job build
artifacts:
paths:
- target/*.jar
Agar bisa mendeploy jar
ke Heroku, kita membutuhkan Heroku CLI. Cara instalasinya bisa dilihat di websitenya, pilih yang versi Debian/Ubuntu. Dengan demikian, kita juga harus menggunakan image ubuntu:latest
agar langkah-langkah tersebut bisa dijalankan.
Selanjutnya, kita hanya perlu menjalankan perintah untuk instalasi Heroku CLI, kemudian menjalankan perintah deploy. Kita membutuhkan api key
Heroku yang terpasang sebagai environment variable dan nama aplikasi di Heroku. Berikut konfigurasi scriptnya
deploy-heroku:
stage: deploy
image: ubuntu:latest
variables:
HEROKU_API_KEY: $HEROKU_API_KEY
script:
- apt install wget openjdk-8-jdk-headless -y
- wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh
- heroku plugins:install heroku-cli-deploy
- heroku deploy:jar target/*.jar --app $HEROKU_APP_NAME
Jangan lupa, kita juga harus menginstal openjdk-8
supaya bisa mendeploy jar. Bila tidak terinstal, maka akan muncul pesan error seperti ini
heroku deploy:jar target/*.jar --app $HEROKU_APP_NAME
Uploading payment-virtualaccount-1.0.1-M.001.jar
▸ 'ENOENT': spawn java ENOENT
ERROR: Job failed: exit code 1
Kesimpulan
Sebisa mungkin, selalu gunakan library open source. Ini akan sangat memudahkan kita dalam proses development dan otomasi workflow. Akan tetapi adakalanya kita tidak bisa menghindari penggunaan library proprietary. Jadi apa boleh buat terpaksa harus dilakukan akal-akalan. Walaupun demikian, tetap harus legal.
Sebetulnya bisa saja tadi kita masukkan file ojdbc8.jar
ke dalam struktur folder project, misalnya di folder .mvn/repository
sejajar dengan src
dan pom.xml
. Perintahnya sebagai berikut
mvn deploy:deploy-file -Durl=file://$(pwd)/.mvn/repository -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -Dfile=/tmp/ojdbc8.jar
Kemudian kita daftarkan folder tersebut sebagai repo lokal di pom.xml
dengan konfigurasi berikut
<repositories>
<repository>
<id>project.local</id>
<name>project</name>
<url>file:${project.basedir}/.mvn/repository</url>
</repository>
</repositories>
Kemudian tinggal kita commit saja folder .mvn/repository
ke Git repo, sehingga proses build berjalan dengan lancar di Gitlab CI maupun Heroku. Teknik ini diajarkan oleh Heroku sendiri di artikel ini.
Walaupun demikian, kita tidak bisa lakukan teknik ini untuk project open source, karena kita tidak diijinkan Oracle untuk mendistribusikan file ojdbc8.jar
tersebut. Bila kita buat repo lokal berisi file tersebut dan kita unggah ke Git repo terbuka, maka kita bisa dimarahi oleh pengacaranya Oracle ;)
Demikian penjelasan tentang penggunaan library proprietary dengan Gitlab CI. Berikut isi file lengkapnya untuk referensi
.gitlab-ci.yml
image: maven:3-jdk-8
stages:
- build
- deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && ssh-keyscan -H "$SSH_HOSTNAME" > ~/.ssh/known_hosts'
- scp root@$SSH_HOSTNAME:/var/lib/ojdbc8.jar /tmp/
build:
stage: build
script:
- mvn install:install-file -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=12.2.0.1 -Dpackaging=jar -Dfile=/tmp/ojdbc8.jar
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
deploy-dev:
stage: deploy
image: ubuntu:latest
variables:
HEROKU_API_KEY: $HEROKU_API_KEY
script:
- apt install wget openjdk-8-jdk-headless -y
- wget -qO- https://cli-assets.heroku.com/install-ubuntu.sh | sh
- heroku plugins:install heroku-cli-deploy
- heroku deploy:jar target/*.jar --app $HEROKU_APP_NAME
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>id.artivisi.belajar</groupId>
<artifactId>belajar-private-jar</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>belajar-private-jar</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cloud-connectors</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupid>com.oracle.jdbc</groupid>
<artifactid>ojdbc8</artifactid>
<version>12.2.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<configuration>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>
Semoga bermanfaat …